如何在无法修改a.cpp时获取命名空间内静态变量mysecret的地址?
首先明确你的场景:我们有两个文件,a.cpp里的secret命名空间包含一个静态变量mysecret,_main函数先通过cin读取这个变量的值,再调用hack(),要求hack()返回的值必须和mysecret一致,且完全不能修改a.cpp。初始的b.cpp里hack()只是固定返回31,显然没法适配任意输入的mysecret,所以核心目标是拿到这个静态变量的实际值。
先贴出你的代码方便参考:
原文件代码
a.cpp
#include <iostream> #include <cassert> using namespace std; int hack(); typedef int (*hackFunction)(); namespace secret { static int mysecret; int _main(hackFunction hack) { cin >> mysecret; assert(hack() == mysecret); return 0; } } int main() { return secret::_main(hack); }
初始b.cpp
int hack() { return 31; }
你提到反汇编后发现mysecret通过0x0(%%rip)寻址,这说明在x86-64架构下,这个静态变量的地址是用RIP相对偏移计算的——核心思路就是在hack()里拿到合适的RIP值,结合偏移算出mysecret的地址,最后取值返回即可。下面是几个可行的方案:
方案1:用GCC内置函数获取返回地址计算
GCC提供了内置函数__builtin_return_address(0),它能获取当前函数的返回地址——也就是调用hack()的那条指令的下一条指令的RIP值。你只需要根据反汇编结果,算出mysecret相对于这个返回地址的偏移,就能直接计算出变量地址。
举个例子,假设反汇编后发现mysecret的地址相对于hack()返回地址的偏移是-0x10(实际值需要你根据自己的反汇编结果调整),代码可以这么写:
#include <cstdint> int hack() { // 获取hack()的返回地址,即secret::_main中调用hack后的下一条指令地址 uintptr_t return_rip = reinterpret_cast<uintptr_t>(__builtin_return_address(0)); // 根据实际偏移计算mysecret的地址 uintptr_t mysecret_addr = return_rip - 0x10; // 取值并返回 return *reinterpret_cast<int*>(mysecret_addr); }
方案2:直接用汇编获取当前RIP计算
x86-64架构中,可以通过lea rax, [rip]直接获取当前指令的RIP值,结合已知的偏移就能算出mysecret的地址。这种方法更直接,不依赖编译器内置函数,在x86-64的GCC/clang环境下都能使用。
代码示例如下,记得替换成你实际反汇编得到的偏移值:
int hack() { int* mysecret_ptr; __asm__ volatile ( "lea rax, [rip + %1]\n" // 用当前RIP加上偏移得到mysecret的地址 "mov %0, rax\n" : "=r"(mysecret_ptr) // 输出变量,将计算好的地址存入mysecret_ptr : "i"(-0x10) // 填入实际偏移值,需根据反汇编结果调整 : "rax" // 告知编译器rax寄存器会被修改,避免优化冲突 ); return *mysecret_ptr; }
方案3:静态编译时利用ELF符号偏移(适合固定环境)
如果程序是静态编译的,mysecret作为静态变量会在ELF文件的.data或.bss段有固定偏移地址。你可以先用readelf或objdump工具分析编译后的可执行文件,找到secret::mysecret的偏移,再在运行时计算程序基地址加上该偏移,就能拿到变量地址。
不过这个方法的局限性是,一旦编译环境(如编译器版本、优化选项)改变,偏移值可能会变化,更适合固定环境下使用。
关键注意事项
- 偏移值必须匹配实际反汇编结果:你提到
mysecret在0x0(%%rip),这应该是指访问mysecret的那条汇编指令中,RIP加上0就是变量地址。你需要找到这条指令的地址与hack()调用点地址的差值,才能算出正确的偏移。 - 注意编译器优化级别:不同的优化选项(如-O0和-O2)会改变指令布局,偏移值也会随之变化,一定要在实际使用的编译环境下反汇编获取偏移。
内容的提问来源于stack exchange,提问作者Alexander Morozov

