青春时代是一个短暂的美梦,当你醒来时,它早已消失得无影无踪了。
 
昨日:篇  今日:篇   总帖:篇   会员:
admin
夜月琉璃Lv18   
PE结构体中导出表/导入表解析——初阶     
作者:Reginald 原文地址:https://bbs.pediy.com/thread-224265.htm


一、导出表解析


Image

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


Image


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,如下:

Image


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


Image


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


Image


如下所示:

Image

3、再来看函数名表中的元素,首地址17DA8【6FA8】,共有4个,地址4B/个


Image


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

17DD0【6FD0】

Image


17DD5【6FD5】

Image


17DDA【6FDA】

Image


17DCB【6FCB】

Image


特别注意:函数名表,其实存放的也是地址值,RVA,这个只是我们自己找到的名称,方便起见,直接写的名字

Image


4、接下来,看下函数序号表,首地址17DB8【6FB8】,4个,序号,2B/个


Image


Image


5、接下来,就分析分析:从这里,也可以看到,序号表里的值,并没有加上索引基数


Image


最终,会得到如下结果:


Image


6、验证下,我们的结果:成功了;


Image


Image


至于,索引基数,还没看到效果呢,————注意看下刚刚的LoadPe里的Ordinal那一列


Image


Image


部分代码:

#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文件

LIBRARY;
EXPORTS;
    Fun4;

二、导入表解析

写一个测试程序,查看导入表RVA

Image


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


Image


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;
Image


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】

Image


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

Image


函数名数组:

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

Image


【783C】Fun2

Image


【784C】Fun4

Image


【7834】Fun1

Image


3、看下IAT,1A0E0【74E0】全0结束,IAT和INT一样,都指向IMAGE_THUNK_DATA32结构体,4B

Image


可见,最高位都是0,所以,也是名称导入的,另外,还可以发现,这个位置的值,和INT的值是一样的,因此,不再赘述了;

44 A4 01 00 

3C A4 01 00 

4C A4 01 00 

34 A4 01 00

Image


#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:

Image


Image



可见,这里的一项,最高位为1,序号导入,这个序号,就是dll export的那个序号


至此,PE结构中,导入/导出表的介绍结束;


PS:I Dare to do sth I feared,作为一枚奋斗青年,也是一枚小白,最近在学习PE结构相关的知识,这篇帖子也算是自己的一个总结;希望能对需要的人以帮助;也期待大神们的更多指导;



 0  已被阅读了99次  楼主 2018-02-01 21:35:46
回复列表

回复:PE结构体中导出表/导入表解析——初阶

桂公网安备 45010302000666号 桂ICP备14001770-3号
您的IP:54.160.153.233,2018-02-26 09:08:00,Processed in 1.14062 second(s).
免责声明: 本网不承担任何由内容提供商提供的信息所引起的争议和法律责任。
Powered by HadSky 5.4.1