导出表
导出表一般用于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开始放我们的地址数组是这样的
如果函数的导出最小的序号减去base
index value 0 0 1 0 2 函数地址 导出序号:2 3 0 4 函数地址 导出序号:4 5 0 6 函数地址 导出序号:6 7 函数地址 导出序号:7
index value 0 函数地址 :导出序号:2 1 0 2 函数地址 导出序号:4 3 0 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;
}