標籤:des blog http os strong ar for 檔案 資料
有很多介紹PE檔案的文章,但是我打算寫一篇關於輸入表的文章,因為它對於破解很有用。
我想解釋它的最好的方法是舉一個例子,你可以跟著我逐步深入,一步一步的思考,最後你將完全明白,我選擇了一個我剛下載下來的小程式,它是用TASM編譯的,有一個比較小的輸入表,所以我想它應該是個不錯的範例。
好了,讓我們開始吧。首先我們得找到輸入表,它的地址放在PE檔案頭位移80處,所以我們用16進位編輯器開啟我們的EXE檔案,我們先得找到PE檔案頭的起始點,這很簡單,因為它總是以PE,0,0開始,我們可以在位移100處找到它。在一般的WIN32程式中檔案頭位移被放在檔案0X3C處,在那我們通常可看到00 01 00 00,由於資料存放區時是低位在前,高位在後的,所以翻轉過來實際就是00000100,就象前面我們說的。接下來我們就可以在PE檔案中找到我們的輸入表,100+80=180在位移180處我們看到0030 0000,翻轉一下,它其實應該是00003000,這說明輸入表在記憶體3000處,我們必須把它轉換成檔案位移。
一般來說,輸入表總是在某個段的起始處,我們可以用PE編輯器來查看虛擬位移,尋找3000並由此發現原始位移。很簡單的。開啟我們看到:
-CODE 00001000 00001000 00000200 00000600
-DATA 00001000 00002000 00000200 00000800
.idata 00001000 00003000 00000200 00000A00
.reloc 00001000 00004000 00000200 00000C00
找一下,我們就發現.idata段的虛擬位移是3000,原始位移是A00,3000-A00=2600,我們要記住2600,以便以後轉換其它的位移。如果你沒找到輸入表的虛擬位移,那麼就找一下最接近的段。
來到位移A00處,我們就看到被稱為IMAGE_IMPORT_DESCRIPTORs(IID)的東東,它用5個欄位表示每一個被調用DLL的資訊,最後以Null結束。
**************************************************************************
(IID) IMAGE_IMPORT_DESCRIPTOR的結構包含如下5個欄位:
OriginalFirstThunk, TimeDateStamp, ForwarderChain, Name, FirstThunk
OriginalFirstThunk
該欄位是指向一32位以00結束的RVA位移地址串,此地址串中每個地址描述一個輸入函數,它在輸入表中的順序是不變的。
TimeDateStamp
一個32位的時間標誌,有特殊的用處。
ForwarderChain
輸入函數列表的32位索引。
Name
DLL檔案名稱(一個以00結束的ASCII字串)的32位RVA地址。
FirstThunk
該欄位是指向一32位以00結束的RVA位移地址串,此地址串中每個地址描述一個輸入函數,它在輸入表中的順序是可變的。
**************************************************************************
好了,你有沒有理解?讓我們看看我們有多少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
每三分之一是個分界,我們知道每個IID包含了一個DLL的調用資訊,現在我們有2個IID,所以我們估計這個程式調用了2個DLL。甚至我可以打賭,你能推測出我們將能找到什麼。
每個IID的第四個欄位表示的是名字,通過它我們可以知道被調用的函數名。第一個IID的名字欄位是8C30 0000,翻轉過來也就是地址0000308C,將它減去2600可以得到原始位移,308C-2600=A8C,來到檔案位移A8C處,我們看到了什嗎?啊哈!原來調用的是KERNEL32.dll。
好了,接下來我們就要去找出KERNEL32.dll中被調用的函數。回到第一個IID。
FirstThunk欄位包含了被調用的函數名的標誌,OriginalFirstThunk僅僅是FirstThunk的備份,甚至有的程式根本沒有,所以我們通常看FirstThunk,它在程式運行時被初始化。
KERNEL32.dll的FirstThunk欄位值是6430 0000,翻轉過來也就是地址00003064,減去2600得A64,在位移A64處就是我們的IMAGE_THUNK_DATA,它儲存的是一串地址,以一串00結束。如下:
A430 0000/B230 0000/C030 0000/CE30 0000/DE30 0000/EA30 0000/F630 0000/0000 0000
通常在一個完整的程式裡都將有這些。我們現在有了7個函數調用,讓我們來看其中的兩個:
DE30 0000翻轉後是30DE,減去2600後等於ADE,看在位移ADE處的字串是ReadFile,
EA30 0000翻轉後是30EA,減去2600後等於AEA,看在位移AEA處的字串是WriteFile,
你可能注意到了,在函數名前還有2個這位元組的00,它是被作為一個提示。
很簡單吧,你可以自己來試一下。回到A00,看第二個DLL的調用
5C30 0000 / 0000 0000 / 0000 0000 / 9930 0000 / 8430 0000
{OrignalFirstThunk} {TimeDateStamp} {ForwardChain} {Name} {First Thunk}
先找它的DLL檔案名稱。9930翻轉為3099-2600 =A99,在位移A99處找到USER32.dll。再看FirstThunk欄位值:8430翻轉為3084-2600=A84,位移A84處儲存的地址為08310000,翻轉後3108-2600=B08,位移B08處字串為MessageBoxA。明白了吧,接下來你就可以把這些用在你自己的EXE檔案上了。
摘要:
在PE檔案頭+80位移處存放著輸入表的地址,輸入表包含了DLL被調用的每個函數的函數名和FirstThunk,通常還有Forward Chain和TimeStamp。
當運行程式時系統調用GetProcAddress,將函數名作為參數,換取真正的函數入口地址,並在記憶體中寫入輸入表。當你對一個程式脫殼時你可能注意到你有了一個已經初始化的FirstThunk。例如,在我的WIN98上,函數GetProcAddress的入口地址是AE6DF7BF,在98上,所有的KERNEL32.dll函數調用地址看上去地址都象:xxxxF7BF,如果你在輸入表中看到這些,你可以利用orignal thunk重建它,或者重建這個PE程式。
第二個執行個體:
如果我們需要保護自己的程式,可以修改PE頭的大小和起始位。
執行個體下載
解說:
前提:需要用16進位編輯工具開啟,比如:winhex,ultraedit,cs32asm等
1. 如果IMAGE_NT_HEADERS的signature域值等於"PE\0\0",那麼就是有效PE檔案。我們看此處位移0100處就是50 45,代表位移頭。
2. 我們前面講過,在DOSMZ中有一項是用來指定PE頭的起始位置的,那就在002c和002d處的值就是00 01,反過來寫就是0100了。
PE頭的起始位置
100 60 00
01 00
00 01
PE頭的大小:
224個位元組 00 E0,這個值我們前面講過,其PE頭的大小我們可以從中看到在0114 0115處。
修改的PE頭:
24*16=384位元組 180 反過來80 01位移移到了0060處
然後根據需要修改位移及大小即可完成pe頭的修改。
破解軟體感悟-PE檔案格式之執行個體總結(五)