PE知識復習之PE的導入表


            PE知識復習之PE的導入表

一丶簡介

  上一講講解了導出表. 也就是一個PE文件給別人使用的時候.導出的函數  函數的地址 函數名稱 序號 等等.

  一個進程是一組PE文件構成的.  PE文件需要依賴那些模塊.以及依賴這些模塊中的那些函數.這個就是導入表需要做的.

確定PE依賴那個模塊. 確定PE依賴的那個函數.  以及確定函數地址.

總共分為三部分講解.

  導入表定位位置: 在擴展頭中有一個數據目錄結構體. 第二項保存的就是導入表的 RVA 以及大小.

如下圖所示:

EXE文件.沒有導出表.有一個導入表. RVA 是 0x1A1C0  位於節Text中. 虛擬地址位 0x11000  文件偏移為 0x400

轉換為 FOA =  1A1C0 - 11000 + 400 = 0x95c0

 

我們發現在文件中定位導入表的時候都是0,原因是程序加載到內存中.需要用到的時候.操作系統才會往這個地方填寫數據.

 

 二丶導入表結構

 

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union { DWORD Characteristics; // 0 for terminating null import descriptor
        DWORD   OriginalFirstThunk;         // RVA to original unbound IAT (PIMAGE_THUNK_DATA) 指向IAT結構注釋表明了
 } DUMMYUNIONNAME;
    DWORD   TimeDateStamp;                  // 時間戳.
                                            // -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;                //指向DLL名字的 RVA
    DWORD   FirstThunk;                     // RVA to IAT (if bound this IAT has actual addresses)
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;

導入表大小為十進制的20個字節.  16進制的 0x14. 如果以16進制為一行. 則是 一行零4個字節

導入表跟導出表不同.導出表只有一個.里面有子表記錄. 而導入表你依賴一個模塊.則有一個導入表存在.

導入表結束位置是20個字節的連續為0的數據為結束位置. 也就是導入表最后一項都為0的時候.說明導入表結束了.

對於導入表來說.我們只需要關心三個成員.上面都標紅了.

會一一進行講解.首先從最簡單的成員開始.

  2.1 Name成員. 確定依賴的模塊的名字是什么

我們說過.一個PE文件.依賴模塊. 那么這個成員就是記錄了.我要依賴的模塊的名字是什么.是一個RVA屬性. RVA指向了一個ASCII碼字符串.以0結尾.

因為在文件中導入表並沒有.所以我們直接在內存中查看.

根據數據目錄 導入表位置 0x1A1C0  + ImageBase(0x400000) == 0x41AC0

在內存中的0x41AC0位置.則是導入表的位置. 我們看一下.

導入表大小總共一行零4個字節. 倒數第二個成員則是 Name的 RVA  0x1A4A6

我們可以加上ImageBase 去內存中查看.

可以通過RVA 屬性.看到導入表依賴的模塊名字就是 VCRUNTIME140D.dll   帶有D結尾的.dll說明是調試DLL. 140是編譯器版本.說明是

VS2015編譯的 .VCRuntime 是運行庫 .  說明我們這個程序是一個 Debug版本編譯的程序. 並且使用編譯器 140版本編譯的.

我們查看的這個Name屬性.描述的就是 VCRUNTIME140D.dll 這個模塊的信息了.如果想看其它依賴的模塊就需要查看下一張導入表.

下一張導入表在第一章導入表的下面.最后一項的導入表全部為0.  我們下一張導入表的 依賴模塊的模塊名稱的 RVA 屬性是 0x1A75A  

VA = Imagebase + RVA = 41A75A

 

依次查看即可.

   2.2 確定依賴的函數的名稱

 上面我們講了Name成員.確定了導入表依賴的DLL的名字.那么我們導入表怎么確定依賴了那些函數那?

這個主要講解導入表的第一個成員跟最后一個成員.

如下圖所示:

 

第一個成員指向了一個INT 表.最后一個成員指向了一個 IAT表.

INT :: 導入名稱表  Improt Name Table

IAT::  導入地址表  Improt Address Table

Name成員直接指向一個 ASC 結尾的字符串.

根據上圖所示. 兩張表是一樣的. 但是所在位置是不一樣的名字也不一樣.一個叫做 INT 一個叫做IAT 

typedef struct _IMAGE_THUNK_DATA32 {
    union {
        DWORD ForwarderString;      // PBYTE 
        DWORD Function;             // PDWORD
        DWORD Ordinal;
        DWORD AddressOfData;        // PIMAGE_IMPORT_BY_NAME
    } u1;
} IMAGE_THUNK_DATA32;
typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;

