PE导出表

来源:互联网 发布:浙江师范大学知行学院 编辑:程序博客网 时间:2024/05/17 23:24


>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

  在可选PE头偏移0x60即得到数据目录项,其结构如下:

typedef struct _IMAGE_DATA_DIRECTORY {DWORD   VirtualAddress;DWORD   Size;} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

  以上结构中的VirtualAddress是后面的十六个表结构。

  数据目录项的第一个结构,就是导出表,结构如下:

typedef struct _IMAGE_EXPORT_DIRECTORY {    DWORD   Characteristics;// 未使用    DWORD   TimeDateStamp;// 时间戳    WORD    MajorVersion;// 未使用    WORD    MinorVersion;// 未使用    DWORD   Name;// 指向该导出表文件名字符串    DWORD   Base;// 导出函数起始序号    DWORD   NumberOfFunctions;// 所有导出函数的个数    DWORD   NumberOfNames;// 以函数名字导出的函数个数    DWORD   AddressOfFunctions;// 导出函数地址表RVA    DWORD   AddressOfNames;// 导出函数名称表RVA    DWORD   AddressOfNameOrdinals;// 导出函数序号表RVA} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

Characteristics00 00 00 00 ,未使用

TimeDateStamp指示创建日期的时间戳

MajorVersion00,未定义

MinorVersion00,未定义

Name 包含这个DLL的名称的ASCII字符串的RVA

Base 导出函数的起始序数。在后面要获得某个函数的导出序数,需要把这个域的值与AddressOfNameOrdinals数组中相应的元素的值相加

NunberOfFunctionsAddressOfFunction数组中的元素的数目。这个值也是这个模块导出的函数的数目。理论上,这个值可能与NumberOfNames域中的值不同,但在实际上它们总是相同

NumberOfNamesAddressOfNames数组中的元素数目

*AddressOfFunctions这个域是一个RVA,并且指向一个函数地址数组。每个导入函数的入口地址(RVA)

*AddressOfNames这个域是一个RVA,并且指向一个字符串指针数组。导出函数的名称的字符创

*AddressOfNameOrdinals 这个域是一个RVA,并且指向一个WORD类型的数组。导出函数的序号


    函数导出的个数与函数名的个数未必一样.所以要将函数地址表和函数名称表分开.。其次,导出一个函数需要函数的名称,相应的地址和导出序数这三部分的内容。总共有三个这样的数组(AddressOfFunctions , AddressOfNames , AddressOfOrdinals),他们是并列的。如下图:

  下面来说说查找某个函数的过程,有两种方式

1).先查AddressOfNames数组下找到函数名所在的索引位置,记为P。在AddressOfOrdinals数组中索引为P的位置得到存放的值,如4,。最后,在AddressOfFunctions数组中索引下标为4中存放的地址即为要找的函数地址(RVA)

2).函数编号-Base域中的值,如2。再到AddressOfFunctions数组中索引下标为2中存放的地址即为要找的函数地址(RVA)

  

  理论部分就到这里,下面用手动的方法来定位导入表以及其相对的域

1.定位_IMAGE_DATA_DIRECTORY DataDirectory

因为_IMAGE_DATA_DIRECTORY DataDirectory在可选PE头偏移0x60出的位置,我们可以偏移得到。但是,可以使用节表紧接着可选PE头的特点,定位_IMAGE_DATA_DIRECTORY DataDirectory。

_IMAGE_DATA_DIRECTORY DataDirectory中有16个表目,一个表目占8个字节,OK。算一下,在16进制编辑器中上移8行即可定位。如图:

2.前8个字节对应的就是导入表部分,可得导入表的RVA为0002AD80。下面开始进行转换,将RVA转为FOA,即相对虚拟地址到文件偏移地址的转换


图可知,RVA0002AD80处于.rdata节表,偏移为2AD80-29000=1D80,则在文件中的偏移(FOA)为17600+1D80=19380,。好了,得到了导入表在文件中的偏移地址,如图:

3.到这里,得到了导入表。要铭记的就是,下面的3个DWORD所对应的地址是RVA,也要进行上面的转换,转换后,即可得到相应的数组在文件中的偏移地址。

(RVA)0002ADA8-> *AddressOfFunctions->(FOA)193A8

(RVA)0002ADB8-> *AddressOfNames->(FOA)193D8

(RVA)0002ADC8-> *AddressOfNameOrdials->(FOA)193C8

