天天看點

[系統安全] PE檔案格式詳解2

文章目錄

  • ​​本篇内容介紹​​
  • ​​導出表 IMAGE_EXPORT_DIRECTORY​​
  • ​​導入表 IMAGE_IMPORT_DESCRIPTOR​​
  • ​​資源 IMAGE_RESOURCE_DIRECTORY​​

本篇内容介紹

上節文章記錄了PE檔案的結構概述,我們知道了PE頭中第三個字段​

​IMAGE_OPTIONAL_HEADER32​

​​結構體中變量​

​DataDirectory​

​​數組中的資料類型為​

​IMAGE_DATA_DIRECTORY​

​,定義如下,而sdk中定義了其指向的一些不同類型的成員資訊。本篇文章就記錄一些比較重要的成員的資訊。

typedef struct _IMAGE_DATA_DIRECTORY {
      DWORD VirtualAddress; //資料塊起始RVA位址
      DWORD Size;   //資料塊大小
    } IMAGE_DATA_DIRECTORY,*PIMAGE_DATA_DIRECTORY;      

補充: 這個數組大小為16,sdk定義了15個(值為0~14)類型資訊,剩下一個預留保持全零。

導出表 IMAGE_EXPORT_DIRECTORY

導出表是PE檔案為其他應用程式提供API的一種函數示例導出方式。Windows下存在導出表的可執行檔案以指定自身的一些變量、函數以及類,并将其導出,以便提供給其他程式使用。

導出表結構為​

​IMAGE_EXPORT_DIRECTORY​

typedef struct _IMAGE_EXPORT_DIRECTORY {
    DWORD   Characteristics;    // 未使用,總為0 
    DWORD   TimeDateStamp;      // 檔案建立時間戳
    WORD    MajorVersion;       // 總為0 
    WORD    MinorVersion;       // 總為0
    DWORD   Name;               // 指向一個代表此dll名字的ASCII字元串的RVA
    DWORD   Base;               // 函數的起始序号
    DWORD   NumberOfFunctions;  // 導出函數的總數
    DWORD   NumberOfNames;      // 以名稱方式導出的函數的總數
    DWORD   AddressOfFunctions;     // 指向輸出函數位址的RVA,是以 4 位元組為一個機關的數組元素,每個元素代表函數入口
    DWORD   AddressOfNames;         // 指向輸出函數名字的RVA,是以 4 位元組為一個機關的數組元素,每個元素代表一個指向字元串的 RVA
    DWORD   AddressOfNameOrdinals;  // 指向輸出函數序号的RVA,是以 2 位元組為一個機關的數組元素,每個元素代表對應名字在 AddressOfFunctions 中的序号
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;      

注意: 檔案中儲存的序号并不是調用函數時使用的,平時調用函數使用的序号減去Base(序号基數)的值才能得到檔案中儲存的序号。也将檔案中儲存的序号稱為原始序号,而将調用函數時使用的序号稱為調用序号。

另外從邏輯上講,導出表由3部分構成,分别是名稱表、函數表與序号表,其中函數表與序号表是必須要有的,而名稱表則是可選的。序号表與名稱表的作用就是索引,引導調用者找到真正需要的函數表,而函數表中儲存的就是這個被導出的函數位址資訊。

示例:分析一個dll檔案的内容

用LordPE檢視區段資訊。

[系統安全] PE檔案格式詳解2

01edit中找到導入表的位置,0x00002570是導入表的RVA,0x00000089是其大小。

注意: 這裡的0x00000089大小和導出表結構體40位元組大小貌似沒有關系,這個大小到底指的是什麼意思以後再回來探索。如下圖為DataDirectory數組内容

[系統安全] PE檔案格式詳解2

導出表在檔案中的偏移=0x00002570-0x00002000+0x00000E00=0x1370,

找到0x1370位置資料跟​

​IMAGE_EXPORT_DIRECTORY​

​對應

[系統安全] PE檔案格式詳解2

結構體IMAGE_EXPORT_DIRECTORY中的變量Name

0x000025C0 --> 檔案偏移位置0x000025C0-0x00002000+0x00000E00=0x13C0 --> PEDemo.dll      
[系統安全] PE檔案格式詳解2

結構體IMAGE_EXPORT_DIRECTORY中的變量AddressOfFunctions(函數入口位址),按照上面的資訊斷定有4個導出函數

0x00002598 --> 0x00002598-0x00002000+0x00000E00=0x1398      
[系統安全] PE檔案格式詳解2

結構體IMAGE_EXPORT_DIRECTORY中的變量AddressOfNames

0x000025a8 --> 0x000025A8-0x00002000+0x00000E00=0x13a8      
[系統安全] PE檔案格式詳解2

檢視0x000025cb-0x00002000+0x00000e00=0x13cb,可以看到導出的函數名

[系統安全] PE檔案格式詳解2

結構體IMAGE_EXPORT_DIRECTORY中的變量AddressOfNameOrdinals

0x000025b8 --> 0x000025b8-0x00002000+0x00000E00=0x13b8      
[系統安全] PE檔案格式詳解2

導出表結構

[系統安全] PE檔案格式詳解2

導入表 IMAGE_IMPORT_DESCRIPTOR

導入表本身僅相當于一個引導者的角色,并不能完成PE檔案中的整個導入工作,隻負責引導系統找到真正儲存有導入資訊的其他兩個結構,這兩個結構分别為​

​IMAGE_THUNK_DATA​

​​與 ​

​IMAGE_IMPORT_BY_NAME​

​。

IMAGE_IMPORT_DESCRIPTOR結構的個數是由導入映像檔案的個數決定的,需要從多少個映像檔案中導入函數,就要有多少個結構,最後以一個空的 IMAGE_IMPORT_DESCRIPTOR結構結束。

IMAGE_IMPORT_DESCRIPTOR結構定義如下

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics;            // 0 for terminating null import descriptor
        DWORD   OriginalFirstThunk;         // 包含指向INT(輸入名稱表)RVA 的結構數組
    };
    DWORD   TimeDateStamp;                  //時間辨別
    DWORD   ForwarderChain;                 //第一個被轉向的API索引
    DWORD   Name;                           //指向被導入的DLL名稱
    DWORD   FirstThunk;                     //指向輸入位址表(IAT)RVA,IAT是一個IMAGE_THUNK_DATA結構的數組
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;      

