You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

域管理员权限下获取登录用户用户名及OpenProcessToken错误6解决

问题描述

我们需要运行一个获取登录用户用户名的程序,该程序通过WinRM推送并以管理员权限运行。普通用户身份运行时可正常返回用户名,但以管理员身份运行时触发OpenProcessToken错误6(无效句柄),现寻求有效解决方法。

现有代码
#include <windows.h>
#include <stdio.h>
#include <tlhelp32.h>
#pragma comment(lib, "advapi32.lib")
#define MAX_NAME 256

BOOL SearchTokenGroupsForSID (VOID) 
{
    DWORD i, dwSize = 0, dwResult = 0;
    HANDLE hToken;
    PTOKEN_USER pUserInfo;
    SID_NAME_USE SidType;
    char lpName[MAX_NAME];
    char lpDomain[MAX_NAME];
    PSID pSID = NULL;
    char *procname = "explorer.exe";
    int pid = 0;

    PROCESSENTRY32 entry;
    entry.dwSize = sizeof(PROCESSENTRY32);

    HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

    if (Process32First(snapshot, &entry) == TRUE)
    {
        while (Process32Next(snapshot, &entry) == TRUE)
        {
            if (stricmp(entry.szExeFile, procname) == 0)
            {  
                HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, entry.th32ProcessID);
                pid = entry.th32ProcessID;
                if (!OpenProcessToken( hProcess, TOKEN_QUERY, &hToken )) 
                {
                    printf( "OpenProcessToken Error %u\n", GetLastError() );
                    return FALSE;
                }
                
                CloseHandle(hProcess);
            }
        }
    }            
    printf("pid of %s: %d\n", procname, pid);

    if(!GetTokenInformation(hToken, TokenUser, NULL, dwSize, &dwSize)) 
    {
        dwResult = GetLastError();
        if( dwResult != ERROR_INSUFFICIENT_BUFFER ) {
            printf( "GetTokenInformation Error %u\n", dwResult );
            return FALSE;
        }
    }


    pUserInfo = (PTOKEN_USER) GlobalAlloc( GPTR, dwSize );


    if(! GetTokenInformation(hToken, TokenUser, pUserInfo, 
                            dwSize, &dwSize ) ) 
    {
        printf( "GetTokenInformation Error %u\n", GetLastError() );
        return FALSE;
    }

            dwSize = MAX_NAME;
            if( !LookupAccountSid( NULL, pUserInfo->User.Sid,
                                  lpName, &dwSize, lpDomain, 
                                  &dwSize, &SidType ) ) 
            {
                dwResult = GetLastError();
                if( dwResult == ERROR_NONE_MAPPED )
                   strcpy_s (lpName, dwSize, "NONE_MAPPED" );
                else 
                {
                    printf("LookupAccountSid Error %u\n", GetLastError());
                    return FALSE;
                }
            }
            printf( "Current user is a member of the %s\\%s group\n", 
                    lpDomain, lpName );


    if (pSID)
        FreeSid(pSID);
    if ( pUserInfo )
        GlobalFree( pUserInfo );
    return TRUE;
}

int main(void)
{
    SearchTokenGroupsForSID();
    return 0;
}
错误原因与解决方案

错误根源

错误6对应ERROR_INVALID_HANDLE,核心问题有两点:

  • 权限请求过度:代码使用PROCESS_ALL_ACCESS打开explorer.exe,该权限包含大量不必要的操作权限,即使是管理员身份,也可能因UAC隔离或跨会话限制导致进程打开失败,返回无效句柄,后续OpenProcessToken调用自然报错。
  • 未校验句柄有效性:代码未检查OpenProcess的返回值,若进程打开失败,直接用无效句柄调用OpenProcessToken必然触发错误。

修复方案

方案1:优化原进程遍历逻辑

修改原代码,降低权限请求并添加有效性校验:

#include <windows.h>
#include <stdio.h>
#include <tlhelp32.h>
#pragma comment(lib, "advapi32.lib")
#define MAX_NAME 256

