PE結構體中匯出表/匯入表解析——初階

Editor發表於2018-01-27

一、匯出表解析

PE結構體中匯出表/匯入表解析——初階


輸出表位置,落在了.rdata段,

PE結構體中匯出表/匯入表解析——初階


16000【5200】

17D70【6F70】
  從而,可以知道17D70,輸出表在磁碟中的偏移是6F70


在010裡,Ctrl + G,輸入6F70

這裡,先看下匯出表的資料結構,40B,


typedef struct _IMAGE_EXPORT_DIRECTORY {

DWORD   Characteristics;

DWORD   TimeDateStamp;

WORD    MajorVersion;

WORD    MinorVersion;

DWORD   Name; // DLL的名稱地址

DWORD   Base; // 索引基數

DWORD   NumberOfFunctions; // 函式地址表大小

DWORD   NumberOfNames; // 函式名錶大小 == 函式序號表大小

DWORD   AddressOfFunctions;     // 函式地址表——首地址

DWORD   AddressOfNames;         // 函式名錶——首地址

DWORD   AddressOfNameOrdinals;  // 函式序號表——首地址

} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;


那就從6F70的位置,開始,找40B,如下

PE結構體中匯出表/匯入表解析——初階

00 00 00 00

71 DB 67 5A 【時間戳】

00 00 【主版本】

00 00 【次版本】

C0 7D 01 00【DLL名稱地址】

01 00 00 00 【索引基數】

04 00 00 00 【函式地址表大小】

04 00 00 00 【函式名錶大小 == 函式序號表大小】

98 7D 01 00【函式地址表——首地址】

A8 7D 01 00 【函式名錶——首地址】

B8 7D 01 00【函式序號表——首地址】


1、看DLL的名稱是啥:地址17DC0【6FC0】,找到了我們自己的庫dll_00.dll

PE結構體中匯出表/匯入表解析——初階


2、再看下函式地址表中的元素,首地址17D98【6F98】,共有4個,地址,4B/個

PE結構體中匯出表/匯入表解析——初階


如下所示:

PE結構體中匯出表/匯入表解析——初階


3、再來看函式名錶中的元素,首地址17DA8【6FA8】,共有4個,地址4B/個

PE結構體中匯出表/匯入表解析——初階

這些都是地址值,要找到真正的函式名:

17DD0【6FD0】

PE結構體中匯出表/匯入表解析——初階

17DD5【6FD5】

PE結構體中匯出表/匯入表解析——初階

17DDA【6FDA】

PE結構體中匯出表/匯入表解析——初階

17DCB【6FCB】

PE結構體中匯出表/匯入表解析——初階


特別注意:函式名錶,其實存放的也是地址值,RVA,這個只是我們自己找到的名稱,方便起見,直接寫的名字

PE結構體中匯出表/匯入表解析——初階


4、接下來,看下函式序號表,首地址17DB8【6FB8】,4個,序號,2B/個

PE結構體中匯出表/匯入表解析——初階

PE結構體中匯出表/匯入表解析——初階

5、接下來,就分析分析:從這裡,也可以看到,序號表裡的值,並沒有加上索引基數

PE結構體中匯出表/匯入表解析——初階

最終,會得到如下結果:

PE結構體中匯出表/匯入表解析——初階


6、驗證下,我們的結果:成功了;

PE結構體中匯出表/匯入表解析——初階

PE結構體中匯出表/匯入表解析——初階


至於,索引基數,還沒看到效果呢,————注意看下剛剛的LoadPe裡的Ordinal那一列

PE結構體中匯出表/匯入表解析——初階

PE結構體中匯出表/匯入表解析——初階


部分程式碼:


#pragma once

#define WIN32DLL_EXPORTS

#ifdef __cplusplus

extern "C" {

#endif

#ifdef WIN32DLL_EXPORTS

#define WIN32DLL_API __declspec(dllexport)

#else

#define WIN32DLL_API __declspec(dllimport)

#endif

WIN32DLL_API void Fun1();

WIN32DLL_API void Fun2();

WIN32DLL_API void Fun3();

#ifdef __cplusplus

}

#endif


def檔案

1

2

3

LIBRARY;

EXPORTS;

Fun4;



二、匯入表解析


寫一個測試程式,檢視匯入表RVA

PE結構體中匯出表/匯入表解析——初階


輸入表位置,落在了.idata段

PE結構體中匯出表/匯入表解析——初階


1A000【7400】

