内核中提供了专用的链表结构: LIST_ENTRY
,还有一些对链表的操作函数。通过枚举用户进程的功能。用户层是直接照一个进程快照,然后遍历快照的方式进行的。内核层就是去遍历LLIST_ENTRY链表达到目的。
// 遍历用户进程 - 熟悉内核链表和结构体
// 先引用ntifs,再引用ntddk
#include <ntifs.h>
#include <ntddk.h>
#include <windef.h>
// 前向声明,声明后就可以使用
extern PVOID PsGetProcessPeb(_In_ PEPROCESS Process);
extern NTKERNELAPI PVOID PsGetProcessWow64Process(_In_ PEPROCESS Process);
extern NTKERNELAPI UCHAR* PsGetProcessImageFileName(IN PEPROCESS Process);
NTKERNELAPI NTSTATUS PsLookupProcessByProcessId(HANDLE ProcessId, PEPROCESS* Process);
extern NTKERNELAPI HANDLE PsGetProcessInheritedFromUniqueProcessId(IN PEPROCESS Process);
// 设置结构体保存进程信息
typedef struct
{
DWORD Pid;
UCHAR ProcessName[2048];
DWORD Handle;
LIST_ENTRY ListEntry;
} ProcessList;
BOOLEAN GetAllProcess()
{
PEPROCESS eproc = NULL;
LIST_ENTRY linkListHead;
// 初始化链表头部
InitializeListHead(&linkListHead);
ProcessList* pdata = NULL;
// 这里步长是4,改成其他的都行,看实际情况
for (int Pid = 0; Pid < 100000; Pid += 4)
{
if ( NT_SUCCESS( PsLookupProcessByProcessId((HANDLE)Pid, &eproc) ) )
{
if (eproc != NULL)
{
STRING nowProcessnameString = { 0 };
// 初始化 8 位字符的计数字符串
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)) * 2);
pdata->Handle = (DWORD)PsGetProcessInheritedFromUniqueProcessId(eproc);
// 插入元素
InsertTailList(&linkListHead, &pdata->ListEntry);
ObDereferenceObject(eproc);
}
}
}
// 输出链表内的数据
while (!IsListEmpty(&linkListHead))
{
LIST_ENTRY* pEntry = RemoveHeadList(&linkListHead);
pdata = CONTAINING_RECORD(pEntry, ProcessList, ListEntry);
DbgPrint("%d | %s | %d \n", pdata->Pid, pdata->ProcessName, pdata->Handle);
ExFreePool(pdata);
}
return TRUE;
}
NTSTATUS unload(PDRIVER_OBJECT driver)
{
DbgPrint("Driver unload success..");
return STATUS_SUCCESS;
}
NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path)
{
driver->DriverUnload = unload;
DbgPrint("3 Hello World\n");
GetAllProcess();
return STATUS_SUCCESS;
}
从程序本身来看,和一般的程序没有啥区别,就是使用api不同了,一些关键函数名称参数不同而已。