OriginalFirstThunk指向導入名稱表的數組,數組元素為IMAGE_THUNK_DATA。數組以空的結構體結束。

一般情況下,數組中的每個元素會再指向IMAGE_IMPORT_BY_NAME結構體。

IMAGE_IMPORT_BY_NAME結構以一個結構體數組形式存在,并以一個空的結構體結束。

IMAGE_THUNK_DATA定義如下

{
      union {
        DWORD ForwarderString;
        DWORD Function;
        DWORD Ordinal;
        DWORD 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 {
    USHORT  Hint;    //導入函數的序号
    UCHAR   Name[1];    //導入函數的名稱
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;      

導入表結構

[系統安全] PE檔案格式詳解2

資源 IMAGE_RESOURCE_DIRECTORY

資源在PE檔案中是以目錄結構的形式存在的,一般情況下這個目錄分為3層,從根目錄開始分别為資源類型、目錄資源ID與資源代碼頁。

這3層目錄結構都是由一個 IMAGE_RESOURCE_DIRECTORY結構為頭部的,并且在其後面跟着一個IMAGE_RESOURCE _DIRECTORY_ENTRY結構數組。

IMAGE_RESOURCE_DIRECTORY結構主要負責指出後面結構數組的成員個數,而後面結構數組的每個成員則分别指向下一層目錄結構(或資源資料)。

IMAGE_RESOURCE_DIRECTORY定義如下

{
      DWORD Characteristics;    //資源屬性辨別
      DWORD TimeDateStamp;    
      WORD MajorVersion;
      WORD MinorVersion;
      WORD NumberOfNamedEntries;    //資源名稱個數
      WORD NumberOfIdEntries;    //資源id個數
    } IMAGE_RESOURCE_DIRECTORY,*PIMAGE_RESOURCE_DIRECTORY;      

IMAGE_RESOURCE_DIRECTORY_ENTRY定義如下

{
        __C89_NAMELESS union {
            __C89_NAMELESS struct {
                DWORD NameOffset:31;    //資源名偏移(指向一個結構體)
                DWORD NameIsString:1;   //值為1時,NameOffset指向IMAGE_RESOURCE_DIR_STRING_U結構體,其中儲存資源名稱
            }DUMMYSTRUCTNAME;
            DWORD Name; //位于第一層目錄時儲存資源類型的值,位于第三層目錄時儲存資源語言區域類型值
            WORD Id;    //資源的id
        }DUMMYUNIONNAME;

        __C89_NAMELESS union {
            DWORD OffsetToData;     //資料偏移RAV
            __C89_NAMELESS struct {
                DWORD OffsetToDirectory:31;     //指出下一層子目錄相對資源目錄起始位址偏移
                DWORD DataIsDirectory:1;
            } DUMMYSTRUCTNAME2;
        } DUMMYUNIONNAME2;
    } IMAGE_RESOURCE_DIRECTORY_ENTRY,*PIMAGE_RESOURCE_DIRECTORY_ENTRY;      

在經過三層目錄的索引後,最後會到達一個 IMAGE_RESOURCE_DATA_ENTRY結構中,這個結構指引到資源資料。

定義如下

{
      DWORD OffsetToData;   //資源資料RAV指針
      DWORD Size;   //資源資料大小
      DWORD CodePage;   //資源代碼頁資訊
      DWORD Reserved;   //保留,恒為0
    } IMAGE_RESOURCE_DATA_ENTRY,*PIMAGE_RESOURCE_DATA_ENTRY;      

繼續閱讀