使用dlsym调用非静态成员函数时,如何传递隐式this指针?
问题:通过偏移量调用未导出的C++非静态成员函数失败
我需要通过dlsym调用主程序中未导出的非静态成员函数,已持有对象指针但调用失败。由于函数未导出,直接dlsym获取不到,于是通过GDB计算了该函数与已知导出函数lua_pushboolean的偏移量,先拿到导出函数指针再计算目标函数指针,但参数传递始终有问题。
目标函数定义:
_int64 __fastcall gplayer_controller::DebugCommandHandler(gplayer_controller *const this, int cmd_type, const void *buf, size_t size)
我的调用代码:
/* open the needed object */ void *handle = dlopen(NULL, RTLD_LOCAL | RTLD_LAZY); if(handle == NULL){ printf("error w/ dlopen\n"); } int (*fptr)(controller *, int, mma *, size_t); fptr = (int (*)(controller *, int, mma *, size_t))dlsym(handle, "lua_pushboolean"); if(fptr == NULL){ printf("error w/ funcion\n"); } else{ printf("found, ptr: %p\n", fptr); } gobject_imp *pImp = (gobject_imp*)skill->GetPlayer()->GetObject().GetImpl(); int (*fptr2)(controller *, int, mma *, size_t) = fptr - 5638326; printf("ptr calculation...: %p\n", fptr2); mma _mma; _mma.cmd = 2040; _mma.skillid = 15000; _mma.level = skill->GetLevel() + 1; printf("data controller %p\n",pImp->_commander ); (*fptr2)(pImp->_commander,2040,&_mma,10);
问题分析与解决
核心问题点
- 函数类型不匹配:将非静态成员函数当作普通C函数声明指针,既没匹配
__fastcall调用约定,也没对齐返回值类型(目标函数返回_int64,代码里声明为int)。 - 偏移量计算错误:直接用函数指针做整数减法,会按指针类型的字节宽度自动缩放,而非按原始字节偏移计算,导致目标地址偏差。
- 调用约定冲突:
__fastcall在x86平台通常用ECX/EDX传递前两个参数,普通函数指针调用会破坏这个约定,导致参数传递混乱。
修复步骤
- 修正函数指针类型:严格匹配目标函数的返回值、调用约定和参数列表,用
typedef明确类型:
typedef _int64 (__fastcall *DebugCmdHandler)(gplayer_controller* const, int, const void*, size_t);
若不想依赖类定义,也需保证调用约定和参数数量、类型完全对齐。
- 正确计算字节偏移:偏移量是字节数,需先将导出函数指针转为
char*做减法,再转回目标函数指针类型:
// 获取导出函数的字节地址 char* lua_pushboolean_addr = reinterpret_cast<char*>(dlsym(handle, "lua_pushboolean")); // 按字节偏移计算目标函数地址 char* target_func_addr = lua_pushboolean_addr - 5638326; // 转回正确的函数指针 DebugCmdHandler fptr2 = reinterpret_cast<DebugCmdHandler>(target_func_addr);
确保调用约定匹配:声明函数指针时必须带上
__fastcall(MSVC)或__attribute__((fastcall))(GCC),保证参数传递符合目标程序的编译约定。验证this指针类型:确认
pImp->_commander确实是gplayer_controller*类型,类型不匹配会直接导致内存访问错误。
修复后的示例代码
typedef _int64 (__fastcall *DebugCmdHandler)(gplayer_controller* const, int, const void*, size_t); void *handle = dlopen(NULL, RTLD_LOCAL | RTLD_LAZY); if(!handle) { printf("dlopen error\n"); return; } void* lua_pushboolean_ptr = dlsym(handle, "lua_pushboolean"); if(!lua_pushboolean_ptr) { printf("dlsym error\n"); dlclose(handle); return; } printf("lua_pushboolean ptr: %p\n", lua_pushboolean_ptr); gobject_imp *pImp = (gobject_imp*)skill->GetPlayer()->GetObject().GetImpl(); // 按字节偏移计算目标函数地址 char* target_addr = reinterpret_cast<char*>(lua_pushboolean_ptr) - 5638326; DebugCmdHandler fptr2 = reinterpret_cast<DebugCmdHandler>(target_addr); printf("target func ptr: %p\n", fptr2); mma _mma; _mma.cmd = 2040; _mma.skillid = 15000; _mma.level = skill->GetLevel() + 1; printf("this ptr: %p\n", pImp->_commander); // 调用时第一个参数即为this指针 _int64 result = fptr2(reinterpret_cast<gplayer_controller*>(pImp->_commander), 2040, &_mma, sizeof(_mma));
额外注意事项
- 偏移量会随程序版本、编译选项变化而改变,这种方式仅适合临时调试或逆向场景,稳定性极低。
- 不同编译器对
__fastcall的实现有差异,需确认目标程序的编译环境,必要时替换为对应调用约定。 - 若
RTLD_LOCAL导致符号无法访问,可尝试替换为RTLD_GLOBAL。
内容的提问来源于stack exchange,提问作者Alvaro Hernandorena
相关产品推荐
相关产品推荐

