• 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
    
  • 相关阅读:
    [置顶] RGB颜色查询对照表
    3.3FactoryMethod——工厂方法
    从零开始学C++之数据封装与抽象:分别用C和C++来实现一个链栈
    C# 自定义事件
    Qt4--加密日记本(子例化QMainWindow文本加密解密)
    SharePoint PerformancePoint Service-PowerShell
    Windows下安装Eric5时出现的“Sorry, please install QtHelp.”问题解决办法
    Char* ,CString ,WCHAR*之间的转换
    [C++基础]关键词volatile
    备忘--简单比较SPSS、RapidMiner、KNIME以及Kettle四款数据分析工具
  • 原文地址:https://www.cnblogs.com/ajanuw/p/13536599.html
Copyright © 2020-2023  润新知