banner
lca

lca

真正的不自由,是在自己的心中设下牢笼。

《從零開始學IDA逆向》學習筆記-16(Aspack脫殼)

puahad 和 popad#

加壳程序:unpackme.aspack.2.2,可從互聯網上搜索。

image.png

ida 打開,手動加載程序,取消勾選創建輸入段。

image.png

加壳程序入口

上圖中,第一條指令是pushadpushad會把所有通用寄存器的值傳到堆棧上。

pushad按如下順序將所有寄存器的值保存到堆棧上。

image.png

pushad 傳值順序

popad是和pushad相反的操作,它將堆棧中的值傳出,按如下順序保存到寄存器。

image.png

在大部分簡單的殼中,使用了pushad指令將寄存器的初始狀態保存,然後再跳轉到OEP執行內存中的原始代碼之前使用POPAD恢復寄存器的初始狀態。
根據這條規律,可以通過pushad-popad法來輕鬆找到OEP

使用 idapython 進行調試#

什麼是pushad-popad法呢?

使用調試器來運行程序,運行調試器有兩種方式,第一種通過菜單欄-調試器-選擇調試器,選擇local windows debugger,第二種方式是使用 python 來運行調試器,這次選用第二種方式運行調試器。

1、通過import idc導入 idc 模塊,輸入idc.load然後按 tab 鍵補全,找到idc.load_debugger,參數為idc.load_debugger("win32",0) //1為遠程調試,返回 True,代表執行成功。

image.png

載入 load_debugger 並設置參數

此時已加載了local windows debugger

image.png

在 pusha 之後設置斷點

image.png

pusha 之後設置斷點

pushad-popad法是在執行 pushad 後一條指令之前,找到堆棧上保存寄存器值的位置,然後在該位置設置一個斷點,在程序解密出原始代碼之後跳轉到 OEP 執行之前會通過 popad 恢復寄存器的初始值,然後觸發該斷點暫停執行,從而確定 OEP 的位置。

按 F2 鍵在 pusha 的下一條指令上設置斷點,那麼執行 pusha 後調試會暫停下來。(pusha 類似於 pushad)

如果使用 python 來設置斷點,可以使用如下語句:

idaapi.add_bpt(0x46b002,0,idc.BPT_SOFT)
idaapi.add_bpt(0x46b002,0,0)
  • 第一個參數是斷點地址
  • 第二個參數是斷點的長度
  • 第三個參數是斷點的類型(軟件斷點 BPT_SOFT 或 0)

image.png

選擇了調試器並且設置了第一處斷點後,現在需要啟動調試器運行程序然後暫停在該斷點上,按 F9 鍵或者使用 python 語句運行程序。

idc.StartDebugger("","","");

在高版本(ida 7.7)中,上述命令報錯,需要使用下述命令。

idc.start_process("","","")

執行上述語句後,調試器會運行到之前設置的斷點處,也就是0x46b002處。

image.png

運行到斷點0x46b002

下圖中,堆棧視圖中的值就是 pushad 保存的寄存器的值,之後會通過 popad 指令讀取,所以可以在第一行上設置一處斷點。

image.png

在這次調試中,斷點的位置是 0x19ff54,就是 esp 執行堆棧中的位置。

點擊 ESP 右側的小箭頭,會跳轉到對應窗口的該地址(此處是匯編窗口)。

image.png

按 F2 在此處可設置斷點,修改成讀取和寫入時觸發,而不是執行時觸發。

image.png

如果窗口未出現,可以打開菜單調試器 - 斷點 - 斷點列表,然後右鍵選擇編輯設置。

如果用 python 可以安裝如下方式設置斷點:

idaapi.add_bpt(0x19ff54,1,3)

image.png

斷點列表如下

image.png

斷點列表

上述代碼中,第一個參數表示斷點地址,第二個參數表示斷點的大小,第三個參數表示類型,3 代表 read-write access(讀取和寫入),執行這語句和手動設置斷點一樣。

BPT_EXEC = 0,
BPT_WRITE = 1,
BPT_RDWR = 3,
BPT_SOFT = 4,

設置斷點類型的參數

在斷點列表中,右鍵單擊第一次設置的斷點,選擇 DISABLE 禁用,或者使用如下 python 語句。

image.png

idaapi.enable_bpt(0x46b002,0)

image.png

  • 第一個參數為斷點地址
  • 第二個參數如果為 1 則啟用斷點,0 為禁用斷點

0x46b002 標記為綠色代表禁用,堆棧上的斷口是紅色為啟用。

image.png

按 F9 鍵繼續調試或者輸入如下的 python 語句

idaapi.continue_process()

下圖中,調試在 popad 指令取回寄存器初始值之後中斷,然後程序將跳轉到 OEP 也就是 0x4271b0,因為 push ret 就和 jmp 類似。

image.png

popa 指令

繼續單步執行執行到 oep。

image.png

既然找到了 oep 入口,那麼就可以重新分析可執行文件,識別函數。

image.png

重新分析程序

image.png

使用 idapython 進行 dump#

找到了 oep 後,下一步就是進行轉存,需要文件的基址以及可執行文件最後一個區段的最高地址。

image.png

上圖可以知道,基址是 0x40000,最高地址是 0x46e000。

轉存腳本如下:

import idaapi
import idc
import struct

start_ea = 0x400000
end_ea = 0x46e000
step = 4  # 每個地址處的數據占用4個字節

file_path = "dump.bin"
with open(file_path, "wb") as f:
    for ea in range(start_ea, end_ea, step):
        # 讀取指定地址處的4個字節數據並進行小端字節序轉換
        bin_data = struct.pack("<L", idaapi.get_32bit(ea))
        f.write(bin_data)

通過文件 - 腳本文件加載上面的 py 腳本,執行後,在當前目錄生成一個 dump.bin 文件,修改其擴展名為 exe。

通過 peeditor 打開,打開區段視圖,對所有的區段右鍵單擊,選擇 dumpfixer。

image.png

此時,圖標已修復。

image.png

修復圖標後,使用 Scylla 0.98 附加進程到被調試的加壳文件,目前執行到 OEP 入口。

image.png

加載此進程。

image.png

輸入 oep 為 004271B0,單擊 IAT Autosearch 和 Get Imports。

image.png

點擊 show invalid,會發現有一處未能識別的 API,嘗試自動修復失敗,需要手動修復。

image.png

0x460818 處的 API

上圖中,0x460818 是第一個有效區段的 API 函數,它上方更多的是未能識別的 API 地址。

檢查第一個未能識別的 0x46080c 處是什麼內容,按 D 鍵改變數據類型重組字節,如下圖:

image.png

這裡的內容不指向任何有效地址,如果按 CTRL+X,也沒有任何引用。

image.png

而對於真正的 API 函數,應該有引用信息表明它在何處被調用。

所以,由於這些不是 API 函數,所以可以刪除。

image.png

單擊 clear 鍵,然後點擊 IAT Autosearch,彈出的窗口選擇否。

image.png

現在的 IAT 起點位與 0x460810,然後點擊 Get Import,就可以找到所有的 API。

image.png

點擊 Fix Dump 並轉存文件,選擇之前導出的文件。

image.png

最後脫殼程序正常運行。

image.png

到此處,整個程序的脫殼就算是結束了。

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。