小甲魚PE詳解之輸入表(導入表)詳解(PE詳解07)

在開始講解輸入表(導入表)概念之前,請允許小甲魚童鞋用簡短的幾句話來總結之前我們學過的內容,並做進一步的思想綜合提升,注意咯!

首先,我們知道PE 文件中的數據被載入內存後根據不同頁面屬性被劃分成很多區塊(節),並有區塊表(節表)的數據來描述這些區塊。

這裏我們需要注意的問題是:
一個區塊中的數據僅僅只是由於屬性相同而放在一起,並不一定是同一種用途的內容。例如接着要講的輸入表、輸出表等就有可能和只讀常量一起被放在同一個區塊中,因爲他們的屬性都是可讀不可寫的。

其次,由於不同用途的數據有可能被放入同一個區塊中,因此僅僅依靠區塊表是無法確定和定位的。那要怎麼辦?對了,PE 文件頭中 IMAGE_OPTIONAL_DEADER32 結構的數據目錄表來
指出他們的位置,我們可以由數據目錄表來定位的數據包括輸入表、輸出表、資源、重定位表和TLS等15 種數據。(數據目錄表,不要以爲他在前邊出現就不重要哦~)

這節課我們談的是輸入表,爲什麼需要輸入表呢?因爲我們從數據目錄表得到的僅僅是一些指定數據的RVA 和數據塊的尺寸,很明顯,不同的數據塊中的數據組織方式(結構)是顯然不同的,例如輸入表和資源數據塊中的數據就完全是牛馬不相及的兩個東西。因此,我們想要深入瞭解PE 文件就必須瞭解這些數據的組織方式,以及瞭解系統如何處理調用它們的。

輸入函數

在代碼分析或編程中經常遇到“輸入函數(Import Functions,也稱導入函數)”的概念。這裏我們就來解釋下,輸入函數就是被程序調用但其執行代碼又不在程序中的函數,這些函數的代碼位於相關的DLL 文件中,在調用者程序中只保留相關的函數信息(如函數名、DLL 文件名等)就可以。

對於磁盤上的PE 文件來說,它無法得知這些輸入函數在內存中的地址,只有當PE 文件被裝入內存後,Windows 加載器纔將相關DLL 裝入,並將調用輸入函數的指令和函數實際所處的地址聯繫起來。這就是“動態鏈接”的概念。動態鏈接是通過PE 文件中定義的“輸入表”來完成的,輸入表中保存的正是函數名和其駐留的DLL 名等。


實例預演(視頻中將演示,這裏只能截圖)

