PE导出表


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

  在可选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;						

Characteristics 00 00 00 00 ,未使用

TimeDateStamp 指示创建日期的时间戳

MajorVersion 00,未定义

MinorVersion 00,未定义

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

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

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

NumberOfNames AddressOfNames数组中的元素数目

*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");

	// AddressOfFunctions
	printf(";\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");

	// AddressOfNames
	printf(";\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");
	
	// AddressOfNameOrdinals
	printf(";\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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值