一、驅動編寫的基本寫法
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); }