◆勞 偉
?
64位Windows環境的安全軟件調試過程淺析
◆勞 偉
(中國農業銀行軟件開發中心 北京 100000)
64位Windows下的安全軟件驅動程序普遍通過ObRegisterCallbacks內核函數注冊進程、線程操作回調函數實現自我保護,其調試方法與普通軟件存在較大差異。本文描述了調試安全軟件時解除進程和線程保護機制的操作過程,分析、證實了微軟未公開的進、線程操作CallbackList鏈表結構。
64位;Windows;安全軟件;ObRegisterCallbacks;CallbackList;調試
安全軟件作為軟件的一種,運行出現問題時同樣需要對其進行調試以定位原因。64位Windows系統引入了PatchGuard機制對內核的關鍵位置進行了監控,阻斷未經授權的內核改動,因此安全軟件普遍采用了微軟官方推薦的通過ObRegisterCallbacks向內核注冊進程、線程操作回調函數的自我保護機制,其調試過程相應地出現了很多變化。
進線程自我保護是指采用相關的技術保護自身的進程和線程不被跟蹤調試或強行結束。
64位Windows環境下實現進線程自我保護的主要方法如下:
(1)采用Ring3 API Inline Hook[1]。該方法修改API函數的前幾個字節,執行跳轉指令至保護函數,通過保護函數對潛在的威脅進行阻斷。該方法安全穩定,但容易被繞過。
(2)破壞PatchGuard保護層,對內核中的API函數進行Inline Hook[2]。該方法易引發爭議,商業類安全軟件基本不會采用該方法。
(3)通過ObRegisterCallbacks向內核注冊進、線程句柄操作回調函數[3]。該方法為微軟官方所推薦,也是目前安全軟件在64位Windows環境下的常規做法。該方法通過內核回調安全軟件注冊的函數對進、線程操作進行監控,對包括跟蹤、調試在內的潛在威脅操作進行阻斷。本文描述的調試過程針對的就是采用此類保護機制的安全軟件。
調試通過ObRegisterCallbacks注冊進、線程保護機制的安全軟件,首先需要解除安全軟件注冊的回調函數,通常采取網絡或串口雙機調試的方式進行操作。
為簡化描述,本文稱被調試的64位Windows系統為目標系統,執行WinDbg輸入調試指令的Windows系統為Host系統。
(1)選擇該方式的目標系統需同時滿足Win8或以上版本、網卡符合微軟所列型號的要求[4]。在目標系統以管理員身份執行下列命令配置內核網絡調試模式。
bcdedit /debug on
bcdedit /dbgsettings net hostip:A.B.C.D port:N
A.B.C.D為Host系統的IP,N為WinDbg接收調試連接的監聽端口。上述操作成功時,目標系統生成一個會話Key并回顯至控制臺,保存下來以便在Host系統配置WinDbg時使用。
目標系統有多個網卡時,需執行下列命令以指定向Host系統發送調試信息、接收調試指令的網卡。
bcdedit /set "{dbgsettings}" busparams BusNo.DevNo.FuncNo
打開設備管理器網卡屬性的常規標簽即可查看網卡PCI總線號(BusNo)、設備號(DevNo)、功能號(FuncNo)等參數。
完成上述操作后,關閉目標系統。
(2)在Host系統以管理員身份運行WinDbg, 點選Kernel Debug內核調試菜單項,打開NET網絡標簽頁,輸入監聽端口號及目標系統生成的會話Key。
點確定按鈕后,開始等待目標系統建立網絡連接,如圖1所示。

圖1 等待目標系統建立連接示意
目標系統部署在虛擬機環境時,推薦采用此方式在宿主機系統上對目標系統進行調試。
(1)以COM1串口連接為例,在目標系統以管理員身份執行下列命令。
bcdedit /debug on
bcdedit /dbgsettings serial debugport:1 baudrate:115200
(2)在宿主機系統上設置虛擬機管道串口,如圖2所示。

圖2 虛擬機管道串口設置示意
(3)在宿主機系統上以管理員身份運行WinDbg, 點選Kernel Debug菜單項,打開COM標簽頁,BandRate波特率輸入115200,Port輸入\.pipedebug,勾選Pipe和Reconnect選項。
點擊確定按鈕,即開始等待目標系統建立串口連接。
注意,WinDbg不保存內核調試參數,如需多次調試,推薦創建帶運行參數的快捷方式以保存相關的設置。以串口調試為例,快捷方式中目標欄的運行參數可設置為:
-k com:pipe,port=\.pipedebug,resets=0,reconnect
啟動目標系統,確認安全軟件完成啟動后,在WinDbg指令窗口上按Ctrl+Break(或Ctrl+Fn+B)中斷目標系統進入調試狀態。
(1)首先確認進程操作回調函數鏈表頭的位置,即Process對象類型中CallbackList成員的內存地址。
執行下列指令查看Process對象類型的結構。
dt nt!_OBJECT_TYPE poi(nt!PsProcessType)

