如何通过进程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,需修复以下问题:
GetOwner是Win32_Process的方法,不是属性,不能用Get()获取,必须用InvokeMethod()调用- 严格管理COM对象的生命周期,使用后必须释放
- 多线程环境下确保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
相关产品推荐
相关产品推荐

