http://dy.163.com/v2/article/detail/F70H7RS20511CJ6O.htmlhtml
本文,我將介紹對AuxKlibQueryModuleInformation進行逆向工程的解決方案。其中,咱們會提到驅動程序可使用記錄的API AuxKlibQueryModuleInformation枚舉全部加載的模塊。這個API是否保證返回的模塊列表老是最新的?爲了回答這個問題,咱們要對Windows 8的AuxKlibQueryModuleInformation進行逆向工程,並解釋它是如何工做的。當多個線程請求訪問加載的模塊列表時,它如何處理這種狀況?注意:處理此請求和其餘請求的內部函數至關大,所以須要一些耐心。或者,你可使用調試器來幫助你跟蹤感興趣的代碼。
python
首先,讓咱們將其劃分爲咱們須要執行的任務:git
1.這個API (AuxKlibQueryModuleInformation)是否保證返回的模塊列表老是最新的?github
2.對Windows 8的AuxKlibQueryModuleInformation進行逆向工程,並解釋其工做原理。json
3.當多個線程請求訪問已加載的模塊列表時,它如何處理這種狀況?windows
爲了解決這個問題,咱們將使用IDA對函數進行靜態反向工程。在本文的示例中,咱們將不使用反編譯器來讀取彙編代碼。使用反編譯器能夠節省大量時間,可是學習如何在反彙編窗口中導航對於逆向工程頗有價值。安全
什麼是「AuxKlibQueryModuleInformation」?框架
MSDN: The AuxKlibQueryModuleInformation例程檢索有關操做系統已加載的映像模塊的信息。函數
NTSTATUS AuxKlibQueryModuleInformation(工具
PULONG BufferSize,
ULONG ElementSize,
PVOID QueryInfo
);
聽起來它應該返回一個映像列表,不過,這些只是猜想,這些映像是加載到內存中的映像仍是僅加載到內核中的映像,咱們稍後將對此進行檢查。
所以,要回答第一個問題,咱們的第一個子任務是找到' AuxKlibQueryModuleInformation '是在哪裏實現的。
在搜索ntoskrnl.exe時,咱們沒有找到該函數。這意味着它必須在其餘地方聲明,經過查看該文檔,咱們就能夠看到這個函數是在aux_klib.h中聲明的,所需的庫是aux_klib.lib。
LIB文件是靜態庫,該靜態庫就是一個包含目標文件的歸檔文件,連接器可使用這些目標文件將代碼添加到二進制文件中。如今,咱們知道這個函數是在aux_klib.lib中定義的。
咱們能夠用 「dumpbin.exe」 工具來反彙編lib 文件,咱們還能夠編譯一個使用AuxKlibQueryModuleInformation的驅動程序,而後查看編譯後的二進制文件,看看結果如何。使用dumpbin.exe快速檢查後,能夠看到函數調用以下:=
AuxKlibQueryModuleInformation:
.....
.....
lea r9,[rsp+20h] ; ReturnLength
mov r8d,esi ; SystemInformationLength
mov rdx,rbx ; SystemInformation
mov ecx,0Bh ; SystemInformationClass
call qword ptr [__imp_ZwQuerySystemInformation] ; <-------
.....
.....
能夠看到,AuxKlibQueryModuleInformation使用ZwQuerySystemInformation來查詢模塊列表,該函數的定義爲:
NTSTATUS WINAPI ZwQuerySystemInformation(
_In_ SYSTEM_INFORMATION_CLASS SystemInformationClass,
_Inout_ PVOID SystemInformation,
_In_ ULONG SystemInformationLength,
_Out_opt_ PULONG ReturnLength
);
SystemInformationClass是咱們想要查詢的信息類型,正如你在反彙編中看到的,這個類等於0xb,可是0xb是什麼呢?
一般,我會搜索SYSTEM_INFORMATION_CLASS枚舉定義,但咱們將查看ZwQuerySystemInformation的實際實現以找出SystemInformationClass,以瞭解如何能夠作到。
此函數是在在ntoskrnl.exe中實現的,以下所示:
這個函數是Zw函數,你能夠點擊這裏讀取更多關於Zw函數的信息。
Zw函數調用Nt函數,並將PreviousMode更改成KernelMode。所以,jmp KiServiceInternal在後臺調用NtQuerySystemInformation。ntdll中存在相同的名稱,但ntoskrnl.exe包含系統調用的實際實現。若是你查看ntdll,會看到相同的ID是在執行「syscall」。
這是由於內核Zw函數和用戶模式Nt函數都通過SSDT來提取系統調用處理程序指針,在本文的示例中,將在內核模式中執行相同的函數(NtQuerySystemInformation)。
如今,讓咱們重複一下當前的任務:咱們須要找到SystemInformationClass 0xb指向的位置。檢查NtQuerySystemInformation後,咱們看到了經過ExpQuerySystemInformation或返回錯誤狀態的全部方法。SystemInformationClass在rcx中傳遞,請注意,我沒有開始從上到下閱讀反彙編,我只想知道當SystemInformationClass = 0xb時會發生什麼。
跟蹤rcx,咱們看到在調用ExpQuerySystemInformation以前它沒有改變。這意味着ExpQuerySystemInformation的第一個參數是SystemInformationClass參數。
查看ExpQuerySystemInformation能夠開始跟蹤rcx中的值,在IDA中,咱們能夠突出顯示rcx寄存器並查看其使用位置。
你能夠看到rcx值被移動到rdi,而後它被覆蓋,如今咱們須要查看rdi,這是rdi寄存器的下一個用法:
咱們知道0xb小於0x49,因此讓咱們觀察一下loc_14069F19C:
這是switch語句的示例。該表包含switch語句中不一樣狀況的處理程序。IDA已經爲你找到了案例!要輕鬆找到案例,你可使用「Search Text」 (Alt-T)並搜索如下文本:「case 11」。因此,在搜索這篇文章後,咱們發現瞭如下內容:
好的!因此咱們找到了查詢模塊列表的實際代碼。從它的名稱中,咱們能夠理解PsLoadedModuleList列表包含一個已加載模塊的列表。
ExpQueryModuleInformation函數以下所示:
咱們在這個函數中看到一個大循環,看看函數的開頭,咱們看到:
如今,咱們假設這個函數枚舉這個列表並返回這個列表中的映像,咱們將在須要的時候驗證這個假設。最後,咱們完成了第一個任務,查找什麼是SystemInformationClass 0xb?
第一個須要回答的問題是,這個API (AuxKlibQueryModuleInformation)是否保證返回的模塊列表老是最新的?
要回答這個問題,咱們須要瞭解返回的值是否老是最新的。「最新的」的通常定義能夠有不一樣的含義。可是通常的答案是:不能保證在調用AuxKlibQueryModuleInformation以後,列表是最新的。看起來PsLoadedModuleList受ERESOURCE同步對象(讀寫鎖)PsLoadedModuleResource保護。在調用ExpQueryModuleInformation以前,咱們會獲取鎖。但在那以後,咱們調用ExReleaseResourceLite,列表能夠再次更新,咱們能夠假定僅在得到鎖時才更改PsLoadedModuleList。
若是AuxKlibQueryModuleInformation被設計爲獲取PsLoadedModuleList的更新版本,那麼它將容許調用者本身獲取或釋放鎖。事實上,PsLoadedModuleResource是在windows 10的ntoskrnl中導出的:
由於能夠從同一線程兩次獲取ERESOURCE鎖,因此這意味着調用方實際上能夠:
1.獲取鎖;
2.調用AuxKlibQueryModuleInformation;
3.用列表作一些有趣的事情(列表也已導出;);
4.運行完成後,釋放鎖;
這將確保在保持鎖定狀態時沒法更新列表,固然,前提是假設PsLoadedModuleList在沒有鎖定的狀況下不會更新。不過這種方法還不夠好,由於實際使用會出現如下問題:
1.即便咱們檢驗了咱們的假設並假設它是正確的,微軟也能夠隨時更改此行爲,例如,微軟2.有時會更改鎖的類型(將ERESOURCE更改成其餘類型的鎖);
3.在Windows 7/8中不可用,列表和鎖沒有導出(這多是個問題,具體取決於你的運行狀況);
出於好奇,讓咱們使用windows import searcher工具來查找究竟是哪些模塊導入這些變量:
>python.exe windows_imports_searcher.py search -i index.json -f ntoskrnl.exe!PsLoadedModule*
Reading file index.json
c:\windows\system32\drivers\ntosext.sys Imports ntoskrnl.exe!PsLoadedModuleResource
c:\windows\system32\drivers\ntosext.sys Imports ntoskrnl.exe!PsLoadedModuleList
咱們看到導入這個列表的惟一驅動是ntosext.sys,咱們可能會錯過使用MmGetSystemRoutineAddress導入已加載模塊列表的其餘模塊。此外,有些組件(例如調試器)能夠很好地利用此列表。這個列表在windows 10中導出多是由於ntosext.sys被移出ntoskrnl.exe。
如今回答第2個問題:對AuxKlibQueryModuleInformation進行逆向工程,並解釋其工做原理。
咱們必須當心處理這個任務。「解釋它是如何工做的」很容易被誤解爲「理解關於AuxKlibQueryModuleInformation實現的每一個小細節」。有時候,在逆向工程中,咱們想要獲得某些東西的概況,這個「東西」能夠是一個完整的程序,也能夠是一個程序的特定特性。咱們必須當心,不能花太多時間來顛倒AuxKlibQueryModuleInformation,由於這裏所須要作的就是了解全局。
好了,咱們已經從上一個任務的分析中獲得了一些映像:
1.AuxKlibQueryModuleInformation在aux_klib.lib靜態庫中定義;
2.它調用ZwQuerySystemInformation來觸發ExpQuerySystemInformation;
3.ExpQuerySystemInformation獲取PsLoadedModuleResource並調用ExpQueryModuleInformation
4.咱們能夠估計ExpQueryModuleInformation會獲取列表的快照,並將其保存到輸出緩衝區。
5.鎖被釋放,緩衝區被返回給用戶;
好的,實際上咱們能夠在此時中止分析了,由於咱們已經有一個大的框架。可是咱們尚未檢查ExpQueryModuleInformation,因此讓咱們驗證一下它的做用。
如上所述,這個函數有一個大的循環。咱們能夠估計此循環枚舉PsLoadedModuleList中的條目,讓咱們驗證一下。
咱們能夠在循環以前看到如下代碼,看起來r14是循環變量,而且已與列表的開頭進行了比較,若是它指向列表的開頭,則循環結束。驗證r14爲循環變量的過程以下:
是的,看來估算是正確的。爲了大體瞭解此循環的做用,讓咱們看一下循環對象內r14的用法:
看起來來自ListItem結構(表明已加載模塊)的值已複製到rsi指向的某些輸出結構,經過查看rsi的來源,來驗證rsi是否包含調用者的輸出緩衝區:
好的,答案是rsi = (SecondParam + 8),接下來,讓咱們來看看第二個參數(rdx)是怎麼來的:
(在ExpQuerySystemInformation內部)
咱們來跟蹤rbx,可使用Alt-Up來查看rbx的第一次分配發生在什麼位置:
rbx包含了ExpQuerySystemInformation的第四個參數,讓咱們追蹤一下調用者,看看它來自哪裏:
(在NtQuerySystemInformation內部)
它是NtQuerySystemInformation的第二個參數,讓咱們看看這個函數的原型:
好極了!咱們是正確的,由於第二個參數是SystemInformation,是調用者的輸出參數。
__kernel_entry NTSTATUS NtQuerySystemInformation(
IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
OUT PVOID SystemInformation,
IN ULONG SystemInformationLength,
OUT PULONG ReturnLength
);
好,讓咱們來看最後一個問題
當多個線程請求訪問加載的模塊列表時,它如何處理這種狀況?
咱們已經知道答案了!這是使用讀寫鎖來處理的,這種類型的鎖容許讀取器一塊兒讀取列表(這是安全的,由於它們不會更改列表),可是若是編寫器但願編輯列表,則只有編寫器能夠訪問列表。奇怪的是,查詢函數使用exacquireresourceexclusive velite函數鎖定列表,不容許其餘人讀取列表。這很奇怪,由於這個函數應該讀取而不是寫入列表。通過驗證,我沒有在ExpQueryModuleInformation內找到對列表的任何寫操做,所以它看起來像是錯誤的編碼,又或許是我沒有理解到位。
最後,咱們想知道函數是否返回用戶模式dll。咱們能夠嘗試找出靜態地插入到PsLoadedModuleList中的內容,可是讓咱們使用動態分析來解決這個問題。咱們如何解決這個問題?咱們能夠編寫調用AuxKlibQueryModuleInformation並查看返回值的代碼,可是有一種更簡單的方法,就是用循環對象自己。
咱們能夠假設這是在將映像放入目標緩衝區以前對映像名稱的轉換,讓咱們在調用RtlUnicodeStringToAnsiString時設置一個斷點,而後查看源字符串。爲了使這個運行有效,咱們必須以某種方式觸發ZwQuerySystemInformation,打開process explorer並經過單擊View->System Information就能夠觸發它。
kd> bp fffff80207203c57 "dS /c 100 rdx; g"
kd> g
ffffa78e`7e605f40 "\SystemRoot\system32\ntoskrnl.exe"
ffffa78e`7e606e90 "\SystemRoot\system32\hal.dll"
ffffa78e`7e606ef0 "\SystemRoot\system32\kdcom.dll"
ffffa78e`7e605f40 "\SystemRoot\system32\ntoskrnl.exe"
ffffa78e`7e606e90 "\SystemRoot\system32\hal.dll"
ffffa78e`7e606ef0 "\SystemRoot\system32\kdcom.dll"
ffffa78e`7e606f50 "\SystemRoot\system32\mcupdate_GenuineIntel.dll"
ffffa78e`7e607d60 "\SystemRoot\System32\drivers\msrpc.sys"
ffffa78e`7e607dd0 "\SystemRoot\System32\drivers\ksecdd.sys"
ffffa78e`7e607e40 "\SystemRoot\System32\drivers\werkernel.sys"
ffffa78e`7e607ec0 "\SystemRoot\System32\drivers\CLFS.SYS"
ffffa78e`7e607f30 "\SystemRoot\System32\drivers\tm.sys"
ffffa78e`7e608010 "\SystemRoot\system32\PSHED.dll"
ffffa78e`7e608070 "\SystemRoot\system32\BOOTVID.dll"
ffffa78e`7e6080e0 "\SystemRoot\System32\drivers\FLTMGR.SYS"
ffffa78e`7e608150 "\SystemRoot\System32\drivers\clipsp.sys"
ffffa78e`7e6081c0 "\SystemRoot\System32\drivers\cmimcext.sys"
ffffa78e`7e608240 "\SystemRoot\System32\drivers\ntosext.sys"
..................
...(truncated)....
..................
從輸出中能夠看到,只有內核映像保存在輸出列表中。
正如你所看到的,逆向工程的大部分工做是跟蹤咱們程序中的數據流。這是反編譯器能夠作得更好的優點所在,由於它們以更高的表示形式顯示信息。全部變量在寄存器之間的「臨時」移動都不在反編譯視圖中體現。
本文參考自:https://repnz.github.io/posts/practical-reverse-engineering/query-module-information/