You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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);

问题分析与解决

核心问题点

  1. 函数类型不匹配:将非静态成员函数当作普通C函数声明指针,既没匹配__fastcall调用约定,也没对齐返回值类型(目标函数返回_int64,代码里声明为int)。
  2. 偏移量计算错误:直接用函数指针做整数减法,会按指针类型的字节宽度自动缩放,而非按原始字节偏移计算,导致目标地址偏差。
  3. 调用约定冲突:__fastcall在x86平台通常用ECX/EDX传递前两个参数,普通函数指针调用会破坏这个约定,导致参数传递混乱。

修复步骤

  1. 修正函数指针类型:严格匹配目标函数的返回值、调用约定和参数列表,用typedef明确类型:
typedef _int64 (__fastcall *DebugCmdHandler)(gplayer_controller* const, int, const void*, size_t);

若不想依赖类定义,也需保证调用约定和参数数量、类型完全对齐。

  1. 正确计算字节偏移:偏移量是字节数,需先将导出函数指针转为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);
  1. 确保调用约定匹配:声明函数指针时必须带上__fastcall(MSVC)或__attribute__((fastcall))(GCC),保证参数传递符合目标程序的编译约定。

  2. 验证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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 08:15:34