調試UPX壓縮的notepad


@date: 2016/11/29

@author: dlive

0x01 運行時壓縮

對比upx壓縮前后的notepad可以看到如下特點

  1. PE頭的大小一樣
  2. 節區名稱改變(.text -> .UPX0, .data -> .UPX1)
  3. 第一個節區的SizeOfRawData=0,即第一個節區在磁盤上的大小為0,但是第一個節區的VirtualSize的值被設置為0x10000
  4. notepad_upx.exe的EP位於第二個節區,原notepad.exe的EP在第一個節區
  5. 資源節區(.rsrc)的大小幾乎無變化

解壓縮代碼與壓縮的源代碼都在第二個節區(UPX1)。文件運行時首先執行解壓縮代碼,把處於壓縮狀態的源代碼解壓到第一個節區(UPX0)。解壓結束后即運行源文件的EP代碼。

0x02 調試UPX壓縮的notepad

EP與OEP:

EP(Enter Point): Windows可執行文件的代碼入口點,是執行應用程序時最先執行的代碼的其實位置

OEP(Original Enter Point): 對於加殼程序,稱源文件的EP為OEP

使用OD打開未加殼的notepad.exe

可以看到notepad.exe的EP為0100739D,入口處代碼調用了GetModuleHandleA用來獲取notepad的ImageBase

下面開始調試notepad_upx.exe,upx的解殼過程可以分為以下幾個階段:

1. 殼入口的初始化操作

用OD打開notepad_upx.exe,程序停在EP處

復習一下前面PE的知識XD : 可以驗證EP地址為01015330 == Address of Entry Point + Image Base (IMAGE_OPTIONAL_HEADER32)

notepad_upx.exe在入口點進行了pushad操作(依次將EAX、ECX、EDX、EBX、ESI、EDI壓棧)

然后將UPX0,UPX1節區的地址分別保存在EDI,ESI寄存器中。調試時這樣同時設置EDI,ESI,就能預見從ESI所指緩沖區到EDI所指緩沖區的內存發生了復制,此時從Source(ESI)讀取數據,解壓縮后保存到Destination(EDI)。

之后代碼通過jmp調到解壓縮代碼。

我們的目標是跟蹤全部UPX EP代碼,並最終找到原notepad的EP代碼,如第一張圖所示

2. 代碼還原

在UPX解壓縮的過程中一共有4個循環。

前里兩個循環完成代碼還原的功能,正式的解碼循環為第二個循環。

跟蹤數量龐大的代碼時,請遵循如下法則:遇到循環時,先了解作用再跳出

在OD中Ctrl+F8調試第二個解碼循環,同時在memory窗口觀察UPX0節區的數據變化(也可以在OD CPU窗口左下方的數據窗口查看UPX0內數據變化),可以看到第二個循環將解碼后的數據寫入UPX0節區。

3. CALL/JMP修復

第三個循環從0101540A開始,主要用於恢復源代碼的CALL/JMP指令(Opcode:E8/E9)的destination地址。

可以在memory窗口中選中UPX0區段,右鍵設置內存寫入斷點來判斷第三個循環確實有對UPX0進行修改。

4. 恢復IAT

UPX壓縮原notepad.exe事,會分析其IAT,提取出程序中調用的API名稱列表,形成API名稱字符串。

用這些API名稱字符串調用GetProcAddress函數,獲取API的地址。然后把API地址輸入原notepad.exe的IAT區域。

最終恢復原notepad.exe的IAT

5. 將程序的控制返回到程序OEP

在notepad.exe全部解壓縮完成后,將程序的控制返回到OEP處。

010154AD地址處的popad指令與notepad_upx.exe的第一條指令pushad對應,用來把寄存器狀態恢復。

最終使用010154BB處的jmp指令跳轉到OEP,跳轉的目標地址(0100739D)就是notepad.exe的EP

可以看到OEP處的代碼和源notepad.exe相同

雖然沒有詳細看懂upx的每條指令的含義,但是對其代碼片段的功能有了基本了解

UPX詳細分析可參考:

http://www.52pojie.cn/thread-294773-1-1.html

http://www.chinapyg.com/thread-76768-1-1.html

0x03 快速查找UPX OEP&UPX脫殼

1.快速查找OEP

這里用到了OD的硬件斷點,關於硬件斷點可以參考:

https://www.zhihu.com/question/52625624/answer/131557817

notepad_upx.exe的EP先執行了pushad,在解壓完成后又使用popad恢復程序初始運行時的寄存器和堆棧狀態。

我們現在知道UPX在解壓完成后一定會執行popad(依次pop edi, esi, ebx, edx,ecx, eax),那么就可以在棧地址上設置硬件斷點,在程序訪問這個地址的數據時就會被斷下。

在數據窗口找到執行完pushad后在esp對應地址,然后選擇該地址開始的1字節,右鍵設置硬件斷點(或者按書p133那樣設置硬件斷點均可)。

當程序執行popad時會首先從這個地址上讀取數據到edi(pop edi),此時會觸發硬件斷點,程序斷下。其下方即是跳轉到OEP的JMP指令。

不過和軟件斷點(INT 3)斷點不同的是,觸發斷點的指令執行(popad)完成后程序才會停止運行,即程序會斷在popad下一條指令。

OD的硬件斷點可以在調試->硬件斷點里看到。

2.UPX脫殼

繼續運行程序到OEP,然后再OD中右鍵選擇Dump debugged process(52pojie OD中為“用Ollydump脫殼調試進程”)

修改EP為OEP(該功能會自動修改EP為EIP,此時EIP即為OEP,所以無需手動修改),點擊脫殼,另存腳本為notepad_dump.exe

對UPX脫殼而言,進行到這一步就可以了,但是如果是另外一些殼,可能還需要對程序的輸入表進行修復操作。

脫殼后的程序可以正常運行,並且使用IDA可以正常反編譯。

使用PEID查殼可以看到提示“Microsoft Visual C++ 6.0”信息,而非UPX

3.修復IAT

(為什么要修復IAT,IAT是什么原因被破壞這個我還不是很清楚,不過先有個大致概念,以后再深入研究)

打開ImportREC程序,首先在進程列表中選擇正在用OD調試的那個notepad_upx.exe進程,然后在OEP中填入739D,然后點擊“IAT AutoSearch”按鈕,接着點擊“GetImports”按鈕,就可以看到程序的輸入表信息了。

點擊右側的“Show Invalid”按鈕,看看是否存在無效的輸入表項目。無效的輸入表項目前面帶有問號(?),如果有可以使用右鍵菜單刪除。這里沒有無效的輸入表項目,所以選擇“Fix Dump”按鈕,對我們的notepad_dump.exe進行修復,得到notepad_dumped_.exe程序。

至此脫殼完成。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM