iT邦幫忙

2021 iThome 鐵人賽

DAY 4
0
Security

打通任督二脈奇幻之旅 - 用 30 天探索 Windows 底層運作原理系列 第 4

【Day 04】- 今天來把 Module 藏起來(基於 PEB 斷鏈,隱藏 DLL 的方法)

Agenda

  • 資安宣言
  • 測試環境與工具
  • 前情提要
  • 學習目標
  • 技術原理與程式碼
  • References
  • 下期預告

資安宣言


撰寫本系列文章目的在於提升資訊安全之實務能力,
並透過實作體悟到資訊安全領域的重要性,
本系列所有文章之內容皆有一定技術水平,
不得從事非法行為、惡意攻擊等非法活動,
「一切不合法規之行為皆受法律所約束」,
為了避免造成公司、廠商或玩家之間困擾,
所有實作不會拿已上市產品、Online Game 等等來作範例學習,
且部分具有深度、價值之內容,將會提升一定閱讀門檻(不對該技術做分析、解說),
請勿透過本系列文章所學,從事任何非法活動,請不要以身試法!!!


測試環境與工具

前情提要

就在昨天(鐵人賽 Day 03)的時候有說到 Blackbone 的其中一個 DLL Injection 方法,
還沒看的人可以先去快速看一下:【Day 03】- 打針!打針!從 R0 注入的那件事!

在昨天的文章中,最後部分有提到兩個隱藏 Module 的方法,
而就在今天!就是這篇文章!將要來說說第一個隱藏技術:
PEB(Process Environment Block)斷鏈

另外:
明天,也就是鐵人賽 Day 05 將會介紹 PEB 斷鏈的缺點與找出隱藏 Module 的方法,
後天,也就是鐵人賽 Day 06 將會介紹其它更厲害、更難以偵測的隱藏 Module 方法!

最後:
這個 Module 系列就會在鐵人賽 Day 06 結束,
當然,隱藏 Module 的方法百百種,這幾天只簡單的講幾個例子~
鐵人賽 Day 07 將會進入 Process 偽裝、隱藏,甚至是 Rookit 常用的技術。

學習目標

  • 1.了解 PEB(Process Environment Block)
  • 2.完成 PEB 斷鏈,達到隱藏 DLL 效果

技術原理與程式碼

(在閱讀這篇文章之前,應該要確保目前腦袋是清晰的)

首先要先說一下,
小弟我目前還屬於菜鳥階段,正不斷努力學習中,
若有發現錯誤或不妥之處還請不吝賜教。
歡迎大家多多留言,互相交流交流。

那麼進入今天主題~~

第一個問題:
什麼是 PEB(Process Environment Block)???
/images/emoticon/emoticon12.gif

  • PEB(Process Environment Block)
    • Process_Environment_Block
    • 出現在 EProcess 中
    • 出現在 TEB(Thread Environment Block) 中
    • 儲存著 Process「執行中」的相關資訊
    • 包含 Module 資訊
    • 包含 Debug 資訊
    • 包含 Title 資訊
    • 包含 Path 資訊
    • Image Information
    • Heap Information
    • ... ... ...
    • ... ... ...

那這個 PEB 長什麼樣子?可以怎麼看?

  1. 使用 Administrator 權限打開 WinDbg
  2. 選 Kernel Debug...
    • (需要打開測試模式,依照提示的指示進行操作)
  3. 選 Local 按確定
    • (需要打開測試模式,依照提示的指示進行操作)
  4. 輸入 !process 0 0 notepad.exe 查看 notepad.exe
    lkd> !process 0 0 notepad.exe
    PROCESS ffff8f899b178080
        SessionId: 1  Cid: 0b2c    Peb: ecacd59000  ParentCid: 10b8
        DirBase: 984ad000  ObjectTable: ffffa685c250d6c0  HandleCount: 262.
        Image: notepad.exe
    
    • 其中 PROCESS 後面的 Address 是 EProcess Address
    • 繼續看就可以看到 Peb: ecacd59000
    • notepad.exe 的 Peb Address 就是 0xecacd59000
  5. 輸入 !peb ecacd59000 查看 notepad.exe 的 PEB
    • 有一大堆... 大家就自己看了...
  6. 輸入 dt _peb 查看 PEB 結構
    • 這邊要提醒一下,MSDN 上面寫的都不太完整 XD(所以要自己操作)
    • 結果大家可以自己看,太多了:
    lkd> dt _peb
    ntdll!_PEB
       +0x000 InheritedAddressSpace : UChar
       +0x001 ReadImageFileExecOptions : UChar
    
        --- --- --- --- ---
        --- --- --- --- ---
        --- --- --- --- ---
    
       +0x010 ImageBaseAddress : Ptr64 Void
       +0x018 Ldr              : Ptr64 _PEB_LDR_DATA
       +0x020 ProcessParameters : Ptr64 _RTL_USER_PROCESS_PARAMETERS
    
        --- --- --- --- ---
        --- --- --- --- ---
        --- --- --- --- ---
    
        --- --- --- --- ---
        --- --- --- --- ---
        --- --- --- --- ---
    
       +0x3a0 WaitOnAddressHashTable : [128] Ptr64 Void
       +0x7a0 TelemetryCoverageHeader : Ptr64 Void
       +0x7a8 CloudFileFlags   : Uint4B
    

