PE格式之千里追蹤輸入表

2021-06-05 19:32:04 字數 3779 閱讀 3618

高數考完了,終於可以輕鬆的寫**了。哈哈

終於開始寫輸入表了。輸入表是我們專案的重頭戲。首先還是先介紹下輸入表的基本知識。

自己寫的話有點麻煩,還是copy一下小甲魚辛辛苦苦打出來的課件吧

pe檔案頭的 image_optional_header 結構中的 datadirectory(資料目錄表) 的第二個成員就是指向輸入表的。而輸入表是以乙個 image_import_descriptor(簡稱iid) 的陣列開始。每個被 pe檔案鏈結進來的 dll檔案都分別對應乙個 iid陣列結構。在這個 iid陣列中,並沒有指出有多少個項(就是沒有明確指明有多少個鏈結檔案),但它最後是以乙個全為null(0) 的 iid 作為結束的標誌。

如果看不清楚的話,這裡給個鏈結,是我自己的部落格裡面的,放大應該就看的清楚了

在pe頭那裡我們可以看到資料目錄表,(用紅框框圈起來的那裡) image_data_directory datadirectory[image_numberof_directory_entries]

接著紅色的箭頭指向了我們的資料目錄表的資料結構

#define image_directory_entry_export          0   // export directory

#define image_directory_entry_import 1 // import directory (輸入表,我們所需要的)

#define image_directory_entry_resource 2 // resource directory

#define image_directory_entry_exception 3 // exception directory

#define image_directory_entry_security 4 // security directory

#define image_directory_entry_basereloc 5 // base relocation table

#define image_directory_entry_debug 6 // debug directory

#defi ne image_directory_entry_architecture 7 // architecture specific data

#define image_directory_entry_globalptr 8 // rva of gp

#define image_directory_entry_tls 9 // tls directory

#define image_directory_entry_load_config 10 // load configuration directory

#define image_directory_entry_bound_import 11 // bound import directory in headers

#define image_directory_entry_iat 12 // import address table

#define image_directory_entry_delay_import 13 // delay load import descriptors

#define image_directory_entry_com_descriptor 14 // com runtime descriptor

然後每個資料目錄表的結構成員有兩個

typedef struct _image_data_directory  image_data_directory, *pimage_data_directory;
這裡的virtualaddress就是我們的輸入表的rva了

得到rva只是第一步,我們接下來要判斷輸入表在哪個區塊裡面。

當然這個時候我們必須要做的就是讀取區塊的資訊了。區塊的資料結構如下(可以在圖裡面找到哦)

typedef struct _image_section_header  misc;

dword virtualaddress; //區塊的rva

dword sizeofrawdata;

dword pointertorawdata;

dword pointertorelocations;

dword pointertolinenumbers;

word numberofrelocations;

word numberoflinenumbers;

dword characteristics;

} image_section_header, *pimage_section_header;

通過輸入表的rva與區塊的rva比較,我們就能知道輸入表在哪個區塊裡面。

好,第二步已經完成了。

現在是最關鍵的第三步:

這裡必須明確掌握rva,  va,  offset。因為這個是pe檔案的最重點。下面再引用一下小甲魚的課件:

在前邊我們**過rva 這個詞,但對於初次接觸pe 檔案的朋友來說,顯得尤其陌生和無奈。中國人不喜歡老外的縮寫,但總要**著接受……不過,在有了前邊知識的鋪墊之後,現在來談這個概念大傢伙應該能夠得心應手了。起碼不用顯得那麼的費解和無奈~

rva 是相對虛擬位址(relative virtual address)的縮寫,顧名思義,它是乙個「相對位址」。pe 檔案中的各種資料結構中涉及位址的字段大部分都是以 rva 表示的,有木有??

更為準確的說,rva 是當pe 檔案被裝載到記憶體中後,某個資料位置相對於檔案頭的偏移量。舉個例子,如果 windows 裝載器將乙個pe 檔案裝入到 00400000h 處的記憶體中,而某個區塊中的某個資料被裝入 0040**xh 處,那麼這個資料的 rva 就是(0040**xh - 00400000h )= **xh,反過來說,將 rva 的值加上檔案被裝載的基位址,就可以找到資料在記憶體中的實際位址。

很明顯,我們發現,dos 檔案頭、pe 檔案頭和區塊表的偏移位置與大小均沒有變化。而各個區塊對映到記憶體後,其偏移位置就發生了變化。

當處理pe 檔案時候,任何的 rva 必須經過到檔案偏移的換算,才能用來定位並訪問檔案中的資料,但換算卻無法用乙個簡單的公式來完成,事實上,唯一可用的方法就是最土最笨的方法:

步驟一:迴圈掃瞄區塊表得出每個區塊在記憶體中的起始 rva(根據image_section_header 中的virtualaddress 字段),並根據區塊的大小(根據image_section_header 中的sizeofrawdata 字段)算出區塊的結束 rva(兩者相加即可),最後判斷目標 rva 是否落在該區塊內。

步驟二:通過步驟一定位了目標 rva 處於具體的某個區塊中後,那麼用目標 rva 減去該區塊的起始 rva ,這樣就能得到目標 rva 相對於起始位址的偏移量 rva2.

步驟三:在區塊表中獲取該區塊在檔案中所處的偏移位址(根據image_section_header 中的pointertorawdata 字段

這個欄位是物理偏移位址

將這個偏移值加上步驟二得到的 rva2 值,就得到了真正的檔案偏移位址。

將這個 真正的檔案偏移位址加上基位址就是輸入表的位址了。

如此一來,我們千里迢迢終於追蹤到了輸入表的位址了。

千里之行之思想

系統發展到一定的程度,或者初期,我們要及時對系統採取分治的思想。系統分治,首先必先分層。但是分層的層次也不應該太多,我們不能為了分層而分層。層次太多,架構太複雜 效能太差。那我們到底要分幾層呢?我們來分析一下系統的基本流程,其流程大致都是這樣的 客戶端 伺服器 cgi 資料庫。從客戶端到資料庫,我們...

差之 10px ,謬以千里

在解決了解析度對bosnma的影響後,最近在研究火狐 chrome等瀏覽器顯示異常的問題。於是開始愜意的google。其實解決瀏覽器之間的相容性問題是web開發公認的棘手問題之一,尤其是在比較大型和正規的企業,他們的 要盡可能的滿足更多的執行環境以爭取最大的客戶流量。在解析度上,客戶的設定除了主流的...

PE格式詳細講解8 輸入表

在此之前,我們已經對這個輸入表進行了一些實踐和理解,這有助於大家對這個概念更進一步的加深認識。小甲魚覺得,越是複雜的問題我們應該越是去動手操作它,認識它,這樣才容易熟悉它!在上一節課我們像小鹿一樣的亂撞,終於撞到了輸入表裡邊包含的函式名稱,嘿嘿,不過位址,我們還是沒能找著 這節課我們將深入來剖析輸入...