日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

(36)内核空间与内核模块,遍历内核模块链表

發布時間:2025/3/21 编程问答 17 豆豆
生活随笔 收集整理的這篇文章主要介紹了 (36)内核空间与内核模块,遍历内核模块链表 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、內核空間

每個進程的低2G都是獨立的,而高2G是共享的。

我們可以做一個小實驗,在一個進程的高2G定義申請一塊內存,去另一個進程里,用相同的線性地址讀取,會發現是同一塊物理內存。

驅動A

#include <ntddk.h>NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path); VOID DriverUnload(PDRIVER_OBJECT driver);// 高2G申請一塊內存 UINT32 g_H2GValue = 0;// 入口函數,相當于main NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path) {g_H2GValue = 0x20201018;DbgPrint("[%p]: %08X\n", &g_H2GValue, g_H2GValue);driver->DriverUnload = DriverUnload;return STATUS_SUCCESS; }// 卸載函數 VOID DriverUnload(PDRIVER_OBJECT driver) {DbgPrint("驅動卸載成功\n"); }

驅動B

#include <ntddk.h>NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path); VOID DriverUnload(PDRIVER_OBJECT driver);// 入口函數,相當于main NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path) {PUINT32 pUint32 = (PUINT32)0xF88DB01C; // 驅動A變量的線性地址,這個值是驅動A打印的DbgPrint("驅動B讀取驅動A的變量值: %08X\n", *pUint32);driver->DriverUnload = DriverUnload;return STATUS_SUCCESS; }// 卸載函數 VOID DriverUnload(PDRIVER_OBJECT driver) {DbgPrint("驅動卸載成功\n"); }

二、內核模塊,驅動名字的由來

高2G里有許多模塊,操作系統內核(如101012分頁的ntoskrnl.exe)也在其中。接下來的課后試驗我們會編程遍歷高2G模塊。

內核模塊一般是.sys,也可以是其他格式,他們都遵循PE格式。

我們經常說“驅動”,這個名字的來源其實是內核程序入口函數的參數。

NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path);

PDRIVER_OBJECT 驅動對象,就是驅動這個名字的由來。

三、PDRIVER_OBJECT 驅動對象

我們可以在windbg中查看 _DRIVER_OBJECT 結構體:

kd> dt _DRIVER_OBJECT ntdll!_DRIVER_OBJECT+0x000 Type : Int2B+0x002 Size : Int2B+0x004 DeviceObject : Ptr32 _DEVICE_OBJECT+0x008 Flags : Uint4B+0x00c DriverStart : Ptr32 Void+0x010 DriverSize : Uint4B+0x014 DriverSection : Ptr32 Void+0x018 DriverExtension : Ptr32 _DRIVER_EXTENSION+0x01c DriverName : _UNICODE_STRING+0x024 HardwareDatabase : Ptr32 _UNICODE_STRING+0x028 FastIoDispatch : Ptr32 _FAST_IO_DISPATCH+0x02c DriverInit : Ptr32 long +0x030 DriverStartIo : Ptr32 void +0x034 DriverUnload : Ptr32 void +0x038 MajorFunction : [28] Ptr32 long

挑幾個比較重要的屬性來說明:

DriverStart:驅動在內存中的基址
DriverSize:驅動在內存中的大小
DriverSection:內核模塊鏈表基址(這個待會詳細說)
DriverName:驅動名

這樣看起來干巴巴的,干脆我們寫一個驅動,看看它里面這個結構的數據是長什么樣的:

#include <ntddk.h>// 卸載函數 VOID DriverUnload(PDRIVER_OBJECT driver) {DbgPrint("驅動程序停止運行了.\r\n"); }// 入口函數,相當于main NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path) { DbgPrint("PDRIVER_OBJECT: %p %wZ\n",driver,reg_path);// 設置一個卸載函數,便于退出driver->DriverUnload = DriverUnload;return STATUS_SUCCESS; }

在windbg中查看這個驅動進程的 _DRIVER_OBJECT 結構體:

