一、驱动编写的基本写法
DriverEntry :相当main函数
DriverUnload : 卸载函数
VOID DriverUnload(PDRIVER_OBJECT pDriverObject) { DbgPrint("已卸载驱动!\n"); } NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath) { DbgPrint("安装驱动成功!\n"); pDriverObject->DriverUnload = DriverUnload; return STATUS_SUCCESS; }
二、了解内核PEB结构体
Windbg使用命令:
dt _PEB [进程EPROCESS] 随便查看一个进程的 EPROCESS ,图中结构体0x18偏移处有一个Ldr指向了 _PEB_LDR_DATA

点击Ldr 之后查看了 _PEB_LDR_DATA 的结构是这样子的,其中
InLoadOrderModuleList 指向了另一个结构体的地址 _
LDR_DATA_TABLE_ENTRY
这里面就有内核进程DLL的地址

下图可以看到双向链表中保存了当前进程的基地址

编写代码的思路就有了,获取 PEB->Ldr->InLoadOrderModuleList->Blink
代码中使用了一个微软提供的宏
CONTAINING_RECORD : 它的功能为已知结构体或类的某一成员、对象中该成员的地址以及这一结构体名或类名,从而得到该对象的基地址。
#include<ntddk.h> typedef struct _LDR_DATA_TABLE_ENTRY { LIST_ENTRY InLoadOrderLinks; LIST_ENTRY InMemoryOrderLinks; LIST_ENTRY InInitializationOrderLinks; PVOID DllBase; PVOID EntryPoint; ULONG SizeOfImage; UNICODE_STRING FullDllName; UNICODE_STRING BaseDllName; ULONG Flags; USHORT LoadCount; USHORT TlsIndex; union { LIST_ENTRY HashLinks; struct { PVOID SectionPointer; ULONG CheckSum; }; }; union { struct { ULONG TimeDateStamp; }; struct { PVOID LoadedImports; }; }; }LDR_DATA_TABLE_ENTRY, * PLDR_DATA_TABLE_ENTRY; VOID DriverUnload(PDRIVER_OBJECT pDriverObject) { DbgPrint("已卸载驱动!\n"); } NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath) { PLDR_DATA_TABLE_ENTRY pLdr = NULL; PLIST_ENTRY pListEntry = NULL; PLIST_ENTRY pCurrentListEntry = NULL; PLDR_DATA_TABLE_ENTRY pCurrentModule = NULL; DbgPrint("安装驱动成功!\n"); pDriverObject->DriverUnload = DriverUnload; // UNICODE_STRING UnicodeString2; //RtlInitUnicodeString(&UnicodeString2, L"ntoskrnl.exe"); pLdr = (PLDR_DATA_TABLE_ENTRY)pDriverObject->DriverSection; pListEntry = pLdr->InLoadOrderLinks.Flink; pCurrentListEntry = pListEntry->Flink; while (pCurrentListEntry != pListEntry) { pCurrentModule = CONTAINING_RECORD(pCurrentListEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks); if (pCurrentModule->BaseDllName.Buffer != 0) { DbgPrint("ModuleName = %wZ ModuleBase = %p \r\n", pCurrentModule->BaseDllName, pCurrentModule->DllBase); } pCurrentListEntry = pCurrentListEntry->Flink; } return STATUS_SUCCESS; }
可以看到已经获取到了内核全部模块的基地址
如果需要获取某个进程的某个DLL的基地址,可以先封装成一个函数 传入eprocess 和需要获取模块的名称,根据函数名判断一下。
NTSTATUS GetModeaddr(PEPROCESS Eprocess, PUNICODE_STRING ModeName) { KAPC_STATE ks; PUNICODE_STRING UnicodeString2; DbgPrint("%wZ", ModeName); DbgPrint("%wZ", &ModeName); RtlInitUnicodeString(&UnicodeString2, ModeName); PEPROCESS Eprocess2 = Eprocess; if (Eprocess2 == NULL) { DbgPrint("Eprocess 获取失败"); return; } __try { ULONG64 peb = *(PULONG64)((ULONG64)Eprocess2 + PEB_OFFSET_IN_EPROCESS); KeStackAttachProcess(Eprocess2, &ks); ULONG64 idr = *(PULONG64)(peb + LDR_OFFSET_IN_PEB); PLIST_ENTRY pListHead = (idr + InLoadOrderModuleList_OFFSET); PLIST_ENTRY pMod = pListHead->Flink; //下一个链表 while (pMod != pListHead) { PCUNICODE_STRING name = &(((PLDR_DATA_TABLE_ENTRY)pMod)->ModuleName); //DbgPrint("name = %wZ\n Base= %p", name, (PVOID)(((PLDR_DATA_TABLE_ENTRY)pMod)->ModuleBaseAddress)); if (RtlCompareUnicodeString(name, &UnicodeString2, TRUE)) { DbgPrint("name = %wZ\n Base= %p", name, (PVOID)(((PLDR_DATA_TABLE_ENTRY)pMod)->ModuleBaseAddress)); base = (PVOID)(((PLDR_DATA_TABLE_ENTRY)pMod)->ModuleBaseAddress); } pMod = pMod->Flink; } } __except (EXCEPTION_EXECUTE_HANDLER) { DbgPrint("EXCEPTION_EXECUTE_HANDLER is occure...\n"); } KeUnstackDetachProcess(&ks); }