PE文件-导出表

导出表

导出表一般用于DLL文件,DLL导出了什么函数都记录在导出表上,在数据目录的第1项,下标为0

导出表结构

typedef struct _IMAGE_EXPORT_DIRECTORY {
    DWORD   Characteristics;              //保留值 恒定为0
    DWORD   TimeDateStamp;            //和文件头中的地址一样
    WORD    MajorVersion;                  //主版本号
    WORD    MinorVersion;                  //次版本号
    DWORD   Name;                            //本PE的名称 谁导出了这些函数
    DWORD   Base;                              //序号基数
    DWORD   NumberOfFunctions;      //函数导出的总数量
    DWORD   NumberOfNames;          //有名字导出的函数的数量
    DWORD   AddressOfFunctions;      //导出函数地址表
    DWORD   AddressOfNames;          //导出函数名称表
    DWORD   AddressOfNameOrdinals;   //序号表
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

重要字段解析:

BASE:导出函数序号的最小值 

AddressOfFunctions:函数地址表 保存了所有导出函数的地址 这里的函数地址的下标并不是真正的函数导出序号,真正的序号是下标加base

AddressOfNames:导出函数的名称表,所有带名称导出的函数的名称

AddressOfNameOrdinals:这个序号表和导出的函数的序号没有任何的关系,他就是函数名称表和函数地址表的一个中转 下面会详细解释

通过函数名称找函数地址(GetProcAddress)

有名称,序号规律的导出了四个函数

 找到pe文件中的函数导出表 

分析:

base为1 说明序号最小的为1

函数总数量:一共导出了4个函数

以名称导出函数:一共四个

最后的序号表一会看

例:现在找到fun3这个函数的地址

首先找函数名称表18df8是一个RVA转FOA  四个指向函数名称的RVA还要转FOA

函数名称表就是一个指针数组 每个元素指向了函数名称首先第一个 18e22 fun1

 第二个00018e27  fun2

 第三个 18e2c 为我们的func3函数 那此时我们的fun3在函数名称这个数组的下标为[2]

拿着这个下标2找序号表(0018e08rva)

序号表 每个序号为两个字节 序号表的个数和导出函数名称表的个数相同 我们看到序号表下标为2的元素值为2     

拿着这个下标去找函数地址表(0018de8 rva) 

 下标为2的地址为000111A9 函数的RVA 加载到OD

观察序号导出和函数序号不连续的情况

老套路 找导出表

我们看 base为2 我们最小的导出函数的序号就是2

函数导出的总数量 实则导出了4个函数 但是函数的总数量是6 是因为上面说的 因为函数地址表数组下标从0开始 2-2放在0下标的位置 4-2放在2下标的位置 6-2放在4下标的位置 7-2放在5下标的位置 也就是说0 2 4 5的地方有值 其他下标添0 也就是6个元素

按名称导出有3个没错

 举例找这个序号为4的函数

用(4 - base(2))当下标直接找函数地址表 

正如刚才说的  0 2 4 5的地方有值 其他下标添0 我们序号是2地址为000111f4

这里说下为什么4 - base

都知道base是导出函数序号的最小值,我们刚才的例子 导出序号为 7 6 4 2 编译器不能从2开始放我们的函数地址,这样他很亏,如果从2开始放我们的地址数组是这样的 

indexvalue
00
10
2函数地址 导出序号:2
30
4函数地址 导出序号:4
50
6函数地址 导出序号:6
7函数地址 导出序号:7
如果函数的导出最小的序号减去base
indexvalue
0函数地址 :导出序号:2
10
2函数地址 导出序号:4
30
4函数地址 导出序号:6
5函数地址 导出序号:7

所以 函数地址在放到函数地址表里的时候 函数导出序号会减去base的值为下标存放

所以函数地址表的下标加base才是真正的函数导出序号

4 - base也是为了得到真正函数地址的下标

代码定位导出表

void fun()
{
HANDLE hAndle = CreateFile(m_filePath,
		GENERIC_READ,
		NULL,
		NULL,
		OPEN_EXISTING,
		FILE_ATTRIBUTE_NORMAL,
		NULL);
	if (hAndle == INVALID_HANDLE_VALUE)
	{
		AfxMessageBox(_T("文件打开失败"));
		return;
	}
	DWORD fileSize = GetFileSize(hAndle, NULL);
	TCHAR szBufpe = new TCHAR[fileSize];
	DWORD readSize = 0;
	ReadFile(hAndle, m_szBufpe, fileSize, &readSize, NULL);
    PIMAGE_DOS_HEADER dosHad = (PIMAGE_DOS_HEADER)m_szBufpe;
    PIMAGE_NT_HEADERS ntHad = (PIMAGE_NT_HEADERS)(m_dosHad->e_lfanew + (LONG)m_szBufpe);
    PIMAGE_OPTIONAL_HEADER optionHad = (PIMAGE_OPTIONAL_HEADER)(&m_ntHad->OptionalHeader);
    PIMAGE_DATA_DIRECTORY DataDir = (PIMAGE_DATA_DIRECTORY)m_optionHad->DataDirectory;
    DWORD rva = rva_to_fa(m_DataDir[0].VirtualAddress) + (DWORD)m_szBufpe;
    PIMAGE_EXPORT_DIRECTORY export = (PIMAGE_EXPORT_DIRECTORY)rva;
}

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值