1A1E8【75E8】


共20B


typedef struct _IMAGE_IMPORT_DESCRIPTOR {

union {

DWORD   Characteristics;            // 0 for terminating null import descriptor

DWORD   OriginalFirstThunk;         // RVA to original unbound IAT (PIMAGE_THUNK_DATA)

} DUMMYUNIONNAME;

DWORD   TimeDateStamp;                  // 0 if not 

DWORD   ForwarderChain;              

DWORD   Name;

DWORD   FirstThunk;                     // RVA to IAT

} IMAGE_IMPORT_DESCRIPTOR;


PE結構體中匯出表/匯入表解析——初階


2C A3 01 00 【OriginalFirstThunk:INT(Import Name Table)匯入名稱表地址RVA】

00 00 00 00 

00 00 00 00 

54 A4 01 00【DLL名稱(地址值)】

E0 A0 01 00【IAT(Import Address Table)匯入地址表地址RVA】


1、首先看下DLL的名字,1A454【7854】

PE結構體中匯出表/匯入表解析——初階


2、看下INT(OriginalFirstThunk):1A32C【772C】,全0結尾

PE結構體中匯出表/匯入表解析——初階


函式名陣列:

44 A4 01 00 ————1A444【7844】——————最高位為0,說明是名稱匯入的,不是序號匯入的;

3C A4 01 00 ————1A43C【783C】——————

4C A4 01 00 ————1A44C【784C】——————

34 A4 01 00 ————1A434【7834】——————


注意:IAT和INT都指向下面的資料結構,4B


typedef struct _IMAGE_THUNK_DATA32 {

union {

DWORD ForwarderString;      // PBYTE 

DWORD Function;             // PDWORD,匯入函式的地址,在載入到記憶體後,這裡才起作用

DWORD Ordinal;              // 假如是序號匯入的,會用到這裡

DWORD AddressOfData;        // PIMAGE_IMPORT_BY_NAME,假如是函式名匯入的,用到這裡,它指向另外一個結構體:PIMGE_IMPORT_BY_NAME

} u1;

} IMAGE_THUNK_DATA32;

// 如果是函式名匯入的,AddressOfData會指向下面這個結構體

typedef struct _IMAGE_IMPORT_BY_NAME {

WORD    Hint;

CHAR   Name[1];

} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;


由上可知,函式名匯入的,因此,上面的地址值,就會指向一個PIMAGE_IMPORT_BY_NAME的結構體:

【7844】Fun3

PE結構體中匯出表/匯入表解析——初階

【783C】Fun2

PE結構體中匯出表/匯入表解析——初階

【784C】Fun4

PE結構體中匯出表/匯入表解析——初階

【7834】Fun1

PE結構體中匯出表/匯入表解析——初階


3、看下IAT,1A0E0【74E0】全0結束,IAT和INT一樣,都指向IMAGE_THUNK_DATA32結構體,4B

PE結構體中匯出表/匯入表解析——初階


可見,最高位都是0,所以,也是名稱匯入的,另外,還可以發現,這個位置的值,和INT的值是一樣的,因此,不再贅述了;

44 A4 01 00 

3C A4 01 00 

4C A4 01 00 

34 A4 01 00


PE結構體中匯出表/匯入表解析——初階


#include <stdio.h>

extern "C" __declspec(dllimport) void Fun1();

extern "C" __declspec(dllimport) void Fun2();

extern "C" __declspec(dllimport) void Fun3();

// 如果是在def中匯出的,需要如下宣告

void Fun4();

#pragma comment(lib, "../Debug/dll_00.lib")

int main(int argc, char** argv) {

Fun1();

Fun2();

Fun3();

Fun4();

getchar();

return 0;

}


三、如果,修改def為


LIBRARY;

EXPORTS;

Fun4 @1;


看下匯入表裡的INT/IAT:

PE結構體中匯出表/匯入表解析——初階


PE結構體中匯出表/匯入表解析——初階

可見,這裡的一項,最高位為1,序號匯入,這個序號,就是dll export的那個序號


至此,PE結構中,匯入/匯出表的介紹結束;


PS:I Dare to do sth I feared,作為一枚奮鬥青年,也是一枚小白,最近在學習PE結構相關的知識,這篇帖子也算是自己的一個總結;希望能對需要的人以幫助;也期待大神們的更多指導;


本文由看雪論壇 Reginald 原創 轉載請註明來自看雪社群

相關文章