日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

PE结构导入表

發布時間:2025/3/21 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 PE结构导入表 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

PE體系

PE結構&整體敘述

PE結構&導入表

PE結構&導出表

PE結構&基址重定位表

PE結構&綁定導入實現

PE結構&延遲加載導入表

導入表簡介

當我們源文件里面需要去如何畫窗口,如何顯示指定字符串這樣功能的代碼,只需要簡單調用Windows API函數。這些調用的函數在源文件中并不存在。這些代碼存儲在DLL文件中,即動態鏈接庫中。在動態鏈接庫里存放的不是函數的源代碼,而是編譯鏈接后生成的字節碼。
源程序調用了動態鏈接庫的相關函數,在進行編譯和鏈接的時候,編譯程序和鏈接程序就會把調用的相關信息寫入最終生成的PE文件中,從而來告訴操作系統這些函數的執行字節碼能從哪里獲取,這些信息就是導入表所要描述的內容

DLL加載方式有兩種:顯式鏈接(Explicit Linking) 和 隱式鏈接(Implicit Linking)

  • 顯示鏈接:程序在使用DLL時進行加載,使用完畢后釋放內存
  • 隱式鏈接:程序在開始時即一同加載DLL,程序終止時再釋放占用的內存
  • IAT提供的機制與DLL的隱式鏈接有關。

    優點:

  • 不把函數庫包含進應用程序中,單獨組成DLL文件,在需要使用時再進行調用。
  • 使用內存映射技術將加載后的DLL代碼、資源在多個進程中實現共享。
  • 在對函數庫進行更新時,只更新DLL文件即可。
  • 導入函數

    當我們調用一個API時,這個API我們不可能自己手寫,基礎源碼這些東西都不知道,所以直接導入頭文件然后使用即可。

    #include<iostream> #include<Windows.h> using namespace std; int main() {LPCSTR text = "hello world";LPCSTR title = "第一個MessageBoxA";MessageBoxA(NULL, text, title, MB_OK);}

    關于API調用的匯編代碼如下:

    .text:00401516 mov [ebp+text], offset aHelloWorld ; "hello world" .text:0040151D mov [ebp+title], offset unk_48800C .text:00401524 mov dword ptr [esp+0Ch], 0 ; uType .text:0040152C mov eax, [ebp+title] .text:0040152F mov [esp+8], eax ; lpCaption .text:00401533 mov eax, [ebp+text] .text:00401536 mov [esp+4], eax ; lpText .text:0040153A mov dword ptr [esp], 0 ; hWnd .text:00401541 mov eax, ds:__imp__MessageBoxA@16 ; MessageBoxA(x,x,x,x) .text:00401546 call eax ; MessageBoxA(x,x,x,x) ; MessageBoxA(x,x,

    關于導入函數就兩行:

    .text:00401541 mov eax, ds:__imp__MessageBoxA@16 ; MessageBoxA(x,x,x,x) .text:00401546 call eax ; MessageBoxA(x,x,x,x) ; MessageBoxA(x,x,

    也就是把一個內存單元里面存的地址放在eax里面去,然后call它。即MessageBoxA的實際地址被放在一個偏移處,查看這個偏移

    地址0x004924C0存放著0x77211930(MessageBoxA的實際地址)
    從0x004924C0內存單元開始取dd長度的內存數據放在eax里面,
    然后直接 call 0x77211930

    查看一下文件中0x004924C0中存放的數據:

    它存放的是0x61562100

    查看一下內存中0x004924C0中存放的數據:

    它存放的是0x77211930
    這意味著,文件被裝載到內存后,這里的值發生了變化,真正的API函數地址的應該是內存中此處的值。那文件中的值0x61562100和內存映像中的值0x77211930它倆有什么關系呢?

    注意

    dump程序時,

    錯誤示范:


    這樣操作后,你得到的文件是一個

    dmp文件,然后我找了三個小時。。啥也沒找到。。。

    正確示范:


    這樣dump后你得到的才是一個exe程序

    文件偏移和虛擬地址轉換才用得上。。。以前dump的時候都是直接用的是PEtools,今晚不知道腦子哪里抽筋,偏偏亂搞了三小時。。。

    觀察一下文件數據,導入表中數據

    RVA數值為0x92000,大小為0xEE8

    核心內容


    導入表數據所在地址RVA=0x92000
    導入表數據大小=0xEE8
    導入函數數據地址表所在地址RVA=0x9228C
    導入地址表表數據大小=0x23C

    轉換成文件偏移后是:
    導入表數據所在地址0x8F200,導入表數據大小=0xEE8
    導入地址表數據所在地址0x8F48C,導入地址表表數據大小=0x23C

    導入表描述符IMAGE_IMPORT_DESCRIPTOR

    導入表數據的起始是一組導入表描述符結構。每組為20個字節,該結構定義如下:

    IMAGE_IMPORT_DESCRIPTOR STRUCT unionCharacteristics ddOriginalFirstThunk dd ;000h -1 ends TimeDateStamp dd ;0004h - 時間表 ForwarderChain dd ;0008h - 鏈表的前一個結構 Name1 dd ;000ch - 指向鏈接庫名字的指針 FirstThunk dd ;0010 -2

    以下作出解釋:

    IMAGE_IMPORT_DESCRIPTOR.OriginalFirstThunk:

    +0000h,雙字。因為它是指向另外數據結構的通路,因此簡稱橋1。該字段指向一個包含了一系列結構的數據。
    指向的數組中每個結構定義了一個導入函數的信息,最后一個內容為全0的結構作為結束。指向的數據中每一項為一個結構,此結構的名稱是IMAGE_THUNK_DATA。該結構實際上只是一個雙字,但在不同時刻卻擁有不同的解釋。該字段有兩種解釋:

  • 雙字最高位為0,表示導入符號是一個數值,該數值是一個RVA。
  • 雙字最高位為1,表示導入符號是一個名稱
  • IMAGE_IMPORT_DESCRIPTOR.TimeDateStamp:

    +0004h,雙字。時間戳,一般不用。如果該導入表被綁定,那么綁定后的這個時間戳就被設置為對應DLL文件的時間戳。操作系統在加載時,可以通過這個時間戳來判斷綁定的信息是否過時

    IMAGE_IMPORT_DESCRIPTOR.ForwarderChain:

    +0008h,雙字。鏈表的前一個結構

    IMAGE_IMPORT_DESCRIPTOR.Name1:

    +000ch,雙字。這個字段的含義和名稱并不一致,這里的Name1是一個RVA,它指向該結構對應的DLL文件的名稱,而這個名稱是以“\0”結尾的Ansi字符串。

    IMAGE_IMPORT_DESCRIPTOR.FirstThunk :

    +0010h,雙字。與OriginalFirstThunk相同,它指向的鏈表定義了針對Name1這個動態鏈接庫引入的所有導入函數,簡稱橋2

    導入表雙橋結構

    重新找個程序舉例,上面那個數據太大了,頭疼。。。


    導入表數據所在地址RVA=0x1B1C4
    導入表數據大小=0x50
    導入函數數據地址表所在地址RVA=0x1B000
    導入地址表表數據大小=0x1C4

    轉換成文件偏移后是:

    導入表數據所在地址0x83C4,導入表數據大小=0x50
    導入地址表數據所在地址0x8200,導入地址表表數據大小=0x1C4

    橋1和橋2最終都通向了一個目的地,都指向了引入函數的“編號-名稱”(Hint/Name)描述部分。而從橋2到目的地的過程中,還經過了另外一個很重要的結構IAT(Import Address Table)

    AC B2 01 00

    橋1,最高位為0,這是一個RVA,表明函數是以字符串類型的函數名導入的。RVA轉換為FOA,值為
    0x84AC,從文件的該位置開始取雙字,直到取出的雙字為"0"結束,每一個雙字都是結構IMAGE_THUNK_DATA。該結構的詳細定義如下

    IMAGE_THUNK_DATA STRUCTunion u1ForwarderString dd?Function dd?Ordinal dd?AddressOfData dd?ends IMAGE_THUNK_DATA ENDS

    0x0001B450轉換成文件偏移為0x8650

    2E 00 5F 5F 76 63 72 74 5F 47 65 74 4D 6F 64 75 6C 65 46 69 6C 65 4E 61 6D 65 57 00

    這些值組成的數據結構就是IMAGE_IMPORT_BY_NAME

    IMAGE_IMPORT_BY_NAME STRUCTHint dw ?;0000h -函數編號Name1 db ?;0004h -表示函數名的字符串IMAGE_IMPORT_BY_NAME ENDS

    以下是對每個字段的具體解釋:

    IMAGE_IMPORT_BY_NAME.Hint:
    +0000h,雙字。函數的編號,在DLL中對每個函數進行了編號,訪問函數時可以通過名稱訪問,也可以通過編號訪問

    IMAGE_IMPORT_BY_NAME.Name1:
    +0004h,大小不確定。函數名字字符串的具體內容,以“\0”作為字符串結束標志。
    其中002E標識該函數在動態鏈接庫的編號,后面緊跟著函數名“GetModuleFileNameW”。

    00 00 00 00

    時間戳,這里為0

    00 00 00 00

    鏈表的前一個結構,這里為0

    9E B4 01 00

    RVA,指向動態鏈接庫RUNTIME140D.dll的名字字符串,轉換為文件偏移是0x869E

    98 B0 01 00

    橋二,轉換成文件偏移是0x8298,

    根據上面可知IAT是0x8200~0x83C4,橋2指向的地方在IAT范圍內,所以指向IAT

    和橋1指向的數據值相同。。但是存儲的位置是不同的。橋1指向的INT與橋2指向的IAT內容完全一樣,但INT和IAT卻存儲在文件的不同位置。

    每一個結構IMAGE_IMPORT_DESCRIPORT都對應一個唯一的動態鏈接庫,以及引用了該動態鏈接庫的多個函數,每個函數的最終“值-名稱”描述均可以沿橋1或者橋2找到。這種導入表結構被稱為雙外結構。
    雙橋結構的導入表在文件中存在兩份內容完全相同的地址列表。一般情況下,橋2指向的地址列表被定義為IAT,而橋1指向的地址列表則被定義為INT(import name table)。

    注意:
    有的鏈接程序只只為導入表存儲一個橋,如Borland公司的Tlink只保留橋2,這樣的導入表我們稱之為單橋結構的導入表(單橋結構的導入表是無法指向綁定導入操作的。)

    文件中的導入表:

    在文件中,橋1指向INT,橋2指向IAT,內容一致。

    內存中的導入表:


    在內存中,橋1可以找到調用的函數名稱或函數的索引編號,橋2卻可以幫你找到該函數指令代碼在內存空間中的地址。
    當PE被加載進虛擬地址空間以后,IAT 的內容會被操作系統更改為函數的VA。這個修改最終會導致通向“值-名稱”描述的橋2發生斷裂,如上圖。

    當橋2發送斷裂以后,如果沒有橋1作為參照(因為橋1和橋2維護了兩個一一對應的函數RVA),我們就無法重新找到該地址到底是調用了那個函數。這就是為什么會存在兩個橋的原因。也是為什么單橋導入表無法實施綁定的原因。

    實際操作

    內存中的導入表:

    橋1的值為01B2AC

    橋2的IAT 值為0x01B098

    兩個函數實際地址值和函數名(函數編號)可以一一對應。。。

    PE裝載器把導入函數輸入至IAT的順序

  • 讀取IID的Name成員,獲取庫名稱字符串(eg:kernel32.dll)
  • 裝載相應庫:
    LoadLibrary(“kernel32.dll”)
  • 讀取IID的OriginalFirstThunk成員,獲取INT地址
  • 逐一讀取INT中數組的值,獲取相應IMAGE_IMPORT_BY_NAME地址(RVA)
  • 使用IMAGE_IMPORT_BY_NAME的Hint(ordinal)或Name項,獲取相應函數的起始地址:GetProcAddress(“GetCurrentThreadld”)
  • 讀取IID的FirstThunk(IAT)成員,獲得IAT地址
  • 將上面獲得的函數地址輸入相應IAT數組值
  • 重復以上步驟4~7,知道INT結束(遇到NULL)
  • 同一個DLL文件的多個函數的導入表


    這是文件中的導入表,上圖中FirstThunk和OriganalFirstThunk一樣也有一份函數指針表,即橋2.只是沒畫而已下面是裝載入內存后的導入表:

    定位導入函數地址表的方法有兩種:

  • 從導入表的而最后一個導入表項## IMAGE_IMPORT_DESCRIPTOR結構中的字段IMAGE_IMPORT_DESCRIPTOR.FirstThunk定位IAT
  • 通過數據目錄第13個數據項的描述直接定位IAT
  • 每個動態鏈接庫都維護了自己的IAT內容,不同的 鏈接庫維護的這些內容可以是不連續的。

    導入表的平面解析

    解釋如下:

    總結

    以上是生活随笔為你收集整理的PE结构导入表的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。