iT邦幫忙

2021 iThome 鐵人賽

DAY 6
0
Security

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

【Day 06 】- Module 隱藏大法,不可能再被發現了吧 / _ \(基於 VAD 斷鏈的隱藏方法)

Agenda

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

資安宣言


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


測試環境與工具

學習目標

  • 1.了解 VadRoot 二元樹
  • 2.列舉 VadRoot 二元樹
  • 3.修改 VadRoot 二元樹,達成隱藏 DLL 的效果
  • 4.探討 User mode 對 VadRoot 的影響

技術原理與程式碼

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

那就開始今天的主題囉~

一、什麼是 VadRoot 二元樹?

二、這棵樹長什麼樣子?

  1. 使用 Administrator 權限打開 WinDbg
  2. 選 Kernel Debug...
    • (需要打開測試模式,依照提示的指示進行操作)
  3. 選 Local 按確定
    • (需要打開測試模式,依照提示的指示進行操作)
  4. 輸入 !process 0 0 notepad.exe
    • 查看 notepad.exe
    lkd> !process 0 0 notepad.exe
    PROCESS ffffe30717823080
        SessionId: 1  Cid: 1534    Peb: a1635df000  ParentCid: 1054
        DirBase: 1285ff000  ObjectTable: ffffcf0189d605c0  HandleCount: 260.
        Image: notepad.exe
    
  5. 輸入 dt _eprocess ffffe30717823080
    • 查看 notepad.exe 的 EProcess
    lkd> dt _eprocess ffffe30717823080
    nt!_EPROCESS
       +0x000 Pcb              : _KPROCESS
       +0x2d8 ProcessLock      : _EX_PUSH_LOCK
       +0x2e0 UniqueProcessId  : 0x00000000`00001534 Void
    
        --- --- --- ---
        --- --- --- ---
        --- --- --- ---
    
        --- --- --- ---
        --- --- --- ---
        --- --- --- ---
    
    -->+0x628 VadRoot          : _RTL_AVL_TREE
       +0x630 VadHint          : 0xffffe307`16a0a650 Void
       +0x638 VadCount         : 0x7a
       +0x640 VadPhysicalPages : 0
       +0x648 VadPhysicalPagesLimit : 0
    
        --- --- --- ---
        --- --- --- ---
        --- --- --- ---
    
  6. 輸入 !vad
    • 第二個查看 VAD 的方式:
    • MSDN-vad


      (太多了列不完,大家自己看)

好,到這大概已經簡單帶過什麼是 VAD 了 XD

