pe檔案結構詳解(二)可執行檔案頭的最後展示了乙個陣列,pe檔案結構詳解(三)pe匯出表中解釋了其中第一項的格式,本篇文章來揭示這個陣列中的第二項:image_directory_entry_import,即匯入表。
也許大家注意到過,在image_data_directory中,有幾項的名字都和匯入表有關係,其中包括:image_directory_entry_import,image_directory_entry_bound_import,image_directory_entry_iat和image_directory_entry_delay_import這幾個匯入都是用來幹什麼的,他們之間又是什麼關係呢?聽我慢慢道來。
舉個實際的例子,看一下下面這張圖:
這個**呼叫了乙個regopenkeyw的匯入函式,我們看到其opcode是ff 15 00 00 19 30氣質ff 15表示這是乙個間接呼叫,即call dword ptr [30190000] ;這表示要呼叫的位址存放在30190000這個位址中,而30190000這個位址在匯入位址表的範圍內,當模組載入時,pe 載入器會根據匯入表中描述的資訊修正30190000這個記憶體中的內容。
那麼匯入表裡到底記錄了那些資訊,如何根據這些資訊修正iat呢?我們一起來看一下匯入表的定義:
[cpp]view plain
copy
typedef
struct _image_import_descriptor dummyunionname;
dword timedatestamp; // 0 if not bound,
// -1 if bound, and real date\time stamp
// in image_directory_entry_bound_import (new bind)
// o.w. date/time stamp of dll bound to (old bind)
dword forwarderchain; // -1 if no forwarders
dword name;
dword firstthunk; // rva to iat (if bound this iat has actual addresses)
} image_import_descriptor;
typedef image_import_descriptor unaligned *pimage_import_descriptor; 使用
rtlimagedirectoryentrytodata並將索引號傳1,會得到乙個如上結構的指標,實際上指向乙個上述結構的陣列,每個匯入的dll都會成為陣列中的一項,也就是說,乙個這樣的結構對應乙個匯入的dll。
characteristics和originalfirstthunk:乙個聯合體,如果是陣列的最後一項characteristics為0,否則originalfirstthunk儲存乙個rva,指向乙個image_thunk_data的陣列,這個陣列中的每一項表示乙個匯入函式。
timedatestamp:映象繫結前,這個值是0,繫結後是匯入模組的時間戳。
forwarderchain:**鏈,如果沒有**器,這個值是-1。
name:乙個rva,指向匯入模組的名字,所以乙個image_import_descriptor描述乙個匯入的dll。
firstthunk:也是乙個rva,也指向乙個image_thunk_data陣列。
既然originalfirstthunk與firstthunk都指向乙個image_thunk_data陣列,而且這兩個域的名字都長得很像,他倆有什麼區別呢?為了解答這個問題,先來認識一下image_thunk_data結構:
[cpp]view plain
copy
typedef
struct _image_thunk_data32 u1;
} image_thunk_data32;
typedef image_thunk_data32 * pimage_thunk_data32;
forwarderstring是**用的,暫時不用考慮,
function表示函式位址,如果是按序號匯入
ordinal就有用了,若是按名字匯入
addressofdata便指向名字資訊。
可以看出這個結構體就是乙個大的union,大家都知道union雖包含多個域但是在不同時刻代表不同的意義那到底應該是名字還是序號,該如何區分呢?可以通過ordinal判斷,如果ordinal的最高位是1,就是按序號匯入的,這時候,低16位就是匯入序號,如果最高位是0,則addressofdata是乙個rva,指向乙個image_import_by_name結構,用來儲存名字資訊,由於ordinal和addressofdata實際上是同乙個記憶體空間,所以addressofdata其實只有低31位可以表示rva,但是乙個pe檔案不可能超過2g,所以最高位永遠為0,這樣設計很合理的利用了空間。實際編寫**的時候微軟提供兩個巨集定義處理序號匯入:image_snap_by_ordinal判斷是否按序號匯入,image_ordinal用來獲取匯入序號。
這時我們可以回頭看看originalfirstthunk與
firstthunk,originalfirstthunk指向的image_thunk_data陣列包含匯入資訊,在這個陣列中只有ordinal和addressofdata是有用的,因此可以通過originalfirstthunk查詢到函式的位址。firstthunk則略有不同,
在pe檔案載入以前或者說在匯入表未處理以前,他所指向的陣列與originalfirstthunk中的陣列雖不是同乙個,但是內容卻是相同的,都包含了匯入資訊,而在載入之後,firstthunk中的function開始生效,他指向實際的函式位址,因為firstthunk實際上指向iat中的乙個位置,iat就充當了image_thunk_data陣列,載入完成後,這些iat項就變成了實際的函式位址,即function的意義。還是上個圖對比一下:
上圖是載入前。
上圖是載入後。
最後總結一下:
匯入表其實是乙個
image_import_descriptor的陣列,每個匯入的dll對應乙個
image_import_descriptor。
image_import_descriptor包含兩個image_thunk_data陣列,陣列中的每一項對應乙個匯入函式。
載入前originalfirstthunk與
firstthunk的陣列都指向名字資訊,載入後firstthunk陣列指向實際的函式位址。
PE檔案結構詳解(四)PE匯入表
pe檔案結構詳解 二 可執行檔案頭的最後展示了乙個陣列,pe檔案結構詳解 三 pe匯出表中解釋了其中第一項的格式,本篇文章來揭示這個陣列中的第二項 image directory entry import,即匯入表。也許大家注意到過,在image data directory中,有幾項的名字都和匯入...
PE檔案結構詳解(四)PE匯入表
pe檔案結構詳解 二 可執行檔案頭的最後展示了乙個陣列,pe檔案結構詳解 三 pe匯出表中解釋了其中第一項的格式,本篇文章來揭示這個陣列中的第二項 image directory entry import,即匯入表。也許大家注意到過,在image data directory中,有幾項的名字都和匯入...
PE檔案結構 匯入表詳解
當pe檔案執行時,pe檔案將被系統載入進入記憶體中,此時windows載入器會定位所有的匯入的函式或者將定位到的內容填寫到可執行檔案的某個位置供使用,這個定位是需要借助於可執行檔案的匯入表來實現的。匯入表中存放了所使用的dll模組名稱及匯入函式的名稱或者函式序列。在pe檔案中定位到pe頭部的可選頭的...