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

如何通过进程ID可靠获取进程所有者的用户及组信息?

可靠获取进程所有者用户及组信息的方案(VS2015 v140)

一、推荐:Win32 API实现(稳定高效,适合拦截场景)

核心思路

通过进程ID打开进程句柄,获取进程令牌,分别提取用户SID和组SID,再转换为可读的用户名/组名。该方案避免了WMI可能带来的稳定性问题,更适合文件操作拦截这类对可靠性要求高的场景。

关键代码实现

#include <windows.h>
#include <sddl.h>
#include <stdio.h>
#include <vector>

#pragma comment(lib, "advapi32.lib")

// 获取进程所有者用户名和域名
BOOL GetProcessOwner(DWORD dwPID, wchar_t* szUserName, DWORD dwUserNameLen, wchar_t* szDomainName, DWORD dwDomainNameLen)
{
    BOOL bResult = FALSE;
    HANDLE hProcess = NULL;
    HANDLE hToken = NULL;
    PTOKEN_USER pTokenUser = NULL;
    DWORD dwSize = 0;

    // 打开进程(优先使用PROCESS_QUERY_LIMITED_INFORMATION,权限更低更容易成功)
    hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, dwPID);
    if (hProcess == NULL) goto Cleanup;

    // 打开进程令牌
    if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken)) goto Cleanup;

    // 获取TokenUser所需的内存大小
    if (!GetTokenInformation(hToken, TokenUser, NULL, 0, &dwSize))
    {
        if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) goto Cleanup;
        pTokenUser = (PTOKEN_USER)LocalAlloc(LPTR, dwSize);
        if (pTokenUser == NULL) goto Cleanup;

        // 获取TokenUser信息
        if (GetTokenInformation(hToken, TokenUser, pTokenUser, dwSize, &dwSize))
        {
            // 转换SID为用户名和域名
            SID_NAME_USE sidType;
            if (LookupAccountSidW(NULL, pTokenUser->User.Sid, szUserName, &dwUserNameLen, szDomainName, &dwDomainNameLen, &sidType))
            {
                bResult = TRUE;
            }
        }
    }

Cleanup:
    if (pTokenUser) LocalFree(pTokenUser);
    if (hToken) CloseHandle(hToken);
    if (hProcess) CloseHandle(hProcess);
    return bResult;
}

// 获取进程所属的组列表
BOOL GetProcessGroups(DWORD dwPID, std::vector<std::pair<std::wstring, std::wstring>>& groups)
{
    BOOL bResult = FALSE;
    HANDLE hProcess = NULL;
    HANDLE hToken = NULL;
    PTOKEN_GROUPS pTokenGroups = NULL;
    DWORD dwSize = 0;

    hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, dwPID);
    if (hProcess == NULL) goto Cleanup;

    if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken)) goto Cleanup;

    if (!GetTokenInformation(hToken, TokenGroups, NULL, 0, &dwSize))
    {
        if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) goto Cleanup;
        pTokenGroups = (PTOKEN_GROUPS)LocalAlloc(LPTR, dwSize);
        if (pTokenGroups == NULL) goto Cleanup;

        if (GetTokenInformation(hToken, TokenGroups, pTokenGroups, dwSize, &dwSize))
        {
            for (DWORD i = 0; i < pTokenGroups->GroupCount; i++)
            {
                wchar_t szGroupName[256] = {0};
                wchar_t szDomainName[256] = {0};
                DWORD dwNameLen = 256;
                DWORD dwDomainLen = 256;
                SID_NAME_USE sidType;

                if (LookupAccountSidW(NULL, pTokenGroups->Groups[i].Sid, szGroupName, &dwNameLen, szDomainName, &dwDomainLen, &sidType))
                {
                    groups.emplace_back(szDomainName, szGroupName);
                }
            }
            bResult = TRUE;
        }
    }

Cleanup:
    if (pTokenGroups) LocalFree(pTokenGroups);
    if (hToken) CloseHandle(hToken);
    if (hProcess) CloseHandle(hProcess);
    return bResult;
}