相应的在文件中的位置如图:(图中的位置有误,AddressOfNames蓝色部分

绿色(193A8):对应*AddressOfFunctions,可以看见其中的4个DWORD数据,分别为

[0] 00001019

[1] 00001014

[2] 0000100F

[3] 00001005

黄色(193C8):对应*AddressOfNameOrdinals,可以看到其中的4个WORD类型的数据,分别为

[0] 0000

[1] 0001

[2] 0002

[3] 0003

蓝色(193D8):对应*AddressOfNames,在后面可以看到,就是相应的函数名所对应的ASCII

[0] _Div@8

[1] _Mul@8

[2] _Plus@8

[3] _Sub@8

差不多手动解析完了吧!


在上面找到的结果中,如果要查找名为 _Plus@8 的函数的地址,其对应在数组中的索引为2,则在AddressOfNameOrdinals对应索引为2中的到0002,

再到AddressOfFunctions下查找索引2中所存的值100F即为所找函数名的地址(RVA)


下面是相应的代码实现部分

RVAtoFOA:

VOID RVAtoFOA(IN LPVOID pFileBuffer, IN DWORD dwRva, OUT LPVOID* pFOA){PIMAGE_DOS_HEADER pDosHeader = NULL;PIMAGE_NT_HEADERS pNTHeader = NULL;PIMAGE_FILE_HEADER pPEHeader = NULL;PIMAGE_OPTIONAL_HEADER pOptionalHeader = NULL;PIMAGE_SECTION_HEADER pSectionHeader = NULL;LPVOID pTempNewBuffer = NULL;DWORD dwRawValue = 0;if (IsPEFile(pFileBuffer) == FALSE) {printf("Error: is not a PE file!\n");}pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;pNTHeader = (PIMAGE_NT_HEADERS)((PBYTE)pFileBuffer + pDosHeader->e_lfanew);pPEHeader = (PIMAGE_FILE_HEADER)((PBYTE)pNTHeader + sizeof(pNTHeader->Signature));pOptionalHeader = (PIMAGE_OPTIONAL_HEADER)((PBYTE)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);pSectionHeader = (PIMAGE_SECTION_HEADER)((PBYTE)pOptionalHeader + pPEHeader->SizeOfOptionalHeader);PIMAGE_SECTION_HEADER ptempSectionHeader = pSectionHeader;bool flag = FALSE;for (int i = 1; i <= pPEHeader->NumberOfSections; i++, ptempSectionHeader++) {if (dwRva > ptempSectionHeader->VirtualAddress && \dwRva < (ptempSectionHeader->VirtualAddress + ptempSectionHeader->Misc.VirtualSize)) {flag = TRUE;break;}}if (!flag) {printf("address error\n");return;}dwRawValue = dwRva - ptempSectionHeader->VirtualAddress + ptempSectionHeader->PointerToRawData;*pFOA = (PBYTE)pFileBuffer + dwRawValue;}


main函数相关代码段:

///////////////////////////////////////////////////////////////////////printf(";>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");PIMAGE_DATA_DIRECTORY pED = GetOptionalHeader(stMapFile.ImageBase)->DataDirectory;DWORD addr = pED[0].VirtualAddress;RVAtoFOA(stMapFile.ImageBase, pED[0].VirtualAddress, &pFOA);PIMAGE_EXPORT_DIRECTORY pEPT = (PIMAGE_EXPORT_DIRECTORY)pFOA;RVAtoFOA(stMapFile.ImageBase, pEPT->Name, &pFOA);printf(";\t\t\tCharacteristics:[%s] \t\t\t;\n",(char*)pFOA);printf(";\t\t\tTimeDateStamp:[0x%X] \t\t\t;\n",pEPT->TimeDateStamp);printf(";\t\t\tNumberOfFunctions:[%d] \t\t\t\t;\n",pEPT->NumberOfFunctions);printf(";\t\t\tNumberOfNames:[%d] \t\t\t\t;\n",pEPT->NumberOfNames);printf(";\t\t\tBase:[%d] \t\t\t\t\t;\n",pEPT->Base);printf(";\t\t\t\t\t\t\t\t\t;\n");// AddressOfFunctionsprintf(";\t\t\tAddressOfFunctions: \t\t\t\t;\n");for (unsigned int i = 0; i < pEPT->NumberOfFunctions; ++i) {RVAtoFOA(stMapFile.ImageBase, (DWORD)((PDWORD)pEPT->AddressOfFunctions + i), &pFOA);printf(";\t\t\t[%d : 0x%08X]\t\t\t\t;\n", i, *(PDWORD)pFOA);}printf(";\t\t\t\t\t\t\t\t\t;\n");// AddressOfNamesprintf(";\t\t\tAddressOfNames: \t\t\t\t;\n");for (unsigned int j = 0; j < pEPT->NumberOfNames; ++j) {RVAtoFOA(stMapFile.ImageBase, (DWORD)((PDWORD)pEPT->AddressOfNames + j), &pFOA);RVAtoFOA(stMapFile.ImageBase,*(PDWORD)pFOA,&pFOA);printf(";\t\t\t[%d : 0x%08s]\t\t\t\t;\n", j, (LPSTR)pFOA);}printf(";\t\t\t\t\t\t\t\t\t;\n");// AddressOfNameOrdinalsprintf(";\t\t\tAddressOfNameOrdinals: \t\t\t\t;\n");for (unsigned int K = 0; K < pEPT->NumberOfFunctions; ++K) {RVAtoFOA(stMapFile.ImageBase, (DWORD)((PWORD)pEPT->AddressOfNameOrdinals + K), &pFOA);//RVAtoFOA(stMapFile.ImageBase,*(PDWORD)pFOA,&pFOA);printf(";\t\t\t[%d : 0x%d]\t\t\t\t\t;\n", K, *(PWORD)pFOA);}printf(";>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");

结果输出:


END