在Windows内核驱动开发中,根据进程ID(PID)查找相关进程(如兄弟进程和父进程)是一个常见需求。这涉及到对Windows内核进程管理机制的理解和使用。下面我将详细介绍实现这一功能的方法。
在Windows内核中:
EPROCESS
结构中的信息可以获取/*
* 根据PID查找父进程信息
* @param ProcessId 目标进程的PID
* @return 父进程的PID,失败返回0
*/
HANDLE GetParentProcessId(HANDLE ProcessId)
{
NTSTATUS status = STATUS_SUCCESS;
PEPROCESS Process = NULL;
HANDLE ParentPid = 0;
// 通过PID获取EPROCESS结构
status = PsLookupProcessByProcessId(ProcessId, &Process);
if (!NT_SUCCESS(status))
{
KdPrint(("PsLookupProcessByProcessId failed: 0x%08X\n", status));
return 0;
}
// 获取父进程PID
// 注意: 这里使用未公开的字段,可能随Windows版本变化
#if (NTDDI_VERSION >= NTDDI_WIN10)
// Windows 10+
ParentPid = PsGetProcessInheritedFromUniqueProcessId(Process);
#else
// Windows 8/7 - 使用偏移访问InheritedFromUniqueProcessId字段
// 注意: 这些偏移可能随系统版本不同而变化
ParentPid = *(HANDLE*)((PUCHAR)Process + FIELD_OFFSET(EPROCESS, InheritedFromUniqueProcessId));
#endif
// 释放引用
ObDereferenceObject(Process);
return ParentPid;
}
/*
* 根据PID查找所有兄弟进程
* @param ProcessId 目标进程的PID
* @param SiblingPids 用于存储兄弟进程PID的数组
* @param MaxCount 数组最大容量
* @param ActualCount 实际找到的兄弟进程数量
* @return NTSTATUS 操作状态
*/
NTSTATUS FindSiblingProcesses(
HANDLE ProcessId,
PHANDLE SiblingPids,
ULONG MaxCount,
PULONG ActualCount
)
{
NTSTATUS status;
HANDLE parentPid;
PVOID buffer = NULL;
ULONG bufferSize = 0x10000; // 初始缓冲区大小
PSYSTEM_PROCESS_INFORMATION procInfo = NULL;
ULONG count = 0;
if (ActualCount) *ActualCount = 0;
// 获取父进程ID
parentPid = GetParentProcessId(ProcessId);
if (parentPid == 0)
{
return STATUS_NOT_FOUND;
}
// 分配内存用于存储进程信息
buffer = ExAllocatePoolWithTag(PagedPool, bufferSize, 'CORP');
if (buffer == NULL)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
// 查询系统进程信息
status = ZwQuerySystemInformation(
SystemProcessInformation,
buffer,
bufferSize,
NULL
);
// 如果缓冲区不足,重新分配更大的缓冲区
if (status == STATUS_INFO_LENGTH_MISMATCH)
{
ExFreePoolWithTag(buffer, 'CORP');
bufferSize *= 2;
buffer = ExAllocatePoolWithTag(PagedPool, bufferSize, 'CORP');
if (buffer == NULL)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
status = ZwQuerySystemInformation(
SystemProcessInformation,
buffer,
bufferSize,
NULL
);
}
if (!NT_SUCCESS(status))
{
ExFreePoolWithTag(buffer, 'CORP');
return status;
}
// 遍历进程信息
procInfo = (PSYSTEM_PROCESS_INFORMATION)buffer;
while (TRUE)
{
HANDLE currentPid = (HANDLE)procInfo->UniqueProcessId;
HANDLE currentParentPid = GetParentProcessId(currentPid);
// 如果当前进程的父进程是目标进程的父进程,且不是目标进程自身
if (currentParentPid == parentPid && currentPid != ProcessId)
{
// 将兄弟进程ID添加到结果数组
if (SiblingPids && count < MaxCount)
{
SiblingPids[count] = currentPid;
}
count++;
}
// 移动到下一个进程信息
if (procInfo->NextEntryOffset == 0)
break;
procInfo = (PSYSTEM_PROCESS_INFORMATION)((PUCHAR)procInfo + procInfo->NextEntryOffset);
}
// 释放缓冲区
ExFreePoolWithTag(buffer, 'CORP');
// 返回实际找到的兄弟进程数量
if (ActualCount) *ActualCount = count;
return STATUS_SUCCESS;
}
// 进程枚举上下文结构
typedef struct _PROCESS_ENUM_CONTEXT {
HANDLE ParentPid; // 父进程ID
HANDLE TargetPid; // 目标进程ID
PHANDLE SiblingPids; // 兄弟进程ID数组
ULONG MaxCount; // 数组最大容量
ULONG Count; // 当前找到的兄弟进程数量
} PROCESS_ENUM_CONTEXT, *PPROCESS_ENUM_CONTEXT;
// 进程枚举回调函数
BOOLEAN ProcessEnumCallback(
PEPROCESS Process,
PVOID Context
)
{
PPROCESS_ENUM_CONTEXT enumContext = (PPROCESS_ENUM_CONTEXT)Context;
HANDLE processPid = PsGetProcessId(Process);
HANDLE parentPid = 0;
// 获取当前进程的父进程ID
#if (NTDDI_VERSION >= NTDDI_WIN10)
parentPid = PsGetProcessInheritedFromUniqueProcessId(Process);
#else
parentPid = *(HANDLE*)((PUCHAR)Process + FIELD_OFFSET(EPROCESS, InheritedFromUniqueProcessId));
#endif
// 检查是否为兄弟进程
if (parentPid == enumContext->ParentPid && processPid != enumContext->TargetPid)
{
// 添加到结果数组
if (enumContext->SiblingPids && enumContext->Count < enumContext->MaxCount)
{
enumContext->SiblingPids[enumContext->Count] = processPid;
}
enumContext->Count++;
}
// 返回TRUE继续枚举
return TRUE;
}
/*
* 使用进程枚举查找兄弟进程
* @param ProcessId 目标进程的PID
* @param SiblingPids 用于存储兄弟进程PID的数组
* @param MaxCount 数组最大容量
* @param ActualCount 实际找到的兄弟进程数量
* @return NTSTATUS 操作状态
*/
NTSTATUS FindSiblingProcessesEx(
HANDLE ProcessId,
PHANDLE SiblingPids,
ULONG MaxCount,
PULONG ActualCount
)
{
NTSTATUS status;
PEPROCESS Process = NULL;
HANDLE parentPid = 0;
PROCESS_ENUM_CONTEXT context = {0};
if (ActualCount) *ActualCount = 0;
// 获取目标进程的EPROCESS结构
status = PsLookupProcessByProcessId(ProcessId, &Process);
if (!NT_SUCCESS(status))
{
return status;
}
// 获取父进程ID
#if (NTDDI_VERSION >= NTDDI_WIN10)
parentPid = PsGetProcessInheritedFromUniqueProcessId(Process);
#else
parentPid = *(HANDLE*)((PUCHAR)Process + FIELD_OFFSET(EPROCESS, InheritedFromUniqueProcessId));
#endif
// 释放进程对象引用
ObDereferenceObject(Process);
if (parentPid == 0)
{
return STATUS_NOT_FOUND;
}
// 初始化枚举上下文
context.ParentPid = parentPid;
context.TargetPid = ProcessId;
context.SiblingPids = SiblingPids;
context.MaxCount = MaxCount;
context.Count = 0;
// 枚举所有进程
status = PsEnumerateProcesses(ProcessEnumCallback, &context);
// 返回找到的兄弟进程数量
if (ActualCount) *ActualCount = context.Count;
return status;
}
VOID TestProcessRelations(HANDLE PID)
{
HANDLE parentPid = 0;
HANDLE siblingPids[100] = {0};
ULONG count = 0;
NTSTATUS status;
// 获取父进程ID
parentPid = GetParentProcessId(PID);
if (parentPid != 0)
{
DbgPrint("Process %u's parent PID: %u\n", (ULONG)(ULONG_PTR)PID, (ULONG)(ULONG_PTR)parentPid);
}
else
{
DbgPrint("Failed to get parent PID for process %u\n", (ULONG)(ULONG_PTR)PID);
}
// 获取兄弟进程
status = FindSiblingProcesses(PID, siblingPids, 100, &count);
if (NT_SUCCESS(status))
{
DbgPrint("Found %u sibling processes for PID %u:\n", count, (ULONG)(ULONG_PTR)PID);
for (ULONG i = 0; i < min(count, 100); i++)
{
DbgPrint(" Sibling PID: %u\n", (ULONG)(ULONG_PTR)siblingPids[i]);
}
}
else
{
DbgPrint("Failed to find sibling processes: 0x%08X\n", status);
}
}
上述代码使用了一些未公开的API和结构体偏移,如EPROCESS
内部字段:
// 这些访问可能在不同Windows版本中失效
ParentPid = *(HANDLE*)((PUCHAR)Process + FIELD_OFFSET(EPROCESS, InheritedFromUniqueProcessId));
在正式产品中,应考虑动态确定这些偏移,或采用更可靠的替代方法。
Windows内核结构随系统版本变化,应根据目标系统版本调整代码:
#if (NTDDI_VERSION >= NTDDI_WIN10_RS3)
// Windows 10 RS3及更高版本的代码
#elif (NTDDI_VERSION >= NTDDI_WIN10)
// Windows 10初始版本的代码
#elif (NTDDI_VERSION >= NTDDI_WINBLUE)
// Windows 8.1的代码
#else
// 较旧Windows版本的代码
#endif
处理进程信息时需注意安全性:
ObDereferenceObject
调用,避免引用泄漏在Windows 10及更高版本中,可以使用更稳定的API:
// 获取父进程ID (Windows 10+)
HANDLE GetParentProcessIdWin10(HANDLE ProcessId)
{
NTSTATUS status;
PEPROCESS Process;
HANDLE ParentPid = 0;
status = PsLookupProcessByProcessId(ProcessId, &Process);
if (NT_SUCCESS(status))
{
ParentPid = PsGetProcessInheritedFromUniqueProcessId(Process);
ObDereferenceObject(Process);
}
return ParentPid;
}
以下是一个更完整的兄弟进程搜索实现,包含更多错误处理和优化:
NTSTATUS EnumerateSiblingProcesses(
IN HANDLE TargetProcessId,
IN PVOID OutputBuffer,
IN ULONG OutputBufferSize,
OUT PULONG ReturnLength
)
{
NTSTATUS status = STATUS_SUCCESS;
HANDLE parentProcessId = NULL;
PSIBLINGS_INFO siblingInfo = (PSIBLINGS_INFO)OutputBuffer;
ULONG requiredSize = sizeof(SIBLINGS_INFO);
ULONG siblingCount = 0;
PVOID processInfoBuffer = NULL;
ULONG processInfoSize = 0x20000;
PSYSTEM_PROCESS_INFORMATION processInfo = NULL;
PSYSTEM_PROCESS_INFORMATION currentProcess = NULL;
// 参数检查
if (!OutputBuffer || OutputBufferSize < sizeof(SIBLINGS_INFO) || !ReturnLength)
{
return STATUS_INVALID_PARAMETER;
}
*ReturnLength = 0;
// 获取目标进程的父进程ID
parentProcessId = GetParentProcessId(TargetProcessId);
if (parentProcessId == NULL)
{
return STATUS_PROCESS_NOT_IN_JOB;
}
// 分配缓冲区并查询系统进程信息
processInfoBuffer = ExAllocatePoolWithTag(PagedPool, processInfoSize, 'forP');
if (!processInfoBuffer)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
status = ZwQuerySystemInformation(
SystemProcessInformation,
processInfoBuffer,
processInfoSize,
NULL
);
if (status == STATUS_INFO_LENGTH_MISMATCH)
{
ExFreePoolWithTag(processInfoBuffer, 'forP');
processInfoSize *= 2;
processInfoBuffer = ExAllocatePoolWithTag(PagedPool, processInfoSize, 'forP');
if (!processInfoBuffer)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
status = ZwQuerySystemInformation(
SystemProcessInformation,
processInfoBuffer,
processInfoSize,
NULL
);
}
if (!NT_SUCCESS(status))
{
ExFreePoolWithTag(processInfoBuffer, 'forP');
return status;
}
// 第一次遍历:计算所需空间
currentProcess = (PSYSTEM_PROCESS_INFORMATION)processInfoBuffer;
while (TRUE)
{
HANDLE currentPid = (HANDLE)currentProcess->UniqueProcessId;
HANDLE currentParentPid = GetParentProcessId(currentPid);
if (currentParentPid == parentProcessId && currentPid != TargetProcessId)
{
siblingCount++;
requiredSize += sizeof(PROCESS_BASIC_INFO);
}
if (currentProcess->NextEntryOffset == 0)
break;
currentProcess = (PSYSTEM_PROCESS_INFORMATION)((PUCHAR)currentProcess + currentProcess->NextEntryOffset);
}
// 返回所需缓冲区大小
*ReturnLength = requiredSize;
// 检查缓冲区是否足够大
if (OutputBufferSize < requiredSize)
{
ExFreePoolWithTag(processInfoBuffer, 'forP');
return STATUS_BUFFER_TOO_SMALL;
}
// 初始化输出结构
RtlZeroMemory(siblingInfo, requiredSize);
siblingInfo->ParentProcessId = parentProcessId;
siblingInfo->NumberOfSiblings = siblingCount;
// 再次遍历:填充兄弟进程信息
PPROCESS_BASIC_INFO processBasicInfo = siblingInfo->ProcessInfo;
ULONG processIndex = 0;
currentProcess = (PSYSTEM_PROCESS_INFORMATION)processInfoBuffer;
while (TRUE)
{
HANDLE currentPid = (HANDLE)currentProcess->UniqueProcessId;
HANDLE currentParentPid = GetParentProcessId(currentPid);
if (currentParentPid == parentProcessId && currentPid != TargetProcessId)
{
processBasicInfo[processIndex].ProcessId = currentPid;
// 复制进程名称
if (currentProcess->ImageName.Buffer)
{
ULONG nameLength = min(
currentProcess->ImageName.Length,
sizeof(processBasicInfo[processIndex].ImageName) - sizeof(WCHAR)
);
RtlCopyMemory(
processBasicInfo[processIndex].ImageName,
currentProcess->ImageName.Buffer,
nameLength
);
}
processIndex++;
}
if (currentProcess->NextEntryOffset == 0)
break;
currentProcess = (PSYSTEM_PROCESS_INFORMATION)((PUCHAR)currentProcess + currentProcess->NextEntryOffset);
}
// 释放资源
ExFreePoolWithTag(processInfoBuffer, 'forP');
return STATUS_SUCCESS;
}
上述代码示例使用了以下结构:
// 定义进程基本信息结构
typedef struct _PROCESS_BASIC_INFO {
HANDLE ProcessId; // 进程ID
WCHAR ImageName[MAX_PATH]; // 进程镜像名称
} PROCESS_BASIC_INFO, *PPROCESS_BASIC_INFO;
// 定义兄弟进程信息结构
typedef struct _SIBLINGS_INFO {
HANDLE ParentProcessId; // 父进程ID
ULONG NumberOfSiblings; // 兄弟进程数量
PROCESS_BASIC_INFO ProcessInfo[1]; // 兄弟进程信息数组(可变长度)
} SIBLINGS_INFO, *PSIBLINGS_INFO;
代码中使用了以下未公开的内核API和结构:
// 系统信息类型枚举
typedef enum _SYSTEM_INFORMATION_CLASS {
SystemProcessInformation = 5,
// ...其他信息类型
} SYSTEM_INFORMATION_CLASS;
// 进程信息结构
typedef struct _SYSTEM_PROCESS_INFORMATION {
ULONG NextEntryOffset;
ULONG NumberOfThreads;
LARGE_INTEGER SpareLi1;
LARGE_INTEGER SpareLi2;
LARGE_INTEGER SpareLi3;
LARGE_INTEGER CreateTime;
LARGE_INTEGER UserTime;
LARGE_INTEGER KernelTime;
UNICODE_STRING ImageName;
KPRIORITY BasePriority;
HANDLE UniqueProcessId;
HANDLE InheritedFromUniqueProcessId;
ULONG HandleCount;
ULONG SessionId;
ULONG_PTR PageDirectoryBase;
// ...更多字段
} SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;
// ZwQuerySystemInformation函数声明
NTSYSCALLAPI
NTSTATUS
NTAPI
ZwQuerySystemInformation(
_In_ SYSTEM_INFORMATION_CLASS SystemInformationClass,
_Out_writes_bytes_opt_(SystemInformationLength) PVOID SystemInformation,
_In_ ULONG SystemInformationLength,
_Out_opt_ PULONG ReturnLength
);
使用官方API:尽可能使用Microsoft公开记录的API,而不是依赖内部结构
动态偏移计算:如果必须访问内部结构,考虑在驱动加载时动态计算偏移量
版本检测:添加操作系统版本检测,针对不同Windows版本使用不同实现
错误处理:实现全面的错误处理,确保资源始终正确释放
应用示例:在生产环境中部署前进行全面测试
本文详细介绍了在Windows内核中根据PID查找父进程和兄弟进程的方法。通过访问进程管理相关的内核结构,可以构建进程之间的关系图。这种功能在系统监控、安全软件和进程管理工具中非常有用。
请注意,内核开发涉及到系统底层操作,错误的实现可能导致系统不稳定。在生产环境中使用前,应该进行全面的测试,并考虑不同Windows版本的兼容性问题。