結構體大小:  4個字節. 他是一個聯合體.找最大的.

里面有4個成員.為當前的4個字節起了四個名字.  真正有用的是下面兩個. 也就是說有的時候需要用第三個成員.

有的時候需要用第四個成員. 而第四個成員是指向一個  IMAGE_IMPORT_BY_NAME的結構的RVA

typedef struct _IMAGE_IMPORT_BY_NAME {
    WORD    Hint;                 //編譯器決定,不是空的話,就是函數在導出表中的 函數地址表的導出索引.
    CHAR   Name[1];               //函數名稱,0結尾.
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

 無論是第一個成員還是最后一個成員.都能確定 我一來的當前模塊的那個函數.

為什么需要兩個表. 這個下面會將. 首先講解的就是無論使用那個表.都能找到依賴當前模塊的函數.

第一個成員找:

  INT表  INT表是4個字節.最后0結尾.  INT表有多大.就是說依賴這個模塊的多少個函數.

  IAT 同上. 0結尾.

那么我們怎么去尋找? 

  看這個表的4個字節.  最高位為1那么就是函數的導出序號. 去掉最高位.就是函數的序號. 也就是說我們看的是序號.

  如果最高位不是1,那么找的就是一個 RVA ,一個指向 IMAGE_IMPROT_BY_NAME的結構.

例如下圖:

INT 或者 IAT表. 都可以通過最高位判斷. 是函數的序號.還是函數的名字.

INT或者IAT就是兩種情況, 高位為1, 那么去掉高位就是依賴的函數序號. 不是1, 那么就是一個RVA. 指向了一個  IMAGE_IMPROT_BY_NAME 結構.

以一個導入表為例

INT的 RVA 為 1A2A8 VA = 41A2A8

41A2A8是INT表開始. 每一個是4個字節,以0結尾. 觀看第一項. 高位為0,所以 0x1A48E 是一個RVA. 一個指向 IMAGE_IMPROT_BY_NAME 的結構

VA = 41A48E

高位兩個字節,是函數在導出表中的導出索引.  后面就是以0結尾的函數名稱了.

總結來說: 不管是INT表還是 IAT表. 主要看其高位值,高位為1,那么去掉高位,就是函數的序號. 高位為0.指向一個結構.這個結構保存了函數的導出序號.以及函數名稱.

在IMAGE_IMPROT_BY_NAME 結構中的 HINT 如果不是空,那么這個序號(索引) 就是導出表的函數地址表的索引. 我們可以直接拿着這個索引去導出表中獲取函數地址.

 

 

  

     2.3 確定函數地址

 如果我們使用DLL的函數.那么在程序中.調用這個DLL的函數.那么就會生成一個間接Call

比如我們程序調用MessageBoxA

反匯編

 

 跳轉過去之后.會看到內存中有一個地址

 這個地址才是真正的MessageBox的地址

在我們導入表中,最后一個成員  IAT表.就是上面所說的表,保存了函數地址表.

那么這和我們說的結構是不一樣的. IAT不是說跟INT是一樣的嗎?

PE加載前加載后的區別.

 一樣是一樣的.但是需要分清 PE加載前.還有PE加載后.如果加載前,那么IAT跟INT一樣.都可以找到依賴的函數名稱.

如果是加載后.也就是在內存中的話.那么IAT表保存的就是函數的地址.

PE加載后如下圖:

IAT表保存的就是函數地址了.

從導入表中找到IAT表.

IAT表的RVA 偏移為 0x1A098  VA == 41A098

IAT表中存儲了函數地址,4個字節為單位.0x6AD79CF0 就是函數 __Vcrt_loadlibraryExW . INT表中存儲的就是 依賴的函數名稱.上面我們也看到了.

三丶知識總結

導入表大小為20個字節. 十六進制 0x14 ,一行零4個字節.

  1.導入表重要成員有三個.  INT表. Name表.  IAT表.

    PE加載前.

        INT 表 IAT表相同. 根據INT或者IAT表的高位,高位為1.去掉高位就是函數序號. 高位為0. 那么是一個RVA偏移. 指向函數名稱表. 

          函數名稱表

            HINT  當前函數在導出函數地址表中的索引

            Name  當前函數的名稱.

    PE加載后INT 表同上. IAT表變成了存儲函數地址的地址表了.

  2. Name 民稱表. 直接指向DLL名稱文件名. 是一個RVA .注意是直接指向.

  3.INT IAT表.的RVA 都是定位INT IAT表位置. 定位的位置是INT IAT表.這個表存儲的才是數據


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM