本機函數(shù)和匯編代碼調(diào)用
對于逆向工程師來說,直接從分析的二進制代碼中調(diào)用函數(shù)的能力是一種捷徑,可以省去很多麻煩。雖然在某些情況下,理解函數(shù)邏輯并在高級語言中重新實現(xiàn)它是可能的,但這并不總是可行的,而且原始函數(shù)的邏輯越脆弱和復(fù)雜,這種方法就越不可行。在處理自定義哈希和加密時,這是一個特別棘手的問題,如果計算中的某個地方出現(xiàn)一個錯誤,就會導(dǎo)致最終輸出完全不同,而且調(diào)試起來非常麻煩。
我們將在本文中,介紹3 種實現(xiàn)此“捷徑”的不同方法,并直接從匯編中調(diào)用函數(shù)。我們首先介紹IDA Pro原生支持的IDA Appcall功能,可以直接通過idpython使用。然后我們演示如何使用Dumpulator實現(xiàn)同樣的行為,最后,我們將展示如何使用Unicorn Engine模擬得到該結(jié)果。本文中使用的實際示例基于MiniDuke惡意軟件示例實現(xiàn)的“經(jīng)過調(diào)整的”SHA1哈希算法。
MiniDuke 實現(xiàn)的改進的 SHA1 Hashing 算法
MiniDuke示例中經(jīng)過修改的SHA1算法用于為惡意軟件配置創(chuàng)建每個系統(tǒng)的加密密鑰。要哈希的緩沖區(qū)包含與所有接口描述的 DWORD 連接的當(dāng)前計算機名稱,例如 'DESKTOP-ROAC4IJ\x00MicrWAN WAN MicrWAN MicrWAN InteWAN InteWAN Inte'。此函數(shù) (SHA1Hash) 在初始摘要和中間階段使用與原始 SHA1 相同的常量,但產(chǎn)生不同的輸出。
MiniDuke SHA1Hash 函數(shù)常量
由于在原始和修改后的 SHA1 中使用的常量都相同,因此差異必須出現(xiàn)在函數(shù)的 1241 條匯編指令中。我們不能說這種調(diào)整是否是故意引入的,但事實仍然是惡意軟件開發(fā)者越來越喜歡插入這樣的“驚喜”,而處理它們的責(zé)任就落在了分析師身上。為此,我們必須首先了解函數(shù)期望其輸入并產(chǎn)生其輸出的形式。
事實證明,Duke-SHA1匯編使用自定義調(diào)用約定,其中要哈希的緩沖區(qū)長度在ecx寄存器中傳遞,而緩沖區(qū)本身的地址在edi中傳遞。從技術(shù)上講,在 eax 中也傳遞了一個值,但是每當(dāng)可執(zhí)行文件調(diào)用該函數(shù)時,該值都是相同的 0xffffffff,因此我們可以將其視為常量。有趣的是,惡意軟件還在每次調(diào)用該函數(shù)時將緩沖區(qū)長度(ecx)設(shè)置為0x40,僅對緩沖區(qū)的前 0x40 個字節(jié)進行有效地哈希處理。
SHA1Hash 函數(shù)參數(shù)
由此產(chǎn)生的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。
生成的SHA1緩沖區(qū)哈希示例
如上所述,查找SHA1和Duke-SHA1的邏輯發(fā)生分歧的確切位置,然后在Python中重新實現(xiàn)Duke-SHA1,不過這個方法非常浪費時間。接下來,我們將使用幾種方法來“插入”函數(shù)的調(diào)用約定并直接調(diào)用它。
IDA–Appcall
Appcall是IDA Pro的一個功能,它允許IDA Python腳本在調(diào)試程序中調(diào)用函數(shù),就像它們是內(nèi)置函數(shù)一樣。這是非常方便的,但它也不是通用的,即當(dāng)用例變得有些不尋常或復(fù)雜時,應(yīng)用難度會急劇上升。雖然在 ecx 中傳遞緩沖區(qū)長度和在 edi 中傳遞緩沖區(qū)是正常的,但在5個寄存器中分割160位的返回值并不是典型的函數(shù)輸出形式,Appcall 需要一些創(chuàng)意來解決這個問題。
接下來,我們創(chuàng)建了一個自定義結(jié)構(gòu)struc_SHA1HASH,它保存了5個寄存器的值,并用作函數(shù)原型的返回類型:
IDA 結(jié)構(gòu)窗口——“struc_SHA1HASH”
現(xiàn)在有了結(jié)構(gòu)定義, Appcall 就可以與這個函數(shù)原型交互,如下面的 PROTO 值所示。
由于IDA Appcall依賴于調(diào)試器,為了調(diào)用這個邏輯,我們首先需要編寫一個腳本來啟動調(diào)試器,對堆棧進行必要的調(diào)整,并執(zhí)行其他必要的管理工作。
IDA視圖——堆棧調(diào)整
使用Appcall是最后一步,有幾種方法可以利用它來調(diào)用函數(shù)。我們可以在不指定原型的情況下直接調(diào)用函數(shù),但這高度依賴于 IDA 的 IDB 中正確類型的函數(shù)。第二種方法是根據(jù)函數(shù)名和定義的原型創(chuàng)建一個可調(diào)用對象。通過這種方式,我們可以調(diào)用帶有特定原型的函數(shù),無論在IDB中設(shè)置了什么類型,如下所示:
使用 Appcall 調(diào)用 Duke-SHA1 的完整腳本如下所示。
還有一些示例輸出:
腳本執(zhí)行——“IDA Appcall”產(chǎn)生與 MiniDuke 樣本相同的 SHA1 哈希值
如果我們只是想將被調(diào)用的函數(shù)用作黑盒,那么上面的方法是可以的,但有時我們可能希望在執(zhí)行的特定狀態(tài)下訪問注冊表值,并且像上面那樣指定原型是一件繁瑣的事情。令人高興的是,這兩個缺點都可以被優(yōu)化。
由于 IDA Appcall 依賴于調(diào)試器并且可以直接從 IDAPython 調(diào)用,因此我們可以從調(diào)試器調(diào)用 Appcall 并對其執(zhí)行進行更精細的控制。例如,我們可以通過為 Appcall 設(shè)置一個特殊選項——APPCALL_MANUAL 來讓 Appcall 在執(zhí)行過程中將控制權(quán)交還給調(diào)試器。
通過這種方式,我們可以使用Appcall來準(zhǔn)備參數(shù),分配一個緩沖區(qū),然后恢復(fù)之前的執(zhí)行上下文。我們也可以避免為返回值指定結(jié)構(gòu)類型(將其輸入為void),因為這將由調(diào)試器處理。有更多的方法來獲取函數(shù)的返回值,因此當(dāng)控制調(diào)試器,就可以使用條件斷點在特定的執(zhí)行狀態(tài)(例如在返回時)打印所需的值。
我們可以通過調(diào)用cleanup_appcall()在任何需要的執(zhí)行時刻恢復(fù)之前的狀態(tài)(在 Appcall 調(diào)用之前)。在我們的例子中,正好在遇到條件斷點之后。
完整的腳本如下:
Dumpulator
Dumpulator是一個python庫,它幫助在minidump文件中進行代碼模擬。dumator的核心模擬引擎基于Unicorn engine,但在同類工具中有一個比較獨特的特點,那就是可以獲得整個過程的內(nèi)存。這帶來了性能改進(在不離開 Unicorn 的情況下模擬大部分已分析的二進制文件),如果我們可以在調(diào)用函數(shù)所需的程序上下文(堆棧等)已經(jīng)就位的時候計算內(nèi)存轉(zhuǎn)儲的時間,那么就更方便了。此外,只有模擬系統(tǒng)調(diào)用才能提供真實的Windows環(huán)境(因為實際上一切都是合法的進程環(huán)境)。
可以使用許多工具(x64dbg - MiniDumpPlugin, process Explorer, process Hacker, Task Manager)或Windows API (MiniDumpWriteDump)捕獲所需進程的一個minidump。我們可以使用x64dbg - MiniDumpPlugin在幾乎所有進程都已經(jīng)設(shè)置為SHA1哈希創(chuàng)建的狀態(tài)下創(chuàng)建一個minidump,就在SHA1Hash函數(shù)調(diào)用之前。注意,沒有必要以這種方式對轉(zhuǎn)儲進行計時,因為在進行轉(zhuǎn)儲后,可以在轉(zhuǎn)儲器中手動設(shè)置環(huán)境,這只是為了方便而已。
使用“x64dbg - MiniDumpPlugin”創(chuàng)建minidump
Dumpulator不僅可以訪問整個轉(zhuǎn)儲的進程內(nèi)存,還可以分配額外的內(nèi)存、讀取內(nèi)存、寫入內(nèi)存、讀取注冊表值和寫入注冊表值。換句話說,模擬器可以做的任何事情。也有可能實現(xiàn)系統(tǒng)調(diào)用,因此可以模擬使用它們的代碼。
要通過Dumpulator調(diào)用Duke-SHA1,我們需要指定將在minidump中調(diào)用的函數(shù)的地址及其參數(shù)。在本例中,SHA1Hash的地址為0x407108。
在 IDA 中打開生成的minidump
因為我們不希望在minidump的當(dāng)前狀態(tài)中使用已經(jīng)設(shè)置的值,所以我們?yōu)楹瘮?shù)定義自己的參數(shù)值。我們甚至可以分配一個新的緩沖區(qū),用作哈希的緩沖區(qū)。完成此任務(wù)的代碼如下所示。
執(zhí)行此腳本將生成正確的Duke-SHA1值
腳本執(zhí)行——“Dumpulator”產(chǎn)生與 MiniDuke 樣本相同的 SHA1 哈希值
Emulation–Unicorn Engine
對于模擬方法,我們可以使用任何類型的CPU模擬器(例如Qiling、Speakeasy等),它們能夠模擬x86匯編,并具有針對Python語言的綁定。因為我們不需要任何更高的抽象級別(系統(tǒng)調(diào)用,API函數(shù)),我們可以使用其他大多數(shù)引擎的基礎(chǔ)設(shè)施——Unicorn Engine。
Unicorn是一個輕量級、多平臺、多體系結(jié)構(gòu)的CPU模擬器框架,基于QEMU,它是用純C語言實現(xiàn)的,并綁定了許多其他語言。我們將使用Python綁定。我們的目標(biāo)是創(chuàng)建一個獨立的函數(shù)SHA1Hash,它可以像Python中的任何其他普通函數(shù)一樣被調(diào)用,產(chǎn)生與MiniDuke中原始函數(shù)相同的SHA1哈希。我們使用的實現(xiàn)背后的想法非常簡單——我們只需提取函數(shù)的操作碼字節(jié)并通過 CPU 模擬使用它們。
提取原始函數(shù)操作碼的所有字節(jié)可以簡單地通過idpython或使用IDA→Edit→Export Data來完成。
使用IDA“Export data”對話框?qū)С鯯HA1Hash函數(shù)的操作碼字節(jié)
與前面的方法一樣,我們需要設(shè)置執(zhí)行上下文。在本文示例中,這意味著為函數(shù)準(zhǔn)備參數(shù),并為提取的操作碼和輸入緩沖區(qū)設(shè)置地址。
請注意,應(yīng)從提取的操作碼列表中刪除最后一條 retn 指令,以免將執(zhí)行轉(zhuǎn)移回堆棧上的返回地址,并且應(yīng)通過指定 ebp 和 esp 的值手動設(shè)置堆棧幀。所有這些都顯示在下面的最終 Python 腳本中。
腳本輸出如下所示:
腳本執(zhí)行——“Unicorn Engine”產(chǎn)生與MiniDuke示例相同的SHA1哈希值
總結(jié)
上述所有直接調(diào)用匯編的方法都各有優(yōu)缺點。給我們留下特別深刻印象的是簡易的Dumpulator,它是免費的,執(zhí)行起來很快,而且非常有效。它非常適合編寫通用字符串解密器、配置提取器和其他上下文,在這些上下文中,必須依次調(diào)用許多不同的邏輯片段,同時保留難以設(shè)置的上下文。
當(dāng)我們希望直接使用調(diào)用特定函數(shù)產(chǎn)生的結(jié)果來豐富IDA數(shù)據(jù)庫時,IDA Appcall功能是最好的解決方案之一。系統(tǒng)調(diào)用可以是Appcall在實際執(zhí)行環(huán)境中使用的函數(shù)的一部分——使用調(diào)試器。Appcall最大的優(yōu)點之一就是快速而簡單的上下文恢復(fù)。由于Appcall依賴調(diào)試器,可以與idpython腳本一起使用,理論上它甚至可以作為模糊器的基礎(chǔ),向函數(shù)提供隨機輸入以發(fā)現(xiàn)意外行為(即錯誤),但這種方法的消耗太大。
通過 Unicorn Engine 使用純仿真是獨立實現(xiàn)特定功能的通用解決方案。使用這種方法,可以按原樣獲取部分代碼并在不連接到原始示例的情況下使用它。此方法不依賴于可運行的示例,并且僅適用于部分代碼重新實現(xiàn)功能。對于不是連續(xù)的、易于轉(zhuǎn)儲的代碼塊的函數(shù),這種方法可能更難實現(xiàn)。對于發(fā)生 API 或系統(tǒng)調(diào)用的部分代碼,或者難以設(shè)置執(zhí)行上下文的代碼,前面提到的方法通常是更好的選擇。
本文翻譯自:https://research.checkpoint.com/2022/native-function-and-assembly-code-invocation/如若轉(zhuǎn)載,請注明原文地址