c++ 动态解析PE导出表

测试环境是x86

main

#include <iostream>
#include <Windows.h>
#include <TlHelp32.h>
#include <string.h>

using namespace std;

const string processName = "game2.exe";
const string dllName = "kernel32.dll";
const string exportFunName = "LoadLibraryA";

// 获取PID
DWORD getPID(string name)
{
	DWORD pid = 0;
	HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if (hSnap != INVALID_HANDLE_VALUE)
	{
		PROCESSENTRY32 pe;
		pe.dwSize = sizeof(pe);
		if (Process32First(hSnap, &pe))
		{
			do {
				if (!_wcsicmp(pe.szExeFile, wstring(name.begin(), name.end()).c_str())) {
					pid = pe.th32ProcessID;
					break;
				}
			} while (Process32Next(hSnap, &pe));
		}
	}
	CloseHandle(hSnap);
	return pid;
}

// 获取模块基址
uintptr_t getModuleBaseAddress(DWORD pid, const wchar_t* modName)
{
	uintptr_t modBaseAddr = 0;
	HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, pid);

	if (hSnap != INVALID_HANDLE_VALUE)
	{
		MODULEENTRY32 me;
		me.dwSize = sizeof(me);
		if (Module32First(hSnap, &me))
		{
			do {
				if (!_wcsicmp(me.szModule, modName)) {
					modBaseAddr = (uintptr_t)me.modBaseAddr;
					break;
				}
			} while (Module32Next(hSnap, &me));
		}
	}
	CloseHandle(hSnap);
	return modBaseAddr;
}

// 读取字节集中的ASCII
void ReadASCII(BYTE* addr, size_t offset, char r[])
{
	size_t i = 0;
	char c;
	while (true)
	{
		c = *(addr + offset + i);
		r[i] = c; // 需要把最后一个0写进去
		if (!c) break;
		i++;
	}
}


void* MyGetProcAddress(string gameProcessName, string modName, string exportFunName)
{
	DWORD pid = getPID(processName);
	if (!pid) return nullptr;

	HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
	if (!hProcess) return nullptr;

	uintptr_t kernel32BaseAddr = getModuleBaseAddress(pid,
		wstring(modName.begin(), modName.end()).c_str());

	printf("kernel32BaseAddr: %x
", kernel32BaseAddr);

	auto RVA2VA = [&](DWORD rva) -> DWORD
	{
		return kernel32BaseAddr + rva;
	};

	// 储存headers+节表 对齐后
	BYTE peHeader[0x1000];
	ReadProcessMemory(hProcess, (LPVOID)kernel32BaseAddr, peHeader, sizeof(peHeader), 0);

	IMAGE_DOS_HEADER* dosHeader = (IMAGE_DOS_HEADER*)peHeader;

	// nt头可以分为PE头,标准头,可选头
	IMAGE_NT_HEADERS* ntHeader = (IMAGE_NT_HEADERS*)(kernel32BaseAddr + dosHeader->e_lfanew);

	IMAGE_DATA_DIRECTORY* exportEntry = (IMAGE_DATA_DIRECTORY*)&ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
	if (!exportEntry->Size)
	{
		// 没有导出表
		return nullptr;
	}

	BYTE* exportDirData = new BYTE[exportEntry->Size];
	ReadProcessMemory(hProcess, (LPVOID)RVA2VA(exportEntry->VirtualAddress),
		exportDirData, exportEntry->Size, 0);

	IMAGE_EXPORT_DIRECTORY* exportDir = (IMAGE_EXPORT_DIRECTORY*)exportDirData;

	// 函数数量
	printf("NumberOfFunctions: %d
", exportDir->NumberOfFunctions);

	// 以函数名导出的数量
	printf("NumberOfNames:     %d
", exportDir->NumberOfNames);

	// 获取fun name表的VA地址
	// 每个大小是DWORD,一共有NumberOfNames个,并且表里面存的都是RVA值
	DWORD* AddressOfNamesVA = (DWORD*)RVA2VA(exportDir->AddressOfNames);


	DWORD itRVA = 0;
	uintptr_t itVA = 0;
	char funName[1024];
	size_t funNameIndex = 0;
	for (; funNameIndex < exportDir->NumberOfNames; funNameIndex++)
	{
		itRVA = *(AddressOfNamesVA + funNameIndex);
		itVA = kernel32BaseAddr + itRVA;
		ReadASCII((BYTE*)itVA, 0, funName);
		// printf("%s
", funName);
		if (!strcmp(funName, exportFunName.c_str()))
		{
			break;
		}
	}
	printf("funName: %s
", funName);
	printf("funNameIndex: %d
", funNameIndex);

	// 拿到索引后去AddressOfNameOrdinals表,找到对应的索引中的值,取出来的值是
	// AddressOfFunctions的索引,里面存的就是地址


	// 获取AddressOfNameOrdinals表的VA地址,每个大小是WORD
	WORD* AddressOfNameOrdinalsVA = (WORD*)RVA2VA(exportDir->AddressOfNameOrdinals);
	WORD AddressOfFunctionsIndex = *(AddressOfNameOrdinalsVA + funNameIndex);


	// 获取AddressOfFunctions表的VA地址,每个大小是DWORD
	DWORD* AddressOfFunctionsVA = (DWORD*)RVA2VA(exportDir->AddressOfFunctions);

	// 每个函数地址存的是RVA地址,需要转为VA使用
	DWORD funAddrVA = *(AddressOfFunctionsVA + AddressOfFunctionsIndex);
	printf("funName Addr RVA: %x
", RVA2VA(funAddrVA));

	delete[] exportDirData;
	CloseHandle(hProcess);

	return (VOID*)RVA2VA(funAddrVA);
}

int main()
{
	void* loadlibrary_a = MyGetProcAddress(processName, dllName, exportFunName);
	printf("loadlibrary_a: %p
", loadlibrary_a);
	return 0;
}
kernel32BaseAddr: 77a80000
NumberOfFunctions: 1603
NumberOfNames:     1603
funName: LoadLibraryA
funNameIndex: 961
funName Addr RVA: 77aa2990
loadlibrary_a: 77AA2990
原文地址:https://www.cnblogs.com/ajanuw/p/13536599.html