利用C++ R3層斷鏈實現模組隱藏功能
一、模組隱藏的實現原理
普通API查詢模組實現思路:其通過查詢在R3中的PEB(Process Environment Block 程序環境塊)與TEB(Thread Environment Block 程序環境塊)來找到一個雙向連結串列,通過遍歷雙向連結串列中某一成員(字串)來查詢全部模組。
模組隱藏實現思路:在R3層的模組隱藏,我們需要做的就是將其該連結串列斷鏈,將某一模組從這個雙向連結串列中摘除,這樣再呼叫傳統的API時就會搜尋不到。
二、結構體成員詳細介紹
<1> TEB結構體 -- 記憶體地址為 fs:[0] 處。
使用Windbg的 "dt _TEB"命令來檢視TEB結構體
kd> dt _TEB ntdll!_TEB +0x000 NtTib : _NT_TIB +0x01c EnvironmentPointer : Ptr32 Void +0x020 ClientId : _CLIENT_ID +0x028 ActiveRpcHandle : Ptr32 Void +0x02c ThreadLocalStoragePointer : Ptr32 Void +0x030 ProcessEnvironmentBlock : Ptr32 _PEB +0x034 LastErrorValue : Uint4B
1. 屬性介紹
1.1)_NT_TIB:重點兩個屬性,棧頂與棧大小。
http://www.nirsoft.net/kernel_struct/vista/NT_TIB.html
1.2) _CLIENT_ID: 儲存該程序ID與當前主執行緒ID。
1.3) _PEB:程序環境塊 ,記住其在 TEB 偏移 0x30處即可。
2. 通過olldbg檢視該結構體
2.1) 開啟任意程序,在暫存器視窗找到 fs:[0],檢視其記憶體地址。
2.2) 在記憶體視窗使用命令 "db 5E7000" 跳轉到該記憶體,使用地址格式(長型-地址)顯示。
<2> PEB結構體 -- fs:[0x30]
使用 Windbg 指令 dt _PEB 檢視 PEB結構體,重點關注最後一個 程序載入資訊表。
kd> dt _PEB ntdll!_PEB +0x000 InheritedAddressSpace : UChar +0x001 ReadImageFileExecOptions : UChar +0x002 BeingDebugged : UChar +0x003 BitField : UChar +0x003 ImageUsesLargePages : Pos 0, 1 Bit +0x003 IsProtectedProcess : Pos 1, 1 Bit +0x003 IsLegacyProcess : Pos 2, 1 Bit +0x003 IsImageDynamicallyRelocated : Pos 3, 1 Bit +0x003 SkipPatchingUser32Forwarders : Pos 4, 1 Bit +0x003 SpareBits : Pos 5, 3 Bits +0x004 Mutant : Ptr32 Void +0x008 ImageBaseAddress : Ptr32 Void +0x00c Ldr : Ptr32 _PEB_LDR_DATA // PEB_LOADER_DATA 程序載入資訊表
1. 檢視 _PEB_LDR_DATA 程序載入資訊表 的結構體
1.1)重點關注 0x00c處的指標,其指向 _PEB_LDR_DATA 這個結構體,在這個結構體中 0x00c、0x014、0x01c 分別表示 模組載入順序 / 載入後在記憶體中的順序 / 模組初始化的順序。
kd > dt _PEB_LDR_DATA ntdll!_PEB_LDR_DATA + 0x000 Length : Uint4B + 0x004 Initialized : UChar + 0x008 SsHandle : Ptr32 Void + 0x00c InLoadOrderModuleList : _LIST_ENTRY // 模組載入順序 + 0x014 InMemoryOrderModuleList : _LIST_ENTRY // 載入後在記憶體中的順序 + 0x01c InInitializationOrderModuleList : _LIST_ENTRY // 模組初始化的順序 + 0x024 EntryInProgress : Ptr32 Void + 0x028 ShutdownInProgress : UChar + 0x02c ShutdownThreadId : Ptr32 Void
2.2)理解其三個成員的順序,其指向_LDR_DATA_TABLE_ENTRY元素中開始的三個成員,而 _LDR_DATA_TABLE_ENTRY 中儲存著就是關於有關模組資訊的元素(比如模組名等)
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 // 入口函式(對於 exe 模組有效) + 0x020 SizeOfImage : Uint4B // 模組大小 + 0x024 FullDllName : _UNICODE_STRING // 完成模組名稱(帶路徑) + 0x02c BaseDllName : _UNICODE_STRING // 模組名稱 + 0x034 Flags : Uint4B
2. 使用olldbg來檢視查詢首先載入模組的模組名稱(TEB->PEB-> InLoadOrderModuleList -> BaseDllName)
2.1)接之前TEB內容查詢到PEB的所在位置 fs:[0x30]。
2.2) 在其0x00c處發現InLoadOrderModuleList成員,其指向的是一個_LDR_DATA_TABLE_ENTRY 結構體。
2.3) 跳轉到 _LDR_DATA_TABLE_ENTRY 結構體,從0x0c開始依次是三個 _LIST_ENTRY 結構體,該結構體雙向連結串列儲存著兩個地址。
2.4)選中第一個進入,在其偏移0x02c處(UNICODE結構體佔四字),可以檢視字串名稱。
2.5)通過開頭 _LIST_ENTRY結構體可以遍歷前一個模組的內容和下一個模組的內容。
三、利用C++斷鏈來實現模組隱藏
如果你看懂上面分析,則原始碼非常好理解。
// 隱藏模組.cpp : 此檔案包含 "main" 函式。程式執行將在此處開始並結束。 // #include "pch.h" #include <iostream> #include <Windows.h> /* 所需要的結構體 1. _LDR_DATA_TABLE_ENTRY 連結串列指向資料 2. _PEB_LDR_DATA 表示其 PEB0x處指向的資料表 3. _LIST_ENTRY 指標指向的連結串列 */ typedef struct _LSA_UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWSTR Buffer; } UNICODE_STRING, *PUNICODE_STRING; typedef struct _PEB_LDR_DATA { DWORD Length; // +0x00 bool Initialized; // +0x04 PVOID SsHandle; // +0x08 LIST_ENTRY InLoadOrderModuleList; // +0x0c LIST_ENTRY InMemoryOrderModuleList; // +0x14 LIST_ENTRY InInitializationOrderModuleList;// +0x1c } PEB_LDR_DATA, *PPEB_LDR_DATA; // +0x24 typedef struct _LDR_MODULE { LIST_ENTRY InLoadOrderModuleList; LIST_ENTRY InMemoryOrderModuleList; LIST_ENTRY InInitializationOrderModuleList; void* BaseAddress; void* EntryPoint; ULONG SizeOfImage; UNICODE_STRING FullDllName; UNICODE_STRING BaseDllName; ULONG Flags; SHORT LoadCount; SHORT TlsIndex; HANDLE SectionHandle; ULONG CheckSum; ULONG TimeDateStamp; } LDR_MODULE, *PLDR_MODULE; //所謂模組控制代碼,即該模組的入口地址 void hide_module(char* szDllName) { HMODULE hMod = GetModuleHandleA(szDllName); PLIST_ENTRY Head, Cur; PPEB_LDR_DATA ldr; PLDR_MODULE ldm; __asm { mov eax, fs:[0x30] mov ecx, [eax + 0x0c] //Ldr mov ldr, ecx } Head = &(ldr->InLoadOrderModuleList); Cur = Head->Flink; do { ldm = CONTAINING_RECORD(Cur, LDR_MODULE, InLoadOrderModuleList); if (hMod == ldm->BaseAddress) { // 三個連結串列同時給斷掉 ldm->InLoadOrderModuleList.Blink->Flink = ldm->InLoadOrderModuleList.Flink; ldm->InLoadOrderModuleList.Flink->Blink = ldm->InLoadOrderModuleList.Blink; // ldm->InInitializationOrderModuleList.Blink->Flink = ldm->InInitializationOrderModuleList.Flink; ldm->InInitializationOrderModuleList.Flink->Blink = ldm->InInitializationOrderModuleList.Blink; // ldm->InMemoryOrderModuleList.Blink->Flink = ldm->InMemoryOrderModuleList.Flink; ldm->InMemoryOrderModuleList.Flink->Blink = ldm->InMemoryOrderModuleList.Blink; break; } Cur = Cur->Flink; } while (Head != Cur); } int main() { // 通過模組名,來獲取模組控制代碼 printf("****按任意鍵隱藏模組*****"); getchar(); hide_module((char*)"kernel32.dll"); printf("****隱藏模組完成*****"); getchar(); getchar(); }
總結
以上所述是小編給大家介紹的利用C++ R3層斷鏈實現模組隱藏功能,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回覆大家的。在此也非常感謝大家對碼農教程網站的支援!
如果你覺得本文對你有幫助,歡迎轉載,煩請註明出處,謝謝!