IDAPython:讓你的生活更美好(五)
我們繼續IDAPython讓生活更美好序列,這一部分我們解決逆向工程師日常遇到的問題:提取執行的內嵌代碼。
惡意軟件會用各種方式存儲內嵌可執行代碼,有些惡意軟件將內嵌代碼加到文件附加段,包括PE資源區段,或者存放在惡意軟件的緩沖區中。
當遇到這個情況,惡意軟件分析者可以有幾個選擇:
可以動態運行樣本在寫入和提取的后面下斷點或者如果文件存儲在資源段,可以使用一些工具比如CFFExplorer 提取資源數據,在IDA中可以高亮選取可疑的二進制數據,然后右鍵保存想要的提取的數據。
雖然這幾個方法都可行,但是都有一些限制。而自動化提取內嵌代碼可以節省分析者大量的時間。為了實現這個目的,我們會用到IDAPython的第三方鏈接庫組件‘pefile’。而這里也會帶來一些挑戰:
1. 我們必須在IDA環境中用PIP安裝第三方python鏈接庫
- 已經標識出了內嵌代碼
需要計算要提取的可執行代碼的大小</pre>
讓我們一次性的解決這些問題吧。
在IDA PRO中加入第三方PYTHON鏈接庫
在IDA中用PIP安裝第三方python鏈接庫之后,如何讓其生效是一個有趣的挑戰。而如果不修改的話是沒有辦法加載第三方鏈接庫的,比如pefile的IDAPython解釋中會出現如下錯誤。
![]()
為了修正這個錯誤,我們必須將PIP的‘site-packages’目錄加到PYTHON的環境變量中。可以使用如下代碼實現:
import sys print sys.pathResult: ['/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python27.zip', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-darwin', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac', '<Truncated>']</pre>
而這里為了包含PIP安裝鏈接庫,我們可以簡單的將‘site-packages’目錄加到pefile包含聲明數組中。但是這個方案不太好,因為這需要分析者人工識別‘site-packages’目錄,但是我沒要找到跨平臺的解決方案。加入相關代碼之后,我們就能夠加載pefile鏈接庫了。
![]()
需要內嵌代碼
為了找到惡意軟件包含的所有內嵌代碼,我們基于MZ頭的已知字符串對二進制進行搜索。分析者請確認已經勾選了‘Load resources’選項這樣才能夠讀取到作為資源存儲的所有數據。另外的,如果內嵌文件包含在附加段中,為了在IDA中能夠看到數據一定要勾選’Manual load’選項。
![]()
現在我們已經有了IDA中加載的必要信息了,現在我們可以開始在PE32文件中搜索數據了。我們有好幾種方法可以實現,但我選擇搜索所有MZ頭中都會包含的靜態信息,如下所示:
![]()
為了找到IDA中所有的字符串事件,我們可以使用循環調用FindBinary()函數來尋找二進制字符串的每一次實例。代碼如下:
def find_string_occurrences(string): results = [] base = idaapi.get_imagebase() + 1024 while True: ea = FindBinary(base, SEARCH_NEXT|SEARCH_DOWN|SEARCH_CASE, '"%s"' % string) if ea != 0xFFFFFFFF: base = ea+1 else: break results.append(ea) return results當在PE32文件中尋找MZ頭字符串標識時,我們需要驗證‘MZ’字符存在于MZ頭的開始處。由于我們之前找的字符串在靜態偏移是固定的,我們只需要簡單的確定‘MZ’的已知偏移就可以了。
def find_embedded_exes(): results = [] exes = find_string_occurrences("!This program cannot be run in DOS mode.") if len(exes) > 1: for exe in exes: m = Byte(exe-77) z = Byte(exe-76) if m == ord("M") and z == ord("Z"): mz_start = exe-77 print "[*] Identified embedded executable at the following offset: 0x%x" % mz_start results.append(mz_start) return results將上面的代碼組合到一起,來找到IDA中所有的內嵌代碼。
![]()
確定可執行代碼的大小
為了確定找到的內嵌代碼的大小,我們將使用前面提到的python第三方pefile鏈接庫。這個鏈接庫可以解析各種可執行文件頭,這樣我們就能夠計算PE文件的大小了。為了實現這個目的,我們會在可選頭中加入’SizeOfHeaders’參數,連同每個段的’SizeOfRawData’字段。下面的代碼會讀出標識出的內嵌代碼的前1024字節,用pefile解析這些數據,計算各個段的大小。
def calculate_exe_size(begin): buff = "" for c in range(0, 1024): buff += chr(Byte(begin+c)) pe = pefile.PE(data=buff) total_size = 0Add total size of headers
total_size += pe.OPTIONAL_HEADER.SizeOfHeaders
Iterate through each section and add section size
for section in pe.sections: total_size += section.SizeOfRawData return total_size</pre>
最后,我們可以使用這些大小值來提取可執行代碼數據然后寫入我們選擇的文件中。
def extract_exe(name, begin, size): buff = "" for c in range(0, size): buff += chr(Byte(begin+c)) f = open(name, 'wb') f.write(buff) f.close()結論
將這些組合到一起,我們將會得到下面的腳本。
https://github.com/pan-unit42/public_tools/blob/master/ida_scripts/idapython_pt5.py
在惡意樣本中運行這個例子,將會得到下面的結果:
![]()
正如我們看到的,我們能夠在IDA中自動提取PE文件了。通過一些小修改,這個實現能夠應用到其他類型的文件中。我希望這個教程能夠讓逆向工程師知道IDAPython能夠實現很多難以置信的功能。
*原文地址: Paloaltonetworks ,東二門陳冠希/編譯,轉載請注明來自FreeBuf黑客與極客(FreeBuf.COM)