Jump to content

微信截图_20220928141315.png

對於逆向工程師來說,直接從分析的二進制代碼中調用函數的能力是一種捷徑,可以省去很多麻煩。雖然在某些情況下,理解函數邏輯並在高級語言中重新實現它是可能的,但這並不總是可行的,而且原始函數的邏輯越脆弱和復雜,這種方法就越不可行。在處理自定義哈希和加密時,這是一個特別棘手的問題,如果計算中的某個地方出現一個錯誤,就會導致最終輸出完全不同,而且調試起來非常麻煩。

我們將在本文中,介紹3 種實現此“捷徑”的不同方法,並直接從彙編中調用函數。我們首先介紹IDA Pro原生支持的IDA Appcall功能,可以直接通過idpython使用。然後我們演示如何使用Dumpulator實現同樣的行為,最後,我們將展示如何使用Unicorn Engine模擬得到該結果。本文中使用的實際示例基於MiniDuke惡意軟件示例實現的“經過調整的”SHA1哈希算法。

MiniDuke 實現的改進的SHA1 Hashing 算法MiniDuke示例中經過修改的SHA1算法用於為惡意軟件配置創建每個系統的加密密鑰。要哈希的緩衝區包含與所有接口描述的DWORD 連接的當前計算機名稱,例如'DESKTOP-ROAC4IJ\x00MicrWAN WAN MicrWAN MicrWAN InteWAN InteWAN Inte'。此函數(SHA1Hash) 在初始摘要和中間階段使用與原始SHA1 相同的常量,但產生不同的輸出。

1.webp.jpg

MiniDuke SHA1Hash 函數常量

由於在原始和修改後的SHA1 中使用的常量都相同,因此差異必須出現在函數的1241 條彙編指令中。我們不能說這種調整是否是故意引入的,但事實仍然是惡意軟件開發者越來越喜歡插入這樣的“驚喜”,而處理它們的責任就落在了分析師身上。為此,我們必須首先了解函數期望其輸入並產生其輸出的形式。

事實證明,Duke-SHA1彙編使用自定義調用約定,其中要哈希的緩衝區長度在ecx寄存器中傳遞,而緩衝區本身的地址在edi中傳遞。從技術上講,在eax 中也傳遞了一個值,但是每當可執行文件調用該函數時,該值都是相同的0xffffffff,因此我們可以將其視為常量。有趣的是,惡意軟件還在每次調用該函數時將緩衝區長度(ecx)設置為0x40,僅對緩衝區的前0x40 個字節進行有效地哈希處理。

2.webp.jpg

SHA1Hash 函數參數

由此產生的160位SHA1哈希值在寄存器中以5個dword的形式返回(從高到低: eax, edx, ebx, ecx, esi)。例如,緩衝DESKTOP-ROAC4IJ\x00MicrWAN WAN MicrWAN MicrWAN InteWAN InteWAN Inte的Duke-SHA1值為1851fff77f0957d1d690a32f31df2c32a1a84af7,返回為EAX:0x1851fff7 EDX:0x7f0957d1 EBX:0xd690a32f ECX:0x31df2c32 ESI:0xa1a84af7。

3.webp.jpg

生成的SHA1緩衝區哈希示例

如上所述,查找SHA1和Duke-SHA1的邏輯發生分歧的確切位置,然後在Python中重新實現Duke-SHA1,不過這個方法非常浪費時間。接下來,我們將使用幾種方法來“插入”函數的調用約定並直接調用它。

IDA–AppcallAppcall是IDA Pro的一個功能,它允許IDA Python腳本在調試程序中調用函數,就像它們是內置函數一樣。這是非常方便的,但它也不是通用的,即當用例變得有些不尋常或複雜時,應用難度會急劇上升。雖然在ecx 中傳遞緩衝區長度和在edi 中傳遞緩衝區是正常的,但在5個寄存器中分割160位的返回值並不是典型的函數輸出形式,Appcall 需要一些創意來解決這個問題。

接下來,我們創建了一個自定義結構struc_SHA1HASH,它保存了5個寄存器的值,並用作函數原型的返回類型:

4.png

IDA 結構窗口——“struc_SHA1HASH”

現在有了結構定義, Appcall 就可以與這個函數原型交互,如下面的PROTO 值所示。

5.png

由於IDA Appcall依賴於調試器,為了調用這個邏輯,我們首先需要編寫一個腳本來啟動調試器,對堆棧進行必要的調整,並執行其他必要的管理工作。

6.png

IDA視圖——堆棧調整

使用Appcall是最後一步,有幾種方法可以利用它來調用函數。我們可以在不指定原型的情況下直接調用函數,但這高度依賴於IDA 的IDB 中正確類型的函數。第二種方法是根據函數名和定義的原型創建一個可調用對象。通過這種方式,我們可以調用帶有特定原型的函數,無論在IDB中設置了什麼類型,如下所示:

7.png

使用Appcall 調用Duke-SHA1 的完整腳本如下所示。

8.png

還有一些示例輸出:

9.webp.jpg

腳本執行——“IDA Appcall”產生與MiniDuke 樣本相同的SHA1 哈希值

如果我們只是想將被調用的函數用作黑盒,那麼上面的方法是可以的,但有時我們可能希望在執行的特定狀態下訪問註冊表值,並且像上面那樣指定原型是一件繁瑣的事情。令人高興的是,這兩個缺點都可以被優化。

由於IDA Appcall 依賴於調試器並且可以直接從IDAPython 調用,因此我們可以從調試器調用Appcall 並對其執行進行更精細的控制。例如,我們可以通過為Appcall 設置一個特殊選項——APPCALL_MANUAL 來讓Appcall 在執行過程中將控制權交還給調試器。

10.png

