獲取核心函式的原始地址
本文以獲取NtReadVirtualMemory講述當該函式被HOOK後如何獲取到正確的地址為例,解析獲取原始核心函式地址的一種思路。思路雖然比較笨拙,但是也不失為一種解決辦法。
上圖中NtReadVirtualMemory函式被hook了,如果我們從SSDT表獲取函式的地址,則獲取到的函式地址為0XA1277AFE而不是原始的函式地址0x805884F7,並且此函式並不是匯出函式,
無法通過MmGetSystemRoutineAddress這個函式來獲取到正確的地址。如果你想恢復或者繞過某幾p對這個函式的HOOK,你又獲取到了一個錯誤的地址,那麼結果當然只有兩種,宕機或者藍屏。
如此等等情況,我們寫的pass某幾p的程式碼中一種簡單的解決方式是使用死地址(或稱硬編碼),但是存在的一個問題是你虛擬機器上的地址跟你主機上的地址是不一樣的,那麼則必須要做實時的
修改編譯,區分在主機上使用的sys和在虛擬機器上使用的sys,那麼換臺電腦又要再次修改。如此改來改去的真的是很麻煩。C"YM"9JSJ
我們使用xuetr檢視驅動模組發現對於核心而言裡面表現出來的模組分兩種情況一種ntoskrnl.exe,另外一種是ntkrnlpa.exe。這樣一來我們就想到一個簡單粗暴的方式是首先獲得核心的入口地址
和模組大小,然後通過特徵碼定位這個函式的地址。在windbg中輸入命令 u nt!NtReadVirtualMemory回車後看到如下圖
考慮到前面幾行有可能被某p或者其他程式inline hook 修改,例如某p將805aa792 這個地址修改為mov eax,xxxxxxxx jmp eax 那麼我們取特徵的時候就要在稍微靠後一點去,這裡我們就從805aa79e處取特徵吧
在windbg 中輸入 db 805aa79e 得到如下圖的資料
我們取到805aa7ae處應該能確定下來了,也就是特徵為
64 a1 24 01 00 00 8b f8-8a 87 40 01 00 00 88 45 e0
那麼找到這個特徵以後,得到的地址就是805aa79e要得到NtReadVirtualMemory函式的地址則將這個地址減去0xC就得到了NtReadVirtualMemory函式的地址了
好了簡單的分析完成那麼接下來該是貼出程式碼的時候了,下面給出程式碼
#include <ntddk.h> #include <windef.h> //模組結構的定義,這個好像有很多不同的定義這裡先用這個 typedef struct _SYSTEM_MODULE_INFORMATION {//Information Class 11 HANDLE Section; PVOID MappedBase; PVOID Base; ULONG Size; ULONG Flags; USHORT LoadOrderIndex; USHORT InitOrderIndex; USHORT LoadCount; USHORT ModuleNameOffset; CHAR ImageName[256]; }SYSTEM_MODULE_INFORMATION,*PSYSTEM_MODULE_INFORMATION; //模組陣列的定義 typedef struct { DWORD dwNumberOfModules; SYSTEM_MODULE_INFORMATION smi[1]; } MODULES, *PMODULES; //ZwQuerySystemInformation函式的申明 NTSYSAPI NTSTATUS ZwQuerySystemInformation( IN ULONG SystemInformationClass, IN OUT PVOID SystemInformation, IN ULONG SystemInformationLength, OUT PULONG ReturnLength OPTIONAL ); //這裡是兩個函式的定義 PMODULES GetModuleList() //獲取全部模組 { ULONG dwRet; PMODULES pmodule =(PMODULES)&pmodule; ZwQuerySystemInformation(11,pmodule,0,&dwRet); pmodule=(PMODULES)ExAllocatePoolWithTag(NonPagedPool,dwRet,'mod'); ZwQuerySystemInformation(11,pmodule,dwRet,NULL); return pmodule; } VOID FreeModList(PMODULES pmodule) { ExFreePoolWithTag(pmodule,'mod'); } ULONG GetModBase(PCHAR szName) { ULONG i=0; PMODULES pmodule = GetModuleList(); for (;i<pmodule->dwNumberOfModules;i++) { if (0 == strcmp(szName,(CHAR*)((ULONG)pmodule->smi.ImageName + pmodule->smi.ModuleNameOffset))) { FreeModList(pmodule); return (ULONG)pmodule->smi.Base; } } FreeModList(pmodule); return 0; } ULONG GetModSize(PCHAR szName) { ULONG i=0; ULONG ret=0; PMODULES pmodule = GetModuleList(); for (;i<pmodule->dwNumberOfModules;i++) { if (0 == strcmp(szName,(CHAR*)((ULONG)pmodule->smi.ImageName + pmodule->smi.ModuleNameOffset))) { ret=(ULONG)pmodule->smi.Size; FreeModList(pmodule); return ret; } } FreeModList(pmodule); return 0; } ULONG GetNtReadVirtualMemory() { ULONG ModBaseAddr=0; ULONG ModSize=0; ULONG i=0; BYTE *p; ModBaseAddr=GetModBase("ntoskrnl.exe"); if (ModBaseAddr==0) { ModBaseAddr=GetModBase("ntkrnlpa.exe"); ModSize=GetModSize("ntkrnlpa.exe"); } else { ModSize=GetModSize("ntoskrnl.exe"); } //DbgPrint("ModBaseAddr=%x\n",ModBaseAddr); //DbgPrint("ModSize=%x\n",ModSize); if (ModBaseAddr==0) return 0; p = (BYTE*)ModBaseAddr + 20; //這個主要是設定一下跳過開始的幾個地方少迴圈一些次數 //64 a1 24 01 00 00 8b f8-8a 87 40 01 00 00 88 45 e0 while (1) { if ((*p== 0x64) && (*(p+1) == 0xa1) && (*(p+2) == 0x24) && (*(p+3) == 0x01) && (*(p+4) == 0x00) && (*(p+5) == 0x00) && (*(p+6) == 0x8b) && (*(p+7) == 0xf8) && (*(p+8) == 0x8a) && (*(p+9) == 0x87) && (*(p+10) == 0x40) && (*(p+11) == 0x01) && (*(p+12) == 0x00) && (*(p+13) == 0x00) && (*(p+14) == 0x88) && (*(p+15) == 0x45) && (*(p+16) == 0xe0)) { DbgPrint("NtReadVirtualMemoryAddr=%x\n",(ULONG)p-0x0C); return (ULONG)p-0x0C; } p++; } return 0; } void unload(PDRIVER_OBJECT pO) { KdPrint(("unload\n")); } NTSTATUS DriverEntry( PDRIVER_OBJECT pObj , PUNICODE_STRING path) { pObj->DriverUnload = unload; GetNtReadVirtualMemory(); return STATUS_SUCCESS; }
上面是完整的程式碼,載入驅動後則會輸出該函式的正確地址,此程式碼僅在xp系統下測試通過,其他系統未做測試。新建一個文字檔案,改名為.c,複製程式碼進去直接winddk編譯即可。