之所以是預演,主要是因爲咱還沒對輸入表進行解剖前就來給大家演示一下嚐嚐鮮找找滋味~不然學習總是無趣的和鬱悶的~(參與演示的例子程序和工具都可以到
解密系列課件與源代碼下載區 找到對應的帖子下載,也可以看咱視頻演示:解密系列視頻講座



以上是咱這次實驗的小青蛙哈~灰常簡單的一個小程序,如圖雙擊程序只顯示一個對話窗口,然後就結束~試驗用小程序,我們儘量的將內部的結構刪減,調試起來才方便些。我們這次體驗的目的就是想靠所學的知識,試圖來找到MessageBox 在內存中的地址。

注:MessageBox 是來自於USER32.DLL 動態鏈接庫裏的一個函數,我們通過對PE 文件的靜態反編譯分析來觀察hello.exe 這個試驗品是如何定位和調用MessageBox 這個在“異鄉”的函數哈。
(MessageBox 有兩個版本,一個是MessageBoxA 還有一個是MessageBoxW 分別帶便ASCII碼形式和UNICODE~歷史故事了~)

體驗開始:
1. 我們用曾經號稱爲屠龍刀的W32DAM 對hello.exe進行反編譯,如圖:



我們可以看到這個程序只有兩個導入模塊(Import Module),分別導入來自兩個動態鏈接庫(USER32.DLL和KERNEL32.DLL)的若干函數,我們還清晰可見,咱要跟蹤的MessageBoxA 就在USER32.DLL 中,這裏程序還自動給我們定位了它的虛擬地址:2A2DC,但我們不要用這個,因爲我們說過這回我們是來探險的,凡事講究人工……

我們通過W32DASM 的查找功能找出MessageBox 這個函數代碼的位置,並試圖查看他的彙編跳轉~
爲啥,這要從彙編調用子程序說起啦~簡單的說就是彙編語言要調用子程序會運用到CALL 和 RET這樣的搭配來進行,學過咱《零基礎入門學習彙編語言》的童鞋還記得小甲魚當時的動畫片演示吧?!上圖……




各位看官看到了吧?push xxxx   push xxxx   push xxxx   push xxxx 之後再來一個Call xxxx ……
沒錯,這就是調用函數的一個標準形式。每個push 其實就是將所要調用的函數需要的參數入棧,爲啥要入棧呢?這又要從地球的起源說起了……我這裏就簡單的說下吧:棧的發明,使得函數和子程序的出現成爲可能!對於局部變量和參數,棧的特性最適合不過~不明白不急哈,這個是彙編語言和編譯原理的範疇了,以後咱學習深入自然會碰到,到時候再來詳細解決就可以。

int MessageBox( HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType // style of message box );
由定義可見MessageBox 函數
共有四個參數,因此我們四次push xxxx 分別將參數按照STDCALL 的方式入棧之後,就可以CALL MessageBox 這個函數了。好,既然是CALL 我們的目標函數了,通過反彙編我們就可以觀察到它的地址是:[0042A2AC],難道就這麼簡單?42A2AC 就是目標函數的地址?

那我們就直接把程序往下拉,試圖找找這個 42A2AC 的地址吧~
可是…… ……




我們悲劇的發覺,程序壓根還沒到
42A2AC 這地方,到了421FF8 就結束了!!什麼情況呢?
或許……或許……或許……在沒詳細講輸入表時我們還不能直接給大家答案……

好吧,小甲魚這裏提問一下,這是一個什麼地址呢?偏移地址 or 虛擬地址?

恩,沒錯,這是一個VA 哈,那這個VA 上節課我們說過可以換算成存放在實際物理內存上的方法。具體就是將這個VA 與該程序的各個區塊的VA 地址逐一對比,由於PE 頭文件有記載每個區塊的VA 地址也同時記載着它的實際物理地址,因此我們可以通過判斷該VA 位於哪個區塊內並求出與區塊VA 的差值進而求出該VA 的實際物理地址。那既然前邊直直的路子走不通,我們就試下把他轉換爲物理地址試試吧?!

好吧,我知道我說話又繞口令了,咱就實例演示下,不然大家要噴了……上圖……




我們看到,咱的
42A2AC 地址加載咱的2A000 和 2B000 之間(改程序VC編寫,映像基地址爲400000打頭哦~),因此我們可以將該地址定位到改程序位於.idata 區塊內。該區塊的VA 起始地址爲42A000,因此42A2AC - 42A000 = 2AC,raw data offs 跟我們說該區塊的物理地址是28000,因此42A2AC 這個VA 所對應的物理偏移地址就是 28000 + 2AC = 282AC。

咱用UE 打開看下282AC 這偏移地址上有啥東西……




282AC 這個地址上存放着 DCA20200 這個數據,翻譯成ASCII 碼也是莫名其妙的說~
但我們把DCA20200 當成一個DWORD 類型的數據來讀的話我們得到數據 0002A2DC(還記得大端與小端吧)
慢着,是不是很熟悉,又是2A****開頭,跟咱之前的地址是不是差不多?那好,我們又按照剛纔的方法轉化爲偏移地址試試,轉化後得到的偏移地址是:282DC,咱再看看282DC 裏邊有啥神祕的東西吧?




哈哈,看到奇蹟了嗎?從282DC 地址讀起,ASCII 碼對應的值是MessageBoxA.USER32.dll
怎麼樣,有點神祕的成就感吧~

但根本問題我們還是沒能解決:MessageBox 的地址是?其實在這裏我們還不能幫大家解決這個問題,因爲我們缺乏對輸入表的深入理解,所以我們期待吧,下節課將揭開輸入表的神祕面紗……
發佈了19 篇原創文章 · 獲贊 14 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章