PE文件解析-导出表

可选PE头中的最后一个字段的数据目录表里面存放了很多PE文件的核心元素,一共是16个元素,最后一个是空的,只有前15个有内容:

 

img

 

 

数据目录表的第一个元素就是导出表。导出表存放的就是导出的函数,提供一个共外部使用的到处函数的一个表。导出表在可选PE头中的最后一项的数据目录表中的第一个元素

 

//  数据目录结构体
typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress;//内存中的偏移 又叫RVA, //文件中的偏移叫FOA
DWORD Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

RVA和FOA如何转换

针对RVA和FOA只有区段部分才需要转换。

首先确定RVA是哪一个区段,然后用RVA减去该区段的首地址

数据RVA-区段地址的RVA = 数据偏移值 = 数据FOA-区段的FOA

数据FOA = 区段FOA+数据RVA-区段地址的RVA

 

 

 

导出表结构体

typedef struct _IMAGE_EXPORT_DIRECTORY {
DWORD Characteristics;没用
DWORD TimeDateStamp;时间戳
WORD MajorVersion;没用
WORD MinorVersion;没用
DWORD Name;
DWORD Base;
DWORD NumberOfFunctions;
DWORD NumberOfNames;
DWORD AddressOfFunctions; // RVA from base of image
DWORD AddressOfNames; // RVA from base of image
DWORD AddressOfNameOrdinals; // RVA from base of image
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
导出表字段含义
Name 导出表文件名(RVA)
Base 导出函数的起始序号
NumberOfFunctions 导出函数的个数
NumberOfNames 导出函数的个数,但是并不是有几个函数就是几个函数,是函数序号最大值的数字减去最小的再加1,里面是什么数就是什么。
AddressOfFunctions 导出函数地址表(RVA)
AddressOfNames 导出函数名称表(RVA)
AddressOfNameOrdinals 导出函数序号表(RVA)

函数地址表,序号表,名称表的关系

img

 

 

一个函数,首先进入函数名称表,找到函数名称表对应的序号,然后拿着这个序号进入函数序号表,函数序号表得到函数地址表的序号,然后根据函数地址表的序号得到函数地址。

如果有地址要名字就反之来,先拿名字到函数地址表得到对应的序号,然后拿序号在函数序号表中得到序号,再来序号到函数名称表中得到名称.

关系图如下:

 

 

导出表总结

导出表是一个用来记录导出函数的一个表,常用在dll可执行文件中,当然别的也有。

可选PE头结构体的最后一个字段数据目录表里面有很多PE文件的核心内容,该数组的第一个内容存放的就是导出表的地址但是这个地址的RVA的只能在内存中使用,如果要用在文件上的PE结构需要转化为FOA来使用,通过FOA可以直接转化为导出表结构体然后来访问需要访问的内容,导出表的各个字段都是存的是RVA地址。

用代码解析导出表

void CPeUtil::GetExportTable()
{
//1 拿到可选PE头中的数据目录表的第一个内容
IMAGE_DATA_DIRECTORY directory = pOptionalHeader->DataDirectory[0];

//2 得到数据目录结构体中的RVA地址
DWORD rva1 = directory.VirtualAddress;
//3 转换为FOA
DWORD foa1 = RvaToFoa(rva1);

//4 通过地址得到导出表结构体的指针
PIMAGE_EXPORT_DIRECTORY pexport = (PIMAGE_EXPORT_DIRECTORY)(foa1+FileBuff);


//5得到导出表的名字
std::cout << RvaToFoa(pexport->Name)+FileBuff << std::endl;

//6输出导出表的函数地址和函数名称和函数名称
//拿到函数地址
DWORD *funaddr = (DWORD*)(RvaToFoa(pexport->AddressOfFunctions) + FileBuff);
//拿到函数地址表
DWORD* peot = (DWORD*)(RvaToFoa(pexport->AddressOfNameOrdinals) + FileBuff);
//拿到函数名称表
DWORD* pent = (DWORD*)(RvaToFoa(pexport->AddressOfNames) + FileBuff);
for (int i = 0; i < pexport->NumberOfFunctions; i++)
{
printf("函数地址为%x\n", *funaddr);
for (int j = 0; j < pexport->NumberOfNames; j++)
{
if (peot[j] == i)
{
char *funName =(RvaToFoa(pent[j]) + FileBuff);
printf("函数名称为%s\n", funName);
break;
}
}
funaddr++;
}
}