好了,到這邊基本上介紹完 PEB 是什麼了(? XD 已經快速帶過

假設你/妳剛才有仔細看的話,會注意到:

lkd> !peb ecacd59000
PEB at 000000ecacd59000
    InheritedAddressSpace:    No
    ReadImageFileExecOptions: No
    BeingDebugged:            No
--->ImageBaseAddress:         00007ff67a610000
    NtGlobalFlag:             0
    NtGlobalFlag2:            0
--->Ldr                       00007ff9baa6f3a0
    Ldr.Initialized:          Yes
--->Ldr.InInitializationOrderModuleList: 000002de3e212440 . 000002de3e27cb90
--->Ldr.InLoadOrderModuleList:           000002de3e2125b0 . 000002de3e27c320
--->Ldr.InMemoryOrderModuleList:         000002de3e2125c0 . 000002de3e27c330

    --- --- --- ---
    --- --- --- ---
    --- --- --- ---
lkd> dt _peb
ntdll!_PEB
   +0x000 InheritedAddressSpace : UChar
   +0x001 ReadImageFileExecOptions : UChar

    --- --- --- --- ---
    --- --- --- --- ---
    --- --- --- --- ---

-->+0x010 ImageBaseAddress : Ptr64 Void
-->+0x018 Ldr              : Ptr64 _PEB_LDR_DATA
   +0x020 ProcessParameters : Ptr64 _RTL_USER_PROCESS_PARAMETERS

    --- --- --- --- ---
    --- --- --- --- ---
    --- --- --- --- ---

    --- --- --- --- ---
    --- --- --- --- ---
    --- --- --- --- ---

   +0x3a0 WaitOnAddressHashTable : [128] Ptr64 Void
   +0x7a0 TelemetryCoverageHeader : Ptr64 Void
   +0x7a8 CloudFileFlags   : Uint4B

再次進入到 WinDbg 輸入 dt _PEB_LDR_DATA
來看一下 Ldr 的數據類型:

lkd> dt _PEB_LDR_DATA
ntdll!_PEB_LDR_DATA
   +0x000 Length           : Uint4B
   +0x004 Initialized      : UChar
   +0x008 SsHandle         : Ptr64 Void
-->+0x010 InLoadOrderModuleList : _LIST_ENTRY
-->+0x020 InMemoryOrderModuleList : _LIST_ENTRY
-->+0x030 InInitializationOrderModuleList : _LIST_ENTRY
   +0x040 EntryInProgress  : Ptr64 Void
   +0x048 ShutdownInProgress : UChar
   +0x050 ShutdownThreadId : Ptr64 Void

看一下 MSDN 可以知道:

  1. Contains information about the loaded modules for the process.
  2. Each item in the list is a pointer to an LDR_DATA_TABLE_ENTRY structure.

哦!所以 Process 中的 Module 資訊是存在這三個表裡的:

  1. InLoadOrderModuleList
    • 這張表按照「載入」順序排列
  2. InMemoryOrderModuleList
    • 這張表按照「記憶體」順序排列
  3. InInitializationOrderModuleList
    • 這張表按照「初始化」順序排列

然後儲存著它們訊息的 structure 是 LDR_DATA_TABLE_ENTRY

所以再次進入到 WinDbg 輸入 dt _LDR_DATA_TABLE_ENTRY
來看一下 LDR_DATA_TABLE_ENTRY structure 長什麼樣子:

lkd> dt _LDR_DATA_TABLE_ENTRY 
ntdll!_LDR_DATA_TABLE_ENTRY
   +0x000 InLoadOrderLinks : _LIST_ENTRY
   +0x010 InMemoryOrderLinks : _LIST_ENTRY
   +0x020 InInitializationOrderLinks : _LIST_ENTRY
   +0x030 DllBase          : Ptr64 Void
   +0x038 EntryPoint       : Ptr64 Void
   +0x040 SizeOfImage      : Uint4B
   +0x048 FullDllName      : _UNICODE_STRING
   +0x058 BaseDllName      : _UNICODE_STRING
   +0x068 FlagGroup        : [4] UChar
   +0x068 Flags            : Uint4B
   +0x068 PackagedBinary   : Pos 0, 1 Bit
   +0x068 MarkedForRemoval : Pos 1, 1 Bit
   +0x068 ImageDll         : Pos 2, 1 Bit
   +0x068 LoadNotificationsSent : Pos 3, 1 Bit
   +0x068 TelemetryEntryProcessed : Pos 4, 1 Bit
   +0x068 ProcessStaticImport : Pos 5, 1 Bit
   
   --- --- --- ---
   --- --- --- ---
   --- --- --- ---
   
   +0x070 HashLinks        : _LIST_ENTRY
   
   --- --- --- ---
   --- --- --- ---
   --- --- --- ---

所以所以,用一張圖來表達以上資訊:
(如果感覺模糊看不清楚,請複製圖片連結)
(在新分頁打開就可以看高清圖片!)

圖片參考自:33c0c3 大大畫的圖

補充一下:
(因為這是補充的,所以就不詳細寫了)
(多看幾次就會看懂我要寫的內容是什麼 XD)

  1. 假設想看某個 Address 的 PEB 的話:
lkd> dt _PEB 0x000000ec`acd59000
ntdll!_PEB
   +0x000 InheritedAddressSpace : 0 ''
   +0x001 ReadImageFileExecOptions : 0 ''
   
    --- --- --- ---
    --- --- --- ---
    --- --- --- ---
    
   +0x003 IsLongPathAwareProcess : 0y0
   +0x004 Padding0         : [4]  ""
   +0x008 Mutant           : 0xffffffff`ffffffff Void
   +0x010 ImageBaseAddress : 0x00007ff6`7a610000 Void
   +0x018 Ldr              : 0x00007ff9`baa6f3a0 _PEB_LDR_DATA
   
    --- --- --- ---
    --- --- --- ---
    --- --- --- ---
  1. 假設想看某個 Address 的 _PEB_LDR_DATA 的話:
    (地址來自上面的範例)
lkd> dt 0x00007ff9`baa6f3a0 _PEB_LDR_DATA
ntdll!_PEB_LDR_DATA
   +0x000 Length           : 0x58
   +0x004 Initialized      : 0x1 ''
   +0x008 SsHandle         : (null) 
   +0x010 InLoadOrderModuleList : _LIST_ENTRY [ 0x? - 0x? ]
   +0x020 InMemoryOrderModuleList : _LIST_ENTRY [ 0x?0 - 0x? ]
   +0x030 InInitializationOrderModuleList : _LIST_ENTRY [ 0x? - 0x? ]
   +0x040 EntryInProgress  : (null) 
   +0x048 ShutdownInProgress : 0 ''
   +0x050 ShutdownThreadId : (null) 
  1. 假設想看:
    • 某個 Address 的 _PEB_LDR_DATA
    • 裡面的 InLoadOrderModuleList
    • 的 _LIST_ENTRY 的話:
    lkd> dt 0x00007ff9`baa6f3a0+0x010 _LIST_ENTRY
    ntdll!_LIST_ENTRY
     [ 0x000002de`3e2125b0 - 0x000002de`3e27c320 ]
       +0x000 Flink            : 0x? _LIST_ENTRY [ 0x? - 0x? ]
       +0x008 Blink            : 0x? _LIST_ENTRY [ 0x? - 0x? ]
    

到了這邊概念大致上已經講完,
接下來講一下 Module 的隱藏方式:
首先,先回到 Blackbone Project 中的 Loader.c 的 BBUnlinkFromLoader Func

  1. 我們可以看到一開始先透過 PsGetProcessPeb 拿到 EProcess 中的 PEB Address
PPEB pPeb = PsGetProcessPeb( pProcess );
if (!pPeb)
{
    DPRINT( xxxxxxxxxx );
    return STATUS_NOT_FOUND;
}
  1. 開始列舉 InLoadOrderModuleList
for (PLIST_ENTRY pListEntry = pPeb->Ldr->InLoadOrderModuleList.Flink;
      pListEntry != &pPeb->Ldr->InLoadOrderModuleList;
      pListEntry = pListEntry->Flink)
{

--- --- ---
--- --- ---

}
  1. Get LDR_DATA_TABLE_ENTRY Address
xxx pEntry = CONTAINING_RECORD( 
                                pListEntry, 
                                LDR_DATA_TABLE_ENTRY, 
                                InLoadOrderLinks 
                                );
#define CONTAINING_RECORD(address, type, field) ((type *)( \
                                          (PCHAR)(address) - \
                                          (ULONG_PTR)(&((type *)0)->field)))