// 使用示例
int main()
{
    DWORD dwPID = 1234; // 替换为目标进程ID
    wchar_t szUserName[256] = {0};
    wchar_t szDomainName[256] = {0};
    DWORD dwUserNameLen = 256;
    DWORD dwDomainNameLen = 256;

    if (GetProcessOwner(dwPID, szUserName, dwUserNameLen, szDomainName, dwDomainNameLen))
    {
        wprintf(L"进程所有者:%s\\%s\n", szDomainName, szUserName);
    }

    std::vector<std::pair<std::wstring, std::wstring>> groups;
    if (GetProcessGroups(dwPID, groups))
    {
        wprintf(L"所属组:\n");
        for (auto& group : groups)
        {
            wprintf(L"  %s\\%s\n", group.first.c_str(), group.second.c_str());
        }
    }

    return 0;
}

对之前错误的说明

你之前使用TokenOwner参数是错误的:TokenOwner表示的是令牌的安全所有者SID,而非进程运行的用户SID。正确应该使用TokenUser来获取进程实际运行的用户信息,这就能解决返回组而非用户的问题。

二、修复后的WMI实现(仅作备选,不推荐拦截场景)

如果必须使用WMI,需修复以下问题:

  1. GetOwner是Win32_Process的方法,不是属性,不能用Get()获取,必须用InvokeMethod()调用
  2. 严格管理COM对象的生命周期,使用后必须释放
  3. 多线程环境下确保COM初始化正确

修复后的关键代码

#include <windows.h>
#include <wbemidl.h>
#include <comdef.h>
#include <stdio.h>

#pragma comment(lib, "wbemuuid.lib")

BOOL GetProcessOwnerViaWMI(DWORD dwPID, wchar_t* szUserName, DWORD dwUserNameLen, wchar_t* szDomainName, DWORD dwDomainNameLen)
{
    BOOL bResult = FALSE;
    HRESULT hres;
    IWbemLocator* pLoc = NULL;
    IWbemServices* pSvc = NULL;
    IEnumWbemClassObject* pEnumerator = NULL;
    IWbemClassObject* pclsObj = NULL;
    VARIANT vtOwner[2]; // 0=用户名,1=域名
    ULONG uReturn = 0;

    // 初始化COM
    hres = CoInitializeEx(0, COINIT_MULTITHREADED);
    if (FAILED(hres)) goto Cleanup;

    hres = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);
    if (FAILED(hres)) goto Cleanup;

    // 创建WMI定位器
    hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*)&pLoc);
    if (FAILED(hres)) goto Cleanup;

    // 连接到WMI服务
    hres = pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), NULL, NULL, 0, NULL, 0, 0, &pSvc);
    if (FAILED(hres)) goto Cleanup;

    // 设置安全级别
    hres = CoSetProxyBlanket(pSvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);
    if (FAILED(hres)) goto Cleanup;

    // 构造查询语句
    wchar_t szQuery[256];
    swprintf_s(szQuery, L"SELECT * FROM Win32_Process WHERE ProcessId = %lu", dwPID);
    hres = pSvc->ExecQuery(_bstr_t(L"WQL"), _bstr_t(szQuery), WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator);
    if (FAILED(hres)) goto Cleanup;

    // 获取进程对象
    hres = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
    if (SUCCEEDED(hres) && uReturn == 1)
    {
        // 初始化变体
        VariantInit(&vtOwner[0]);
        VariantInit(&vtOwner[1]);

        // 调用GetOwner方法
        hres = pclsObj->InvokeMethod(_bstr_t(L"GetOwner"), 0, NULL, vtOwner, NULL);
        if (SUCCEEDED(hres) && vtOwner[0].vt == VT_BSTR && vtOwner[1].vt == VT_BSTR)
        {
            wcsncpy_s(szUserName, dwUserNameLen, vtOwner[0].bstrVal, _TRUNCATE);
            wcsncpy_s(szDomainName, dwDomainNameLen, vtOwner[1].bstrVal, _TRUNCATE);
            bResult = TRUE;
        }

        // 清理变体
        VariantClear(&vtOwner[0]);
        VariantClear(&vtOwner[1]);
    }

Cleanup:
    // 释放所有COM对象
    if (pclsObj) pclsObj->Release();
    if (pEnumerator) pEnumerator->Release();
    if (pSvc) pSvc->Release();
    if (pLoc) pLoc->Release();
    CoUninitialize();
    return bResult;
}

注意事项

WMI在高并发或拦截场景下可能存在性能问题或稳定性风险,因此优先推荐Win32 API方案。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 02:24:57