利用C++實現模組隱藏(R3層斷鏈)

  • 2019 年 10 月 14 日
  • 筆記

一、模組隱藏的實現原理

  普通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。

  https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-tsts/a11e7129-685b-4535-8d37-21d4596ac057?redirectedfrom=MSDN

  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++斷鏈來實現模組隱藏

  如果你看懂上面分析,則源程式碼非常好理解。

  1 // 隱藏模組.cpp : 此文件包含 "main" 函數。程式執行將在此處開始並結束。    2 //    3    4 #include "pch.h"    5 #include <iostream>    6 #include <Windows.h>    7    8    9 /* 所需要的結構體   10 1. _LDR_DATA_TABLE_ENTRY 鏈表指向數據   11 2. _PEB_LDR_DATA 表示其 PEB0x處指向的數據表   12 3. _LIST_ENTRY 指針指向的鏈表   13 */   14   15 typedef struct _LSA_UNICODE_STRING {   16     USHORT Length;   17     USHORT MaximumLength;   18     PWSTR  Buffer;   19 }   20 UNICODE_STRING, *PUNICODE_STRING;   21   22 typedef struct _PEB_LDR_DATA   23 {   24     DWORD Length; // +0x00   25     bool Initialized; // +0x04   26     PVOID SsHandle; // +0x08   27     LIST_ENTRY InLoadOrderModuleList; // +0x0c   28     LIST_ENTRY InMemoryOrderModuleList; // +0x14   29     LIST_ENTRY InInitializationOrderModuleList;// +0x1c   30 } PEB_LDR_DATA, *PPEB_LDR_DATA; // +0x24   31   32 typedef struct _LDR_MODULE   33 {   34     LIST_ENTRY          InLoadOrderModuleList;   35     LIST_ENTRY          InMemoryOrderModuleList;   36     LIST_ENTRY          InInitializationOrderModuleList;   37     void*               BaseAddress;   38     void*               EntryPoint;   39     ULONG               SizeOfImage;   40     UNICODE_STRING   FullDllName;   41     UNICODE_STRING      BaseDllName;   42     ULONG               Flags;   43     SHORT               LoadCount;   44     SHORT               TlsIndex;   45     HANDLE              SectionHandle;   46     ULONG               CheckSum;   47     ULONG               TimeDateStamp;   48 } LDR_MODULE, *PLDR_MODULE;   49   50 //所謂模組句柄,即該模組的入口地址   51 void hide_module(char* szDllName)   52 {   53     HMODULE hMod = GetModuleHandleA(szDllName);   54     PLIST_ENTRY Head, Cur;   55     PPEB_LDR_DATA ldr;   56     PLDR_MODULE ldm;   57     __asm   58     {   59         mov eax, fs:[0x30]   60         mov ecx, [eax + 0x0c] //Ldr     61         mov ldr, ecx   62     }   63     Head = &(ldr->InLoadOrderModuleList);   64     Cur = Head->Flink;   65     do   66     {   67         ldm = CONTAINING_RECORD(Cur, LDR_MODULE, InLoadOrderModuleList);   68         if (hMod == ldm->BaseAddress)   69         {   70             // 三個鏈表同時給斷掉   71             ldm->InLoadOrderModuleList.Blink->Flink =   72                 ldm->InLoadOrderModuleList.Flink;   73             ldm->InLoadOrderModuleList.Flink->Blink =   74                 ldm->InLoadOrderModuleList.Blink;   75   76             //   77             ldm->InInitializationOrderModuleList.Blink->Flink =   78                 ldm->InInitializationOrderModuleList.Flink;   79             ldm->InInitializationOrderModuleList.Flink->Blink =   80                 ldm->InInitializationOrderModuleList.Blink;   81   82             //   83             ldm->InMemoryOrderModuleList.Blink->Flink =   84                 ldm->InMemoryOrderModuleList.Flink;   85             ldm->InMemoryOrderModuleList.Flink->Blink =   86                 ldm->InMemoryOrderModuleList.Blink;   87             break;   88         }   89         Cur = Cur->Flink;   90     } while (Head != Cur);   91 }   92   93   94   95 int main()   96 {   97     // 通過模組名,來獲取模組句柄   98     printf("****按任意鍵隱藏模組*****");   99     getchar();  100     hide_module((char*)"kernel32.dll");  101     printf("****隱藏模組完成*****");  102     getchar();  103     getchar();  104  105 }