DLL注入后如何调用目标进程的静态库静态类成员及未导出单例函数
咱们一个一个来解决你的问题:
问题1:如何从DLL注入的目标进程中调用静态库的静态类成员函数?
首先得明确一个核心误区:如果你直接在注入的DLL里链接同一个静态库并调用成员函数,本质是让DLL自带了一份静态库的代码副本——这和目标进程里的静态库实例是完全独立的,根本不是一回事。要调用目标进程里原本的静态库成员函数,核心思路是找到该函数在目标进程内存中的实际地址,通过函数指针来调用。
具体步骤如下:
- 获取函数的相对虚拟地址(RVA):你手里有目标进程的目标文件(.obj/.pdb等资源),可以用工具解析符号:
- 用
dumpbin /symbols命令从.obj文件里提取函数符号对应的RVA; - 如果有.pdb文件,用调试工具(比如x64dbg、WinDbg)或者dbghelp库直接解析符号地址。
- 用
- 计算函数的实际内存地址:在注入的DLL中,用
GetModuleHandle获取目标进程中加载静态库的模块基地址(如果静态库是直接链接到主EXE的,那主EXE的模块就是基地址),然后用「基地址 + RVA」得到函数的实际内存地址。 - 通过函数指针调用:定义匹配的函数指针类型,将计算出的地址强制转换后调用即可。
举个代码例子,假设静态类成员函数是StaticLibClass::DoStaticWork(int):
// 定义函数指针类型,要和目标函数的签名完全匹配 typedef void(*StaticWorkFuncPtr)(int); void CallTargetStaticFunc() { // 获取目标主程序的模块基地址 HMODULE hTargetModule = GetModuleHandle(L"YourTarget.exe"); if (!hTargetModule) return; // 替换成你实际解析到的RVA uintptr_t funcRva = 0x12345; StaticWorkFuncPtr pTargetFunc = (StaticWorkFuncPtr)((uintptr_t)hTargetModule + funcRva); // 调用目标进程里的静态成员函数 pTargetFunc(42); }
问题2:调用目标进程未导出的单例函数是否可行?怎么操作?
当然可行,但你之前的做法确实会出问题——原因你猜的没错:静态变量是每个模块(EXE/DLL)独立存储的,你在注入的DLL里调用VictimSingleton::Get(),创建的是DLL自己数据段里的单例,和目标进程EXE里的单例完全是两个不同的内存对象,自然地址不一样。
解决思路和问题1一致:找到目标进程中VictimSingleton::Get()的实际内存地址,通过函数指针调用它,这样拿到的就是目标进程的单例实例了。具体步骤:
获取
VictimSingleton::Get()的内存地址:- 利用你手里的目标文件/.pdb,用调试工具(x64dbg/WinDbg)找到该函数的RVA;
- 或者用dbghelp库的
SymInitialize+SymFromNameAPI自动解析符号地址,避免手动找RVA的麻烦。
在注入的DLL中通过函数指针调用:
注意:如果DoThing()是非虚函数,你同样需要获取它的地址并用函数指针调用;如果是虚函数,直接通过单例指针调用就行(因为虚表在目标进程的实例内存里)。
代码示例:
// 前向声明类,不需要完整定义,只要能声明指针类型就行 class VictimSingleton; // 定义Get()函数的指针类型 typedef VictimSingleton*(*VictimSingletonGetPtr)(); // 如果DoThing()是非虚函数,定义它的指针类型 typedef void(*VictimDoThingPtr)(VictimSingleton*); void SomeFuncInHook() { HMODULE hTargetExe = GetModuleHandle(L"YourTarget.exe"); if (!hTargetExe) return; // 替换成实际的Get()函数RVA uintptr_t getFuncRva = 0x67890; VictimSingletonGetPtr pGetTargetSingleton = (VictimSingletonGetPtr)((uintptr_t)hTargetExe + getFuncRva); // 调用目标进程的Get(),拿到真正的单例实例 VictimSingleton* vs = pGetTargetSingleton(); // 调用DoThing()的两种情况: // 情况1:DoThing()是虚函数,直接调用 vs->DoThing(); // 情况2:DoThing()是非虚函数,需要获取它的地址 uintptr_t doThingRva = 0xABCDE; VictimDoThingPtr pDoThing = (VictimDoThingPtr)((uintptr_t)hTargetExe + doThingRva); pDoThing(vs); }
额外提醒:如果用dbghelp库解析符号,记得要初始化符号库,并且确保目标进程的.pdb文件路径能被正确找到,这样可以避免手动计算RVA的错误,适配不同的编译版本。
内容的提问来源于stack exchange,提问作者Hinchy
相关产品推荐
相关产品推荐