現在要來寫個 Driver 列舉這棵樹的 DLL 資訊,
當然,我們得先來看看這棵樹的整體樣貌,以及了解到底要「列舉什麼」:

  1. 在 EProcess(ffffd88966e915c0)中看到有個 _RTL_AVL_TREE 類型的 VadRoot:
    • 輸入 dt _eprocess ffffd88966e915c0
    +0x628 VadRoot          : _RTL_AVL_TREE 
    
    • 太多找不到 VadRoot?
    • 輸入:dt _eprocess ffffd88966e915c0 VadRoot
    • 會直接幫你過濾:
    lkd> dt _eprocess ffffd88966e915c0 vadroot
    nt!_EPROCESS
       +0x628 VadRoot : _RTL_AVL_TREE
    
  2. 輸入 dt _RTL_AVL_TREE
    • 查看 _RTL_AVL_TREE 長什麼樣子
    lkd> dt _RTL_AVL_TREE
    nt!_RTL_AVL_TREE
       +0x000 Root             : Ptr64 _RTL_BALANCED_NODE
    
    • 發現這是一個「根」
  3. 輸入 dt ffffd88966e915c0+0x628 _RTL_BALANCED_NODE
    • 查看 Root 底下有什麼東東
    lkd> dt ffffd88966e915c0+0x628 _RTL_BALANCED_NODE
    nt!_RTL_BALANCED_NODE
       +0x000 Children         : [2] 0xffffd889`6858fd10 _RTL_BALANCED_NODE
       +0x000 Left             : 0xffffd889`6858fd10 _RTL_BALANCED_NODE
       +0x008 Right            : 0xffffd889`6864ec80 _RTL_BALANCED_NODE
       +0x010 Red              : 0y0
       +0x010 Balance          : 0y00
       +0x010 ParentValue      : 0x78
    
    • 這邊可以看出這個節點有兩個 Children,
    • 分別是 Left、Right,每個 Children 都有個指針
    • 進去 Children 看的話可以發現和 Left、Right 是一樣的:
    lkd> dt ffffd88966e915c0+0x628 _RTL_BALANCED_NODE Children[0]
    nt!_RTL_BALANCED_NODE
       +0x000 Children    : [0] 0xffffd889`6858fd10 _RTL_BALANCED_NODE
    lkd> dt ffffd88966e915c0+0x628 _RTL_BALANCED_NODE Children[1]
    nt!_RTL_BALANCED_NODE
       +0x000 Children    : [1] 0xffffd889`6864ec80 _RTL_BALANCED_NODE
    
  4. 往 Left 走
    lkd> dt 0xffffd889`6858fd10 _RTL_BALANCED_NODE
    nt!_RTL_BALANCED_NODE
       +0x000 Children         : [2] 0xffffd889`671c59d0 _RTL_BALANCED_NODE
       +0x000 Left             : 0xffffd889`671c59d0 _RTL_BALANCED_NODE
       +0x008 Right            : 0xffffd889`687a8a90 _RTL_BALANCED_NODE
       +0x010 Red              : 0y1
       +0x010 Balance          : 0y01
       +0x010 ParentValue      : 1
    
  5. 標籤 TAG:
    • 每個節點都會有個 TAG
    • 這個 TAG 可以讓我們知道:這個節點的「範圍」儲存了什麼樣的資訊
    • 而每個 TAG 都儲存在這個節點位置的 -C 位置
    • 意思就是:
    • 0xffffd889`687a8a90 這個節點要往前 C 個位置
    • 0xffffd889`687a8a90 -c = 0xFFFFD889687A8A84
    • 所以輸入:db FFFFD889687A8A84
    lkd> db FFFFD889687A8A84
    ffffd889`687a8a84  56 61 64 -- -- 67  Vad zi.. `.\.8.g
    ffffd889`687a8a94  89 d8 ff -- -- 68  ......vh......Xh
    ffffd889`687a8aa4  89 d8 ff -- -- 00  ................
    
    --- --- --- ---
    --- --- --- ---
    --- --- --- ---
    
  6. Vad Tag
    • 節點的 Tag 是 Vad 就代表是 _MMVAD Structure
    • 為什麼我知道?某本書說的,我忘了哪一本。
    • 再來就是舊版本的 Windows 就是 _MMVAD Structure
    • 所以不難猜到 _MMVAD Structure
    • 輸入 dt _MMVAD
    lkd> dt _MMVAD
    nt!_MMVAD
       +0x000 Core             : _MMVAD_SHORT
       +0x040 u2               : <unnamed-tag>
       +0x048 Subsection       : Ptr64 _SUBSECTION
       +0x050 FirstPrototypePte : Ptr64 _MMPTE
       +0x058 LastContiguousPte : Ptr64 _MMPTE
       +0x060 ViewLinks        : _LIST_ENTRY
       +0x070 VadsProcess      : Ptr64 _EPROCESS
       +0x078 u4               : <unnamed-tag>
       +0x080 FileObject       : Ptr64 _FILE_OBJECT
    
  7. 輸入:dt _mmvad 0xffffd889687a8a90
    • 查看這個節點的 _MMVAD 內容
    lkd> dt _mmvad 0xffffd889687a8a90
    nt!_MMVAD
       +0x000 Core             : _MMVAD_SHORT
       +0x040 u2               : <unnamed-tag>
       +0x048 Subsection       : 0xffffd889`66c422d0 _SUBSECTION
       +0x050 FirstPrototypePte : 0xffff9d02`3a34e2c0 _MMPTE
       +0x058 LastContiguousPte : 0xffff9d02`3a34e390 _MMPTE
       +0x060 ViewLinks        : _LIST_ENTRY [ 0x?? - 0x?? ]
       +0x070 VadsProcess      : 0xffffd889`66e915c1 _EPROCESS
       +0x078 u4               : <unnamed-tag>
       +0x080 FileObject       : (null) 
    
  8. 輸入:dt 0xffffd889`66c422d0 _SUBSECTION
    • 重點要看 Subsection
    • 因為這裡面包含我們要找的東東
    • 我每個都看過了,所以直接告訴大家捷徑
    lkd> dt 0xffffd889`66c422d0 _SUBSECTION
    nt!_SUBSECTION
       +0x000 ControlArea      : 0xffffd889`66c42250 _CONTROL_AREA
       +0x008 SubsectionBase   : 0x?? _MMPTE
       +0x010 NextSubsection   : 0x?? _SUBSECTION
       +0x018 GlobalPerSessionHead : _RTL_AVL_TREE
       +0x018 CreationWaitList : (null) 
       +0x018 SessionDriverProtos : (null) 
       +0x020 u                : <unnamed-tag>
       +0x024 StartingSector   : 0
       +0x028 NumberOfFullSectors : 2
       +0x02c PtesInSubsection : 1
       +0x030 u1               : <unnamed-tag>
       +0x034 UnusedPtes       : 0y00.. ..00 (0)
       +0x034 ExtentQueryNeeded : 0y0
       +0x034 DirtyPages       : 0y0
       +0x034 u2               : <unnamed-tag>
    
  9. 輸入 dt 0xffffd889`66c42250 _CONTROL_AREA
    • 重點要看 ControlArea
    • 因為這裡面包含我們要找的東東
    • 我每個都看過了,所以直接告訴大家捷徑
    lkd> dt 0xffffd889`66c42250 _CONTROL_AREA
    nt!_CONTROL_AREA
       +0x000 Segment          : 0xffff9d02`3a2f7690 _SEGMENT
       +0x008 ListHead         : _LIST_ENTRY [ 0x?? - 0x?? ]
       +0x018 NumberOfSectionReferences : 1
       +0x020 NumberOfPfnReferences : 0x1b
       +0x028 NumberOfMappedViews : 0x3c
       +0x030 NumberOfUserReferences : 0x3d
       +0x038 u                : <unnamed-tag>
       +0x03c u1               : <unnamed-tag>
       +0x040 FilePointer      : _EX_FAST_REF
       +0x048 ControlAreaLock  : 0n0
       +0x04c ModifiedWriteCount : 0
       +0x050 WaitList         : (null) 
       +0x058 u2               : <unnamed-tag>
       +0x068 FileObjectLock   : _EX_PUSH_LOCK
       +0x070 LockedPages      : 1
       +0x078 u3               : <unnamed-tag>
    
  10. 輸入:dt 0xffffd889`66c42250 _CONTROL_AREA FilePointer.
    • 查看 FilePointer 地址
    lkd> dt 0xffffd889`66c42250 _CONTROL_AREA FilePointer.
    nt!_CONTROL_AREA
       +0x040 FilePointer  : 
          +0x000 Object       : 0xffffd889`66c4423e Void
          +0x000 RefCnt       : 0y1110
          +0x000 Value        : 0xffffd889`66c4423e
    
  11. 輸入:dt _file_object 0xffffd88966c44230
    • 把第十步拿到的地址,最後面那個數字改成「0」
    • 因為最後面那個數字會隨機變化:1 - F
    • 然後這就是我們要找的東西
    • 下面可以看到 FileName 了
    • 手動列舉到了 profapi.dll 這個 DLL
    lkd> dt _file_object 0xffffd88966c44230
    nt!_FILE_OBJECT
       +0x000 Type             : 0n5
       +0x002 Size             : 0n216
       +0x008 DeviceObject     : 0x?? _DEVICE_OBJECT
       +0x010 Vpb              : 0x?? _VPB
       +0x018 FsContext        : 0x?? Void
       +0x020 FsContext2       : 0x?? Void
       +0x028 SectionObjectPointer : 0x?? _SECTION_OBJECT_POINTERS
       +0x030 PrivateCacheMap  : (null) 
       +0x038 FinalStatus      : 0n0
       +0x040 RelatedFileObject : 0x?? _FILE_OBJECT
       +0x048 LockOperation    : 0 ''
       +0x049 DeletePending    : 0 ''
       +0x04a ReadAccess       : 0x1 ''
       +0x04b WriteAccess      : 0 ''
       +0x04c DeleteAccess     : 0 ''
       +0x04d SharedRead       : 0x1 ''
       +0x04e SharedWrite      : 0 ''
       +0x04f SharedDelete     : 0x1 ''
       +0x050 Flags            : 0x44042
    ->>+0x058 FileName         : _UNICODE_STRING "\Windows\System32\profapi.dll"
       +0x068 CurrentByteOffset : _LARGE_INTEGER 0x0
       +0x070 Waiters          : 0
       +0x074 Busy             : 0
       +0x078 LastLock         : (null) 
       +0x080 Lock             : _KEVENT
       +0x098 Event            : _KEVENT
       +0x0b0 CompletionContext : (null) 
       +0x0b8 IrpListLock      : 0
       +0x0c0 IrpList          : _LIST_ENTRY [ 0x?? - 0x?? ]
       +0x0d0 FileObjectExtension : (null)
    

所以我們的 Driver 要做什麼事情ㄋ??
第一種方法:

  1. 先找到要隱藏的目標,我們稱這個節點為 X
  2. 將 X 的下一個節點和 X 上面的節點合併(連起來)就可以了

第二種方法:

  1. 直接砍掉 FILE OBJECT(這個不好,因為不怎麼穩定)

第三種方法:

  1. 改 _FILE_OBJECT 的 Pointer

效果呈現如下:
隱藏前:

隱藏後:

/images/emoticon/emoticon01.gif

成功擊敗 Process Explorer :D

注意:已經有按照名稱排序。

最後說一下,當我自己寫 Driver 快寫好的時候..
我發現 Blackbone 已經把 VAD Unlink 寫好了!!!
差點吐血 XDD
/images/emoticon/emoticon05.gif

所以大家想看原始碼就到 Blackbone 看吧~~
附上連結在這...
就不講解程式碼了,因為我沒時間了,因為我把時間拿去寫 Driver 了.. ...

以上所有實作、測試都是在 16299.15 測試的,其它版本邏輯一樣,作法不同。

然後最好是不要想把 ntdll.dll 隱藏,
因為當系統需要它的時候又找不到時,就會...XDDDDDDDDDDDDDDDDD

分享一下寫這個驅動時常常看到的畫面:

/images/emoticon/emoticon08.gif

最後簡單整理了一下思維:

  1. [R0、R3] PEB 斷鏈是為了防止:
    • CreateToolhelp32Snapshot/EnumProcessModulesEx... ...
    • 等等的這些 API 列舉到注入的 DLL。
  2. [R0、R3] 刪除 PE 頭部(MZ/0x5A4D/Header)
    • 是為了防止透過暴力搜尋 PE 特徵來找出 DLL。
  3. [R0] 修改 VadRoot
    • 是為了防止透過 ZwQueryVirtualMemory 之類的低效率方式找到 DLL。

所以還有沒有找出 DLL 的方法??
答:有阿 XD
只要 DLL 存在就一定會占用記憶體空間,
某個 DLL 與某個 DLL 之間的記憶體地址差距
相較其它 DLL 來的大是不是就感覺有點問題?
東西就在 Memory 裡面,不管躲得再好都有機會找出來,

以下補充:
在 User mode 有沒有什麼其他有趣的 DLL 隱藏法?
答:有,請發揮你/妳的想像力,想像力就是你/妳的超能力!
這裡來講幾個 XD

  1. 研究 FreeLibrary 函數
    • 能不能執行這個函數,然後只執行「一半」就好?
  2. 備份還原法
    • 先正常 Load DLL
    • 然後「備份起來」
    • 再正常 FreeLibrary 將 DLL 完整卸載
    • 再還原一開始備份好的數據 XD
  3. 自建 LoadLibrary 函數
    • 既然正常載入都會有痕跡
    • 那我乾脆自己實現載入 DLL 的功能
    • 這樣是不是就能避開所有系統已知的預設痕跡了呢?

好啦,這篇/這個 Module 系列就到這結束了!

大家若有發現哪裡寫得不好或錯誤的地方,都留個言討論一下吧 XD
那我們下期見 o( ̄▽ ̄)ブ

References

下期預告


上一篇
【Day 05 】- 藏起來的 Module 還是被發現了QuO(基於暴力搜尋 PE Header 的方法與解法)
下一篇
【Day 07】- 惡搞行程(Process)、鍵盤(Keyboard)大作戰之序章
系列文
打通任督二脈奇幻之旅 - 用 30 天探索 Windows 底層運作原理15

尚未有邦友留言

立即登入留言