圖3 Win7 x64的Process對象類型的結構示意
poi()指令獲取指針所指對象的地址。PsProcessType是指向Process對象類型的指針。如圖3左下淺灰標記所示,Win7 x64系統的進程操作回調函數鏈表頭偏移位置是0xc0,因該位置在不同系統之間存在差異,本文后續表述用PlistHeadOffset對其進行指代。
(2)CallbackList是由表頭節點和各表項節點構成的雙向環形鏈表。迄今為止微軟未公開表項節點的結構,已知的推論均源自相關人員對內核代碼的靜態分析[5]。
typedef struct _CALLBACK_ENTRY_ITEM {
LIST_ENTRY EntryItemList; // 16B
OB_OPERATION Operations; // 4B
ULONG Active; // 4B
CALLBACK_ENTRY* CallbackEntry; // 8B
POBJECT_TYPE ObjectType; // 8B
POB_PRE_OPERATION_CALLBACK PreOperation; // 8B
POB_POST_OPERATION_CALLBACK PostOperation; // 8B
__int64 unk; // 8B
} CALLBACK_ENTRY_ITEM,
*PCALLBACK_ENTRY_ITEM;
表項節點的成員推論如下:EntryItemList為指向下一節點的FLINK指針和指向上一節點的BLINK指針;Operations為操作類型的集合;Active為節點活動標記;CallbackEntry為指向回調入口的指針;ObjectType為指向觸發回調的對象類型的指針;PreOperation和PostOperation為操作前、后回調的函數指針。
(3)執行下條指令顯示進程操作回調函數鏈表的內容。
!list -x "dq @$extret L8"
poi(nt!PsProcessType)+PlistHeadOffset

