概述:内核模式下遍历系统当前进程,以及链表的基本使用

相关接口

进程相关

相关知识点

PID

PID 一定是4的整数倍

为什么 PID 一定是4的整数倍

Windows进程的PID(Process ID)是4的倍数的原因与Windows内核中句柄分配机制有关。以下是详细的解释:

  1. 句柄分配机制:在Windows NT内核的操作系统中,进程ID(PID)和线程ID(TID)实际上是基于全局的句柄表PspCidTable生成的,这个表也被称为CID句柄表(Client ID handle table)。这个句柄表中的每个项都包含了进程或线程的对象地址。

  2. 句柄值与索引:在Windows中,句柄值是用来检索句柄表的一个“伪索引”。由于句柄值的低2位被用作标志位,因此它们对于作为句柄表索引本身没有意义。这意味着句柄值实际上是4的倍数,因为每4个字节对应一个句柄索引值。

  3. 内核句柄重用:分配内核句柄的相同代码也用于分配进程和线程ID。由于内核句柄是4的倍数,因此进程和线程ID也是如此。这是一个实现细节,因此不应编写依赖于这个规律的代码。

  4. 结构定义:在Windows内核中,句柄类型是由EXHANDLE结构定义的,其中包含一个ULONG值,用于存储索引。由于低2位被用作标志位,所以有效的索引从第3位开始,这就是为什么句柄值(以及由此产生的PID和TID)是4的倍数的原因。

  5. 历史和兼容性:这个机制在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;
 
}