关于Windows中获取指定进程pid的几种方法
字数 1849 2025-08-10 16:34:28
Windows中获取指定进程PID的多种方法详解
1. 使用NtQuerySystemInformation函数
原理
NtQuerySystemInformation是Windows未公开的NT API函数,可以检索指定的系统信息。通过遍历系统中所有进程信息并与目标进程名匹配来获取PID。
实现代码
#include <Windows.h>
#include <winternl.h>
#include <iostream>
#include <string.h>
using namespace std;
typedef NTSTATUS(WINAPI* _NtQuerySystemInformation)(
_In_ SYSTEM_INFORMATION_CLASS SystemInformationClass,
_Inout_ PVOID SystemInformation,
_In_ ULONG SystemInformationLength,
_Out_opt_ PULONG ReturnLength
);
_NtQuerySystemInformation lNtQuerySystemInformation = (_NtQuerySystemInformation)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtQuerySystemInformation");
DWORD GetPid(LPCWSTR ProcessName)
{
char szInfo[0x70000];
ULONG uReturnedLEngth = 0;
NTSTATUS status = lNtQuerySystemInformation(SystemProcessInformation, szInfo, sizeof(szInfo), &uReturnedLEngth);
PSYSTEM_PROCESS_INFORMATION pinfo = (PSYSTEM_PROCESS_INFORMATION)szInfo;
while (true)
{
if (pinfo->NextEntryOffset == 0) {
break;
}
pinfo = (PSYSTEM_PROCESS_INFORMATION)((PCHAR)pinfo + pinfo->NextEntryOffset);
if(lstrcmpiW(pinfo->ImageName.Buffer, ProcessName) == 0) {
cout << "lsass pid is:" << (DWORD)pinfo->UniqueProcessId << endl;
break;
}
}
return (DWORD)pinfo->UniqueProcessId;
}
关键点
- 需要从ntdll.dll动态获取函数地址
- 使用
SystemProcessInformation参数获取进程信息 - 遍历进程列表并比较进程名
- 通过
UniqueProcessId获取PID
2. 使用WTSEnumerateProcessesW函数
原理
通过RPC服务获取进程列表,利用WTS_PROCESS_INFO结构中的ProcessId字段获取PID。
实现代码
#include <windows.h>
#include <wtsapi32.h>
#include <iostream>
#pragma comment(lib, "Wtsapi32.lib")
DWORD GetPid(LPCWSTR ProcessName) {
PWTS_PROCESS_INFOW wts;
DWORD Count;
DWORD i = 0;
BOOL result = WTSEnumerateProcessesW(WTS_CURRENT_SERVER_HANDLE, 0, 1, &wts, &Count);
if(result == 0){
cout << "WTSEnumerateProcessesW Error" << endl;
return 1;
}
for (i; i < Count; i++) {
if (lstrcmpiW(wts[i].pProcessName, ProcessName) == 0) {
DWORD pid = wts[i].ProcessId;
cout << "lsass pid is:" << pid << endl;
return pid;
}
}
WTSFreeMemory(wts);
}
关键点
- 需要链接Wtsapi32.lib库
- 使用
WTS_CURRENT_SERVER_HANDLE获取当前服务器句柄 - 遍历进程列表比较进程名
- 使用
WTSFreeMemory释放内存
3. 使用注册表查询(RegQueryValueExA)
原理
LSASS进程的PID存储在注册表HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa下的LsaPid键值中。
实现代码
#include <windows.h>
#include <malloc.h>
#include <iostream>
using namespace std;
int main(){
HKEY lhKey = HKEY_LOCAL_MACHINE;
LPCSTR pSubKey = "SYSTEM\\CurrentControlSet\\Control\\Lsa";
HKEY hkResult;
LSTATUS Open = RegOpenKeyExA(lhKey, pSubKey, 0, KEY_ALL_ACCESS, &hkResult);
if (Open != ERROR_SUCCESS) {
cout << "RegOpenKeyExA Error" << endl;
return 1;
}
DWORD BufferSize = TOTALBYTES;
LPCSTR pValueName = "LsaPid";
DWORD pType = REG_DWORD;
DWORD pData;
DWORD pcbData = sizeof(DWORD);
LSTATUS Value = RegQueryValueExA(hkResult, pValueName, NULL, &pType, (LPBYTE)&pData, &pcbData);
if (Value != ERROR_SUCCESS){
cout << "RegOpenKeyExA Error" << endl;
return 1;
}
cout << "lsass pid is:" << pData << endl;
RegCloseKey(hkResult);
}
关键点
- 需要管理员权限
- 查询
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa路径 - 读取
LsaPid键值 - 键值类型为
REG_DWORD
4. 使用QueryServiceStatusEx函数
原理
LSASS提供的服务包括CNG KeyIsolation (KeyIso)、Security Accounts Manager (SamSs)和Credential Manager (VaultSvc),通过查询这些服务的状态获取PID。
实现代码
#include <windows.h>
#include <iostream>
using namespace std;
int main() {
SERVICE_STATUS_PROCESS ProcessInfo;
DWORD bBufSize = sizeof(DWORD64);
DWORD lpcbBytesNeeded;
SC_HANDLE Handle = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (Handle == NULL) {
cout << "OpenSCManagerA Error" << endl;
return 1;
}
SC_HANDLE Handle2 = OpenServiceW(Handle, L"VaultSvc", SC_MANAGER_ALL_ACCESS);
if (Handle == NULL) {
cout << "OpenServiceA Error" << endl;
return 1;
}
BOOL Query_info = QueryServiceStatusEx(Handle2, SC_STATUS_PROCESS_INFO, (LPBYTE)&ProcessInfo, sizeof(SERVICE_STATUS_PROCESS), &lpcbBytesNeeded);
if (Query_info == 0) {
cout << "QueryServiceStatusEx Error" << endl;
return 1;
}
cout << "lsass pid is:" << (DWORD)ProcessInfo.dwProcessId << endl;
CloseServiceHandle(Handle2);
CloseServiceHandle(Handle);
}
关键点
- 需要打开服务控制管理器
- 可以查询VaultSvc、KeyIso或SamSs服务
- 使用
SC_STATUS_PROCESS_INFO参数获取进程信息 - 从
SERVICE_STATUS_PROCESS.dwProcessId获取PID
5. 使用NtFsControlFile函数
原理
通过连接LSASS的命名管道(\Device\NamedPipe\lsass),使用NtFsControlFile函数获取管道服务器的进程ID。
实现代码
#include "function.h"
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
int main() {
HANDLE lFileHandle;
OBJECT_ATTRIBUTES Object;
IO_STATUS_BLOCK Io;
UNICODE_STRING lNtFileName;
lRtlInitUnicodeString(&lNtFileName, L"\\Device\\NamedPipe\\lsass");
InitializeObjectAttributes(&Object, &lNtFileName, OBJ_CASE_INSENSITIVE, 0, NULL);
NTSTATUS Status = lNtOpenFile(&lFileHandle, FILE_READ_ATTRIBUTES, &Object, &Io, FILE_SHARE_READ, NULL);
if (!NT_SUCCESS(Status)) {
cout << "NtOpenFile Error" << endl;
return 1;
}
LPCSTR InputBuffer = "ServerProcessId";
DWORD OutputBuffer;
lNtFsControlFile(lFileHandle, NULL, NULL, NULL, &Io, FSCTL_PIPE_GET_PIPE_ATTRIBUTE, (PVOID)InputBuffer, strlen(InputBuffer)+1, &OutputBuffer, sizeof(OutputBuffer));
if (!NT_SUCCESS(Status)) {
cout << "NtFsControlFile Error" << endl;
return 1;
}
cout << "lsass pid is:" << OutputBuffer << endl;
}
关键点
- 需要连接到
\Device\NamedPipe\lsass管道 - 使用
FSCTL_PIPE_GET_PIPE_ATTRIBUTE控制码 - 传入"ServerProcessId"作为输入缓冲区
- 输出缓冲区接收PID
6. 通过安全事件日志获取
原理
Windows安全事件4608("Windows正在启动")记录了LSASS进程启动时的PID。
实现代码
#include <windows.h>
#include <winevt.h>
#include <iostream>
using namespace std;
#pragma comment(lib, "Wevtapi.lib")
DWORD IsAdmin() {
HANDLE TokenHandle;
BOOL OpenToken = OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &TokenHandle);
if (OpenToken == 0) {
cout << "OpenProcessToken Error" << endl;
return 1;
}
TOKEN_ELEVATION TokenInformation;
DWORD ReturnLength;
GetTokenInformation(TokenHandle, TokenElevation, &TokenInformation, sizeof(TokenInformation), &ReturnLength);
DWORD isadmin = TokenInformation.TokenIsElevated;
if (isadmin == 0) {
cout << "Please raise process permissions" << endl;
return 1;
}
return 0;
}
DWORD GetLsaPidFromEventLogs(void) {
if (IsAdmin() == 1) {
return 1;
}
DWORD dwProcessId = 0;
EVT_HANDLE hResults = EvtQuery(NULL, L"Security", L"*/*[EventID=4608]", EvtQueryTolerateQueryErrors);
if (hResults == NULL) {
cout << "EvtQuery Error" << endl;
return 1;
}
BOOL Result = EvtSeek(hResults, 0, NULL, 0, EvtSeekRelativeToLast);
if (Result != TRUE) {
cout << "EvtSeek Error" << endl;
return 1;
}
DWORD dwReturned = 0;
EVT_HANDLE hEvent;
BOOL Result1 = EvtNext(hResults, 1, &hEvent, INFINITE, 0, &dwReturned);
if (Result1 != TRUE) {
cout << "EvtNext Error" << endl;
return 1;
}
LPCWSTR ppValues[] = { L"Event/System/Execution/@ProcessID" };
EVT_HANDLE hContext = EvtCreateRenderContext(ARRAYSIZE(ppValues), ppValues, EvtRenderContextValues);
if (hContext == NULL) {
cout << "EvtCreateRenderContext Error" << endl;
return 1;
}
EVT_VARIANT pProcessId = { 0 };
BOOL Result2 = EvtRender(hContext, hEvent, EvtRenderEventValues, sizeof(EVT_VARIANT), &pProcessId, &dwReturned, NULL);
if (Result2 != TRUE) {
cout << "EvtRender Error" << endl;
return 1;
}
dwProcessId = pProcessId.UInt32Val;
cout << "lsass pid is:" << dwProcessId << endl;
EvtClose(hEvent);
EvtClose(hContext);
EvtClose(hResults);
return dwProcessId;
}
关键点
- 需要管理员权限
- 查询安全日志中事件ID为4608的记录
- 使用XPath路径
Event/System/Execution/@ProcessID提取PID - 需要正确释放所有事件句柄
总结对比
| 方法 | 所需权限 | 可靠性 | 适用性 | 备注 |
|---|---|---|---|---|
| NtQuerySystemInformation | 普通用户 | 高 | 所有进程 | 使用未公开API |
| WTSEnumerateProcessesW | 普通用户 | 高 | 所有进程 | 需要RPC服务 |
| 注册表查询 | 管理员 | 中 | 仅LSASS | 直接读取注册表 |
| QueryServiceStatusEx | 管理员 | 高 | LSASS相关服务 | 通过服务状态获取 |
| NtFsControlFile | 普通用户 | 中 | 有命名管道的进程 | 需要管道存在 |
| 安全事件日志 | 管理员 | 低 | 仅LSASS | 依赖日志记录 |
选择方法时应考虑所需权限、目标进程类型和系统环境等因素。对于常规进程枚举,推荐使用NtQuerySystemInformation或WTSEnumerateProcessesW;对于特定获取LSASS进程PID,注册表查询和查询服务状态是更直接的方法。