Jump to content
  • Entries

    16114
  • Comments

    7952
  • Views

    863104055

Contributors to this blog

  • HireHackking 16114

About this blog

Hacking techniques include penetration testing, network security, reverse cracking, malware analysis, vulnerability exploitation, encryption cracking, social engineering, etc., used to identify and fix security flaws in systems.

微信截图_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 或系統調用的部分代碼,或者難以設置執行上下文的代碼,前面提到的方法通常是更好的選擇。