ZwQuerySystemInformation查询结果延迟原因及实时进程枚举咨询
问题描述
我正尝试在Windows内核模式下通过进程镜像名称查找进程ID,现有代码可正常运行,但新创建的进程有时会延迟最长60秒才出现在查询结果中,无法被及时找到。我的代码如下:
void WaitForProcess() { LARGE_INTEGER delay; HANDLE value = 0; delay.QuadPart = -20000000; while (!value) { fpDbgPrintEx(0, 0, "Search\n"); fpKeDelayExecutionThread(KernelMode, FALSE, &delay); //Sleep for 2 seconds, then retry value = HkGetProcessId(675793765); //notepad.exe } } HANDLE GetProcessId(DWORD processHash) { HANDLE retVal = 0; ULONG retLen = 0; PSYSTEM_PROCESS_INFORMATION processInfo; PVOID infoBuf = NULL; WCHAR processName[256]; fpZwQuerySystemInformation(SystemProcessInformation, 0, 0, &retLen); if (!retLen) goto Done; if (!(infoBuf = fpExAllocatePool(NonPagedPool, retLen))) goto Done; processInfo = infoBuf; if (!NT_SUCCESS(fpZwQuerySystemInformation(SystemProcessInformation, processInfo, retLen, &retLen))) goto Done; while (processInfo->NextEntryOffset) { if (processInfo->ImageName.Buffer && (processInfo->ImageName.Length / sizeof(WCHAR)) < 256) { RtlZeroMemory(processName, 256 * sizeof(WCHAR)); memcpy(processName, processInfo->ImageName.Buffer, processInfo->ImageName.Length); processName[(processInfo->ImageName.Length) / sizeof(WCHAR)] = '\0'; fpDbgPrintEx(0, 0, "Process: %wZ\n", &processInfo->ImageName); if (HashString(processName) == processHash) { retVal = processInfo->UniqueProcessId; break; } } processInfo = (PSYSTEM_PROCESS_INFORMATION)((DWORD_PTR)processInfo + processInfo->NextEntryOffset); } Done: if (infoBuf) fpExFreePool(infoBuf); return retVal; }
延迟原因分析
- 进程初始化阶段的ImageName未填充:新进程创建时,内核会先分配进程对象,再完成镜像加载并设置
ImageName字段。在这个间隙内,ZwQuerySystemInformation返回的进程信息中ImageName可能为空或未完成初始化,导致你的枚举逻辑直接跳过该进程,直到进程完全初始化完成(最长可能到60秒,取决于系统调度和进程加载速度)。 - ZwQuerySystemInformation的内部缓存机制:Windows内核对
SystemProcessInformation类别的查询结果存在短期缓存,不会实时更新进程列表,导致新进程信息延迟出现在枚举结果中。
实时检测进程的解决方案
方法1:注册进程创建回调(推荐)
使用PsSetCreateProcessNotifyRoutineEx(或低版本系统的PsSetCreateProcessNotifyRoutine)注册回调函数,系统创建新进程时会立即通知驱动,无需轮询。示例代码框架:
NTSTATUS ProcessCreateCallback( _Inout_ PEPROCESS Process, _In_ HANDLE ProcessId, _Inout_opt_ PPS_CREATE_NOTIFY_INFO CreateInfo ) { if (CreateInfo != NULL && CreateInfo->ImageName.Buffer != NULL) { WCHAR processName[256]; RtlZeroMemory(processName, sizeof(processName)); memcpy(processName, CreateInfo->ImageName.Buffer, min(CreateInfo->ImageName.Length, sizeof(processName)-sizeof(WCHAR))); processName[CreateInfo->ImageName.Length / sizeof(WCHAR)] = L'\0'; if (HashString(processName) == 675793765) // 匹配notepad.exe的哈希 { fpDbgPrintEx(0, 0, "Found target process: %wZ, PID: %d\n", &CreateInfo->ImageName, ProcessId); // 此处处理找到进程后的逻辑 } } return STATUS_SUCCESS; } // 驱动初始化时注册回调 NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) { UNREFERENCED_PARAMETER(RegistryPath); NTSTATUS status = PsSetCreateProcessNotifyRoutineEx(ProcessCreateCallback, FALSE); if (!NT_SUCCESS(status)) { fpDbgPrintEx(0, 0, "Failed to register process callback: 0x%X\n", status); return status; } DriverObject->DriverUnload = DriverUnload; return STATUS_SUCCESS; } // 驱动卸载时注销回调 VOID DriverUnload(PDRIVER_OBJECT DriverObject) { UNREFERENCED_PARAMETER(DriverObject); PsSetCreateProcessNotifyRoutineEx(ProcessCreateCallback, TRUE); }
方法2:优化轮询枚举逻辑
如果必须使用轮询,可做以下优化:
- 不要跳过
ImageName为空的进程,记录其UniqueProcessId,后续轮询时单独针对这些PID调用ZwQueryInformationProcess查询进程名称,确认是否为目标进程。 - 适当缩短轮询间隔(注意内核中频繁轮询会增加系统开销,需权衡)。
注意事项
- 进程回调函数必须执行高效,避免阻塞系统线程,禁止在回调中执行耗时操作。
- 驱动需确保具备足够权限,内核模式下通常默认满足,但需注意系统版本兼容性(
PsSetCreateProcessNotifyRoutineEx从Windows Vista开始支持)。
内容的提问来源于stack exchange,提问作者Louis Bernard
相关产品推荐
相关产品推荐

