【问题标题】:Basics of GetTokenInformationGetTokenInformation 基础知识
【发布时间】:2014-02-14 20:02:54
【问题描述】:

我一直试图让这个电话合作,但没有成功。

我正在尝试获取当前用户的 SID 值以获取用户的帐户权限(使用 LsaEnumerateAccountRights)。虽然我不知道为什么我对 GetTokenInformation 的调用返回错误。检索进程令牌没有错误。

这是我迄今为止在这个主题上的工作:

    HANDLE h_Process;
HANDLE h_Token;
HANDLE h_retToken;

TOKEN_USER tp;
DWORD cb = sizeof(TOKEN_USER);
PDWORD ret;

DWORD dw_TokenLength;

h_Process = GetCurrentProcess();

if (OpenProcessToken(h_Process, TOKEN_READ, &h_Token) == FALSE)
{
    printf("Error: Couldn't open the process token\n");
    return -1;
}

if (GetTokenInformation(h_Token, TokenUser, &tp, cb, &dw_TokenLength) == FALSE)
{
    printf("Error: Could not retrieve Token User information");
    return -1;
}

除此之外,我还不如问一个我还没有遇到过的后续问题,如何从形成的 TOKEN_USER 结构中检索 SID?

对于这么简单的问题,我提前道歉,我很困惑,希望继续得到一些帮助。与此相关的所有问题都要复杂得多,并且对我当前的问题几乎没有什么了解。

提前致谢, 乔恩

【问题讨论】:

    标签: c++ token rights


    【解决方案1】:

    根据documentation For GetTokenInformation,如果函数失败,您可以通过调用GetLastError来检索更多信息。

    返回值

    如果函数成功,则返回值非零。

    如果函数失败,返回值为零。要获取更多错误信息,请致电GetLastError

    所以你需要对扩展错误进行一些检查:

    if (!GetTokenInformation(h_Token, TokenUser, &tp, cb, &dw_TokenLength))
    {
        int lastError = GetLastError();
    
        // Should be a switch, of course. Omitted for brevity
        if (lastError == ERROR_INSUFFICIENT_BUFFER) 
        {
            //
        }
    }  
    

    作为使用具有不同缓冲区要求的 WinAPI 函数时的一般经验法则,您通常

    • 使用 NULL 缓冲区调用函数以确定所需的缓冲区大小(在这种情况下,在 ReturnLength 参数中返回)
    • 分配指定大小的缓冲区
    • 再次调用函数,传递分配的缓冲区,获取信息

    【讨论】:

    • 好吧,很好的调用缓冲区不足。为什么会出现这个错误?我遇到了这个问题,注意到显然需要两次调用该方法(link),我不明白需要两次调用。还有为什么 DWORD cb = sizeof(TOKEN_USER) 没有提供足够的内存分配/缓冲?
    • 缓冲区不足导致报错。 :-) 内容不够大。再次,我请您参考文档。如果函数失败,ReturnLength 参数设置为所需的缓冲区大小,这样您就可以分配足够的内存并再次调用该函数。许多具有可变缓冲区要求的 API 调用的一般规则是“使用 NULL 缓冲区调用一次函数以确定所需的缓冲区大小,分配内存,然后再次调用它以实际检索信息”。
    • 我将尝试回答您的实际问题。 Windows 经常这样做“首先调用以获取大小”的原因是结构版本更改。当您构建时,编译器知道该结构(编译器)发布时的内容。当您在该编译器之前或之后构建的操作系统上运行应用程序时,该结构可能已被 MS 更改并且具有或多或少的字段。这只能在运行时知道,所以这就是为什么他们会返回该代码并让您再试一次。你不会知道额外的字节是什么,所以你通常会忽略它们。
    • 或者在这种情况下根本不是这样!结果是实际的 SID 字节也写入了这个缓冲区(这解释了大小的可变性),并且 SID_AND_ATTRIBUTES 中的 PSID 只是指向这个区域。在我的情况下,PSID 指向返回的缓冲区 +0x10,它是 sizeof(TOKEN_USER) 并表明该结构根本没有改变大小,但该函数将 SID 数据附加到返回的缓冲区。
    • @user2957811:你知道我不是问这个问题的人,你说的话不会被问的人看到吗?
    【解决方案2】:

    首先要了解的是,导致系统调用的 Win32 UM(用户模式)API 通常需要您预先提供缓冲区。这与内核可以访问 UM 堆分配而 UM 不能访问 KM 分配的事实有关。

    这些调用通常遵循一个约定,您调用一次以获得所需的缓冲区大小,然后使用足够大的已分配缓冲区再次调用。如果您可以预先创建一个合理大小的缓冲区,那就更好了。系统调用可能会因为它导致的上下文切换而代价高昂,因此如果它是热路径,那么从 2 次调用到 1 次调用可能会大大提高性能。

    这是您需要的样本。这有一个将永远尝试的循环,但也很常见只尝试两次。如果需要的缓冲区

    DWORD bytesReturned = 128;
    LPVOID tokenUser = nullptr;
    auto cleanup = ScopeExit([&]() 
    {
        LocalFree(tokenUser);
    });
    for (;;) {
        tokenUser = LocalAlloc(LMEM_FIXED, bytesReturned);
        THROW_HR_IF_NULL(E_OUTOFMEMORY, tokenUser);
        if (!GetTokenInformation(token.get(), TokenUser, &tokenUser, bytesReturned, &bytesReturned))
        {
            if (ERROR_INSUFFICIENT_BUFFER == GetLastError())
            {
                LocalFree(tokenUser);
                tokenUser = nullptr;
                continue;
            }
            THROW_HR(HRESULT_FROM_WIN32(GetLastError()));
        }
        break;
    }
    

    您的代码的另一个大问题是您传递了对 TOKEN_USER tp 的引用。 The API actually just takes a PVOID。 SID 的缓冲区将只是在 tokenUser 的缓冲区中。您需要将其转换为 TOKEN_USER* 才能正确访问内存。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-04-16
      • 2018-05-25
      • 2014-11-28
      • 2023-04-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多