关于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 依赖日志记录

选择方法时应考虑所需权限、目标进程类型和系统环境等因素。对于常规进程枚举,推荐使用NtQuerySystemInformationWTSEnumerateProcessesW;对于特定获取LSASS进程PID,注册表查询和查询服务状态是更直接的方法。

Windows中获取指定进程PID的多种方法详解 1. 使用NtQuerySystemInformation函数 原理 NtQuerySystemInformation 是Windows未公开的NT API函数,可以检索指定的系统信息。通过遍历系统中所有进程信息并与目标进程名匹配来获取PID。 实现代码 关键点 需要从ntdll.dll动态获取函数地址 使用 SystemProcessInformation 参数获取进程信息 遍历进程列表并比较进程名 通过 UniqueProcessId 获取PID 2. 使用WTSEnumerateProcessesW函数 原理 通过RPC服务获取进程列表,利用 WTS_PROCESS_INFO 结构中的 ProcessId 字段获取PID。 实现代码 关键点 需要链接Wtsapi32.lib库 使用 WTS_CURRENT_SERVER_HANDLE 获取当前服务器句柄 遍历进程列表比较进程名 使用 WTSFreeMemory 释放内存 3. 使用注册表查询(RegQueryValueExA) 原理 LSASS进程的PID存储在注册表 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa 下的 LsaPid 键值中。 实现代码 关键点 需要管理员权限 查询 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa 路径 读取 LsaPid 键值 键值类型为 REG_DWORD 4. 使用QueryServiceStatusEx函数 原理 LSASS提供的服务包括CNG KeyIsolation (KeyIso)、Security Accounts Manager (SamSs)和Credential Manager (VaultSvc),通过查询这些服务的状态获取PID。 实现代码 关键点 需要打开服务控制管理器 可以查询VaultSvc、KeyIso或SamSs服务 使用 SC_STATUS_PROCESS_INFO 参数获取进程信息 从 SERVICE_STATUS_PROCESS.dwProcessId 获取PID 5. 使用NtFsControlFile函数 原理 通过连接LSASS的命名管道( \Device\NamedPipe\lsass ),使用 NtFsControlFile 函数获取管道服务器的进程ID。 实现代码 关键点 需要连接到 \Device\NamedPipe\lsass 管道 使用 FSCTL_PIPE_GET_PIPE_ATTRIBUTE 控制码 传入"ServerProcessId"作为输入缓冲区 输出缓冲区接收PID 6. 通过安全事件日志获取 原理 Windows安全事件4608("Windows正在启动")记录了LSASS进程启动时的PID。 实现代码 关键点 需要管理员权限 查询安全日志中事件ID为4608的记录 使用XPath路径 Event/System/Execution/@ProcessID 提取PID 需要正确释放所有事件句柄 总结对比 | 方法 | 所需权限 | 可靠性 | 适用性 | 备注 | |------|----------|--------|--------|------| | NtQuerySystemInformation | 普通用户 | 高 | 所有进程 | 使用未公开API | | WTSEnumerateProcessesW | 普通用户 | 高 | 所有进程 | 需要RPC服务 | | 注册表查询 | 管理员 | 中 | 仅LSASS | 直接读取注册表 | | QueryServiceStatusEx | 管理员 | 高 | LSASS相关服务 | 通过服务状态获取 | | NtFsControlFile | 普通用户 | 中 | 有命名管道的进程 | 需要管道存在 | | 安全事件日志 | 管理员 | 低 | 仅LSASS | 依赖日志记录 | 选择方法时应考虑所需权限、目标进程类型和系统环境等因素。对于常规进程枚举,推荐使用 NtQuerySystemInformation 或 WTSEnumerateProcessesW ;对于特定获取LSASS进程PID,注册表查询和查询服务状态是更直接的方法。