可选PE头中的最后一个字段的数据目录表里面存放了很多PE文件的核心元素,一共是16个元素,最后一个是空的,只有前15个有内容:
数据目录表的第一个元素就是导出表。导出表存放的就是导出的函数,提供一个共外部使用的到处函数的一个表。导出表在可选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) |
函数地址表,序号表,名称表的关系
一个函数,首先进入函数名称表,找到函数名称表对应的序号,然后拿着这个序号进入函数序号表,函数序号表得到函数地址表的序号,然后根据函数地址表的序号得到函数地址。
如果有地址要名字就反之来,先拿名字到函数地址表得到对应的序号,然后拿序号在函数序号表中得到序号,再来序号到函数名称表中得到名称.
关系图如下:
导出表总结
导出表是一个用来记录导出函数的一个表,常用在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++;
}
}