C/C++ 获取 PE 文件导出表

December 17, 2023
测试
测试
测试
测试
8 分钟阅读

得到特定PE文件内的导出表信息,并输出。

// 内存偏移转文件偏移
int rva_to_raw(PIMAGE_SECTION_HEADER pSection,int nSectionNum,int nRva)
{
	int nRet = 0;

	// 遍历节区
	for (int i=0;i<nSectionNum;i++){
		// 导出表地址在这个节区内
		if (pSection[i].VirtualAddress <= nRva && nRva < pSection[i+1].VirtualAddress){
			// 文件偏移 = 该段的 PointerToRawData + (内存偏移 - 该段起始的RVA(VirtualAddress))
			nRet = nRva - pSection[i].VirtualAddress + pSection[i].PointerToRawData;
			break;
		}
	}

	return nRet;
}

void printExpTable(const string& strFilePath)
{
	// 二进制方式读文件
	fstream cFile(strFilePath, ios::binary | ios::in);
	if (!cFile){cout << "打开文件失败" << endl; return;}

	// 读 dos 头
	IMAGE_DOS_HEADER dosHeader; 
	cFile.read((char*)&dosHeader,sizeof(IMAGE_DOS_HEADER));
	
	// 读 nt 头(64位)
	IMAGE_NT_HEADERS64 ntHeader;
	cFile.seekg(dosHeader.e_lfanew, ios::beg);
	cFile.read((char*)&ntHeader, sizeof(IMAGE_NT_HEADERS64));
	if (!ntHeader.OptionalHeader.DataDirectory[0].VirtualAddress){
		cout << "文件没有导出函数" << endl;
		cFile.close(); return;
	}

	// 读节区头
	int nSectionNum = ntHeader.FileHeader.NumberOfSections;	
	shared_ptr<IMAGE_SECTION_HEADER> pShareSection(new IMAGE_SECTION_HEADER[nSectionNum]);
	PIMAGE_SECTION_HEADER pSection = pShareSection.get();
	cFile.read((char*)pSection, sizeof(IMAGE_SECTION_HEADER)*nSectionNum);
	
	// 计算导出表 RAW
	IMAGE_EXPORT_DIRECTORY expDir;
	int nExportOffset = rva_to_raw(pSection,nSectionNum,ntHeader.OptionalHeader.DataDirectory[0].VirtualAddress);
	if (!nExportOffset){
		cout << "RAW 获取失败" << endl;
		cFile.close(); return;
	}

	// 读导出表
	cFile.seekg(nExportOffset, ios::beg);
	cFile.read((char*)&expDir, sizeof(IMAGE_EXPORT_DIRECTORY));

	// 读导出表头
	cFile.seekg(rva_to_raw(pSection, nSectionNum, expDir.Name), ios::beg);
	char szExportName[50];
	cFile.get(szExportName,50);
	cout << "IMAGE_EXPORT_DIRECTORY.Name = " << szExportName << endl;

	// 获取到处函数个数
	int nAddressNum = expDir.NumberOfFunctions;

	// 获取导出表函数名
	shared_ptr<int> pShareName(new int[nAddressNum]);
	int* pName = pShareName.get();
	cFile.seekg(rva_to_raw(pSection, nSectionNum, expDir.AddressOfNames), ios::beg);
	cFile.read((char*)pName, sizeof(int)*nAddressNum);
	
	// 获取导出表函数序号
	shared_ptr<short> pShareOrder(new short[nAddressNum]);
	short* pOrder = pShareOrder.get();
	cFile.seekg(rva_to_raw(pSection, nSectionNum, expDir.AddressOfNameOrdinals), ios::beg);
	cFile.read((char*)pOrder, sizeof(short)*nAddressNum);
	
	// 获取导出表函数地址
	shared_ptr<int> pShareFunc(new int[nAddressNum]);
	int* pFunc = pShareFunc.get();
	cFile.seekg(rva_to_raw(pSection, nSectionNum, expDir.AddressOfFunctions), ios::beg);
	cFile.read((char*)pFunc, sizeof(int)*nAddressNum);
	
	// 遍历导出表
	char szFuncName[50];
	for (int i=0;i<nAddressNum;i++){
		cFile.seekg(rva_to_raw(pSection, nSectionNum, pName[i]), ios::beg);
		cFile.get(szFuncName, 50);
		cout << "[Index:" << dec << i << "]\t"
			<< "[ID:" << hex << pOrder[i] << "]\t"
			<< "[RVA:" << pFunc[i] << "]\t"
			<< "[Name:" << szFuncName << "]\t"
			<< endl;
	}

	cFile.close();
}

效果图:

继续阅读

更多来自我们博客的帖子

如何安装 BuddyPress
由 测试 December 17, 2023
经过差不多一年的开发,BuddyPress 这个基于 WordPress Mu 的 SNS 插件正式版终于发布了。BuddyPress...
阅读更多
Filter如何工作
由 测试 December 17, 2023
在 web.xml...
阅读更多
如何理解CGAffineTransform
由 测试 December 17, 2023
CGAffineTransform A structure for holding an affine transformation matrix. ...
阅读更多