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
- http://www.openrce.org/reference_library/files/reference/PE Format.pdf
- https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
- 上面的代码没进过严格测试,绝对有问题
c++ 动态解析PE导出表的更多相关文章
- 解析PE文件
最近在自学解析PE文件,根据小辣椒(CFF Explorer)以及各论坛上大佬的帖子,做了个黑屏打印PE文件的,历时7天完成,在此想跟有相关需要的同学们分享下思路,有不足之处也希望大家不吝赐教,指点出 ...
- 使用Newtonsoft.Json.dll(JSON.NET)动态解析JSON、.net 的json的序列化与反序列化(一)
在开发中,我非常喜欢动态语言和匿名对象带来的方便,JSON.NET具有动态序列化和反序列化任意JSON内容的能力,不必将它映射到具体的强类型对象,它可以处理不确定的类型(集合.字典.动态对象和匿名对象 ...
- 理解AngularJS生命周期:利用ng-repeat动态解析自定义directive
ng-repeat是AngularJS中一个非常重要和有意思的directive,常见的用法之一是将某种自定义directive和ng-repeat一起使用,循环地来渲染开发者所需要的组件.比如现在有 ...
- 开源一个动态解析protobuf的工具
好久没写博客了,主要是这一年技术没啥长进都打杂了,还有就是生活琐事越来越多,人也越来越懒了…… 之前项目中用到了Protobuf,然后测试发现这玩意不好测,总不能每次定个协议或者改下都要编译Java代 ...
- Protobuf动态解析那些事儿
需求背景 在接收到 protobuf 数据之后,如何自动创建具体的 Protobuf Message 对象,再做反序列化.“自动”的意思主要有两个方面:(1)当程序中新增一个 protobuf Mes ...
- jsoncpp动态解析节点类型
在互联网无处不在的今天,JSON作为轻量级数据存储格式,被广泛应用到互联网数据传输中.众所周知,JSON由键/值对.对象.数组组成,其中键/值对的值包括以下几种类型: enum ValueType { ...
- Protobuf动态解析在Java中的应用 包含例子程序
最近在做ProtoBuf相关的项目,其中用到了动态解析,网上看了下相关资料和博文都比较少,自己来写一个记录一下学习过程. Protocol Buffers是结构化数据格式标准,提供序列化和反序列方 ...
- 在C#中,Newtonsoft.Json + dynamic动态解析jsonString,jsonString转实体
记录一下 引用 using Newtonsoft.Json; using Newtonsoft.Json.Linq; var jsonString = "{\"ApiResourc ...
- C#匿名类型和动态解析减少定义传输类模板
C#作为强类型语言,在序列化和反序列化(json)场景中对字符串解析常常需要定义强类型模板,造成编码上的繁琐.其实可以使用匿名类型和动态解析减少json序列化时候的数据模板定义: string a = ...
随机推荐
- TRUNK与VTP
TRUNK协议: 交换机之间VLAN通信: 同一个VLAN可以跨越多个交换机 主干功能支持多个VLAN的数据 Trunk(主干) VLAN 中交换机之间的链路:用来承载多个VLAN的数据流. Trun ...
- Vue3.0网页版聊天|Vue3.x+ElementPlus仿微信/QQ界面|vue3聊天实例
一.项目简介 基于vue3.x+vuex+vue-router+element-plus+v3layer+v3scroll等技术构建的仿微信web桌面端聊天实战项目Vue3-Webchat.基本上实现 ...
- Lambda 表达式 学习
最近几天在学习Lambda,给我的理解就是一个匿名函数的升级版,代码极大可能的简洁了很多,不需要像以前一样必须使用众多的代码才能实现相关功能. 慢慢积累学习,将Java 8的相关知识进行一个学习. 用 ...
- Spring Boot:定时任务与图片压缩处理
一.定时任务 1.创建定时任务 2.@Scheduled 二.图片压缩处理 1.添加thumbnailator依赖 2.创建图片处理类 3.基本使用方法 一.定时任务 项目中可以采用定时任务进行一些操 ...
- spark SQL (五)数据源 Data Source----json hive jdbc等数据的的读取与加载
1,JSON数据集 Spark SQL可以自动推断JSON数据集的模式,并将其作为一个Dataset[Row].这个转换可以SparkSession.read.json()在一个Dataset[Str ...
- dedecms万能SQL标签使用方法大全
注意:dede_archives这是系统默认的数据库表,如果你修改过表前缀dede_,请自行更改表名.在以下示例的标签中,有一部分只写出了SQL语句,具体的完整标签写法请参考:织梦SQL标签调用方法. ...
- 织梦dedecms首页、列表页、文章页文章点击浏览次数实时调用方法
首先呢,先在根目录 /plus 目录下找到count.php 复制一份然后命名为viewclick.php(你也可以命名为你容易理解的名字)用编辑器将viewclick.php打开然后删除以下几行代 ...
- 给jekyll博客添加搜索功能
使用SWIFTYPE为jekyll博客添加搜索引擎 步骤 1.首先去swiftype注册一个账号 2.接着添加自己想要配置的网站地址并为新设定的引擎添加一个名字(非会员只能设置一个引擎). 3.收到验 ...
- 一文读懂云上DevOps能力体系
简介: 阿里云ECS自动化运维套件架构师,深度拆解云上运维能力体系建设:自动化运维等级金字塔.自动化运维的进阶模式.DevOps的基础核心.云上标准化部署三大能力-- 序言 云计算行业已经有十多年的发 ...
- 【uva 11134】Fabled Rooks(算法效率--问题分解+贪心)
题意:要求在一个N*N的棋盘上放N个车,使得它们所在的行和列均不同,而且分别处于第 i 个矩形中. 解法:问题分解+贪心. 由于行.列不相关,所以可以先把行和列均不同的问题分解为2个"在区间 ...