BOOL SearchTokenGroupsForSID (VOID) 
{
    DWORD dwSize = 0, dwResult = 0;
    HANDLE hToken = NULL;
    PTOKEN_USER pUserInfo = NULL;
    SID_NAME_USE SidType;
    char lpName[MAX_NAME];
    char lpDomain[MAX_NAME];
    char *procname = "explorer.exe";
    DWORD pid = 0;

    PROCESSENTRY32 entry;
    entry.dwSize = sizeof(PROCESSENTRY32);

    HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (snapshot == INVALID_HANDLE_VALUE)
    {
        printf("CreateToolhelp32Snapshot Error %u\n", GetLastError());
        return FALSE;
    }

    if (Process32First(snapshot, &entry) == TRUE)
    {
        while (Process32Next(snapshot, &entry) == TRUE)
        {
            if (stricmp(entry.szExeFile, procname) == 0)
            {  
                // 仅请求获取令牌所需的最小权限
                HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, entry.th32ProcessID);
                if (hProcess == NULL)
                {
                    printf("OpenProcess Error %u\n", GetLastError());
                    continue;
                }
                
                pid = entry.th32ProcessID;
                if (OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
                {
                    CloseHandle(hProcess);
                    break; // 找到有效令牌后退出循环
                }
                else
                {
                    printf("OpenProcessToken Error %u\n", GetLastError());
                    CloseHandle(hProcess);
                }
            }
        }
    }            
    CloseHandle(snapshot);
    printf("pid of %s: %lu\n", procname, pid);

    if (hToken == NULL)
    {
        printf("Failed to get valid process token\n");
        return FALSE;
    }

    if(!GetTokenInformation(hToken, TokenUser, NULL, dwSize, &dwSize)) 
    {
        dwResult = GetLastError();
        if( dwResult != ERROR_INSUFFICIENT_BUFFER ) {
            printf( "GetTokenInformation Error %u\n", dwResult );
            CloseHandle(hToken);
            return FALSE;
        }
    }

    pUserInfo = (PTOKEN_USER) GlobalAlloc(GPTR, dwSize);
    if (pUserInfo == NULL)
    {
        printf("GlobalAlloc failed\n");
        CloseHandle(hToken);
        return FALSE;
    }

    if(!GetTokenInformation(hToken, TokenUser, pUserInfo, dwSize, &dwSize)) 
    {
        printf( "GetTokenInformation Error %u\n", GetLastError() );
        GlobalFree(pUserInfo);
        CloseHandle(hToken);
        return FALSE;
    }

    dwSize = MAX_NAME;
    DWORD dwDomainSize = MAX_NAME;
    if( !LookupAccountSid(NULL, pUserInfo->User.Sid, lpName, &dwSize, lpDomain, &dwDomainSize, &SidType) ) 
    {
        dwResult = GetLastError();
        if( dwResult == ERROR_NONE_MAPPED )
            strcpy_s(lpName, MAX_NAME, "NONE_MAPPED");
        else 
        {
            printf("LookupAccountSid Error %u\n", GetLastError());
            GlobalFree(pUserInfo);
            CloseHandle(hToken);
            return FALSE;
        }
    }
    printf( "登录用户:%s\\%s\n", lpDomain, lpName );

    GlobalFree(pUserInfo);
    CloseHandle(hToken);
    return TRUE;
}

int main(void)
{
    SearchTokenGroupsForSID();
    return 0;
}

方案2:使用WTS API直接获取活跃会话用户(推荐)

通过WTS接口跳过进程遍历,直接获取当前活跃登录用户的令牌,避免跨会话权限问题:

#include <windows.h>
#include <stdio.h>
#include <wtsapi32.h>
#pragma comment(lib, "advapi32.lib")
#pragma comment(lib, "wtsapi32.lib")
#define MAX_NAME 256

BOOL GetInteractiveUserInfo(char* lpDomain, DWORD dwDomainSize, char* lpName, DWORD dwNameSize)
{
    DWORD dwSessionCount = 0;
    PWTS_SESSION_INFO pSessionInfo = NULL;

    // 枚举所有会话
    if (!WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pSessionInfo, &dwSessionCount))
    {
        printf("WTSEnumerateSessions Error %u\n", GetLastError());
        return FALSE;
    }

    BOOL bSuccess = FALSE;
    for (DWORD i = 0; i < dwSessionCount; i++)
    {
        // 找到活跃的用户会话
        if (pSessionInfo[i].State == WTSActive)
        {
            HANDLE hUserToken = NULL;
            if (WTSQueryUserToken(pSessionInfo[i].SessionId, &hUserToken))
            {
                DWORD dwTokenInfoSize = 0;
                GetTokenInformation(hUserToken, TokenUser, NULL, 0, &dwTokenInfoSize);
                PTOKEN_USER pTokenUser = (PTOKEN_USER)LocalAlloc(LPTR, dwTokenInfoSize);
                
                if (GetTokenInformation(hUserToken, TokenUser, pTokenUser, dwTokenInfoSize, &dwTokenInfoSize))
                {
                    SID_NAME_USE sidType;
                    if (LookupAccountSid(NULL, pTokenUser->User.Sid, lpName, &dwNameSize, lpDomain, &dwDomainSize, &sidType))
                    {
                        bSuccess = TRUE;
                    }
                    else
                    {
                        printf("LookupAccountSid Error %u\n", GetLastError());
                    }
                }
                else
                {
                    printf("GetTokenInformation Error %u\n", GetLastError());
                }

                LocalFree(pTokenUser);
                CloseHandle(hUserToken);
                break;
            }
            else
            {
                printf("WTSQueryUserToken Error %u\n", GetLastError());
            }
        }
    }

    LocalFree(pSessionInfo);
    return bSuccess;
}

int main(void)
{
    char lpDomain[MAX_NAME] = {0};
    char lpName[MAX_NAME] = {0};
    DWORD dwDomainSize = MAX_NAME;
    DWORD dwNameSize = MAX_NAME;

    if (GetInteractiveUserInfo(lpDomain, dwDomainSize, lpName, dwNameSize))
    {
        printf("登录用户:%s\\%s\n", lpDomain, lpName);
    }
    else
    {
        printf("获取登录用户信息失败\n");
    }

    return 0;
}

方案说明

  • 方案1优化了原逻辑,降低权限请求并添加校验,适用于依赖进程遍历的场景,但仍可能受跨会话限制影响。
  • 方案2通过WTS API直接获取活跃会话的用户令牌,管理员权限下可稳定获取登录用户信息,无需依赖explorer.exe进程,是更可靠的方案。

内容的提问来源于stack exchange,提问作者shawnixer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 08:37:05