kd> dt _DRIVER_OBJECT 81ECC880 ntdll!_DRIVER_OBJECT+0x000 Type : 0n4+0x002 Size : 0n168+0x004 DeviceObject : (null) +0x008 Flags : 0x12+0x00c DriverStart : 0xf8910000 Void+0x010 DriverSize : 0x6000+0x014 DriverSection : 0x81d65498 Void+0x018 DriverExtension : 0x81ecc928 _DRIVER_EXTENSION+0x01c DriverName : _UNICODE_STRING "\Driver\內核編程基礎"+0x024 HardwareDatabase : 0x80690a90 _UNICODE_STRING "\REGISTRY\MACHINE\HARDWARE\DESCRIPTION\SYSTEM"+0x028 FastIoDispatch : (null) +0x02c DriverInit : 0xf8911020 long _empty_!DriverEntry+0+0x030 DriverStartIo : (null) +0x034 DriverUnload : 0xf8911000 void _empty_!DriverUnload+0+0x038 MajorFunction : [28] 0x804fb87e long nt!IopInvalidDeviceRequest+0

可以看到,除了剛才說的幾個屬性,HardwareDatabase 其實就是入口函數第二個參數,我們用工具注冊驅動時,就是在注冊表里做了修改,用的就是這個字符串。

接下來,著重介紹 DriverSection 屬性。

四、DriverSection / 內核模塊鏈表

在windbg 中查看 DriverSection 屬性,類型是 void,它實際上是 _LDR_DATA_TABLE_ENTRY 類型。這個結構體我在上一篇《3環PEB斷鏈》中介紹了,它是一個鏈表的項。

kd> dt _LDR_DATA_TABLE_ENTRY ntdll!_LDR_DATA_TABLE_ENTRY+0x000 InLoadOrderLinks : _LIST_ENTRY+0x008 InMemoryOrderLinks : _LIST_ENTRY+0x010 InInitializationOrderLinks : _LIST_ENTRY+0x018 DllBase : Ptr32 Void+0x01c EntryPoint : Ptr32 Void+0x020 SizeOfImage : Uint4B+0x024 FullDllName : _UNICODE_STRING+0x02c BaseDllName : _UNICODE_STRING+0x034 Flags : Uint4B+0x038 LoadCount : Uint2B+0x03a TlsIndex : Uint2B+0x03c HashLinks : _LIST_ENTRY+0x03c SectionPointer : Ptr32 Void+0x040 CheckSum : Uint4B+0x044 TimeDateStamp : Uint4B+0x044 LoadedImports : Ptr32 Void+0x048 EntryPointActivationContext : Ptr32 Void+0x04c PatchInformation : Ptr32 Void

和3環有點區別,在0環中InMemoryOrderLinks 和 InInitializationOrderLinks 是沒用的,只需要關注第一個鏈表 InLoadOrderLinks。_LIST_ENTRY 這個結構體存了兩個地址,指向前一個節點和下一個節點:

kd> dt _LIST_ENTRY ntdll!_LIST_ENTRY+0x000 Flink : Ptr32 _LIST_ENTRY+0x004 Blink : Ptr32 _LIST_ENTRY

這里多一句嘴,我們在Windows中見到過很多"ENTRY"了,PDE PTE,還有這里的LIST_ENTRY,這個ENTRY其實就是“項”的意思。

通過這個 InLoadOrderLinks,我們可以遍歷整個高2G的模塊了。InLoadOrderLinks.Flink 指向的就是下一個 _LDR_DATA_TABLE_ENTRY。下面給出遍歷內核模塊鏈表的代碼:

