一、匯出表解析
輸出表位置,落在了.rdata段,
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,如下
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
2、再看下函式地址表中的元素,首地址17D98【6F98】,共有4個,地址,4B/個
如下所示:
3、再來看函式名錶中的元素,首地址17DA8【6FA8】,共有4個,地址4B/個
這些都是地址值,要找到真正的函式名:
17DD0【6FD0】
17DD5【6FD5】
17DDA【6FDA】
17DCB【6FCB】
特別注意:函式名錶,其實存放的也是地址值,RVA,這個只是我們自己找到的名稱,方便起見,直接寫的名字
4、接下來,看下函式序號表,首地址17DB8【6FB8】,4個,序號,2B/個
5、接下來,就分析分析:從這裡,也可以看到,序號表裡的值,並沒有加上索引基數
最終,會得到如下結果:
6、驗證下,我們的結果:成功了;
至於,索引基數,還沒看到效果呢,————注意看下剛剛的LoadPe裡的Ordinal那一列
部分程式碼:
#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
輸入表位置,落在了.idata段
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;
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】
2、看下INT(OriginalFirstThunk):1A32C【772C】,全0結尾
函式名陣列:
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
【783C】Fun2
【784C】Fun4
【7834】Fun1
3、看下IAT,1A0E0【74E0】全0結束,IAT和INT一樣,都指向IMAGE_THUNK_DATA32結構體,4B
可見,最高位都是0,所以,也是名稱匯入的,另外,還可以發現,這個位置的值,和INT的值是一樣的,因此,不再贅述了;
44 A4 01 00
3C A4 01 00
4C A4 01 00
34 A4 01 00
#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:
可見,這裡的一項,最高位為1,序號匯入,這個序號,就是dll export的那個序號
至此,PE結構中,匯入/匯出表的介紹結束;
PS:I Dare to do sth I feared,作為一枚奮鬥青年,也是一枚小白,最近在學習PE結構相關的知識,這篇帖子也算是自己的一個總結;希望能對需要的人以幫助;也期待大神們的更多指導;
本文由看雪論壇 Reginald 原創 轉載請註明來自看雪社群