IAT表和導入表


1.關於IAT(import address table)表
當exe程序中調用dll中的函數時,反匯編可以看到,call后面並不是跟的實際函數的地址,而是給了一個地址;
這些連起來就是一張表,就是IAT表;
 
1)內存鏡像中的dll中函數的調用;
例如:一個exe中調用系統提供的dll中的MessageBox函數時:
可以看到call的地址是42d2c4;
 
查看42d2c4中的內容:
可以看到:42d2c4中保存的是762b0026;
762b0026是dll的領空;也就是dll中MessageBox函數的地址;
 
總結:
    exe程序調用dll中的函數時,會使用FF15call;
    call的並不是實際的函數地址,而是該函數對應的IAT表的地址;
    通過IAT表來找到實際的函數地址;
 
2)文件鏡像中的IAT相關
接下來取文件中找42d2c4中保存的內容;
需要將RVA轉成FOA;
42d2c4-400000 = 2d2c4;在.idata節;
foa = 2d2c4 - 2d000 + 2b000 = 2b2c4;
可以看到:文件鏡像中保存的值為2d2f4;
 
繼續追到2d2f4,對應的foa為2b2f4
看到並不是MessageBox的地址,而是一MessageBox先關的描述;
 
結論:IAT表在文件和內存中是不一樣的;
 
3)原因分析
對於一個exe程序,在運行時會加載到獨立的4gb空間;
exe程序是可以按ImageBase來加載的;
但該exe引用的dll並不能保證,因為ImageBase可能被占用;
因此對於dll來說有重定位表來記錄dll中的可能需要修改絕對地址;
對於引用dll的exe程序來說,因為無法確定dll中引用的函數的地址,所以不會再文件鏡像中將引用dll的函數的地址寫死;
而是保存的是這些函數相關的信息;
當程序運行后,並且所有dll都加載到該程序的4gb空間后,此時dll中函數的地址才確定;
IAT表中將保存dll中函數在內存中的真實地址;
因此IAT表中在文件和內存中不同;
 
4)IAT表總結
IAT表在程序執行前和程序執行后是不一樣的;
程序在編譯時給分了一個空間,保存一個偏移,這個偏移指向的是引用dll中的函數的函數名,而不是真正的函數地址;
當程序加載完畢,所有的dll都貼到程序的4gb空間后,會把IAT表中的值改為真正要用的函數的地址;因為dll的地址因不是按ImageBase加載而不一樣;
 
2.導入表
比如說當去餐館吃飯時,餐館會提供一個菜單,告訴誰提供了哪些菜;
自己在點餐時也需要給餐館提供一個單,來告訴自己點了哪些菜;
對於程序來說dll提供導出表,導出表中保存的是導出函數的清單以及地址;
程序也需要提供一個導入表,來說明使用了哪些函數;
 
1)導入表的結構
typedef struct _IMAGE_IMPORT_DESCRIPTOR {                                    
    union {                                    
        DWORD   Characteristics;                                               
        DWORD   OriginalFirstThunk;                                 //RVA 指向IMAGE_THUNK_DATA結構數組            
    };                                    
    DWORD   TimeDateStamp;                                       //時間戳            
    DWORD   ForwarderChain;                                                  
    DWORD   Name;                        //RVA,指向dll名字,該名字已0結尾            
    DWORD   FirstThunk;                                         //RVA,指向IMAGE_THUNK_DATA結構數組            
} IMAGE_IMPORT_DESCRIPTOR;                                    
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;         

                  

數據目錄的第二項就是導入表;
導入表結構可能有多個,一個dll對應一個導入表結構;
其中比較重要的兩個屬性:
    OriginalFirstThunk    ->指向INT表(import name table);
        表中存的是一些IMAGE_THUNK_DATA結構;
        這個表以0結尾;
    FirstThunk    ->指向IAT表(import address table);
        IAT表有兩種方式可以找到:1】通過數據目錄的第13個結構;2】導入表結構的這個屬性;
        
2)導入表在文件加載前和文件加載后有區別
1】pe文件加載前:
IAT表和INT表中保存的結構都是IMAGE_THUNK_DATA,且都是以0結尾;
在文件加載前兩張表里的內容完全一樣;
IMAGE_THUNK_DATA用來保存函數名以及函數序號等信息;
IMAGE_THUNK_DATA結構中只有一個聯合,占4字節的空間;
 
IMAGE_THUNK_DATA結構:
typedef struct _IMAGE_THUNK_DATA32 {
    union {
        PBYTE  ForwarderString;
        PDWORD Function;
        DWORD Ordinal;                            //序號
        PIMAGE_IMPORT_BY_NAME  AddressOfData;    //指向IMAGE_IMPORT_BY_NAME
    } u1;
} IMAGE_THUNK_DATA32;
typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;

 

