关于获取指定进程Pid的讨论
字数 2109 2025-08-06 18:08:11
获取指定进程PID的多种方法详解
前言
获取指定进程的PID是系统编程和安全研究中的常见需求。本文详细介绍了7种不同的方法,包括常规方法和一些不常见但有效的技术。
1. NtQuerySystemInformation方法
函数原型
__kernel_entry NTSTATUS NtQuerySystemInformation(
[in] SYSTEM_INFORMATION_CLASS SystemInformationClass,
[in, out] PVOID SystemInformation,
[in] ULONG SystemInformationLength,
[out, optional] PULONG ReturnLength
);
实现步骤
- 调用
NtQuerySystemInformation函数 - 设置
SystemInformationClass参数为适当的进程信息类 - 解析返回的系统信息结构获取PID
特点
- 是内核函数,功能强大
- 可以获取系统级的详细信息
- 需要正确处理缓冲区大小和返回长度
2. WTSEnumerateProcessesW方法
函数原型
BOOL WTSEnumerateProcessesW(
[in] HANDLE hServer,
[in] DWORD Reserved,
[in] DWORD Version,
[out] PWTS_PROCESS_INFOW *ppProcessInfo,
[out] DWORD *pCount
);
数据结构
typedef struct _WTS_PROCESS_INFOA {
DWORD SessionId;
DWORD ProcessId;
LPSTR pProcessName;
PSID pUserSid;
} WTS_PROCESS_INFOA, *PWTS_PROCESS_INFOA;
实现步骤
- 调用
WTSEnumerateProcessesW获取进程列表 - 遍历
WTS_PROCESS_INFOW结构数组 - 通过
ProcessId字段获取目标进程PID
特点
- 使用RPC服务获取进程列表
- 可以获取会话ID和用户SID等额外信息
- 需要释放返回的进程信息内存
3. 注册表查询方法(RegQueryValueExA)
关键注册表路径
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa
相关函数
LSTATUS RegOpenKeyExA(
[in] HKEY hKey,
[in] LPCSTR lpSubKey,
[in] DWORD ulOptions,
[in] REGSAM samDesired,
[out] PHKEY phkResult
);
LSTATUS RegQueryValueExA(
[in] HKEY hKey,
[in, optional] LPCSTR lpValueName,
[reserved] LPDWORD lpReserved,
[out, optional] LPDWORD lpType,
[out, optional] LPBYTE lpData,
[in, out, optional] LPDWORD lpcbData
);
实现步骤(以lsass.exe为例)
- 使用
RegOpenKeyExA打开LSA注册表项 - 使用
RegQueryValueExA查询LsaPid键值 - 解析返回的数据获取lsass进程PID
特点
- 特别适用于获取系统关键进程(如lsass)的PID
- Windows 8.1及以上版本有LSA保护机制
- 需要管理员权限
4. 服务状态查询方法(QueryServiceStatusEx)
相关LSASS服务
- CNG KeyIsolation (KeyIso)
- Security Accounts Manager (SamSs)
- Credential Manager (VaultSvc)
关键函数
SC_HANDLE OpenSCManagerW(
[in, optional] LPCWSTR lpMachineName,
[in, optional] LPCWSTR lpDatabaseName,
[in] DWORD dwDesiredAccess
);
SC_HANDLE OpenServiceW(
[in] SC_HANDLE hSCManager,
[in] LPCWSTR lpServiceName,
[in] DWORD dwDesiredAccess
);
BOOL QueryServiceStatusEx(
[in] SC_HANDLE hService,
[in] SC_STATUS_TYPE InfoLevel,
[out] LPBYTE lpBuffer,
[in] DWORD cbBufSize,
[out] LPDWORD pcbBytesNeeded
);
数据结构
typedef struct _SERVICE_STATUS_PROCESS {
DWORD dwServiceType;
DWORD dwCurrentState;
DWORD dwControlsAccepted;
DWORD dwWin32ExitCode;
DWORD dwServiceSpecificExitCode;
DWORD dwCheckPoint;
DWORD dwWaitHint;
DWORD dwProcessId;
DWORD dwServiceFlags;
} SERVICE_STATUS_PROCESS, *LPSERVICE_STATUS_PROCESS;
实现步骤
- 使用
OpenSCManagerW连接服务控制管理器 - 使用
OpenServiceW打开目标服务 - 调用
QueryServiceStatusEx获取服务状态 - 从
SERVICE_STATUS_PROCESS结构中读取dwProcessId
特点
- 适用于获取服务进程的PID
- 可以获取服务的详细状态信息
- 需要适当的访问权限
5. NtQueryInformationFile方法
关键函数
NTSTATUS NtQueryInformationFile(
[in] HANDLE FileHandle,
[out] PIO_STATUS_BLOCK IoStatusBlock,
[out] PVOID FileInformation,
[in] ULONG Length,
[in] FILE_INFORMATION_CLASS FileInformationClass
);
NTSTATUS NtOpenFile(
[out] PHANDLE FileHandle,
[in] ACCESS_MASK DesiredAccess,
[in] POBJECT_ATTRIBUTES ObjectAttributes,
[out] PIO_STATUS_BLOCK IoStatusBlock,
[in] ULONG ShareAccess,
[in] ULONG OpenOptions
);
枚举值
FileProcessIdsUsingFileInformation - 用于查询使用文件的进程ID
实现步骤
- 使用
NtOpenFile打开目标进程文件(如lsass.exe)获取句柄 - 调用
NtQueryInformationFile查询文件信息 - 指定
FileInformationClass为FileProcessIdsUsingFileInformation - 解析返回的信息获取PID
特点
- 使用未公开的内核函数
- 需要正确处理对象属性和访问权限
- 适用于获取特定文件(如可执行文件)关联的进程PID
6. 命名管道方法(NtFsControlFile)
关键函数
NTSTATUS NtFsControlFile(
[in] HANDLE FileHandle,
[in, optional] HANDLE Event,
[in, optional] PIO_APC_ROUTINE ApcRoutine,
[in, optional] PVOID ApcContext,
[out] PIO_STATUS_BLOCK IoStatusBlock,
[in] ULONG FsControlCode,
[in, optional] PVOID InputBuffer,
[in] ULONG InputBufferLength,
[out, optional] PVOID OutputBuffer,
[in] ULONG OutputBufferLength
);
实现步骤
- 创建或连接到命名管道
- 使用
NtOpenFile获取管道文件句柄 - 调用
NtFsControlFile发送控制代码 - 从输出缓冲区解析服务器进程PID
替代函数
GetNamedPipeServerProcessId - 封装了NtFsControlFile的功能
特点
- 通过命名管道机制获取PID
- 可以用于本地或远程进程
- 需要正确处理管道创建和连接
7. 安全事件日志方法
相关事件
事件ID 4608 (S): Windows正在启动 - 包含lsass进程的PID
关键函数
BOOL OpenProcessToken(
[in] HANDLE ProcessHandle,
[in] DWORD DesiredAccess,
[out] PHANDLE TokenHandle
);
BOOL GetTokenInformation(
[in] HANDLE TokenHandle,
[in] TOKEN_INFORMATION_CLASS TokenInformationClass,
[out, optional] LPVOID TokenInformation,
[in] DWORD TokenInformationLength,
[out] PDWORD ReturnLength
);
EVT_HANDLE EvtQuery(
[in] EVT_HANDLE Session,
[in] LPCWSTR Path,
[in] LPCWSTR Query,
[in] DWORD Flags
);
EVT_HANDLE EvtSeek(
[in] EVT_HANDLE ResultSet,
[in] LONGLONG Position,
[in] EVT_HANDLE Bookmark,
[in] DWORD Timeout,
[in] DWORD Flags
);
EVT_HANDLE EvtNext(
[in] EVT_HANDLE ResultSet,
[in] DWORD EventArraySize,
[out] PEVT_HANDLE EventArray,
[in] DWORD Timeout,
[in] DWORD Flags,
[out] PDWORD Returned
);
EVT_HANDLE EvtCreateRenderContext(
[in] DWORD ValuePathsCount,
[in] LPCWSTR* ValuePaths,
[in] DWORD Flags
);
BOOL EvtRender(
[in] EVT_HANDLE Context,
[in] EVT_HANDLE Fragment,
[in] DWORD Flags,
[in] DWORD BufferSize,
[out] PVOID Buffer,
[out] PDWORD BufferUsed,
[out] PDWORD PropertyCount
);
实现步骤
- 检查管理员权限(
OpenProcessToken+GetTokenInformation) - 使用
EvtQuery查询安全事件日志 - 使用
EvtSeek定位到特定事件(4608) - 使用
EvtNext获取事件内容 - 创建渲染上下文(
EvtCreateRenderContext) - 使用
EvtRender解析事件内容获取PID
特点
- 从系统事件日志中提取信息
- 需要管理员权限
- 适用于获取系统关键进程启动时的PID
- 可以获取历史记录
总结
本文详细介绍了7种获取指定进程PID的方法,每种方法各有特点和适用场景:
- NtQuerySystemInformation - 强大的系统级查询
- WTSEnumerateProcessesW - 通过RPC获取进程列表
- 注册表查询 - 适用于系统关键进程
- 服务状态查询 - 适用于服务进程
- NtQueryInformationFile - 通过文件对象查询
- 命名管道方法 - 通过进程间通信机制
- 安全事件日志 - 从系统日志中提取
在实际应用中,应根据具体需求和环境选择合适的方法,并考虑权限要求、系统版本兼容性等因素。对于安全敏感的操作,还需要注意避免触发防护机制。