通過這種方式,我們可以使用Appcall來準備參數,分配一個緩衝區,然後恢復之前的執行上下文。我們也可以避免為返回值指定結構類型(將其輸入為void),因為這將由調試器處理。有更多的方法來獲取函數的返回值,因此當控制調試器,就可以使用條件斷點在特定的執行狀態(例如在返回時)打印所需的值。

11.png

我們可以通過調用cleanup_appcall()在任何需要的執行時刻恢復之前的狀態(在Appcall 調用之前)。在我們的例子中,正好在遇到條件斷點之後。

12.png

完整的腳本如下:

13.png

DumpulatorDumpulator是一個python庫,它幫助在minidump文件中進行代碼模擬。 dumator的核心模擬引擎基於Unicorn engine,但在同類工具中有一個比較獨特的特點,那就是可以獲得整個過程的內存。這帶來了性能改進(在不離開Unicorn 的情況下模擬大部分已分析的二進製文件),如果我們可以在調用函數所需的程序上下文(堆棧等)已經就位的時候計算內存轉儲的時間,那麼就更方便了。此外,只有模擬系統調用才能提供真實的Windows環境(因為實際上一切都是合法的進程環境)。

可以使用許多工具(x64dbg - MiniDumpPlugin, process Explorer, process Hacker, Task Manager)或Windows API (MiniDumpWriteDump)捕獲所需進程的一個minidump。我們可以使用x64dbg - MiniDumpPlugin在幾乎所有進程都已經設置為SHA1哈希創建的狀態下創建一個minidump,就在SHA1Hash函數調用之前。注意,沒有必要以這種方式對轉儲進行計時,因為在進行轉儲後,可以在轉儲器中手動設置環境,這只是為了方便而已。

14.webp.jpg

使用“x64dbg - MiniDumpPlugin”創建minidump

Dumpulator不僅可以訪問整個轉儲的進程內存,還可以分配額外的內存、讀取內存、寫入內存、讀取註冊表值和寫入註冊表值。換句話說,模擬器可以做的任何事情。也有可能實現系統調用,因此可以模擬使用它們的代碼。

要通過Dumpulator調用Duke-SHA1,我們需要指定將在minidump中調用的函數的地址及其參數。在本例中,SHA1Hash的地址為0x407108。

15.webp.jpg

在IDA 中打開生成的minidump

因為我們不希望在minidump的當前狀態中使用已經設置的值,所以我們為函數定義自己的參數值。我們甚至可以分配一個新的緩衝區,用作哈希的緩衝區。完成此任務的代碼如下所示。

16.png

執行此腳本將生成正確的Duke-SHA1值

17.webp.jpg

腳本執行——“Dumpulator”產生與MiniDuke 樣本相同的SHA1 哈希值

Emulation–Unicorn Engine對於模擬方法,我們可以使用任何類型的CPU模擬器(例如Qiling、Speakeasy等),它們能夠模擬x86彙編,並具有針對Python語言的綁定。因為我們不需要任何更高的抽象級別(系統調用,API函數),我們可以使用其他大多數引擎的基礎設施——Unicorn Engine。

Unicorn是一個輕量級、多平台、多體系結構的CPU模擬器框架,基於QEMU,它是用純C語言實現的,並綁定了許多其他語言。我們將使用Python綁定。我們的目標是創建一個獨立的函數SHA1Hash,它可以像Python中的任何其他普通函數一樣被調用,產生與MiniDuke中原始函數相同的SHA1哈希。我們使用的實現背後的想法非常簡單——我們只需提取函數的操作碼字節並通過CPU 模擬使用它們。

提取原始函數操作碼的所有字節可以簡單地通過idpython或使用IDA→Edit→Export Data來完成。

18.png

使用IDA“Export data”對話框導出SHA1Hash函數的操作碼字節

與前面的方法一樣,我們需要設置執行上下文。在本文示例中,這意味著為函數準備參數,並為提取的操作碼和輸入緩衝區設置地址。

19.png

請注意,應從提取的操作碼列表中刪除最後一條retn 指令,以免將執行轉移回堆棧上的返回地址,並且應通過指定ebp 和esp 的值手動設置堆棧幀。所有這些都顯示在下面的最終Python 腳本中。

20.png

腳本輸出如下所示:

21.png

腳本執行——“Unicorn Engine”產生與MiniDuke示例相同的SHA1哈希值

總結上述所有直接調用彙編的方法都各有優缺點。給我們留下特別深刻印象的是簡易的Dumpulator,它是免費的,執行起來很快,而且非常有效。它非常適合編寫通用字符串解密器、配置提取器和其他上下文,在這些上下文中,必須依次調用許多不同的邏輯片段,同時保留難以設置的上下文。

當我們希望直接使用調用特定函數產生的結果來豐富IDA數據庫時,IDA Appcall功能是最好的解決方案之一。系統調用可以是Appcall在實際執行環境中使用的函數的一部分——使用調試器。 Appcall最大的優點之一就是快速而簡單的上下文恢復。由於Appcall依賴調試器,可以與idpython腳本一起使用,理論上它甚至可以作為模糊器的基礎,向函數提供隨機輸入以發現意外行為(即錯誤),但這種方法的消耗太大。

通過Unicorn Engine 使用純仿真是獨立實現特定功能的通用解決方案。使用這種方法,可以按原樣獲取部分代碼並在不連接到原始示例的情況下使用它。此方法不依賴於可運行的示例,並且僅適用於部分代碼重新實現功能。對於不是連續的、易於轉儲的代碼塊的函數,這種方法可能更難實現。對於發生API 或系統調用的部分代碼,或者難以設置執行上下文的代碼,前面提到的方法通常是更好的選擇。

0 Comments

Recommended Comments

There are no comments to display.

Guest
Add a comment...