#include <ntddk.h>typedef struct _LDR_DATA_TABLE_ENTRY {LIST_ENTRY InLoadOrderLinks;LIST_ENTRY InMemoryOrderLinks;LIST_ENTRY InInitializationOrderLinks;PVOID DllBase;PVOID EntryPoint;UINT32 SizeOfImage;UNICODE_STRING FullDllName;UNICODE_STRING BaseDllName;UINT32 Flags;UINT16 LoadCount;UINT16 TlsIndex;LIST_ENTRY HashLinks;PVOID SectionPointer;UINT32 CheckSum;UINT32 TimeDateStamp;PVOID LoadedImports;PVOID EntryPointActivationContext;PVOID PatchInformation; } LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path); VOID DriverUnload(PDRIVER_OBJECT driver);// 入口函數,相當于main NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path) {PLDR_DATA_TABLE_ENTRY pLdteHead; // 內核模塊鏈表頭PLDR_DATA_TABLE_ENTRY pLdteCur; // 遍歷指針pLdteHead = (PLDR_DATA_TABLE_ENTRY)driver->DriverSection;pLdteCur = pLdteHead;do {PLDR_DATA_TABLE_ENTRY pLdte = CONTAINING_RECORD(pLdteCur, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);DbgPrint("DllBase: %p, SizeOfImage: %08X %wZ\n", pLdteCur->DllBase, pLdteCur->SizeOfImage, &(pLdteCur->FullDllName));pLdteCur = (PLDR_DATA_TABLE_ENTRY)pLdteCur->InLoadOrderLinks.Flink;} while (pLdteHead != pLdteCur);driver->DriverUnload = DriverUnload;return STATUS_SUCCESS; }// 卸載函數 VOID DriverUnload(PDRIVER_OBJECT driver) {DbgPrint("驅動卸載成功\n"); }

注意,windbg 無法打印帶中文的 UNICODE_STRING,建議把項目名取成全英文。
還有一點,這里打印的內核模塊名是寫死的 ntoskrnl.exe,即使系統當前是 2-9-9-12分頁,這是XP的BUG。
https://bbs.pediy.com/thread-97717.htm

五、定位未導出函數 PspTerminateProcess

課上老師和同學給出了幾種辦法,接下來分別介紹。比如,我們想找 PspTerminateProcess 這個函數,這是一個未導出函數,用來殺進程的。

1.windbg+pdb

在有內核PDB的情況下,用windbg可以直接找到該函數:

kd> u PspTerminateProcess l40 nt!PspTerminateProcess: 8062f050 8bff mov edi,edi 8062f052 55 push ebp 8062f053 8bec mov ebp,esp 8062f055 56 push esi 8062f056 64a124010000 mov eax,dword ptr fs:[00000124h] 8062f05c 8b7508 mov esi,dword ptr [ebp+8] 8062f05f 3b7044 cmp esi,dword ptr [eax+44h] 8062f062 7507 jne nt!PspTerminateProcess+0x1b (8062f06b) 8062f064 b80d0000c0 mov eax,0C000000Dh 8062f069 eb5a jmp nt!PspTerminateProcess+0x75 (8062f0c5) 8062f06b 57 push edi 8062f06c 8dbe48020000 lea edi,[esi+248h] 8062f072 f6470120 test byte ptr [edi+1],20h 8062f076 7412 je nt!PspTerminateProcess+0x3a (8062f08a) 8062f078 8d8674010000 lea eax,[esi+174h] 8062f07e 50 push eax 8062f07f 56 push esi 8062f080 68caf06280 push offset nt!NtTerminateProcess+0x14c (8062f0ca) 8062f085 e800feffff call nt!PspCatchCriticalBreak (8062ee8a) 8062f08a 6a08 push 8 8062f08c 58 pop eax 8062f08d f00907 lock or dword ptr [edi],eax 8062f090 6a00 push 0 8062f092 56 push esi 8062f093 e854faf4ff call nt!PsGetNextProcessThread (8057eaec) 8062f098 8bf8 mov edi,eax 8062f09a 85ff test edi,edi 8062f09c 741e je nt!PspTerminateProcess+0x6c (8062f0bc) 8062f09e ff750c push dword ptr [ebp+0Ch] 8062f0a1 57 push edi 8062f0a2 e824d3f4ff call nt!PspTerminateThreadByPointer (8057c3cb) 8062f0a7 57 push edi 8062f0a8 56 push esi 8062f0a9 e83efaf4ff call nt!PsGetNextProcessThread (8057eaec) 8062f0ae 8bf8 mov edi,eax 8062f0b0 85ff test edi,edi 8062f0b2 75ea jne nt!PspTerminateProcess+0x4e (8062f09e) 8062f0b4 3986bc000000 cmp dword ptr [esi+0BCh],eax 8062f0ba 7406 je nt!PspTerminateProcess+0x72 (8062f0c2) 8062f0bc 56 push esi 8062f0bd e882c1ffff call nt!ObClearProcessHandleTable (8062b244) 8062f0c2 33c0 xor eax,eax 8062f0c4 5f pop edi 8062f0c5 5e pop esi 8062f0c6 5d pop ebp 8062f0c7 c20800 ret 8