// addr:   結構體中某個成員地址
// type:   結構體原本的樣子
// field:  結構體的某個成員
//詳細說明請谷哥...
  1. 如果找到要隱藏的 Module 的話就將其斷鏈!
// Unlink
if ((PVOID)pEntry->DllBase == pBase)
{
    RemoveEntryList( &pEntry->InLoadOrderLinks );
    RemoveEntryList( &pEntry->InInitializationOrderLinks );
    RemoveEntryList( &pEntry->InMemoryOrderLinks );
    //RemoveEntryList( &pEntry->HashLinks );
    //最後這個 HashLinks 今天沒時間講了,有空再補充上來。

    break;
}
  1. 斷鏈的意思就是:

    圖片取自:unlink-module-hide
  2. Blackbone 實作的斷鏈方法:
FORCEINLINE
BOOLEAN
RemoveEntryList(
    _In_ PLIST_ENTRY Entry
    )

{

    PLIST_ENTRY PrevEntry;
    PLIST_ENTRY NextEntry;

    NextEntry = Entry->Flink;
    PrevEntry = Entry->Blink;
    if ((NextEntry->Blink != Entry) || (PrevEntry->Flink != Entry)) {
        FatalListEntryError((PVOID)PrevEntry,
                            (PVOID)Entry,
                            (PVOID)NextEntry);
    }

    PrevEntry->Flink = NextEntry;
    NextEntry->Blink = PrevEntry;
    return (BOOLEAN)(PrevEntry == NextEntry);
}
  1. 「普通的」 工具找看看能不能找到 DLL