圖4 進程操作回調函數鏈表內容示意
如圖4所示,fffffa80`01847d00是進程操作回調函數鏈表頭的內存地址,減去偏移量0xc0等于fffffa80`01847c40,即目標系統內核中的Process對象類型的內存地址。
推論指出各表項節點的Operations、ObjectType、PreOperation的偏移位置分別為0x10、0x20、0x28。
表頭節點為LIST_ENTRY結構,包括FLINK和BLINK兩個分別指向第一個和最后一個表項節點的指針,無表項節點時兩指針均指向表頭,故圖4右上劃線標記處并非PreOperation,灰灰底標記處是否屬于安全軟件注冊的PreOperation,還需進一步核實。
安全軟件通過驅動程序注冊進程操作回調函數時,傳給內核的參數需滿足下列要求[6]:
Operations:打開句柄、復制句柄操作均觸發回調機制時,傳入數值3。
ObjectType:傳入PsProcessType,即操作Process對象類型時觸發回調機制。
PreOperation:傳入安全軟件駐留內存的操作前回調函數的入口地址。
圖4各表項節點的Operations(中間欄每塊第二行劃線標記低位4字節)均為3;ObjectType均指向Process對象類型(地址fffffa80`01847c40);PreOperation指向地址(灰底標記)均位于安全軟件驅動程序可駐留的內存地址范圍內。綜上所述,圖4各表項節點均具有進程操作回調節點的特征。
(4)執行下條指令顯示進程操作前回調函數所屬的模塊信息,以確認安全軟件的進程操作回調節點。
!list -x ".if (poi(@$extret+0x28) < 0) {lmv a
poi(@$extret+0x28) }" poi(nt!PsProcessType)+ PlistHeadOffset

圖5 進程操作前回調函數模塊信息示意
根據模塊信息中的文件名稱、時間戳、校驗值等數據可以從網上搜索、判定其所屬的機構。經核實,圖5所示的模塊均為安全軟件的驅動模塊,分屬兩家軟件商。
(5)解除目標系統安全軟件的進程保護,需將相關節點的進程操作前回調的函數指針清0。下列指令清除圖4中的前兩處函數指針(灰底標記位置)。
eq 0xfffff8a0`00512d98 0; 0xfffff8a0`0040f9c8 0
解除了進程保護機制之后,還需進一步解除安全軟件的線程保護機制。
(1)確認線程操作回調函數鏈表頭的位置,即Thread對象類型中CallbackList成員的內存地址。
首先執行下列指令查看Thread對象類型的結構。
dt nt!_OBJECT_TYPE poi(nt!PsThreadType)

圖6 Win7 x64的Thread對象類型的結構示意
PsThreadType是指向內核Thread對象類型的指針。
Win7 x64系統的線程操作回調函數鏈表頭的偏移位置是0xc0,如圖6中灰底標記所示,因該位置在不同系統之間存在差異,本文后續表述用TlistHeadOffset對其進行指代。
根據結構推論,線程操作回調函數鏈表由表頭節點和各表項節點組成,各表項節點均為CALLBACK_ENTRY_ITEM結構。
(2)執行下條指令顯示線程操作回調函數鏈表的內容。
!list -x "dq @$extret L8"
poi(nt!PsThreadType)+ TlistHeadOffset

圖7 線程操作回調函數鏈表內容示意
目標系統的線程操作回調函數鏈表頭的內存地址如圖7所示為fffffa80`01847bb0,減去偏移量0xc0等于fffffa80`01847af0即目標系統Thread對象類型的內存地址。
推論指出各表項節點的Operations、ObjectType、PreOperation的偏移位置分別為0x10、0x20、0x28。
表頭節點為LIST_ENTRY結構,只包括兩個指針,故圖7藍色標記處并非PreOperation。
安全軟件通過驅動程序注冊線程操作回調函數時,傳給內核的參數需滿足下列要求[6]:
Operations:打開句柄、復制句柄操作均觸發回調機制時,傳入數值3。
ObjectType:傳入PsThreadType,即操作Thread對象類型時觸發回調機制。
PreOperation:傳入安全軟件駐留內存的操作前回調函數的入口地址。
圖7各表項節點的Operations均為3;ObjectType均指向了Thread對象類型(地址fffffa80`01847af0);PreOperation指向地址(灰底標記)均位于安全軟件驅動程序可駐留的內存地址范圍內。綜上所述,圖7各表項節點均具有線程操作回調節點的特征。
(3)執行下條指令顯示線程操作前回調函數所屬的模塊信息,以確認安全軟件的線程操作回調節點。
!list -x ".if (poi(@$extret+0x28) < 0) { lmv a
poi(@$extret+0x28)}"poi(nt!PsThreadType)+TlistHeadOffset
(4)將相關節點的線程操作前的回調函數指針清0,以解除目標系統安全軟件的線程保護。下列指令清除圖7中的前兩處函數指針(灰底標記位置)。
eq 0xfffff8a0`00512dd8 0; eq 0xfffff8a0`0040fa08 0
解除安全軟件的進、線程保護機制后,將目標系統恢復至運行狀態。
在目標系統中啟動相關的調試軟件,如圖8所示,調試軟件成功附加安全軟件的相關進程,證實了結構推論的有效性。

圖8 調試軟件附加安全軟件進程的示意
安全軟件和其他軟件的進程調試過程沒有本質的差異,故本文不再對其展開描述。需要注意的是結束調試前,調試軟件需脫離被調試的進程,以避免安全軟件出現異常。
調試完畢后,在被調試的目標系統上以管理員身份執行bcdedit /debug off命令,關閉內核調試模式,重啟系統將目標系統恢復至進線程保護機制完全生效的狀態。
64位Windows環境下調試安全軟件時需要通過雙機調試的方式解除內核中安全軟件注冊的進、線程操作回調函數。本文通過具體的調試過程,分析、證實了微軟未公開的CallbackList鏈表結構,為開展安全軟件調試工作提供了依據和參考。
[1]Hack214.另類繞過Ring3下inline hook[J].黑客防線,2008.
[2]Kelvyn Taylor.PatchGuard觸安全廠商軟肋[N].每周電腦報,2007.
[3]tianhz.教你在64位Win7系統下使用ObRegisterCallbacks內核函數來實現進程保護[EB/OL].https://bbs.pediy.com/thread-189926.htm.
[4]Microsoft.Setting Up Kernel-Mode Debugging over a NetworkCable Manually[EB/OL].https://docs.microsoft. com/ en-us/windows-hardware/drivers/debugger/setting-up-a-network-debugging-connection.
[5]douggem.ObRegisterCallbacksandcountermeasures. [EB/OL].https://douggemhax.wordpress.com/2015/05/27/obregistercallbacks-and-countermeasures.
[6]Microsoft._OB_OPERATION_REGISTRATION structure[EB/OL].https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/ns-wdm-_ob_operation_registration.