1、認(rèn)識Import表
著者: [yAtEs] [Jamesluton@hotmail.com]
譯者:hying[CCG]
標(biāo)題:PE輸入表說明
例子:下載
有很多介紹PE文件的文章,但是我打算寫一篇關(guān)于輸入表的文章,因?yàn)樗鼘τ?a target="_blank" href="http://www.49028c.com" class="UBBWordLink">破解很有用。
我想解釋它的最好的方法是舉一個(gè)例子,你可以跟著我逐步深入,一步一步的思考,最后你將完全明白,我選擇了一個(gè)我剛下載下來的小程序,它是用TASM編譯的,有一個(gè)比較小的輸入表,所以我想它應(yīng)該是個(gè)不錯(cuò)的范例。
好了,讓我們開始吧。首先我們得找到輸入表,它的地址放在PE文件頭偏移80處,所以我們用16進(jìn)制編輯器打開我們的EXE文件,我們先得找到PE文件頭的起始點(diǎn),這很簡單,因?yàn)樗偸且訮E00開始,我們可以在偏移100處找到它。在一般的WIN32程序中文件頭偏移被放在文件0X3C處,在那我們通??煽吹?0 01 00 00,由于數(shù)據(jù)存儲時(shí)是低位在前,高位在后的,所以翻轉(zhuǎn)過來實(shí)際就是00000100,就象前面我們說的。接下來我們就可以在PE文件中找到我們的輸入表,100+80=180在偏移180處我們看到0030 0000,翻轉(zhuǎn)一下,它其實(shí)應(yīng)該是00003000,這說明輸入表在內(nèi)存3000處,我們必須把它轉(zhuǎn)換成文件偏移。
一般來說,輸入表總是在某個(gè)段的起始處,我們可以用PE編輯器來查看虛擬偏移,尋找3000并由此發(fā)現(xiàn)原始偏移。很簡單的。打開我們看到:
-CODE 00001000 00001000 00000200 00000600
-DATA 00001000 00002000 00000200 00000800
.idata 00001000 00003000 00000200 00000A00
.reloc 00001000 00004000 00000200 00000C00
找一下,我們就發(fā)現(xiàn).idata段的虛擬偏移是3000,原始偏移是A00,3000-A00=2600,我們要記住2600,以便以后轉(zhuǎn)換其它的偏移。如果你沒找到輸入表的虛擬偏移,那么就找一下最接近的段。
來到偏移A00處,我們就看到被稱為IMAGE_IMPORT_DESCRIPTORs(IID)的東東,它用5個(gè)字段表示每一個(gè)被調(diào)用DLL的信息,最后以Null結(jié)束。
**************************************************************************
(IID) IMAGE_IMPORT_DESCRIPTOR的結(jié)構(gòu)包含如下5個(gè)字段:
OriginalFirstThunk TimeDateStamp ForwarderChain Name FirstThunk
OriginalFirstThunk
該字段是指向一32位以00結(jié)束的RVA偏移地址串,此地址串中每個(gè)地址描述一個(gè)輸入函數(shù),它在輸入表中的順序是不變的。
TimeDateStamp
一個(gè)32位的時(shí)間標(biāo)志,有特殊的用處。
ForwarderChain
輸入函數(shù)列表的32位索引。
Name
DLL文件名(一個(gè)以00結(jié)束的ASCII字符串)的32位RVA地址。
FirstThunk
該字段是指向一32位以00結(jié)束的RVA偏移地址串,此地址串中每個(gè)地址描述一個(gè)輸入函數(shù),它在輸入表中的順序是可變的。
**************************************************************************
好了,你有沒有理解?讓我們看看我們有多少IID,它們從偏移A00處開始
3C30 0000 / 0000 0000 / 0000 0000 / 8C30 0000 / 6430 0000
{OrignalFirstThunk} {TimeDateStamp} {ForwardChain} {Name} {First Thunk}
5C30 0000 / 0000 0000 / 0000 0000 / 9930 0000 / 8430 0000
{OrignalFirstThunk} {TimeDateStamp} {ForwardChain} {Name} {First Thunk}
0000 0000 / 0000 0000 / 0000 0000 / 0000 0000 / 0000 0000
每三分之一是個(gè)分界,我們知道每個(gè)IID包含了一個(gè)DLL的調(diào)用信息,現(xiàn)在我們有2個(gè)IID,所以我們估計(jì)這個(gè)程序調(diào)用了2個(gè)DLL。甚至我可以打賭,你能推測出我們將能找到什么。
每個(gè)IID的第四個(gè)字段表示的是名字,通過它我們可以知道被調(diào)用的函數(shù)名。第一個(gè)IID的名字字段是8C30 0000,翻轉(zhuǎn)過來也就是地址0000308C,將它減去2600可以得到原始偏移,308C-2600=A8C,來到文件偏移A8C處,我們看到了什么?啊哈!原來調(diào)用的是KERNEL32.dll。
好了,接下來我們就要去找出KERNEL32.dll中被調(diào)用的函數(shù)?;氐降谝粋€(gè)IID。
FirstThunk字段包含了被調(diào)用的函數(shù)名的標(biāo)志,OriginalFirstThunk僅僅是FirstThunk的備份,甚至有的程序根本沒有,所以我們通??碏irstThunk,它在程序運(yùn)行時(shí)被初始化。
KERNEL32.dll的FirstThunk字段值是6430 0000,翻轉(zhuǎn)過來也就是地址00003064,減去2600得A64,在偏移A64處就是我們的IMAGE_THUNK_DATA,它存儲的是一串地址,以一串00結(jié)束。如下:
A430 0000/B230 0000/C030 0000/CE30 0000/DE30 0000/EA30 0000/F630 0000/0000 0000
通常在一個(gè)完整的程序里都將有這些。我們現(xiàn)在有了7個(gè)函數(shù)調(diào)用,讓我們來看其中的兩個(gè):
DE30 0000翻轉(zhuǎn)后是30DE,減去2600后等于ADE,看在偏移ADE處的字符串是ReadFile,
EA30 0000翻轉(zhuǎn)后是30EA,減去2600后等于AEA,看在偏移AEA處的字符串是WriteFile,
你可能注意到了,在函數(shù)名前還有2個(gè)這字節(jié)的00,它是被作為一個(gè)提示。
很簡單吧,你可以自己來試一下?;氐紸00,看第二個(gè)DLL的調(diào)用
5C30 0000 / 0000 0000 / 0000 0000 / 9930 0000 / 8430 0000
{OrignalFirstThunk} {TimeDateStamp} {ForwardChain} {Name} {First Thunk}
先找它的DLL文件名。9930翻轉(zhuǎn)為3099-2600 =A99,在偏移A99處找到USER32.dll。再看FirstThunk字段值:8430翻轉(zhuǎn)為3084-2600=A84,偏移A84處保存的地址為08310000,翻轉(zhuǎn)后3108-2600=B08,偏移B08處字符串為MessageBoxA。明白了吧,接下來你就可以把這些用在你自己的EXE文件上了。
摘要:
在PE文件頭+80偏移處存放著輸入表的地址,輸入表包含了DLL被調(diào)用的每個(gè)函數(shù)的函數(shù)名和FirstThunk,通常還有Forward Chain和TimeStamp。
當(dāng)運(yùn)行程序時(shí)系統(tǒng)調(diào)用GetProcAddress,將函數(shù)名作為參數(shù),換取真正的函數(shù)入口地址,并在內(nèi)存中寫入輸入表。當(dāng)你對一個(gè)程序脫殼時(shí)你可能注意到你有了一個(gè)已經(jīng)初始化的FirstThunk。例如,在我的WIN98上,函數(shù)GetProcAddress的入口地址是AE6DF7BF,在98上,所有的KERNEL32.dll函數(shù)調(diào)用地址看上去地址都象:xxxxF7BF,如果你在輸入表中看到這些,你可以利用orignal thunk重建它,或者重建這個(gè)PE程序。
好了,我已經(jīng)告訴你它們是如何工作的,我不是專家,如果你發(fā)現(xiàn)什么錯(cuò)誤,請告訴我。
2、Import表的重建
標(biāo)題:重建 PE 文件的輸入表
原著:TiTi/BLiZZARD
翻譯:Sun Bird [CCG]
日期:2000年5月24日
1. 前言
=======
大家好 :) 我之所以寫這篇短文,是由于我在 Dump 時(shí)發(fā)現(xiàn),很多加壓、加密軟件都使
得輸入表(Import Table)不可用,所以 Dump 出的可執(zhí)行文件必須要重建輸入表。而在普
通的講授 Win32 匯編的站點(diǎn)上我沒有找到這樣的介紹,所以如果你對此感興趣,那么這篇
短文對你會(huì)有些幫助。
例如,為了讓從內(nèi)存中 Dump 出的經(jīng) PETite v2.1 壓縮過的可執(zhí)行文件正常運(yùn)行,必
須重建輸入表。(對于 ASPack、PEPack、PESentry……也同樣)這就是所有 Dump 軟件都
具備重建輸入表功能的原因(例如 G-RoM/UCF 制作的 Phoenix Engine(ProcDump 內(nèi)含),
或者由 Virogen/PC 和我制作的 PE Rebuilder)。
鑒于這個(gè)問題十分特殊,而且比較復(fù)雜,所以我假定你已經(jīng)了解了 PE 文件結(jié)構(gòu)。(你
需要閱讀有關(guān) PE 文件的文檔)
2. 預(yù)備知識
===========
首先是一些關(guān)于輸入表和 RVA/VA 的簡介。
輸入表的相對虛擬地址(RVA)儲存在 PE 文件頭部的相應(yīng)目錄入口(它的偏移量為
[ PE 文件頭偏移量+80h ])。由于是虛擬偏移量,所以它和文件輸入表中的偏移量(VA)
是不匹配的(除非文件純粹是剛剛從內(nèi)存中 Dump 出來的)。于是我們首先要做的事情是,
找到 PE 文件的輸入表,將 RVA 轉(zhuǎn)換為相應(yīng)的 VA。為此,我們可以采用不同的辦法:你可
以自行編制軟件來分析塊(Sections)目錄并計(jì)算 VA,但最簡單的辦法是使用專門為此設(shè)
計(jì)的應(yīng)用程序接口(API)。這個(gè) API 包括在 IMAGEHLP.DLL(Win9X 和 NT 系統(tǒng)都使用的
一個(gè)庫)中,名為 ImageRvaToVa。下面是對它的描述(完整的內(nèi)容詳見 MSDN 庫):
# LPVOID ImageRvaToVa(
# IN PIMAGE_NT_HEADERS NtHeaders
# IN LPVOID Base
# IN DWORD Rva
# IN OUT PIMAGE_SECTION_HEADER *LastRvaSection
#);
#
# 參數(shù):
#
# NtHeaders
#
# 指示一個(gè) IMAGE_NT_HEADERS 結(jié)構(gòu)。通過調(diào)用 ImageNtHeader 函數(shù)可以獲得這個(gè)結(jié)構(gòu)。
#
# Base
#
# 指定通過調(diào)用 MapViewOfFile 函數(shù)映射入內(nèi)存的一個(gè)映象的基址(Base Address)。
#
# Rva
#
# 指定相對虛擬地址的位置。
#
# LastRvaSection
#
# 指向一個(gè)指定的最終 RVA 塊的 IMAGE_SECTION_HEADER 結(jié)構(gòu)。這是一個(gè)可選參數(shù)。當(dāng)被
#指定時(shí),它指向一個(gè)變量,該變量包含指定映象的最后塊值,以便將 RVA 轉(zhuǎn)換為 VA。
就這么簡單。你只需要將 PE 文件映射入內(nèi)存,然后調(diào)用這個(gè)函數(shù)就能夠得到輸入表的正
確 VA。
注意,下面我會(huì)忽略所有的 RVA/VA 注釋,但是,當(dāng)你對重建的 PE 文件進(jìn)行讀出或?qū)懭?br/>RVAs 操作時(shí),不要忘記它們之間的轉(zhuǎn)換。
3. 完整說明
===========
這里是一個(gè)完整改變輸入表的例子(這個(gè) PE 文件的輸入表已經(jīng)被 PETite v2.1 壓縮過,
并且是直接從內(nèi)存中 Dump 出來的):
我們用“`”表示 00,用“-”表示非字符串
0000C1E8h : 00 00 00 00 00 00 00 00 00 00 00 00 BA C2 00 00 ````````````----
0000C1F8h : 38 C2 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ----````````````
0000C208h : C5 C2 00 00 44 C2 00 00 00 00 00 00 00 00 00 00 --------````````
0000C218h : 00 00 00 00 D2 C2 00 00 54 C2 00 00 00 00 00 00 ````--------````
0000C228h : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ````````````````
0000C238h : 7F 89 E7 77 4C BC E8 77 00 00 00 00 E6 9F F1 77 --------````----
0000C248h : 1A 38 F1 77 10 40 F1 77 00 00 00 00 4F 1E D8 77 --------````----
0000C258h : 00 00 00 00 00 00 4D 65 73 73 61 67 65 42 6F 78 ``````MessageBox
0000C268h : 41 00 00 00 77 73 70 72 69 6E 74 66 41 00 00 00 A```wsprintfA```
0000C278h : 45 78 69 74 50 72 6F 63 65 73 73 00 00 00 4C 6F ExitProcess```Lo
0000C288h : 61 64 4C 69 62 72 61 72 79 41 00 00 00 00 47 65 adLibraryA````Ge
0000C298h : 74 50 72 6F 63 41 64 64 72 65 73 73 00 00 00 00 tProcAddress````
0000C2A8h : 47 65 74 4F 70 65 6E 46 69 6C 65 4E 61 6D 65 41 GetOpenFileNameA
0000C2B8h : 00 00 55 53 45 52 33 32 2E 64 6C 6C 00 4B 45 52 ``USER32.dll`KER
0000C2C8h : 4E 45 4C 33 32 2E 64 6C 6C 00 63 6F 6D 64 6C 67 NEL32.dll`comdlg
0000C2D8h : 33 32 2E 64 6C 6C 00 00 00 00 00 00 00 00 00 00 32.dll``````````
正如你看到的,這個(gè)輸入表被分成三個(gè)主要部分:
- C1E8h - C237h:IMAGE_IMPORT_DESCRIPTOR 結(jié)構(gòu)部分,對應(yīng)著每一個(gè)需要輸入的動(dòng)態(tài)
鏈接庫(DLL)。這部分以關(guān)鍵字 00 結(jié)束。
IMAGE_IMPORT_DESCRIPTOR struct
OriginalFirstThunk dd 0 ;原拆分 IAT 的 RVA
TimeDateStamp dd 0 ;沒有使用
ForwarderChain dd 0 ;沒有使用
Name dd 0 ;DLL 名字符串的 RVA
FirstThunk dd 0 ;IAT 部分的 RVA
IMAGE_IMPORT_DESCRIPTOR ends
- C238h - C25Bh:這部分雙字(DWord) 稱作“IAT”,由 IMAGE_IMPORT_DESCRIPTOR
結(jié)構(gòu)中的 FirstThunk 部分指明。這部分每一個(gè) DWord 對應(yīng)一個(gè)輸入函數(shù)。
- C25Ch - C2DDh : 這里是輸入函數(shù)和 DLL 文件的名稱。問題是,這些是沒有規(guī)定順序
的:有時(shí)候 DLL 文件在函數(shù)前面,有時(shí)候正好相反,另外一些時(shí)候它們混在一起。
輸入表的簡介
------------
OriginalFirstThunk 是 IAT 的一部分,它是 PE 文件引導(dǎo)時(shí)首先要搜索的。如果存在,PE
文件的引導(dǎo)部分將使用它來糾正在 FirstThunk IAT 部分的問題。當(dāng)調(diào)入內(nèi)存后,F(xiàn)irstThunk
的每一個(gè) Dword (包含有函數(shù)名字符串的 RVA),將被 RVA 替換為函數(shù)的真實(shí)地址(當(dāng)調(diào)用這
些函數(shù)時(shí),它們調(diào)入內(nèi)存的位置將被執(zhí)行)。所以,只要 OriginalFirstThunk 沒有被改變,基
本上這里不存在輸入表的問題。 下面來看我們的問題
------------------
好了,經(jīng)過簡單描述后,下面來看我們的問題。如果你試圖運(yùn)行包含上面顯示的輸入表的可
執(zhí)行文件,它不會(huì)被調(diào)入,Windows 會(huì)顯示一個(gè)錯(cuò)誤信息。為什么?很簡單,因?yàn)?br/>OriginalFirstThunk 被刪除了。事實(shí)上,你應(yīng)該注意到,在這個(gè)輸入表的每一個(gè)IMAGE_IMPORT_DESCRIPTOR 結(jié)構(gòu),OriginalFirstThunk 的內(nèi)容都是 00000000h。嗯,所以我們
可以推測出,當(dāng)我們運(yùn)行這個(gè)可執(zhí)行程序時(shí),PE 文件的引導(dǎo)部分試圖從 FirstThunk 部分獲得
輸入函數(shù)的名字。但是,正象你注意到的,這部分根本沒有包含函數(shù)名字符串的 RVA,但是函數(shù)
地址的 RVA 在內(nèi)存中。
我們需要怎么做
--------------
現(xiàn)在,為了讓這個(gè)可執(zhí)行文件運(yùn)行,我們需要重建 FirstThunk 部分的內(nèi)容,讓它們指向我
們在輸入表第三部分看到的函數(shù)名字符串。這不是一項(xiàng)很困難的任務(wù),但是,我們需要知道哪個(gè)
IAT 對應(yīng)哪個(gè)函數(shù),而函數(shù)字符串和 FirstThunk 內(nèi)容并不采用同樣的存儲方法。所以,對于每
一個(gè) IAT,我們需要驗(yàn)證它對應(yīng)的是哪個(gè)函數(shù)名(事實(shí)上,根據(jù) IMAGE_IMPORT_
DESCRIPTOR.Name DWord 我們已經(jīng)有了 DLL 名稱,這些并沒有被改變)。
如何驗(yàn)證每一個(gè)函數(shù)
------------------
正向我們上面所見到的,在內(nèi)存中,每一個(gè)被破壞的 IAT 都有一個(gè)函數(shù)地址的 RVA。這些
地址并沒有被破壞,所以,我們只要重新找回指向錯(cuò)誤 IAT 的函數(shù)地址,把它們指向函數(shù)名字
符串。
為此,在 Kernel32.dll 中有一個(gè)非常有用的 API:GetProcAddress。它允許你得到給定函
數(shù)的地址。這里是它的描述:
GetProcAddress(
HMODULE hModule // DLL 模塊的句柄
LPCSTR lpProcName // 函數(shù)名
);
所以,對于每一個(gè)被破壞的 IAT,在 GetProcAddress 返回我們尋找的函數(shù)地址之前,只需
要分析包含在輸入表第三部分的所有函數(shù)名。
- hModule 參數(shù)是 DLL 模塊的句柄(也就是說,模塊映象在內(nèi)存中的基址),我們可以通
過 GetModuleHandleA API 得到:
HMODULE GetModuleHandle(
LPCTSTR lpModuleName // 返回模塊名地址句柄
);
(lpModuleName 只需要指向我們從 IMAGE_IMPORT_DESCRIPTOR.Name 部分得到的 DLL 文件
名字符串)
- lpProcName 僅指向函數(shù)名字符串。
注意,有時(shí)候函數(shù)是按序號輸入的。這些序號是在每個(gè) [ 函數(shù)名偏移量-2 ] 處的單字(WORDS)。
所以,你在分析程序時(shí)需要檢查函數(shù)是按名稱還是按序號輸入的。
使用上面輸入表的實(shí)例
--------------------
針對上面輸入表的例子,我將說明如何修復(fù)第一個(gè)輸入 DLL 的第一個(gè)輸入函數(shù)。
1. 我們來看第一個(gè) IMAGE_IMPORT_DESCRIPTOR 結(jié)構(gòu)部分(C1E8h),.Name 部分(C1E4h,指向
C1BAh)指出了 DLL 名。我們看到,那是 USER32.dll。
2. 我們來看 .FirstThunk 部分,它們指向 IAT 部分;每個(gè)對應(yīng)一個(gè)這個(gè) DLL(user32.dll)的
輸入函數(shù)。在這里是 C1F8h,指向 C238h。所以,在 C238h,我們可以修復(fù)被破壞的 IATs。(你
會(huì)注意到,這個(gè) IAT 部分包含二個(gè) DWords,所以,這個(gè) DLL 有二個(gè)函數(shù)輸入)
3. 我們得到了第一個(gè)被破壞的 IAT。它的值是 77E7897Fh。這是函數(shù)在內(nèi)存中的地址。
4. 對每一個(gè)輸入表第三部分中的函數(shù),我們調(diào)用 GetProcAddress API。當(dāng)該 API 返回 7E7897Fh
時(shí)就意味著,我們到達(dá)了正確的函數(shù)。所以我們讓被破壞的 IAT 指向正確函數(shù)名(在本例中為 'wsprintfA')。
5. 現(xiàn)在我們只需要將 IAT 指向:偏移量(函數(shù)名字符串)-2。為什么是 -2 ?因?yàn)橛袝r(shí)候使用了
函數(shù)序列。
所以在本例中,我們改變地址 C238h,讓它指向 C26Ah(以代替 77E7897Fh)。
6. 就這樣,這個(gè)函數(shù)被修復(fù)了,下面你只需要對所有的 IATs 重復(fù)這個(gè)過程就可以了。
后記
----
我描述的是一般的操作過程。當(dāng)然只有在 DLLs 被正常調(diào)入內(nèi)存后才能夠這樣做。對于其他情
況,你需要將它們調(diào)入,或者你需要仔細(xì)研究它們的輸出表才能找到正確的函數(shù)地址。
3、IceDump和NticeDump使用
IceDump和NticeDump是一款配合SoftICE擴(kuò)展其內(nèi)存操作的工具,IceDump支持Windows 9x、Windows Millennium系統(tǒng),NticeDump支持Windows NT/2000。它們的出現(xiàn),使SoftICE如虎添翼,TRW2000的許多特色功能在SoftICE里也可實(shí)現(xiàn)了。
1.Icedump操作簡介
運(yùn)行IceDump前,首先要確定SoftICE版本號,按Ctrl+D切換到SoftICE下命令:VER,查看版本號。然后在相應(yīng)SoftICE版本號目錄下運(yùn)行icedump.exe文件,它會(huì)調(diào)用自身的VXD文件,裝載成功出現(xiàn)圖7.16所示畫面。如果發(fā)現(xiàn)SoftICE沒運(yùn)行或版本不符,就拒絕運(yùn)行。 如果想從內(nèi)存中卸載它,可以在DOS下鍵入"icedump u"。
1)/DUMP <起始地址> [<長度> <文件名>]
抓取內(nèi)存中的數(shù)據(jù)到文件里,類似TRW2000中的W命令。<文件名>參數(shù)可以指定盤符和路徑,當(dāng)在Ring-0下還原時(shí)最好清除還原區(qū)域內(nèi)的全部斷點(diǎn),否則會(huì)給SoftICE帶來不必要麻煩。(這一點(diǎn)在所有的IceDump命令里都應(yīng)該值得注意)
在Win32系統(tǒng)下讀者可能會(huì)想到用/BHRAMA或/PEDUMP從內(nèi)存內(nèi)中重建一個(gè)可用的PE鏡像。請看下面關(guān)于/OPTION命令的說明。
注意: IceDump 6.015以前類似的命令是PAGEIN D <address> [<length> <200456124836.htm>]
2)/LOAD <地址> <長度> <文件名>
把<文件>指定長度的字節(jié)內(nèi)容調(diào)入到內(nèi)存中的<地址>處。與/DUMP的作用相反,同樣需要注意的是不要設(shè)置斷點(diǎn)。
3)/BHRAMA <Bhrama dumper server 窗口名>
用Procdump的Bhrama(由G-Rom出品的著名脫殼工具)來初始化dumping。用戶必須提供窗口的名稱,可以從標(biāo)題條找到它。為了使工作簡單化,可以在winice.dat里設(shè)置F3鍵:
F3="/BHRAMA ProcDump32 - Dumper Server;"
4) /TRACEX <low EIP> [<high EIP>]
控制跟蹤器并退出SoftICE。注意該命令只能用于跟蹤當(dāng)前線程,如果要跟蹤其它線程,請使用/TRACE命令。
/TRACEX <low EIP>: 跟蹤當(dāng)前線程。注意,如果跟蹤當(dāng)前線程彈出SoftICE窗口后想繼續(xù)跟蹤,必須使用/TRACEX命令,否則跟蹤器會(huì)失去對當(dāng)前線程的控制。
當(dāng)線程的EIP到達(dá)<low EIP>時(shí),跟蹤停止并彈出SoftICE窗口。這也要求EIP真正可以到達(dá),否則SoftICE不會(huì)彈出。
/TRACEX <low EIP> <high EIP> 跟蹤當(dāng)前線程,注意事項(xiàng)同上。
當(dāng)線程的EIP到達(dá)<low EIP>與<high EIP>之間的區(qū)域內(nèi)時(shí)停止并彈出SoftICE窗口。注意這里沒有作<low EIP>和<high EIP>的邊界檢查,所以錯(cuò)誤的參數(shù)地址會(huì)使SoftICE不能中斷。
5) /SCREENDUMP [<文件名>]
把SoftICE屏幕內(nèi)容保存到一個(gè)文件中。注意該功能只支持通用顯示驅(qū)動(dòng)模式。這個(gè)命令的用法類似于/DUMP,如果沒有指定<文件名>,IceDump將在模式0、1、2、3和4中切換。
模式1:默認(rèn)模式,將以ASCII格式輸出。
模式0:字節(jié)屬性也將被抓取。
模式2:可以把屏幕內(nèi)容保存成一個(gè)HTML文件。
模式3:會(huì)把屏幕內(nèi)容保存成LaTeX格式的文件。
模式4 :把屏幕內(nèi)容保存為EPS (encapsulated Postscript)格式。
2.NticeDump操作簡介
Nticedump遠(yuǎn)不如IceDump功能強(qiáng)大,并且Nticedump裝載方式不同于IceDump,它是通過給SoftICE打補(bǔ)丁來實(shí)現(xiàn)0特權(quán)級控制權(quán)的,這是因?yàn)樵赪indows 2000上,要切換到0特權(quán)級不象Windows9x那么容易了。
要打補(bǔ)丁的文件是/WINNT/SYSTEM32/DRIVERS/Ntice.sys,在Nticedump目錄里有一補(bǔ)丁工具ntid.exe,把安裝目錄下相應(yīng)SoftICE版本的Icedump文件與ntid.exe一同復(fù)制到/WINNT/SYSTEM32/DRIVERS/目錄下,然后運(yùn)行ntid.exe程序就能正確補(bǔ)丁Ntice.sys。這樣Nticedump和SoftICE就完全結(jié)合了。
1) 抓取內(nèi)存數(shù)據(jù):PAGEIN D 基地址 長度 文件名
例: PAGEIN D 400000 512 /??/C:/memory.dmp
注意: 在NT輸入輸出管理系統(tǒng)中,象"C:/memory.dmp"不是合法路徑。"/??/C:/200456124836.htm.dmp"是在C盤根目錄下創(chuàng)建"200456124836.htm.dmp"文件。
2) 抓取進(jìn)程: PAGEIN B <Bhrama窗口名>
例: PAGEIN B ProcDump32 - Dumper Server
3) 導(dǎo)入文件: PAGEIN L 基地址 長度 文件名
Example: PAGEIN L 400000 512 /??/C:/memory.dmp
4) 幫助: PAGEIN 例: PAGEIN
4、Import REConstructor使用
Import REConstructor可以從雜亂的IAT中重建一個(gè)新的Import表(例如加殼軟件等),它可以重建Import表的描述符、IAT和所有的ASCII函數(shù)名。用它配合手動(dòng)脫殼,可以脫UPX、CDilla1、PECompact、PKLite32、Shrinker、ASPack ASProtect等殼。該工具位于:光盤/tools/PE tools/Rebuilders/Import REConstructor。
在運(yùn)行Import REConstructor之前,必須滿足如下條件:
1) 目標(biāo)文件己完全被Dump到另一文件;
2) 目標(biāo)文件必須正在運(yùn)行中;
3) 事先要找到真正的入口點(diǎn)(OEP);
4) 最好加載IceDump,這樣建立的輸入表較少存在跨平臺的問題。
步驟如下:
(1)找被脫殼的入口點(diǎn)(OEP);
(2)完全Dump目標(biāo)文件;
(3)運(yùn)行Import REConstructor和需要脫殼的應(yīng)用程序;
(4)在Import REConstructor下拉列表框中選擇應(yīng)用程序進(jìn)程;
(5)在左下角填上應(yīng)用程序的真正入口點(diǎn)偏移(OEP);
(6)按"IAT AutoSearch"按鈕,讓其自動(dòng)檢測IAT位置, 出現(xiàn)"Found address which may be in the Original IAT.Try 'Get Import'"對話框,這表示輸入的OEP發(fā)揮作用了。
(7)按"Get Import"按鈕,讓其分析IAT結(jié)構(gòu)得到基本信息;
(8)如發(fā)現(xiàn)某個(gè)DLL顯示"valid :NO" ,按"Show Invalids"按鈕將分析所有的無效信息,在Imported Function Found欄中點(diǎn)擊鼠標(biāo)右鍵,選擇"Trace Level1 (Disasm)",再按"Show Invalids"按鈕。如果成功,可以看到所有的DLL都為"valid:YES"字樣;
(9)再次刷新"Show Invalids"按鈕查看結(jié)果,如仍有無效的地址,繼續(xù)手動(dòng)用右鍵的Level 2或3修復(fù);
(10)如還是出錯(cuò),可以利用"Invalidate function(s)"、"Delete thunk(s)"、編輯Import表(雙擊函數(shù))等功能手動(dòng)修復(fù)。
(11)開始修復(fù)已脫殼的程序。選擇Add new section (缺省是選上的) 來為Dump出來的文件加一個(gè)Section(雖然文件比較大,但避免了許多不必要的麻煩) 。
(12)按"Fix Dump"按鈕,并選擇剛在(2)步Dump出來的文件,在此不必要備份。如修復(fù)的文件名是"Dump.exe",它將創(chuàng)建一個(gè)"Dump_.exe",此外OEP也被修正。
(13)生成的文件可以跨平臺運(yùn)行。
1、ASProtect v0.95保護(hù)
教程寫作: 看雪
技術(shù)指導(dǎo):D.boy 和RuFeng
寫作日期:2000年5月30日
目標(biāo)程序:ShowDep 4.0 beta 1
程序大小:Showdep.exe 為177K
程序下載:ShowDep 4.0
使用工具:Softice 4.05; ProcDump 1.6.2 Final; FrogsICE v0.43;Icedump 6.016
首先忠心感謝D.boy 和RuFeng的熱心幫助,是在他們的幫助下,我才對PE文件有一定程度的了解。我把從他們那里吸取的經(jīng)驗(yàn)總結(jié)了一篇文章,希望對大家有所幫助。為了使初學(xué)者能更好理解這文章,本人盡量寫的仔細(xì)點(diǎn)。
一、Import表的一些理論知識
在你看這篇文章之前你應(yīng)對PE文件的結(jié)構(gòu)有一定了解否則請先看看PE介紹.
1、現(xiàn)在不少軟件脫殼后Import表被損壞或地址發(fā)生改變,需要我們手動(dòng)找到正確的Import表來替換被破壞的Import表,并修正Import表的地址,程序才能正確執(zhí)行。
程序裝載時(shí)需要裝載很多函數(shù)和DLL文件這時(shí)程序需要判定目標(biāo)函數(shù)的地址并將該函數(shù)插補(bǔ)到該執(zhí)行文件的映像中,所需要的信息都是放在PE文件的Import表,PE文件中的每一個(gè)輸入函數(shù)都明確的列于該表中。 一般來說Import表是存放在程序的.idata塊,它一般包含其他外來DLL的函數(shù)及數(shù)據(jù)信息。但也有不少程序的Import表不存在idata塊中,給我們判斷Import表的地址造成困難,但不要著急,只要了解Import表的結(jié)構(gòu)你就能迅速定位Import表的地址。
2、Import表以一個(gè)IMAGE_IMPORT_DESCRIPTOR數(shù)組開始。每一個(gè)被PE文件隱式連結(jié)進(jìn)來的DLL都有一個(gè)IMAGE_IMPORT_DESCRIPTOR。在這個(gè)數(shù)組中,沒有字段指出該結(jié)構(gòu)數(shù)組的項(xiàng)數(shù),但它的最后一個(gè)單元是NULL,可以由此計(jì)數(shù)算出該數(shù)組的項(xiàng)數(shù)。IMAGE_IMPORT_DESCRIPTOR的格式如下:
image_import_descriptors結(jié)構(gòu):
IMAGE_IMPORT_DESCRIPTOR struct
riginalFirstThunk dd 0 該字段是一個(gè)指針數(shù)組的RVA偏移。其中每一個(gè)指針都指向一IMAGE_IMPORT_BY_NAME結(jié)構(gòu)
TimeDateStamp dd 0 時(shí)間及日期標(biāo)志,在這可以忽略
ForwarderChain dd 0 正向鏈結(jié)索引,在這可以忽略
Name dd 0 以NULL結(jié)尾的ASCII字符的RVA地址,該字符串包含輸入的DLL名,比如"Kernel32.dll" 或"USER32.DLL" (關(guān)鍵!,我們定位Import表的依據(jù))
FirstThunk dd 0 該字段是在Image_thunk_data聯(lián)合結(jié)構(gòu)中的RVA偏移。大多數(shù)情況下,Image_thunk_data是指IMAGE_IMPORT_BY_NAME結(jié)構(gòu)的指針。如果不是一個(gè)指針的話,那它就是該功能在DLL中的序號。
IMAGE_IMPORT_DESCRIPTOR ends
3、為了使大家更好理解,我們以ShowDep 4.0 beta的Import表為例,ShowDep 4.0用 ASProtect 加殼,用prodump 分析,發(fā)現(xiàn)沒有idata段。 脫殼大師D.boy很成功分析出該軟件的Import表:import rav 0042D104,size 00001470 ;Image Base(基址)=00400000。
該軟件的Import表的image_import_descriptors結(jié)構(gòu)如下:
-----SHOWDEP!.rdata+1104--------------------------dword-------------PROT---(0)--
0030:0042D104 ①0002D23C ②00000000 ③00000000 ④0002DA8A <............... ^
0030:0042D114 ⑤0002C070 ⑥0002D43C ⑦00000000 ⑦00000000 p...<........... v
(圖一)
為了解釋方便,我在每個(gè)數(shù)據(jù)前加了序號。上圖就是一個(gè)典型的Import表開始處的image_import_descriptors結(jié)構(gòu),Import表就是以這個(gè)數(shù)組開始的一段連續(xù)內(nèi)存空間,在這里大小是1470的連續(xù)空間。各項(xiàng)數(shù)據(jù)含義如下:
IMAGE_IMPORT_DESCRIPTOR struct
riginalFirstThunk dd 0 ①0002D23C
TimeDateStamp dd 0 ②00000000
ForwarderChain dd 0 ③00000000
Name dd 0 ④0002DA8A(關(guān)鍵!,我們定位Import表的依據(jù))
FirstThunk dd 0 ⑤0002C070
IMAGE_IMPORT_DESCRIPTOR ends
現(xiàn)在我們將image_import_descriptors結(jié)構(gòu)中每項(xiàng)的地址均顯示分析一下:
① Name選項(xiàng)(我們定位Import表地址就是以此為依據(jù)的)
在這例Name項(xiàng)值為:④0002DA8A
下命令DD 42DA8A (顯示內(nèi)存地址42DA8A的數(shù)據(jù),其中42DA8A=④0002DA8A+I(xiàn)mage Base(基址))
-----SHOWDEP!.rdata+1A8A--------------------------dword-------------PROT---(0)--
0030:0042DA8A 4E52454B 32334C45 4C4C442E 019A0000 KERNEL32.DLL.... ^
0030:0042DA9A 64616F4C 73727543 0041726F 6F4C01AB LoadCursorA...Lo v
(圖二)
想必大家己睜大眼睛了,發(fā)現(xiàn)什么有價(jià)值的東西?對,就是KERNEL32.DLL!就是我們的突破口。
Import表裝載的基本原理是:根據(jù)Import表的指示找到外部模塊的文件名,再使用Win32 API函數(shù)GetModuleHandleA獲得該模塊在內(nèi)存中的句柄。如果沒在內(nèi)存中就使用LoadLibraryA API調(diào)用裝入該模塊。隨后使用獲得的模塊句柄調(diào)用Win32 API函數(shù)GetProcAddress 獲得該模塊中Import表指定功能的實(shí)際地址,加上裝入基址,并且填入Import表的FirstThunk所指的IMAGE_IMPORT_BY_NAME結(jié)構(gòu)指針數(shù)組中,完成該模塊的一個(gè)功能的人工裝入填寫。循環(huán)調(diào)用函數(shù)GetProcAddress以獲得其他功能調(diào)用的地址,加上裝入基址,并填入之,以完成一個(gè)外部模塊的裝入。再循環(huán)上述過程對其他模塊進(jìn)行裝入。
因此我們可以從函數(shù)LoadLibraryA入手,該函數(shù)會(huì)裝入外部模塊,我們監(jiān)視這函數(shù)的入口參數(shù)是否為KERNEL32.DLL,以此來確定Import表的狀況。即確定 image_import_descriptors結(jié)構(gòu)中的name選項(xiàng)。
函數(shù)LoadLibrary:
HINSTANCE LoadLibrary(
LPCTSTR lpLibFileName // 執(zhí)行模塊的文件名和地址
);
只要函數(shù)LoadLibrary參數(shù)的模塊名為KERNEL32.DLL,就會(huì)出現(xiàn)圖二的情況,這時(shí)KERNEL32.DLL地址為0042DA8A。因此image_import_descriptors結(jié)構(gòu)中name為0042DA8A―400000=2DA8A,這時(shí)利用S命令在內(nèi)存中查找字條串002DA8A,就可確定import表的地址。
(到這里我們就能確定Import表的地址了,下面幾項(xiàng)可幫助我們大家更好理解Import表)
②riginalFirstThunk項(xiàng)
在這里riginalFirstThunk項(xiàng)值為:①0002D23C
下命令DD 42D23C (顯示內(nèi)存地址42D23C的數(shù)據(jù),其中42DA8A=①0002D23C+I(xiàn)mage Base(基址))
-----SHOWDEP!.rdata+123C--------------------------dword-------------PROT---(0)--
0030:0042D23C 0002D798 0002D7A8 0002D7BE 0002D782 ................ ^
0030:0042D24C 0002D7CC 0002D7E0 0002D7F6 0002D80A ................ ^
0030:0042D25C 0002D81C 0002D830 0002D840 0002D854 ....0...@...T... v
0030:0042D26C 0002D868 0002D87C 0002D890 0002D8A0 h...|........... v
(圖三)
圖三是一個(gè)指針數(shù)組,其中每一個(gè)指針都指向一IMAGE_IMPORT_BY_NAME結(jié)構(gòu)。
我們以第一個(gè)指針0002D798為例,顯示它所指的IMAGE_IMPORT_BY_NAME結(jié)構(gòu).
下命令:dd 42d798(注意:0002D798+I(xiàn)mage Base(基址))
-----SHOWDEP!.rdata+1798--------------------------dword-------------PROT---(0)--
0030:0042D798 6547011B 636F4C74 69546C61 0000656D ..GetLocalTime.. ^
0030:0042D7A8 6F430025 6E69746E 65446575 45677562 %.ContinueDebugE ^
0030:0042D7B8 746E6576 022B0000 65736552 65764574 vent..+.ResetEve v
0030:0042D7C8 0000746E 615702CB 6F467469 62654472 nt....WaitForDeb v
(圖四)
③FirstThunk項(xiàng)
在這例,F(xiàn)irstThunk項(xiàng)的值為:⑤0002C070
下命令dd 42c070 (注意:0002c070+I(xiàn)mage Base(基址)):
-----SHOWDEP!.rdata+0070--------------------------dword-------------PROT---(0)--
0030:0042C070 0002D798 0002D7A8 0002D7BE 0002D782 ................ ^
0030:0042C080 0002D7CC 0002D7E0 0002D7F6 0002D80A ................ ^
0030:0042C090 0002D81C 0002D830 0002D840 0002D854 ....0...@...T... v
0030:0042C0A0 0002D868 0002D87C 0002D890 0002D8A0 h...|........... v
(圖五)
圖五是一個(gè)指針數(shù)組(Image_thunk_data聯(lián)合結(jié)構(gòu)),大多數(shù)情況下,Image_thunk_data是指IMAGE_IMPORT_BY_NAME結(jié)構(gòu)的指針。如果不是一個(gè)指針的話,那它就是該功能在DLL中的序號。不知你發(fā)現(xiàn)沒有,圖五的數(shù)據(jù)和圖三完全相同,原因就是這個(gè)。
二、確定Import表的地址和大小并修正Import表
通過上面的講解,想必大家對Import表己比較熟悉了吧,現(xiàn)在以脫ShowDep 4.0 beta 1的殼為例,講解定位Import表的位置和大小的幾種方法。
方法一:通過idata來確定Import表的位置
大部分加殼程序的塊表中都有.idata這一項(xiàng),.idata包含其他外來DLL的函數(shù)及數(shù)據(jù)信息,也就是說Import表的起始地址就是.idata的地址。如是這種情況,脫殼就簡單多了。
先dump取正確的Import表(假設(shè)取名為idata.bin),然后在解壓入口點(diǎn)full dump取整個(gè)程序(假設(shè)取名為dump.exe)用 Procdump打開dump.exe文件, 察看.idata Section. 記下Raw Size和 Raw Offset的值。用HexWorkshop打開dump.exe 將idata.bin拷貝/粘貼到 dump.exe(粘貼位置為Raw Offset 大小為Raw Size). 再修正Entry Point和Import表的地址(此值就是.idata的RVA)
具體操作參考后面幾節(jié)的脫殼教學(xué)實(shí)例。
方法二:沒.idata一項(xiàng),利用bpx loadlibrarya來判斷Import表的位置
由于ShowDep 4.0 beta 1沒.idata一項(xiàng),因此要確定Import表的位置和大小較困難,步驟如下:
①分析ShowDep 4.0 beta 1的文件PE頭
運(yùn)行 Procdump,點(diǎn)擊pe-editor按鈕,選中ShowDep.exe文件:
Size of image : 000A0000 ; 這個(gè)PE文件執(zhí)行時(shí)分配的內(nèi)存空間。
Image Base : 00400000 ; 基址
②確定Import表的地址
a.先裝載Icedump
在這用Icedump 6.016版本,其命令操作形式完全和以前的版本不同。先在Icedump目錄里運(yùn)行相應(yīng)SOFTICE版本的icedump.exe(我用的SOFTICE是4.05版,因此在win9x/405目錄下運(yùn)行icedump.exe),如Icedump裝載成功,Icedump會(huì)返回如下信息:
icedump v6.0.1.6 for winice v4.05 loader
icedump unloaded
icedump loaded ←出現(xiàn)這句話表示Icedump裝載成功
C:>
(圖六)
b.再裝載FrogsICE
由于ShowDep能檢測到SOFTICE的存在,因此裝載Frogsice就可躲過。在我機(jī)子里:FrogsICE 1.00 Final和Icedump不能很好兼容工作,因此我將FrogsICE換成版本v0.43,hehe..它們配合的很好。雙擊FPloader.exe文件即可裝載成功。
OK到此你的SOFTICE的功能己大大加強(qiáng)里。
這時(shí)試試運(yùn)行ShowDep,這時(shí)屏幕將藍(lán)屏給你一菜單選項(xiàng),告知ShowDep發(fā)現(xiàn)了SOFTICE,是否欺騙它,這時(shí)你按ESC按鈕,程序即可正常運(yùn)行。(注:我的SOFTICE用icepath打過補(bǔ)?。?/P>
c、攔截函數(shù)loadlibararya
下命令:bpx loadlibrarya do "dd esp->4"
(注:在TRW2000下實(shí)現(xiàn)同樣功能的命令是: bpx loadlibrarya do "dd *(esp+4)"
這個(gè)命令就是當(dāng)攔截loadlibararya函數(shù)時(shí),顯示其入口參數(shù)的在內(nèi)存的值,如:
0137:00710242 PUSH EAX
0137:00710243 CALL [loadlibarary] ;當(dāng)調(diào)用此函數(shù)將中斷,并顯示push參數(shù)的值,在這里即:d eax
ok斷點(diǎn)設(shè)置好后,運(yùn)行ShowDep程序,將中斷,此時(shí)按F5一直到數(shù)據(jù)窗口顯示為:KERNEL32.DLL字符。在我win97系統(tǒng)下,只要按兩下F5即可看到如下情況:
-----SHOWDEP!.rdata+1A8A--------------------------dword-------------PROT---(0)--
0030:0042DA8A 4E52454B 32334C45 4C4C442E 019A0000 KERNEL32.DLL.... ^
0030:0042DA9A 64616F4C 73727543 0041726F 6F4C01AB LoadCursorA...Lo v
(圖六)
hehe...看看圖六和圖三是不是一樣??!其中前面的地址0042DA8A就是關(guān)鍵。
0042DA8A就是image_import_descriptors結(jié)構(gòu)中的name項(xiàng)的值,因?yàn)樵摻Y(jié)構(gòu)如下:
0030:0042D104 0002D23C 00000000 00000000 0002DA8A <............... ^
0030:0042D114 0002C070 0002D43C 00000000 00000000 p...<........... v
因此這時(shí)我要在數(shù)據(jù)窗口向前查找字符串0002DA8A(0002DA8A=0042DA8A-基址):
下命令:S DS:400000 L FFFFFFFF 8A DA 02 00
(注:在TRW2000下實(shí)現(xiàn)同樣功能的命令是: S 30:0 L FFFFFFFF 8A DA 02 00)(用上面的好象不行但愿新版能改進(jìn))
結(jié)果如下:
-----SHOWDEP!.rdata+1100--------------------------dword-------------PROT---(0)--
013F:0042D100 0042B385 0002D23C 00000000 00000000 ..B.<........... ^
013F:0042D110 0002DA8A 0002C070 0002D43C 00000000 ....p...<....... v
(圖七)
仔細(xì)比較圖七和圖一,發(fā)現(xiàn)這就是Import表的IMAGE_IMPORT_DESCRIPTOR數(shù)組。
如你看不習(xí)慣,可再下命令: D 42D110-C 這樣就可顯示和圖一一樣的畫面了。
這樣就可確定Import表的地址是2D104=0042D104―400000(基址)
③確定Import表的大小
現(xiàn)己將Import表的起始地址確定了:RVA=42D104。只要確定Import表的尾部就可計(jì)算出其大小,Import表在內(nèi)存里是連續(xù)存放的一段數(shù)據(jù),其一般結(jié)尾處一段內(nèi)存空間都是0在此例,你開始先定位來到Import表的起始處,按ALT+↓向下翻頁(或ALT+PageDown),直到看到如下情況:
013F:0042E54A 65530262 766E4574 6E6F7269 746E656D b.SetEnvironment
013F:0042E55A 69726156 656C6261 011D0041 4C746547 VariableA...GetL
013F:0042E56A 6C61636F 666E4965 0000576F 00000000 ocaleInfoW......
013F:0042E57A 00000000 00000000 00000000 00000000 ................ v
013F:0042E58A 00000000 00000000 00000000 00000000 ................ v
(圖八)
字符串0000576F就是Import表的最后一項(xiàng),其后面一位000000的地址為:42e574(其邊界就是上面紅色的W如你在SOFTICE不能確定可DUMP后在十六進(jìn)制工具Hexworkshop很方便知邊界地址)
因此Import表的大?。?2E574-42D104=1470
④、找入口點(diǎn)
在SOFTICE你會(huì)來到如下:
0137:00710B35 MOV EDX[EAX]
0137:00710B37 MOV EAX[EBP+08]
0137:00710B3A ADD EDX[EAX+18]
0137:00710B3D MOV EAX[EBP+08]
0137:00710B40 MOV EAX[EAX+1C]
0137:00710B43 CALL 007104C8 ←按F8進(jìn)去
0137:00710B48 POP EDI
0137:00710B49 POP ESI
0137:00710B4A POP EBX
0137:00710B4B POP ECX
按F8進(jìn)去來到:
0137:007104C6 8BC0 MOV EAXEAX
0137:007104C8 89C4 MOV ESPEAX
0137:007104CA 89D0 MOV EAXEDX
0137:007104CC 8B1D34567100 MOV EBX[00715634]
137:007104D2 89041C MOV [EBX+ESP]EAX
0137:007104D5 61 POPAD
0137:007104D6 50 PUSH EAX ←此處EAX=422c3a即入口點(diǎn)的值
0137:007104D7 C3 RET ←返回到入口點(diǎn)
(圖九)
此時(shí)程序己完全解壓準(zhǔn)備運(yùn)行了。記下程序入口點(diǎn):00422c3a 在dump前,清除所有的斷點(diǎn):bc *. 因此你可在SOFTICE下用命令:
:/dump 400000 A0000 c:/temp/dump.exe
(如你是用Icedump 6.016以前版本用此命令:pagein d 400000 A0000 c:/temp/dump.exe)
⑤修正PE文件頭
用 Procdump打開剛建好的 dump.exe文件,點(diǎn)擊pe-editor按鈕,然后再點(diǎn)擊SECTIONS按鈕,在每個(gè)section點(diǎn)擊右鍵,選中Edit section,把所有的 section 的PSize == VSize offset == RVA (即讓物理地址和大小等于虛擬地址和大小)。如你是用Procdump脫的殼,可省去這一步。
在改完所有的sections后,按OK存盤后,你在資源管理器中刷新一下,就會(huì)發(fā)現(xiàn)dumped.exe的圖標(biāo)回來了,但還不能運(yùn)行,你還要修正入口點(diǎn)和import表。
將入口點(diǎn)(Entry Point)改為:00422c3a(記著:00422c3a-imagebase=22c3a)
再點(diǎn)擊Directory按鈕,將Import Table改為 RVA (2D104 );而其選項(xiàng)Size只要比0大就可;(這程序DUMP后improt并沒被破壞,只要把import rav/size 填上就OK了?。?在這例中,Import表的尺寸沒用上,但方法要掌握,如碰到Import表損壞的程序,就要替換Import表程了,這時(shí)需要Import表的大小了。
然后點(diǎn)擊OK退出Procdump,再運(yùn)行 dumped.exe ,程序成功運(yùn)行!
方法三:利用S命令查找字串KERNEL32.DLL來確定Import表的位置
如果軟件沒.idata項(xiàng),用方法二bpx loadlibrarya do "dd esp->4"也不能看到KERNEL32.DLL,這種情況就要用S命令來協(xié)助了。
先bpx loadlibrarya do "dd esp->4"
中斷后,憑經(jīng)驗(yàn)來判斷Import表完全觸壓時(shí)機(jī),一般中斷在第一次或第二次(某些情況要幾次)Import表就基本解壓結(jié)束了。這時(shí)下命令:S DS:400000 l FFFFFFFF 'KERNEL32.DLL'
會(huì)在數(shù)據(jù)區(qū)找到KERNEL32.DLL ,但這不一定是image_import_descriptors結(jié)構(gòu)中對應(yīng)的KERNEL32.DLL項(xiàng),這要跟據(jù)具體情況來分析了,一般我們要找到的KERNEL32.DLL是在xxx:4xxxxxx的地址形式處。
如不能確定何處是關(guān)鍵的KERNEL32.DLL,只好dump后,用十六進(jìn)制工具來分析了,這樣較直觀,打開后查找image_import_descriptors類似結(jié)構(gòu)。
當(dāng)然Import表確定方法多種,這里將本人常用的幾種方法列出供參考!也歡迎你將自己的經(jīng)驗(yàn)告訴大家,互相提高。
2、ASProtect v0.94b保護(hù)
英文原作:r!sc 《Almost Manual Unpacking (using Softice and Icedump)》
原作日期:6th febuary 2000
教程翻譯: 看雪
翻譯日期:2000年5月26日
聲 明: 本文以r!sc的教程為基礎(chǔ),以自己的觀點(diǎn)補(bǔ)充調(diào)整。
目標(biāo)程序:aspack.exe . 231424 . v 2.001
程序下載:AsPack
使用工具:Softice 4.05; ProcDump 1.6.2 Final; FrogsICE v0.43;Icedump 6.016
part1 . 理論知識
part2 . 分析原文件的PE頭
part3 . 抓取import table
part4 . Dump整個(gè)程序并修正文件頭
=part1===part1===part1===part1===part1===part1===part1===part1===part1===part1=
理論知識
這種被壓縮或加密的PE文件,執(zhí)行時(shí),在內(nèi)存中將會(huì)完全解壓。其中import表在裝載中也會(huì)被完全解壓或解密(―攻擊點(diǎn))。其中抓取import表就很關(guān)鍵了,其具體位置,可用 Procdump分析文件頭得到。然后,要跟蹤程序完全解壓后的跳到程序處的入口點(diǎn),然后在內(nèi)存里dump取程序的整個(gè)部分。將剛dump取的正確import表用十六進(jìn)制工具粘貼到完全dump的程序,再 修正文件頭,這樣程序就可正常運(yùn)行。
=part2===part2===part2===part2===part2===part2===part2===part2===part2===part2=
分析原文件的PE頭
運(yùn)行 Procdump,點(diǎn)擊pe-editor按鈕,選中ASPack.exe文件,我們想要得到程序解壓后的尺寸,import表的地址和大小...幸運(yùn)的是這個(gè)文件的每個(gè)塊(section )都存在。
Size of image : 00079000 ; 這個(gè)PE文件執(zhí)行時(shí)分配的內(nèi)存空間。
Image Base : 00400000 ; 基址
.idata ;.idata包含其他外來DLL的函數(shù)及數(shù)據(jù)信息
Virtual Size : 00002000 ; idata在內(nèi)存的尺寸
Virtual Offset : 00046000 ; idata的地址(+imagebase == 00446000)
.rdata
Virtual Size : 00001000
Virtual Offset : 00049000
import表有可能在idata塊或rdata塊,到底在哪部分?看看它們的尺寸,我將馬壓在.idata ...
=part3===part3===part3===part3===part3===part3===part3===part3===part3===part3=
抓取import table
1、先裝載Icedump
在這用Icedump 6.016版本,其命令操作形式完全和以前的版本不同。先在Icedump目錄里運(yùn)行相應(yīng)SOFTICE版本的icedump.exe(我用的SOFTICE是4.05版,因此在win9x/405目錄下運(yùn)行icedump.exe),如Icedump裝載成功,Icedump會(huì)返回如下信息:
icedump v6.0.1.6 for winice v4.05 loader
icedump unloaded
icedump loaded ←出現(xiàn)這句話表示Icedump裝載成功
C:>
2、再裝載FrogsICE
由于aspack能檢測到SOFTICE的存在,因此裝載Frogsice就可躲過。在我機(jī)子里:FrogsICE 1.00 Final和Icedump不能很好兼容工作,因此我將FrogsICE換成版本v0.43,hehe..它們配合的很好。雙擊FPloader.exe文件即可裝載成功。
OK到此你的SOFTICE的功能己大大加強(qiáng)里。
這時(shí)試試運(yùn)行aspack,這時(shí)屏幕將藍(lán)屏給你一菜單選項(xiàng),告知aspack發(fā)現(xiàn)了SOFTICE,是否欺騙它,這時(shí)你按ESC按鈕,程序即可正常運(yùn)行。
3、 記住我們的第一步是抓取import table,它在內(nèi)存的446000到449000處,因此程序運(yùn)行時(shí)注意這段內(nèi)存代碼的解壓情況。
由于SOFTICE不能LOAD aspack.exe我們用:bpx loadlibrarya命令來攔斷。
loadlibrarya命令解釋:如果import表沒在內(nèi)存中就使用LoadLibraryA API調(diào)用裝入該模塊,因此我們可以攔截此函數(shù)來觀察import表。
:bpx loadlibrarya 然后運(yùn)行aspack將中斷如下:
Break due to BPX KERNEL32!LoadLibraryA
:dd 446000 l 40 (下此命令觀察內(nèi)存446000到449000處的數(shù)據(jù))
.
-------SPACK!.idata--------------------dword----------ROT--?(0)--
0030:00446000 ???????? ???????? ???????? ???????? ................
0030:00446010 ???????? ???????? ???????? ???????? ................
0030:00446020 ???????? ???????? ???????? ???????? ................
0030:00446030 ???????? ???????? ???????? ???????? ................
上圖是SOFTICE的數(shù)據(jù)窗口,顯示 ??...說明import表在內(nèi)存中沒解壓。
再按F5一下,程序?qū)⒅袛嗳缦拢海ㄓ浿涸谶@例中只能要按一下F5,否則將不能抓取正確的import表)
0030:00446000 00000000 00000000 00000000 0004669C .............f..
0030:00446010 0004612C 00000000 00000000 00000000 a..............
0030:00446020 000468B6 000461AC 00000000 00000000 .h...a..........
0030:00446030 00000000 000468D0 000461B4 00000000 .....h...a......
這時(shí)446000處不是?? ?? ?? ??,意味著import表己被解壓了。
.import表以一個(gè)IMAGE_IMPORT_DESCRIPTOR數(shù)組開始。 image_import_descriptors數(shù)據(jù)有5組dwords組成。
image_import_descriptors結(jié)構(gòu):
①dd offset original_first_thunk
②dd timedatestamp 時(shí)間及日期標(biāo)志
③dd forwardchain 正向鏈結(jié)索引
④dd offset library name以NULL結(jié)尾的ASCII字符的RVA地址,該字符串包含輸入的DLL名,
比如"Kernel32.dll"或"USER32.DLL"。
⑤dd offset first_thunk 該字段是在Image_thunk_data聯(lián)合結(jié)構(gòu)中的RVA偏移
其中timedatestamp和forwardchain通常設(shè)置為00000000 original first thunk 選項(xiàng)不是必須的.
:dd 446000 l 40
0030:00446000 00000000 00000000 00000000 0004669C .............f..
0030:00446010 0004612C 00000000 00000000 00000000 a..............
地址4669c 指向 LibraryName (RVA 你需要加上基址imagebase+400000)
:db 44669c l 10
0030:0044669C 4B 45 52 4E 45 4C 33 32-2E 44 4C 4C 00 00 00 00 KERNEL32.DLL....
地址612c指向 first_thunk 庫。
:dd 44612c l 10
0030:0044612C 000466AA 000466C2 000466DA 000466F2 .f...f...f...f..
這些是以NULL結(jié)尾的ASCII字符的RVA地址, . . 466aa 是第一個(gè)API函數(shù)的地址,466c2是第二個(gè)API函數(shù)地址...它們以以NULL結(jié)尾。
:db 0004466aa l 20
0030:004466AA 00 00 44 65 6C 65 74 65-43 72 69 74 69 63 61 6C ..DeleteCritical
0030:004466BA 53 65 63 74 69 6F 6E 00-00 00 4C 65 61 76 65 43 Section...LeaveC
通過上面的分析可知這就是原始的.import表,快dump it!!(看看上文的image_import_descriptors地址)
:/dump 446000 2000 c:/aspack.idata.bin
(如你是用Icedump 6.016以前版本用此命令:pagein d 446000 2000 c:/aspack.idata.bin)
為了方便大家對比,特將dump正確的import表放在此下載。
=part4===part4===part4===part4===part4===part4===part4===part4===part4===part4=
Dump整個(gè)程序并修正文件頭
1、現(xiàn)在我們要找程序的入口點(diǎn),下命令:bpx loadlibrarya ,然后按14下F5然后按F10一步一步跟蹤來到如下代碼:
0137:00C1150E 8B4508 MOV EAX[EBP+08]
0137:00C11511 8B10 MOV EDX[EAX] DS:004664FC=00400000
0137:00C11513 8B4508 MOV EAX[EBP+08]
0137:00C11516 035018 ADD EDX[EAX+18]
0137:00C11519 8B4508 MOV EAX[EBP+08]
0137:00C1151C 8B401C MOV EAX[EAX+1C]
0137:00C1151F E874F9FFFF CALL 00C10E98 ←在此按F8進(jìn)入
0137:00C11524 5F POP EDI
0137:00C11525 5E POP ESI
0137:00C11526 5B POP EBX
0137:00C11527 59 POP ECX
0137:00C11528 59 POP ECX
0137:00C11529 5D POP EBP
0137:00C1152A C20400 RET 0004
F8進(jìn)入后來到如下:
0137:00C10E96 8BC0 MOV EAXEAX
0137:00C10E98 89C4 MOV ESPEAX
0137:00C10E9A 89D0 MOV EAXEDX
0137:00C10E9C 8B1D6C66C100 MOV EBX[00C1666C]
0137:00C10EA2 89041C MOV [EBX+ESP]EAX
0137:00C10EA5 61 POPAD
0137:00C10EA6 50 PUSH EAX ;push 442b98 即為入口點(diǎn)
0137:00C10EA7 C3 RET ;返回到己完全解壓的代碼處,即入口點(diǎn)處。
0137:00C10EA8 C3 RET
來到入口點(diǎn):
0167:00442B98 55 PUSH EBP ←此處為入口點(diǎn)
0167:00442B99 8BEC MOV EBPESP
0167:00442B9B 83C4F4 ADD ESP-0C
在0167:00442B98 處就可dump整個(gè)內(nèi)存數(shù)據(jù)了,此時(shí)程序己完全解壓準(zhǔn)備運(yùn)行了。記下程序入口點(diǎn):00442B98
在dump前,清除所有的斷點(diǎn):bc *.
./dump 400000 79000 c:/aspack.dumped.exe
(如你是用Icedump 6.016以前版本用此命令:pagein d 400000 79000 c:/aspack.dumped.exe)
2、替換正確的import表
用Hexworkshop打開aspack.dumped.exe和aspack.idata.bin. Goto到exe文件的46000偏移處Select Block大小為2000. 拷貝aspack.idata.bin文件的同樣大小(2000)的Block粘貼到exe文件中以替換掉不正確的.idata section然后存盤。(注意:以上所有數(shù)據(jù)都是十六進(jìn)制)
3、修正PE文件頭
用 Procdump打開剛建好的 aspack.dumped.exe文件,點(diǎn)擊pe-editor按鈕,然后再點(diǎn)擊SECTIONS按鈕,在每個(gè)section點(diǎn)擊右鍵,選中Edit section,把所有的 section 的PSize = VSize offset = RVA 。
如:CODE 的PSize=0001E000; VSize=00042000;offset =00000400;RVA=00001000;
改成:PSize = VSize= 00042000;offset = RVA =00001000;
在改完所有的sections后,按OK存盤后,你在資源管理器中刷新一下,就會(huì)發(fā)現(xiàn)aspack.dumped.exe的圖標(biāo)回來了,但還不能運(yùn)行,你還要修正入口點(diǎn)和import表。
將入口點(diǎn)(Entry Point)改為:00042B98(記著:00442B98-imagebase=42B98)
再點(diǎn)擊Directory按鈕,將Import Table改為 RVA (46000 );而其選項(xiàng)Size只要比0大就可;
然后點(diǎn)擊OK退出Procdump,再運(yùn)行 aspack.dumped.exe ,程序運(yùn)行的很甜美!
這時(shí)你用W32DASM不能反匯編,你可用 Procdump編輯第一個(gè)section characteristics:
將其 c0000060 (data writable)改為: 60000040 (code executable)或 e0000060 (code data etc etc)
注:大家抓取屏幕可在Icedump 6.016中,用:/Screendump抓取。
不加參數(shù)命令:/Screendump 選取模式,重復(fù)執(zhí)行,會(huì)在0、1、2、3、4五種模式下轉(zhuǎn)換。
模式1(默認(rèn))是以文本方式存盤,模式2是以HTML文件存盤。其它的請參考其readme.
模式選好后,就可用命令: /SCREENDUMP [路徑]文件名 抓取整個(gè)SOFTICE的屏幕。
3、ASProtect v0.9x保護(hù)
Advanced Zip Password Recovery 3.0的脫殼
教程寫作: ****
作者信箱: break_ice@hotmail.com
寫作日期: 2000年3月25日
版權(quán)聲明: 本文沒有版權(quán)允許任意轉(zhuǎn)貼和修改. 但如果只引用文中部分內(nèi)容時(shí)請最好注明原文出處以表示對一位Cracker同行的勞動(dòng)的尊重.
使用工具:
TRW2000 1.03
ProcDump 1.6.2
Hexworkshop 3.02
下載:Advanced Zip Password Recovery 3.0
比起其Beta版來AZPR 3.0正式版的保護(hù)更為加強(qiáng). 1. 對Softice的多處Check用FrogsICE不能完全騙過; 2. CRC的校驗(yàn); 3. 動(dòng)態(tài)地址(好象是這個(gè)名吧?); 4.對Loader的防范這回用Process Patch不行了.
用Softice跟蹤它會(huì)是一種痛苦 (當(dāng)然完全可以用Softice只是你要了解程序的Anti-debugger技巧在關(guān)鍵的Check后改變跳轉(zhuǎn)方向). 這次祭出我們中國人的驕傲TRW2000來對付它.
先用Procdump的PE Editor查看一下程序的.idata section記下數(shù)值. Virtual size=2000 Virtual offset=21000. 另外記下程序的Image Base: 400000.
1. 運(yùn)行TRW Load程序. 程序中斷在入口處. 但接下來無論是否BPX設(shè)斷只要G繼續(xù)運(yùn)行程序便會(huì)出錯(cuò). 下Faults off G OK程序退出.
2. 試BPM 421000 (.idata的offset) 再Load程序. G 程序可以被正常中斷 COOL!
3. G當(dāng)程序第三次(? 記不清了. 原則是d 421000顯示的data區(qū)見到XXXX0200 000000字樣)中斷時(shí)BD*F10直到下面的語句
*********************************
注意: 程序每次Load時(shí)Offset都不一樣你的機(jī)器中的XXXX:YYYYYYYY肯定和下面的不同. 下Code on指令這樣才好參照下面的代碼. 另外下面所摘的幾段代碼是引用高手tiamath的.因?yàn)槲也磺宄赥RW下如何dump屏幕 (誰能指教一下:);而用ICEDump的pagein n命令來Dump Softice的屏幕我的機(jī)器會(huì)死機(jī). :(
*********************************
0167:004F34CD 50 PUSH EAX
0167:004F34CE B890274F00 MOV EAX004F2790
0167:004F34D3 50 PUSH EAX
0167:004F34D4 B8A4274F00 MOV EAX004F27A4
0167:004F34D9 50 PUSH EAX
0167:004F34DA B8A0284F00 MOV EAX004F28A0
0167:004F34DF 50 PUSH EAX
0167:004F34E0 B8AC274F00 MOV EAX004F27AC
0167:004F34E5 50 PUSH EAX
0167:004F34E6 B8646C4E00 MOV EAX004E6C64
0167:004F34EB 50 PUSH EAX
0167:004F34EC 8B4508 MOV EAX[EBP+08]
0167:004F34EF 8D4824 LEA ECX[EAX+24]
0167:004F34F2 8B4508 MOV EAX[EBP+08]
0167:004F34F5 8B500C MOV EDX[EAX+0C]
0167:004F34F8 8B4508 MOV EAX[EBP+08]
0167:004F34FB 8B4008 MOV EAX[EAX+08]
0167:004F34FE E899F4FFFF CALL 004F299C
0167:004F3503 33C0 XOR EAXEAX <--在這里Dump .idata
下指令 W 421000 L 2000 azprdata.bin
OK. 再往下直到
0167:004F3637 8D4818 LEA ECX[EAX+18]
0167:004F363A 8B4508 MOV EAX[EBP+08]
0167:004F363D 8B10 MOV EDX[EAX]
0167:004F363F 8B4508 MOV EAX[EBP+08]
0167:004F3642 8B401C MOV EAX[EAX+1C]
0167:004F3645 E8EAF6FFFF CALL 004F2D34 <--F8進(jìn)入
0167:004F364A 5F POP EDI
0167:004F364B 5E POP ESI
到了
0167:004F2D72 E9148B1DA8 JMP A86CB88B
0167:004F2D77 8E4F00 MOV CS[EDI+00]
0167:004F2D7A EB01 JMP 004F2D7D
0167:004F2D7C EB89 JMP 004F2D07
0167:004F2D7E 041C ADD AL1C
0167:004F2D80 EB02 JMP 004F2D84
0167:004F2D82 EBE8 JMP 004F2D6C
0167:004F2D84 61 POPAD
0167:004F2D85 EB01 JMP 004F2D88
0167:004F2D87 E850EB02E9 CALL E95218DC
0167:004F2D8C 17 POP SS
0167:004F2D8D E802000000 CALL 004F2D94
0167:004F2D92 E91758C35E JMP 5F1285AE
0167:004F2D97 5B POP EBX
0167:004F2D98 59 POP ECX
此時(shí)小心地跟蹤碰到JMP時(shí)按F8而不要按F10
直到顯示變成
0167:004F2D72 E9148B1DA8 JMP A86CB88B
0167:004F2D77 8E4F00 MOV CS[EDI+00]
0167:004F2D7A EB01 JMP 004F2D7D
0167:004F2D7C EB89 JMP 004F2D07
0167:004F2D7E 041C ADD AL1C
0167:004F2D80 EB02 JMP 004F2D84
0167:004F2D82 EBE8 JMP 004F2D6C
0167:004F2D84 61 POPAD
0167:004F2D85 EB01 JMP 004F2D88
0167:004F2D87 E850EB02E9 CALL E95218DC
0167:004F2D8C 17 POP SS
0167:004F2D8D E802000000 CALL 004F2D94
0167:004F2D92 E91758C35E JMP 5F1285AE
0167:004F2D94 58 POP EAX <-- 光標(biāo)位于此行時(shí) EAX=401000
0167:004F2D95 C3 RET <--這里
下Suspend指令. 回到Windows. 用ProcDump來Dump(full)脫殼的程序得到azprdump.exe. 你也可以用TRW的PEDUMP命令來得到脫殼程序但我個(gè)人的經(jīng)驗(yàn)很容易死機(jī)所以我寧愿用ProcDump來做.
4. 用PE Editor修改程序的Entry Point為1000. 并查看脫殼后程序的.idata section. 此時(shí)Raw size=1670 Raw offset=1FC00. 修改Directory中Import Table的RVA=21000SIZE=1670.
5. 用Hexworkshop打開azprdump.exe和azprdata.bin. Goto到exe文件的1FC00偏移處Select Block大小為1670. 拷貝.bin文件的同樣大小(1670)的Block粘貼到exe文件中以替換掉不正確的.idata section.
現(xiàn)在再試著運(yùn)行程序應(yīng)該可以正常運(yùn)行了. 如果程序出錯(cuò)再做一件事: 把 .bss section的raw size值改為00000000 (高手tiamath的建議).
有了脫殼的程序大家應(yīng)該會(huì)Patch它成為注冊版了吧. 只需改一個(gè)字節(jié).
結(jié)語: 用本文所描述的方法可以對絕大多數(shù)Asprotect+Aspack保護(hù)的程序進(jìn)行成功的脫殼. 大家讀完這篇教程不妨找?guī)讉€(gè)程序開刀. 比方說The Bat! 1.39現(xiàn)在不應(yīng)該再難住大家了.
好了這次就到這兒了下回見.
致謝:
感謝SV Hobgoblin tiamath. 沒有他們的幫助我不可能做到這一步
(編輯:天命孤獨(dú))
新聞熱點(diǎn)
疑難解答
圖片精選