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\n", 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\n", exportDir->NumberOfFunctions); // 以函数名导出的数量
printf("NumberOfNames: %d\n", 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\n", funName);
if (!strcmp(funName, exportFunName.c_str()))
{
break;
}
}
printf("funName: %s\n", funName);
printf("funNameIndex: %d\n", 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\n", RVA2VA(funAddrVA)); delete[] exportDirData;
CloseHandle(hProcess); return (VOID*)RVA2VA(funAddrVA);
} int main()
{
void* loadlibrary_a = MyGetProcAddress(processName, dllName, exportFunName);
printf("loadlibrary_a: %p\n", loadlibrary_a);
return 0;
}
kernel32BaseAddr: 77a80000
NumberOfFunctions: 1603
NumberOfNames: 1603
funName: LoadLibraryA
funNameIndex: 961
funName Addr RVA: 77aa2990
loadlibrary_a: 77AA2990
上一篇:逆向-PE导出表


下一篇:Windows PE导出表编程3(暴力覆盖导出函数)