IMAGE_IMPORT_BY_NAME結構:
typedef struct _IMAGE_IMPORT_BY_NAME {
    WORD    Hint;        //可能為空,編譯器決定 如果不為空 是函數在導出表中的索引
    BYTE    Name[1];    //函數名稱,以0結尾
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

 

2】pe文件加載后:
文件加載后,系統會調用通過函數名或函數序號找函數地址的函數GetProcAddr() ;
得到函數地址后,用函數地址替換IAT表中的數據;
 
使用兩張表INT、IAT的原因:
    如果只有IAT表,在程序加載完后,IAT表中的函數名將替換成函數地址,就無法知道函數名,也就是留一個函數名的備份;
    
3.解析導入表
主要步驟:
    1】定位導入表:
        數據目錄的第二個結構為導入表;
        通過該結構,可以找到導入表rva,轉成foa可以在文件中找到導入表;
        每一個引用的dll都對應一個導入表,需要循環解析,直到遇到全0的導入表結構為止;
        也就是sizeOf(IMAGE_IMPORT_DESCRIPTOR) 個 0  代表導入表結束;
    2】解析dll名:
        導入表中儲存了引用的dll的名字信息;
        在導入表結構的Name屬性中;
    3】解析OriginalFirstThunk:
        OriginalFirstThunk指向INT表;
        INT表中保存了IMAGE_THUNK_DATA結構,該結構只有一個聯合,占4字節;  
        判斷最高位是否為1,如果是那么除去最高位的值就是函數的導出序號;有些函數匿名導出的,不能用函數名找地址;                
        如果不是,那么這個值是一個RVA 指向IMAGE_IMPORT_BY_NAME ;  
        IMAGE_IMPORT_BY_NAME結構中有2個屬性;
            Hint    ->是函數在導出表中的索引,並不是函數的序號,沒有實際作用;
            Name    ->函數名,只有1個字節,也就是函數名的開始,因為字符串長度不固定,從字符串開始到0結束為一個函數名;  
        調用 GetProcAddr(m,函數的名字或者導出序號) 函數可以找到函數地址;
        當IMAGE_THUNK_DATA結構為0時,表示該INT表結束;
    4】解析FirstThunk:
         FirstThunk指向IAT表,IAT表在程序加載前和INT表是一樣的;   
 
輸出導入表信息:
         
 
代碼:
#include "stdafx.h"
#include "PeTool.h"
#include "string.h"
 
#define SRC "C:\\Users\\Administrator\\Desktop\\Hello.exe"
 
//解析導入表
void printImport(){
    //定義pe頭結構指針
    PIMAGE_DOS_HEADER dosHeader = NULL;        //dos頭指針
    PIMAGE_FILE_HEADER peHeader = NULL;        //pe頭指針
    PIMAGE_OPTIONAL_HEADER32 opHeader = NULL;    //可選pe頭指針
    PIMAGE_DATA_DIRECTORY dataDir = NULL;        //數據目錄指針
    PIMAGE_IMPORT_DESCRIPTOR importDir= NULL;    //導入表指針
 
    //1.將文件讀入內存
    LPVOID pFileBuffer = NULL;
    DWORD fileSize = ReadPEFile(SRC, &pFileBuffer);
    if(!pFileBuffer){
        printf("讀取dll文件失敗\n");
        return;
    }
 
    //2.初始化頭結構指針
    dosHeader = (PIMAGE_DOS_HEADER) pFileBuffer;
    peHeader = (PIMAGE_FILE_HEADER) ((DWORD)pFileBuffer + dosHeader->e_lfanew + 4);
    opHeader = (PIMAGE_OPTIONAL_HEADER32) ((DWORD)peHeader + IMAGE_SIZEOF_FILE_HEADER);
    dataDir = opHeader ->DataDirectory;
    importDir = (PIMAGE_IMPORT_DESCRIPTOR) ((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer, (dataDir + 1)->VirtualAddress ));
 
 
    //3.輸出導入表信息
    while(importDir->OriginalFirstThunk){
        printf("按enter鍵顯示下一個dll導入表信息:\n");
        getchar();
        LPSTR name = (LPSTR)((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer, importDir->Name ));
        printf("================%s================\n", name);
        //解析INT表
        PDWORD thunk =(PDWORD) ((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer, (DWORD)importDir->OriginalFirstThunk)) ;
        while(*thunk){
            if((*thunk & 0x80000000) >> 31 == 1){    //如果第一位為1,表示為序號導入
                DWORD ord = *thunk & 0x7fffffff;
                printf("按序號導入:%d\n", ord);
            }else{
                PIMAGE_IMPORT_BY_NAME pName = (PIMAGE_IMPORT_BY_NAME)((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer, *thunk));
                printf("按函數名導入:%s\n",pName->Name);
            }
            //下一個INT表項
            thunk++;
        }
        //下一張導入表
        importDir++;
        printf("\n");
    }
}
 
int main(int argc, char* argv[])
{
    //輸出導入表信息
    printImport();
    
    getchar();
}

 

 
結果:
   
        
                    


免責聲明!

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



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