上節課講了許多有關殼的知識,下面我們就開始講如何脫殼,以及脫殼時要用的的一些工具,首先是查殼,當前流行的查殼工具主要以peid和fileinfo這兩個軟件為代表。
PEiD的原理是利用查特征串搜索來完成識別工作的。各種開發語言都有固定的啟動代碼部分,利用這點就可識別出何種語言編譯的。同樣,不同的殼也有其特征碼,利用這點就可以識別是被何種殼所加密。PEiD提供了一個擴展接口文件userdb.txt ,用戶可以自定義一些特征碼,這樣就可識別出新的文件類型。
![]() |
一般的壓縮殼,如Aspack等都有專用的脫殼機。而加密殼(如ASProtect,Armadillo) 一般很少有脫殼機,必須手工脫殼。手工脫殼一般情況是分三步:一是查找程序的真正入口點(OEP);二是抓取內存映像文件;三是輸入表重建。(當然現在的加密殼復雜些,要考慮更多的東西)OEP是Original Entry Point縮寫,即程序加殼前的真正的入口點。
如何尋找oep
外殼初始化的現場環境(各寄存器值)與原程序的現場環境是相同的。加殼程序初始化時保存各寄存器的值,外殼執行完畢,會恢復各寄存器內容。其代碼形式一般如下:
PUSHFD ; 將標志寄存器入棧保存
PUSHAD ; push eax, ecx, edx, ebx, esp, ebp, esi, edi
…… ; 外殼代碼部分
POPAD ; pop edi, esi, ebp, esp, ebx, edx, ecx, eax
POPFD ; 恢復標志寄存器
JMP OEP ;
OEP: …… ; 解壓后的程序原代碼
方法一:利用UPX 通用脫殼機自動脫殼
![]() |
脫殼后的程序,利用PEid進行分析,PEid給出如下的信息:
Borland Delphi 6.0-7.0
方法二:跨段指令尋找OEP
用Ollydbg來調試脫殼 ,運行Ollydbg,點擊菜單“Options/Debugging options”,選擇Events項,將第一次暫停設在WinMain函數上。 Ollydbg打開實例Bandook v1.35.exe.
相關代碼:
006E0440 $ 60 pushad //一開始Ollydbg就會中斷這行,這個就是外殼的入口點,注意這個pushad指令 .
如下圖所示
![]() |
絕大多數加殼程序在被加密的程序中加上一個或多個段,所以依據跨段的轉移指令(JMP)就可找到真正的入口點,此時就會有POPAD/POPFD 指令出現。UPX 用了一次跨段的轉移指令(JMP),在跳到OEP處會看到虛擬地址的值有一個突變,此時就能確定OEP了。
UPX殼比較簡單,中斷WinMain后,只需要在Ollydbg里往下翻屏,就會發現這個跨段轉移指令:
![]() |
上圖相關代碼如下:
006E05A2 > 61 popad //注意這里的popad指令,和開始的pushad對應
006E05A3 - E9 444EE7FF jmp 005553EC //這里跳到OEP,將光標移到這,按F4執行到這行
這句006E05A3 jmp 005553EC就是跳到OEP的指令,執行到這,UPX外殼己將程序解壓完畢,并模擬Windows加載器將原始程序加載到內存, 005553EC 就是映射到內存目標程序的入口點,此時就可抓取內存映像文件了。
相關代碼:
005553EC 55 push ebp
005553ED 8BEC mov ebp,esp
選擇Plugins->OllyDump->Dump debugged process項,Dump保存成新的文件。
![]() |
方法三:編譯語言特點找OEP
各類語言編譯的文件入口點都有一些規律,可以這利用這點來尋找入口點。
1)Delphi程序
執行程序,用LordPE(或Prodump)選dump(full)脫殼,存為dump.exe。接著用Hex Workshop打開 dump.exe,搜索文本“runtime”,搜到后,向前查找離“runtime”最近的十六進制數字“55 8B EC”,數字所在的地址就是程序的OEP。
2)Visual C程序
可以利用Visual C啟動部分幾個函數GetCommandLineA(W)、GetVersion、GetModuleHandleA(W)、GetStartupInfoA(W) 等來定位程序的OEP。
常見的各類編譯語言的入口匯編代碼都要熟悉,因為一些加密強殼會偷OEP處的代碼到殼里,一般情況各編譯語言入口代碼都相同,到時只需要直接引用相關程序的入口代碼,這給我們恢復代碼帶來方便。
其它技術:
通過堆棧平衡尋找OEP,通過內存斷點尋找OEP
補充講解一下windowsapi函數,這可是脫殼的時候離不開的,如果熟悉了它可以更快的找到程序的入口及程序領空
Win32 API是基于C語言的接口,但是Win32 API中的函數可以由用不同語言編寫的程序調用。
Windows的主要部分有三個主要子系統,分別是Kernel/User/GDI。
Kernel:操作系統核心功能服務,包括進行與線程控制、內存管理、文件訪問等;
User:負責處理用戶接口,包括鍵盤和鼠標輸入、窗口和菜單管理等;
GDI:圖形設備接口,允許程序在屏幕和打印機上顯示文本和圖形。
![]() |
常用Win32 API函數
GetWindowText函數,作用是取得一個窗體的標題文字,或文本控件的內容。
GetDlgItemText函數,作用是獲取對話框文本。
GetModuleFileName函數
InternetOpen函數
InternetOpenURL函數
SetWindowsHookEx函數
AdjustTokenPrivileges函數
LookupPrivilegeValue函數
OpenProcessToken函數
GetCurrentProcessId函數
GetCurrentProcess函數
GetWindowThreadProcessId函數
GetComputerName函數