到這裡,告一段落了!!!!!
不過最後我再補充一項知識,
假設想在 User mode 中做到 PEB 斷鏈有可能嗎?
答案是有的,但是必須想辦法拿到 PEB 的 Address。

我們知道 EProcess 是存在於 Kernel mode,
那要怎麼在 User mode 拿到 PEB 的 Address 呢?
最一開始有說到 PEB 出現在 TEB(Thread Environment Block) 中

TEB(Thread Environment Block):
當 Windows 載入 Process 建立 Thread 時,系統會為每個 Thread 分配 TEB,
且 FS 暫存器會被設置成 FS:0 指向目前 Thread 的 TEB Address,
所以可以拿到 TEB Address。

TEB(Thread Environment Block)結構:

lkd> dt _TEB
ntdll!_TEB
   +0x000 NtTib            : _NT_TIB
   +0x038 EnvironmentPointer : Ptr64 Void
   +0x040 ClientId         : _CLIENT_ID
   +0x050 ActiveRpcHandle  : Ptr64 Void
   +0x058 ThreadLocalStoragePointer : Ptr64 Void
-->+0x060 ProcessEnvironmentBlock : Ptr64 _PEB
   +0x068 LastErrorValue   : Uint4B
   +0x06c CountOfOwnedCriticalSections : Uint4B
   +0x070 CsrClientThread  : Ptr64 Void
   
    --- --- --- ---
    --- --- --- ---
    --- --- --- ---
    
    --- --- --- ---
    --- --- --- ---
    --- --- --- ---

看到了吧?有 Process Environment Block 耶!!!
可以再看一下,位於 0x00 的 NtTib 是 _NT_TIB 結構,

_NT_TIB 結構:

lkd> dt _NT_TIB
ntdll!_NT_TIB
   +0x000 ExceptionList    : Ptr64 _EXCEPTION_REGISTRATION_RECORD
   +0x008 StackBase        : Ptr64 Void
   +0x010 StackLimit       : Ptr64 Void
   +0x018 SubSystemTib     : Ptr64 Void
   +0x020 FiberData        : Ptr64 Void
   +0x020 Version          : Uint4B
   +0x028 ArbitraryUserPointer : Ptr64 Void
->>+0x030 Self             : Ptr64 _NT_TIB

所以呢~所以呢~
直接:

//mov eax,fs:[0x030]    //x86 Get PEB
mov eax,gs:[0x060]      //x64 Get PEB
//mov ecx,[eax + 0x00c] //x86 PEB 的 Ldr
mov ecx,[eax + 0x018]   //x64 PEB 的 Ldr
mov xxx,ecx             //把拿到的 Address 放進 xxx 變數
pop ecx
pop eax

好了,這篇真的結束了!
明天將介紹一些方法,來對抗這種 PEB 斷鏈隱藏 Module 的手法!

如果你/妳有發現哪裡寫得不好或是有地方寫錯,
歡迎留個言,大家一起討論討論鴨~~
那我們下期見 o( ̄▽ ̄)ブ

References

下期預告


上一篇
【Day 03】- 打針!打針!從 R0 注入的那件事!
下一篇
【Day 05 】- 藏起來的 Module 還是被發現了QuO(基於暴力搜尋 PE Header 的方法與解法)
系列文
打通任督二脈奇幻之旅 - 用 30 天探索 Windows 底層運作原理15

尚未有邦友留言

立即登入留言