C++如何将字面量绝对内存地址转换为函数指针并调用函数
绝对内存地址转换为可调用函数的实现说明
核心结论
理论上语法层面支持该操作,但实际运行能否成功有极多前置约束,和读取绝对地址字符串的场景差异极大。
你提到的读取固定地址字符串的操作,本质是对当前进程虚拟地址空间内、具备可读权限的已映射内存做读操作,约束很少;但跳转到固定地址执行函数属于控制流跳转,要满足的条件严格得多。
具体语法实现步骤
假设你明确知道目标地址对应的函数签名,语法上的实现只需要三步:
- 第一步:定义完全匹配的函数指针类型
必须100%匹配目标函数的返回值类型、参数列表、调用约定,任何一点不匹配都会触发未定义行为,轻则参数错乱、返回值错误,重则栈损坏直接崩溃。比如你提到的print函数如果签名是接收一个const char*参数、无返回值,先定义对应类型:// 函数指针类型定义,格式:返回值(*类型名)(参数列表) using PrintFunc = void(*)(const char*); - 第二步:地址转换为函数指针
固定整数值地址要先保证宽度和当前系统指针宽度一致(64位系统不要用32位int存地址,会截断),再用reinterpret_cast做强制转换:#include <cstdint> // 用uintptr_t存地址,保证和指针宽度一致 const uintptr_t target_addr = 0x8a537bb543d52a11; PrintFunc print = reinterpret_cast<PrintFunc>(target_addr); - 第三步:合法校验后调用
确认地址指向的内存是有效函数指令、所在内存页有可执行权限后,就可以和普通函数一样调用:print("test call");
为什么硬编码固定地址大概率跑不通
语法上能写不代表运行能成功,绝大多数场景下这种硬编码固定地址的写法都会直接崩溃,核心限制有几个:
- 虚拟地址空间隔离:现代操作系统每个进程都有独立的虚拟地址空间,如果你拿到的
0x8a537bb543d52a11是其他进程里的print函数地址,在你的程序进程里这个地址要么根本没映射到物理内存,要么存的是完全无关的数据,跳转过去直接触发段错误。只有这个地址属于当前运行进程自身地址空间内的有效函数地址时,才有执行的可能。 - ASLR地址随机化:主流操作系统默认开启地址空间布局随机化,程序每次启动时,代码段、堆、栈的加载基址都会随机变化,你这次运行拿到的函数地址,下次启动就会失效,硬编码固定值没有实际意义。
- 内存权限保护:现代CPU和操作系统都支持数据执行保护(DEP/NX),如果目标地址所在的内存页没有标记可执行权限,CPU直接拒绝执行指令,抛出异常终止程序。
- 签名不匹配风险:哪怕地址是对的,只要函数指针的参数个数、类型、返回值、调用约定和实际函数不一致,就会出现栈不平衡、寄存器传参错误等问题,属于C++标准定义的未定义行为,没有任何稳定运行的保证。
给初学者的提示:这种写法属于底层hack手段,只在逆向工程、热补丁、内核开发这类特殊场景使用,常规业务开发永远不要硬编码函数固定地址,正常的函数地址获取应该通过链接符号、动态库导出接口等方式拿到,不需要手动写整数值地址。
内容的提问来源于stack exchange,提问作者Mini-Yip
相关产品推荐
相关产品推荐

