概述:内核模式下遍历系统当前进程,以及链表的基本使用
相关接口
相关知识点
PID
PID 一定是4的整数倍
为什么 PID 一定是4的整数倍
Windows进程的PID(Process ID)是4的倍数的原因与Windows内核中句柄分配机制有关。以下是详细的解释:
句柄分配机制:在Windows NT内核的操作系统中,进程ID(PID)和线程ID(TID)实际上是基于全局的句柄表PspCidTable生成的,这个表也被称为CID句柄表(Client ID handle table)。这个句柄表中的每个项都包含了进程或线程的对象地址。
句柄值与索引:在Windows中,句柄值是用来检索句柄表的一个“伪索引”。由于句柄值的低2位被用作标志位,因此它们对于作为句柄表索引本身没有意义。这意味着句柄值实际上是4的倍数,因为每4个字节对应一个句柄索引值。
内核句柄重用:分配内核句柄的相同代码也用于分配进程和线程ID。由于内核句柄是4的倍数,因此进程和线程ID也是如此。这是一个实现细节,因此不应编写依赖于这个规律的代码。
结构定义:在Windows内核中,句柄类型是由
EXHANDLE结构定义的,其中包含一个ULONG值,用于存储索引。由于低2位被用作标志位,所以有效的索引从第3位开始,这就是为什么句柄值(以及由此产生的PID和TID)是4的倍数的原因。历史和兼容性:这个机制在Windows的多个版本中一直保持不变,主要是因为底层代码难以改变。即使在最新的Windows版本中,这个规律依然存在。
总结来说,Windows进程PID是4的倍数是由于内核中句柄分配机制的设计,特别是与句柄值的位结构和内核句柄重用代码有关。这是一个实现细节,不应该依赖于应用程序开发中。
完整代码
#include <ntifs.h>
#include <windef.h>
extern PVOID PsGetProcessPeb(_In_ PEPROCESS Process); // 函数返回给定进程的进程环境块(Process Environment Block,PEB)的指针
NTKERNELAPI NTSTATUS PsLookupProcessByProcessId(HANDLE ProcessId, PEPROCESS* Process); // 此函数通过给定的进程 ID 查找对应的进程,并将进程的 EPROCESS 结构指针存储在提供的指针变量中。
extern NTKERNELAPI PVOID PsGetProcessWow64Process(_In_ PEPROCESS Process); // 此函数返回给定进程的 Wow64 进程环境的指针。Wow64 进程环境是用于在 64 位 Windows 上运行 32 位应用程序的兼容性层。
extern NTKERNELAPI UCHAR* PsGetProcessImageFileName(IN PEPROCESS Process); // 此函数返回给定进程的映像文件名,即执行该进程的可执行文件的名称。
extern NTKERNELAPI HANDLE PsGetProcessInheritedFromUniqueProcessId(IN PEPROCESS Process); // 此函数返回给定进程所继承的唯一进程 ID,即父进程ID。
typedef struct
{
DWORD Pid;
UCHAR ProcessName[2048];
DWORD Handle;
LIST_ENTRY ListEntry;
} ProcessList;
// 根据进程ID返回进程EPROCESS结构体失败返回NULL
PEPROCESS LookupProcess(HANDLE Pid)
{
PEPROCESS eprocess = NULL;
NTSTATUS Status = STATUS_UNSUCCESSFUL;
Status = PsLookupProcessByProcessId(Pid, &eprocess);
if (NT_SUCCESS(Status))
{
return eprocess;
}
return NULL;
}
// 内核链表操作
BOOLEAN GetAllProcess()
{
PEPROCESS eproc = NULL;
LIST_ENTRY linkListHead;
// 初始化链表头部
InitializeListHead(&linkListHead);
ProcessList* pData = NULL;
for (int temp = 0; temp < 100000; temp += 4) // pid 一定是4的整数倍
{
eproc = LookupProcess((HANDLE)temp);
if (eproc != NULL)
{
STRING nowProcessnameString = { 0 };
RtlInitString(&nowProcessnameString, PsGetProcessImageFileName(eproc));
// 分配内核堆空间
pData = (ProcessList*)ExAllocatePool(PagedPool, sizeof(ProcessList));
RtlZeroMemory(pData, sizeof(ProcessList));
// 设置变量
pData->Pid = (DWORD)PsGetProcessId(eproc);
RtlCopyMemory(pData->ProcessName, PsGetProcessImageFileName(eproc), strlen(PsGetProcessImageFileName(eproc)));
pData->Handle = (DWORD)PsGetProcessInheritedFromUniqueProcessId(eproc);
// 插入元素
InsertTailList(&linkListHead, &pData->ListEntry);
ObDereferenceObject(eproc); // 内核对象引用计数+1
}
}
// 输出链表内的数据
while (!IsListEmpty(&linkListHead))
{
LIST_ENTRY* pEntry = RemoveHeadList(&linkListHead);
pData = CONTAINING_RECORD(pEntry, ProcessList, ListEntry);
DbgPrint("Pid[%d], ProcessName[%s], Handle[0x%x] \n", pData->Pid, pData->ProcessName, pData->Handle);
ExFreePool(pData);
}
return TRUE;
}
// 绕过签名检查
BOOLEAN BypassCheckSign(PDRIVER_OBJECT pDriverObject)
{
#ifdef _WIN64
typedef struct _KLDR_DATA_TABLE_ENTRY
{
LIST_ENTRY listEntry;
ULONG64 __Undefined1;
ULONG64 __Undefined2;
ULONG64 __Undefined3;
ULONG64 NonPagedDebugInfo;
ULONG64 DllBase;
ULONG64 EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING path;
UNICODE_STRING name;
ULONG Flags;
USHORT LoadCount;
USHORT __Undefined5;
ULONG64 __Undefined6;
ULONG CheckSum;
ULONG __padding1;
ULONG TimeDateStamp;
ULONG __padding2;
} KLDR_DATA_TABLE_ENTRY, * PKLDR_DATA_TABLE_ENTRY;
#else
typedef struct _KLDR_DATA_TABLE_ENTRY
{
LIST_ENTRY listEntry;
ULONG unknown1;
ULONG unknown2;
ULONG unknown3;
ULONG unknown4;
ULONG unknown5;
ULONG unknown6;
ULONG unknown7;
UNICODE_STRING path;
UNICODE_STRING name;
ULONG Flags;
} KLDR_DATA_TABLE_ENTRY, * PKLDR_DATA_TABLE_ENTRY;
#endif
PKLDR_DATA_TABLE_ENTRY pLdrData = (PKLDR_DATA_TABLE_ENTRY)pDriverObject->DriverSection;
pLdrData->Flags = pLdrData->Flags | 0x20;
return TRUE;
}
// 卸载驱动
void UnloadDriver(PDRIVER_OBJECT driver)
{
DbgPrint("Uninstall Driver Is Ok \n");
}
// 驱动入口地址
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
if (BypassCheckSign(Driver))
DbgPrint("Bypass Sign Success.");
DbgPrint("Driver loaded. \n");
GetAllProcess();
Driver->DriverUnload = UnloadDriver;
return STATUS_SUCCESS;
}