8062f050 就是函數頭,然而這個值由于重定位,可能會變的,所以我們就要用其他辦法,確保每次都能找到這個函數。

2.通過已導出函數

第二種辦法是根據已導出函數找未導出函數,在驅動里找已導出函數使用的函數是 MmGetSystemRoutineAddress 。我們通過IDA交叉引用,并沒有找到調用 PspTerminateProcess 的導出函數。

所以,這種辦法在這里無法使用。

3.模塊基址+偏移

雖然模塊基址會變,但是函數相對基址的偏移是不變的,通過這個規律也可以找到想要的函數。
PspTerminateProcess 相對內核基址的偏移 = 8062f050 - 804D8000 = 157050
只要找到內核基址,加上 0x157050 就是 PspTerminateProcess 的地址。

這種方法我就不貼代碼了,因為原理比較簡單。

4.特征碼匹配(最常用)

特征碼提取時,要避免使用全局變量等和重定位有關的指令,也要避免提取這種所有函數都有的指令。

8062f050 8bff mov edi,edi 8062f052 55 push ebp 8062f053 8bec mov ebp,esp

看看函數頭部的匯編:

kd> u PspTerminateProcess l10 nt!PspTerminateProcess: 8062f050 8bff mov edi,edi 8062f052 55 push ebp 8062f053 8bec mov ebp,esp 8062f055 56 push esi 8062f056 64a124010000 mov eax,dword ptr fs:[00000124h] 8062f05c 8b7508 mov esi,dword ptr [ebp+8] 8062f05f 3b7044 cmp esi,dword ptr [eax+44h] 8062f062 7507 jne nt!PspTerminateProcess+0x1b (8062f06b) 8062f064 b80d0000c0 mov eax,0C000000Dh 8062f069 eb5a jmp nt!PspTerminateProcess+0x75 (8062f0c5) 8062f06b 57 push edi 8062f06c 8dbe48020000 lea edi,[esi+248h] 8062f072 f6470120 test byte ptr [edi+1],20h 8062f076 7412 je nt!PspTerminateProcess+0x3a (8062f08a) 8062f078 8d8674010000 lea eax,[esi+174h] 8062f07e 50 push eax

選取這部分作為特征碼:

8062f056 64a124010000 mov eax,dword ptr fs:[00000124h] 8062f05c 8b7508 mov esi,dword ptr [ebp+8] 8062f05f 3b7044 cmp esi,dword ptr [eax+44h] 8062f062 7507 jne nt!PspTerminateProcess+0x1b (8062f06b) 8062f064 b80d0000c0 mov eax,0C000000Dh 8062f069 eb5a jmp nt!PspTerminateProcess+0x75 (8062f0c5) 8062f06b 57 push edi 8062f06c 8dbe48020000 lea edi,[esi+248h] 8062f072 f6470120 test byte ptr [edi+1],20h 8062f076 7412 je nt!PspTerminateProcess+0x3a (8062f08a) 8062f078 8d8674010000 lea eax,[esi+174h]

用dd打印一下:

接下來編程只需要找這段作為特征碼匹配即可。代碼如下:

