木馬核心技術剖析讀書筆記之木馬的隱藏
檔案隱藏
常用的檔案隱藏方法有基於使用者模式的 Rootkit 也有基於核心模式的 Rootkit
基於使用者模式的 Rootkit
在 Windows 中,應用層的大多數功能都是通過呼叫 Native API 完成的。以 Windows7 為例,Native API 通過 sysenter 指令進入核心,進而呼叫 SSDT(System
Service Dispatch Table,系統服務排程表)中對應的函式來完成功能。因此,利用使用者模
式的 Rootkit 實現的檔案隱藏,很容易被使用核心介面列舉檔案的工具繞過,達不到隱
藏的效果
基於核心模式的 Rootkit
基於核心模式 Rootkit 的檔案隱藏,通常對 Windows 核心中相關檔案瀏覽函式掛鉤,甚至對 Windows 檔案系統的核心模組進行修改。此類隱藏方法實現上更偏系統底層,在系統中有更大的控制權限,還能針對大部分反病毒軟體和 Anti-Rootkit,進行有針對性的處理,能達到更好的隱藏效果。
基於檔案列舉函式 Hook 的隱藏
Windows 系統提供了遍歷檔案、目錄的函式 FindFirstFileA,該函式實際上是對 FindFirstFileExw 的簡單封裝。對 FindFirstFileExW 進一步分析後發現,該函式是呼叫 ntdll!ZwQueryDirectoryFile 函式,該函式是核心 Ntoskrnl.exe 實現的,該函式的地址儲存在 SSDT 中
SSDT 是 Windows 核心匯出的一個數據結構。Windows 核心元件中,SSDT 包含了 Windows 執行體提供的系統功能支援介面,這是 Ntoskml.exe 裡實現的系統核心服務SSDT以陣列的形式儲存這些介面對應函式的地址,介面在陣列中的索引也就是該介面的系統呼叫編號,這個索引或者系統呼叫編號可以被用於定位函式的記憶體地址
要掛鉤 ZwqueryDirectowFile 函式,首先需要定位到該函式的地址,再確定採用的 Hook 方式,然後編寫自定義的具體程式碼或者函式
一般可以採用兩種方式對 SSDT 裡的函式進行Hook:
- 直接函式地址替換法,用自己實現函式的地址替換SSDT裡要掛鉤函式的地址
- Inline hook方法,修改 SSDT 中函式頭部的若干個位元組(32位系統中是5位元組),使該函式開始執行時,直接跳轉到自己實現的函式執行
Hook ZwQueryDirectoryFile 函式的具體過程如下:
- 從 SSDT 中獲取函式地址
- 修改核心記憶體保護屬性
- Inline Hook
- 實現自定義函式
基於 FSD Hook 的檔案隱藏
FSDs(File System Driver,檔案系統驅動)主要用來管理系統檔案格式。不同於其他標準的 Windows 核心驅動,FSDs 驅動必須經過 I/O 管理器註冊才能使用,並且在執行的過程中與記憶體管理器產生大量互動操作。另外,為了增強系統性能,檔案系統驅動經常依賴 Windows 系統 cache 管理器的服務。因此,與標準驅動相比,FSDs 大量使用了 Ntoskrnl.exe 的匯出函式
Windows 有兩種型別的檔案系統驅動:
- 本地 FSDs,它管理直接連線到計算機的卷。本地 FSDs 包含了 Ntfs.sys、Fastfat.sys、Exfat.sys、Udfs.sys、Cdfs.sys 和原始 FSD 整合在 Ntoskrnl.exe)等
- 網路 FSDs,它允許使用者訪問遠端連線計算機的卷。木馬只是隱藏本地的檔案,而本地檔案是通過本地 FSDs 管理,因此只需要考慮本地 FSDs 的情況
nt!NtQueryDirectoryFile 函式是核心為使用者模式提供的介面,處在檔案瀏覽功能整個呼叫鏈的頂端,掛鉤該函式實現的檔案隱藏容易被其他反病毒工具或 Anti-Rootkit 工具發現
在 Ntfs.sys 中 Ntfs!NtfaFsdDirectoryControl 函式負責處理 flmgr.sys 發來的 IRP,進一步對 Ntfs!NtfsFsdDirectoryControl 函式進行分析,確定該函式呼叫 ntfs!NtfsCommonDirectoryControl,其又呼叫了 ntfs!NtfsQueryDirectory
匯出表中沒有的函式,不能通過解析 PE 檔案匯出表獲取該函式的地址,需要使用該函式的特徵碼:
1)利用 IDA 等反彙編工具分析該函式的程式碼,選取搜尋使用的特徵碼,也就是一段獨一無二能標明該函式的指令
2)通過解析 ntfs.sys 的檔案格式,獲取到 OEP 的地址
3)通過獲取 ntfs 在記憶體中載入的基址,然後從 ntfs 的 OEP 的虛擬地址開始,使用 1 到的特徵碼搜尋,直至搜尋出想要的函式
基於自定義檔案系統的隱藏
在 Windows 系統中,使用位於核心模式的檔案系統驅動管理檔案系統格式。本地檔案系統驅動通過儲存裝置驅動管理底層的硬碟,並通過 I/O 管理器與上層的應用層進行互動,Windows 在 I/O 管理器之上,針對各種檔案操作的 IRP 進行了封裝,提供了一系列的檔案操作和訪問函式,如 NtcreateFile、NtReadFile、NtwriteFile 和 NtqueryDirectorylnformation 等。檔案系統驅動通過卷驅動管理器以及更下層的分割槽管理器對硬碟J的文科進行管玾和操作
綜合整個過程可以看到,處於硬碟分割槽之上的檔案和資料,都可以通過檔案系統驅動檢視和操作。實際上,在硬碟進行分割槽的時候,經常會有一部分硬碟區域未被劃分為分割槽,作為空白區域存在。由於該部分硬碟空間不屬於任何分割槽,通過檔案系統是無法檢視和操作到該部分硬碟空間的內容。另外,在硬碟的第一個扇區(MBR)和第一個分割槽(VBR)的起始扇區之間的區域也是一部分空白區域,但該部分的空間相對較小
模組隱藏
摘鏈隱藏
Windows 系統中,映象載入器管理程序載入的所有模組,這些載入的模組被維護在與程序相關的 PEB(Process Environment Block)結構中。PEB 包含了映象載入器、堆管理器示其他 Windows 元件需要的資訊。PEB 結構中的成員 Ldr 包含了三個重要的連結串列:
- InLoadOrderModuleList 是 Windows 系統按照模組載入順序維護的一個程序中所有模組資訊的連結串列
- InMemoryOrderModuleList 是 Windows 系統按照記憶體中的儲存順序維護的一個程序中所有模組資訊的連結串列
- InInitializationOrderModuleList 是 Windows 系統按照 DLL 模組被初始化的順序維護的一個程序中所有模組資訊的連結串列
通常,PEB 的地址需要通過 TEB(Thread Environment Block)獲取。TEB 是系統維護的一個與執行緒相關的結構,它儲存映象載入器和多個 Windows DLL 的上下文資訊。由於程序中載入的 DLL 元件執行在使用者模式,它們需要 TEB 在使用者態可寫,故 TEB 和 PEB 是存在於使用者空間而不是系統核心地址空間。在 Windbg 中使用 !thread
命令找到 TEB 的地址。從圖中可以看到,TEB 的地址是 O×7ffdf000,這個地址正好是程序中使用者空間的地址(32 位系統中,使用者空間的地址範圍是 0~O×7FFFFFFF)
在具體的木馬編碼實現時,在 32 位系統中常通過 FS 暫存器的值來得到 TEB 的地址,進而解析 TEB 得到 PEB 地址;在 64 位系統,常通過 GS 暫存器來獲取 TEB 的地址
基於 PE Loader 的隱藏
Windows 系統中有靜態和動態兩種 DLL 模組載入方式:
- 靜態載入方式是指在程序時,核心對程序相關資料結構初始化後,由存在於 ntdll.dll 的映象載入器載入程序。映象載入器對程序主模組的匯入表進行解析,並尋找其依賴的 DLL 模組進行動態載入
- 動態載入方式是利用 Windows 系統提供的兩個 API 函式 LoadLibrary 和 LoadLibraryEx 來載入 DLL 模組
通常,檢視程序所載入模組的工具,都是基於 PEB 結構中的模組資訊連結串列來顯示的。如果不使用系統標準的載入介面,自行將 DLL 模組載入到記憶體並初始化,DLL 模組的資訊則不會被新增在 PEB 的 Ldr 結構中,也就起到了隱藏的效果
要自行載入 DLL 到程序中,則需要模仿 LoadLibrary 等函式,實現一個 DLL 模組的載入器,這個載入器通常稱為 PE Loader。根據對 Windows 映象載入器,尤其是 LoadLibrary 等函式的分析,一個比較典型的 PE 載入器的執行流程如下:
- 分配記憶體,對映對應段,Windows 系統中可執行檔案具有明確的格式,稱為 PE 檔案格式 Portable Executable File Format)。一個 PE 檔案主要包含 DOS 頭部、PE 檔案頭、段表頭和段
- 修復匯入表
- 修復重定位表
- 呼叫 DllMain 初始化 DLL
埠隱藏
Windows XP 下的埠隱藏
通過 Hook tcpip!TCPQueryInformationEx 函式,對該函式執行後返回的結果進行修改就可以實現埠的隱藏,但是由於 tcpip.sys 沒有匯出 TcpQueryInformationEx 和 TCPDispatchDeviceControl 函式,需要使用特徵碼搜尋才能確定這些函式的地址,相比
較而言,tcpip!TCPDispatchDevicecontrol位於tcpip!TCPQueryInformationEx 的上層,若從 tcpip!GsDriverEntry 函式開始搜尋,更容易先搜尋到 tcpip!TCPDispatchDeviceControl 函式,故這裡確定 Hook tcpip!TCPDispatchDeviceControl 函式,實現網路通訊埠隱藏
Vista 之後的埠隱藏
從 Windows Vista 系統開始,系統核心網路元件發生了較大變化。網路連線資訊不再由 tcpip.sys 進行管理,而是引入了新的核心網路元件 Nsiproxy.sys 和 netio.sys 等
以 Windows 7 系統為例,對它自帶的 netstat.exe 可執行檔案格式進行分析,從它的匯入表中發現了多個從 IPHLPAPI.dll 匯入的函式,但沒有發現 GetTcpStatsFromStackEx 函式。根據函式名稱觀察,IPHLPAPI!InternalGetTcpTable2 函式比較可疑。
經過除錯分析,該函式被 netstat.exe 用來獲取埠和網路連線。在 IPHLPAPI.dll 中,
IPHLPAPI!InternalGetTcpTable2 函式呼叫 IPHLPAPI!GetTcpTable2 函式來實現功能,而 IPHLPAPI!GetTcpTable2 函式是對 IPHLPAPI!GetTcpTableIntenal 函式的封裝
埠複用
埠複用也是木馬常用的埠隱藏方法。埠複用是重複使用系統中已經開啟的埠,從而繞過防火牆的攔截。在應用程式進行網路通訊時,需要將本地化和特定的埠繫結到一個套接字上,然後用該套接字進行通訊。當系統接收到資料包時,會根據包中的埠號找到特定的應用程式進行轉發。當木馬程式使用埠複用技術接收到資料包後,系統先將該資料包下發給木馬程式判斷模組,由其根據資料包的特徵結構判斷該資料包應該先轉發給木馬埠複用模組還是直接轉發給應用程式