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