商城首页欢迎来到中国正版软件门户

您的位置: 首页 > 文章列表 > 编程开发 > C++ 获取类成员函数指针内存入口地址底层指针技巧实战分析【实战】

C++ 获取类成员函数指针内存入口地址底层指针技巧实战分析【实战】

  发布于2026-07-03 阅读(0)

扫一扫,手机访问

---

在C++里折腾过成员函数指针的人,大概都遇到过这样一个问题:明明是想拿到函数在内存里的真实入口地址,结果却总是一堆看不懂的偏移、索引或者标志位。这玩意儿跟普通函数指针完全是两码事——因为虚函数表、this指针调整、多重继承这些机制的存在,成员函数指针往往不止一个字段。那么,有没有什么办法能绕过这些“包装”,直接看到底层的真实地址?下面这几种方法,都是经过实战验证的。

C++ 获取类成员函数指针内存入口地址底层指针技巧实战分析【实战】

一、通过联合体(union)逐字节解析成员函数指针结构

多数编译器(比如MSVC、GCC)在实现成员函数指针时,用的都是一个多字节的内部结构。我们可以用一个联合体把它映射成整数数组,从而直接拿到原始字节。

具体来说:先定义一个联合体,里面同时放一个成员函数指针类型,和一个长度相当的size_t数组。然后把目标函数指针赋给联合体的对应字段。读那个数组的首元素或者全部元素——注意,这个值未必是真正的代码地址,它可能是偏移、虚表索引,或者是标志位的组合。比如在x86_64 Linux GCC的环境下,非虚成员函数指针通常显示为一个8字节的值,但如果是带虚函数调用的指针,你看到的可能就是0x1或者0xffffffffffffffff这样的特殊标记。

二、借助汇编内联强制转换为void*并检查指令流

这个方法更直接:用内联汇编跳过类型系统的限制,把成员函数指针强行解释成可执行地址,然后看看那里到底是不是有效的函数入口。

步骤也很简单:声明一个成员函数指针,确保它绑定到了一个非虚的、静态绑定的函数上。然后用reinterpret_cast转成整数,再转成void*。需要提醒的是,这个操作在标准C++里是未定义行为,只适合调试或者逆向分析场景。为了不让编译器优化掉地址读取,可以在GCC里插一句asm volatile ("" ::: "rax")。最后用hexdump或者调试器,看看那个地址的前16字节指令长什么样——如果首字节是0x55push %rbp)或者0x48(Rex.W前缀),那大概率就是真正的函数入口了

三、利用虚函数表(vtable)手动索引定位虚函数地址

对于虚成员函数,真正的地址其实藏在对象的虚函数表里。我们可以通过对象首地址偏移拿到vtable指针,然后按声明顺序索引。

具体做法:先创建一个类的实例,通常第一个成员就是虚函数表指针(在对象内存布局的最开头)。用reinterpret_cast(obj_ptr)[0]拿到vtable首地址。然后根据虚函数在类里的声明顺序(从0开始),读取vtable_ptr[index]里存的地址,这个地址就是虚函数的真实入口,可以直接安全地转成void*。不过要注意,如果涉及多重继承,子对象的偏移可能会导致vtable指针不在对象起始位置,这时最好结合clang -cc1 -fdump-record-layouts的输出确认一下内存布局

四、使用std::memcpy逐字节提取并跨ABI比对

最后一个方法比较“暴力”:直接绕过编译器的类型保护,用内存拷贝把成员函数指针的原始字节序列导出来,这样就能跨平台、跨编译器地分析它的编码规律了。

先声明一个std::array raw_bytes用来接收数据,然后调用std::memcpy(raw_bytes.data(), &mfptr, sizeof(mfptr))复制。接下来逐字节打印raw_bytes的内容——在MSVC x64下,非虚函数通常显示为[addr_lo, addr_hi, 0x00, 0x00, ...];而GCC可能会填充0xcc0x00。如果把同一个函数在不同编译器下生成的字节序列拿来对比,一旦发现第0到7字节全是零,那就说明这个指针指向的是虚函数,得去查vtable才能找到真正的地址

本文转载于:https://www.php.cn/faq/2458127.html 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。

热门关注