#include <ntddk.h>typedef struct _LDR_DATA_TABLE_ENTRY {LIST_ENTRY InLoadOrderLinks;LIST_ENTRY InMemoryOrderLinks;LIST_ENTRY InInitializationOrderLinks;PVOID DllBase;PVOID EntryPoint;UINT32 SizeOfImage;UNICODE_STRING FullDllName;UNICODE_STRING BaseDllName;UINT32 Flags;UINT16 LoadCount;UINT16 TlsIndex;LIST_ENTRY HashLinks;PVOID SectionPointer;UINT32 CheckSum;UINT32 TimeDateStamp;PVOID LoadedImports;PVOID EntryPointActivationContext;PVOID PatchInformation; } LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;// 函數聲明 NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path); VOID GetKernelBase(PDRIVER_OBJECT driver, PVOID *pKrnlBase, PUINT32 uKrnlImageSize); PVOID MemorySearch(PVOID bytecode, UINT32 bytecodeLen, PVOID pBeginAddress, PVOID pEndAddress); VOID DriverUnload(PDRIVER_OBJECT driver); typedef NTSTATUS (*_PspTerminateProcess)(PEPROCESS pEprocess, NTSTATUS ExitCode); _PspTerminateProcess PspTerminateProcess;// 入口函數 NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path) {UINT32 bytecode[] = {0x0124a164, 0x758b0000, 0x44703b08, 0x0db80775,0xebc00000, 0xbe8d575a, 0x00000248, 0x200147f6,0x868d1274, 0x00000174};PVOID pKrnlBase; // 內核基址UINT32 uKrnlImageSize; // 內核大小PEPROCESS pEprocess; // 要關閉的進程的EPROCESS// 獲取內核模塊基址和大小GetKernelBase(driver, &pKrnlBase, &uKrnlImageSize);DbgPrint("內核基址: %p,大小: %X\n", pKrnlBase, uKrnlImageSize);// 獲取 PspTerminateProcess 函數地址PspTerminateProcess = (_PspTerminateProcess)((UINT32)MemorySearch( \bytecode,sizeof(bytecode),pKrnlBase,(PVOID)((UINT32)pKrnlBase+uKrnlImageSize)) - 6);DbgPrint("PspTerminateProcess: %p\n", PspTerminateProcess);// 根據PID獲取EPROCESSPsLookupProcessByProcessId((HANDLE)1796,&pEprocess); // 記事本PID是1796// 調用 PspTerminateProcess 關閉進程PspTerminateProcess(pEprocess, 0);DbgPrint("記事本進程被 PspTerminateProcess 函數關閉了.\n");driver->DriverUnload = DriverUnload;return STATUS_SUCCESS; }// 獲取內核基址,大小 VOID GetKernelBase(PDRIVER_OBJECT driver, PVOID *pKrnlBase, PUINT32 uKrnlImageSize) {PLDR_DATA_TABLE_ENTRY pLdteHead; // 內核模塊鏈表頭PLDR_DATA_TABLE_ENTRY pLdteCur; // 遍歷指針UNICODE_STRING usKrnlBaseDllName; // 內核模塊名RtlInitUnicodeString(&usKrnlBaseDllName,L"ntoskrnl.exe");pLdteHead = (PLDR_DATA_TABLE_ENTRY)driver->DriverSection;pLdteCur = pLdteHead;do {PLDR_DATA_TABLE_ENTRY pLdte = CONTAINING_RECORD(pLdteCur, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);//DbgPrint("DllBase: %p, SizeOfImage: %08X %wZ\n", pLdteCur->DllBase, pLdteCur->SizeOfImage, &(pLdteCur->FullDllName));if (RtlCompareUnicodeString(&pLdteCur->BaseDllName, &usKrnlBaseDllName, TRUE) == 0){*pKrnlBase = pLdteCur->DllBase;*uKrnlImageSize = pLdteCur->SizeOfImage;return;}pLdteCur = (PLDR_DATA_TABLE_ENTRY)pLdteCur->InLoadOrderLinks.Flink;} while (pLdteHead != pLdteCur);return; }// 特征碼搜索 PVOID MemorySearch(PVOID bytecode, UINT32 bytecodeLen, PVOID pBeginAddress, PVOID pEndAddress) {PVOID pCur = pBeginAddress;while (pCur != pEndAddress){if (RtlCompareMemory(bytecode,pCur,bytecodeLen) == bytecodeLen){return pCur;}((UINT32)pCur)++;}return 0; }// 卸載驅動 VOID DriverUnload(PDRIVER_OBJECT driver) {DbgPrint("驅動卸載成功\n"); }

驅動運行前:

驅動運行后:

總結

以上是生活随笔為你收集整理的(36)内核空间与内核模块,遍历内核模块链表的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。