文件隱藏
常用的文件隱藏方法有基於用戶模式的 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 函數的封裝
端口複用
端口複用也是木馬常用的端口隱藏方法。端口複用是重複使用系統中已經開啓的端口,從而繞過防火牆的攔截。在應用程序進行網絡通信時,需要將本地化和特定的端口綁定到一個套接字上,然後用該套接字進行通信。當系統接收到數據包時,會根據包中的端口號找到特定的應用程序進行轉發。當木馬程序使用端口複用技術接收到數據包後,系統先將該數據包下發給木馬程序判斷模塊,由其根據數據包的特徵結構判斷該數據包應該先轉發給木馬端口複用模塊還是直接轉發給應用程序