【问题标题】:Access violation writing location 0x00000000 when using LookupAccountName使用 LookupAccountName 时访问冲突写入位置 0x00000000
【发布时间】:2018-01-03 20:56:27
【问题描述】:

NetUserAdd创建用户帐户后,发现需要使用NetLocalGroupAddMembers将用户添加到Users组,所以我调用CreateWellKnownSid获取Users的SID,LookupAccountSid获取来自该 SID 的字符串名称并将其传递给 NetLocalGroupAddMembers.

我还需要指定用户名,但该功能需要domain\name格式为level 3LOCALGROUP_MEMBERS_INFO_3),但我没有。我决定调用LookupAccountName 来获取用户名SID 并将其传递给level 0 (LOCALGROUP_MEMBERS_INFO_0)。

我就是这样做的:

//LocalAlloc
    UINT memAttributes = LMEM_FIXED;
    SIZE_T sidSize = SECURITY_MAX_SID_SIZE;

//LookupAccountName
    PSID accountSID;
    SID_NAME_USE typeOfAccount;

//NetLocalGroupAddMembers
    NET_API_STATUS localGroupAdd;
    DWORD levelOfData = 0;  //LOCALGROUP_MEMBERS_INFO_0
    LOCALGROUP_MEMBERS_INFO_0 localMembers;
    DWORD totalEntries = 0;



//Allocate memory for LookupAccountName
        if (!(accountSID = LocalAlloc(memAttributes, sidSize)))
        {
            wprintf(L"\nMemory allocation for account SID failed: \n");
            ShowError(GetLastError());
            exit(1);

        }


if (!LookupAccountNameW(NULL, argv[1], accountSID,
                                    (LPDWORD)&sidSize, NULL, 0, &typeOfAccount))
            {
                fwprintf(stderr, L"Error getting SID from name: \n");
                ShowError(GetLastError());
                return 1;

            }

//Here I should be able to use NetLocalGroupAddMembers
            //to add the user passed as argument to the Users group. 
            localMembers.lgrmi0_sid = accountSID;

            localGroupAdd = NetLocalGroupAddMembers(NULL, name, levelOfData, (LPBYTE)&localMembers, totalEntries);

            if (localGroupAdd != NERR_Success)
            {
                fwprintf(stderr, L"Error adding member to the local group: \n");
                ShowError(GetLastError());
                return 1;
            }
            else
            {
                wprintf(L"\nUser %s has been successfully added.\n", argv[1]);

            }

这是我得到的错误:

在 UserCreator.exe 中的 0x743F059A (sechost.dll) 处引发异常: 0xC0000005:访问冲突写入位置0x00000000。

有什么线索吗?

【问题讨论】:

  • 总是尝试删除像 (LPDWORD) 这样的强制转换,因为它们往往会隐藏错误。在这种情况下这不是问题,但在 64 位程序的其他情况下可能是问题。
  • 特别是sidSize的类型应该是DWORD,而不是SIZE_T(至少,如果你想将它也用于LookupAccountNameW)。

标签: c winapi


【解决方案1】:

ReferencedDomainName 参数实际上不是可选的。

LPCTSTR machine = NULL, username = /*TEXT("Anders")*/ argv[1];
TCHAR domain[MAX_PATH];
BYTE accountSIDbuf[SECURITY_MAX_SID_SIZE];
PSID accountSID = (PSID) accountSIDbuf;
DWORD cbSid = SECURITY_MAX_SID_SIZE, cchRD = MAX_PATH;
SID_NAME_USE snu;

if (!LookupAccountName(machine, username, accountSID, &cbSid, domain, &cchRD, &snu))
{
    printf("Error %u\n", GetLastError());
    return ;
}

LPTSTR sidstr;
if (!ConvertSidToStringSid(accountSID, &sidstr)) { return ; }
_tprintf(_T("SID of %s\\%s is %s\n"), domain, username, sidstr);
LocalFree(sidstr);

您的代码的另一个问题是ShowError(GetLastError()); 在调用其他函数后您不能使用GetLastError()。改写为

DWORD error = GetLastError();
fwprintf(stderr, L"Error getting SID from name: \n");
ShowError(error);

但在这种情况下即使这样也是错误的,因为NetLocalGroupAddMembers 没有调用SetLastError,它只是直接返回错误代码。

编辑:

只是为了澄清参数的用法;如果要查询所需的域缓冲区大小,可以这样做:

DWORD cchRD = 0;
LookupAccountName(..., NULL, &cchRD, &snu); // The function is still going to report failure
LPTSTR domain = malloc(cchRD * sizeof(*domain));
LookupAccountName(..., domain, &cchRD, &snu);

在我的示例中,我通过传入一个“足够大”的缓冲区来避免这种情况。

【讨论】:

  • 我更改了第一个参数以添加机器变量,但仍然得到相同的错误。像这样:LPCWSTR machine = NULL; if (!LookupAccountNameW(machine, argv[1], accountSID, (LPDWORD)&sidSize, NULL, 0, &typeOfAccount)) { fwprintf(stderr, L"Error getting SID from name.\n"); return 1; }
  • 其实ReferencedDomainName参数可选的。不是 cchReferencedDomainName,这就是导致 AV 的原因,因为 API 正在尝试将所需的缓冲区大小写入该地址,如 documented
  • @IInspectable,是的,这就是它的记录方式,但我不确定它是否在每个系统上一直都是正确的,但我的记忆可能是错误的。即使它一直是可选的,回到 Win 95,如果字符串参数为 NULL,则该函数将“失败”,然后您必须重新分配并调用它。还不如一开始就提供一个大缓冲区。
  • 文档不一定适用于曾经发布的每个 Windows 版本。它仅引用受支持的 Windows 版本(即,截至今天的 Windows Vista 和更高版本)。这里的问题是,OP 未能提供指向 DWORD 变量的有效指针,接收所需的大小。将nullptr 作为ReferenceDomainName 传递是有效的,并且遵循API 中的通用模式。
  • @SergioCalderon 当 cchReferencedDomainName 为 0(即 cchReferencedDomainName=0 并使用 &cchReferencedDomainName 调用函数)时,您要求函数报告所需的大小。该函数将返回失败,您需要分配一个缓冲区并再次调用该函数。在我的示例中,我首先传递了一个足够大的缓冲区。要使函数成功,您必须为 ReferencedDomainName 参数传递一个有效的缓冲区。
猜你喜欢
  • 2013-04-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-20
相关资源
最近更新 更多