循环中call *(%rax, %rcx,8)的含义及RAX/RCX变化原因咨询
函数指针循环调用的汇编分析与无断点调试方案
汇编代码逻辑拆解
这段x86-64汇编的核心功能是遍历名为init_functions的函数指针数组,依次调用每个非空指针指向的函数,直到遇到NULL指针停止(你提到循环11次,说明数组前11个是有效函数指针,第12个为NULL)。逐行关键逻辑解析:
- 栈帧初始化:
push %rbp; mov %rsp,%rbp; sub $0x10,%rsp—— 创建栈帧,分配16字节局部栈空间。 - 循环计数器初始化:
movl $0x0,-0x4(%rbp)—— 栈上-0x4位置存储int类型的循环索引i,初始值为0。 - 循环判断逻辑:
movslq -0x4(%rbp),%rcx—— 将32位的i扩展为64位,存入RCX(适配x86-64的地址宽度)。lea 0x7406(%rip),%rax—— 通过RIP相对寻址计算init_functions数组的基地址,存入RAX(注释已给出数组地址为0x55555555e2a0)。cmpq $0x0,(%rax,%rcx,8)—— 检查数组第i个元素(每个函数指针占8字节,地址为RAX + RCX*8)是否为NULL,是则跳转到循环结束。
- 函数调用逻辑:
若指针非空,重新加载RCX和RAX(汇编冗余操作,不影响逻辑),执行call *(%rax,%rcx,8)—— 间接调用该函数指针指向的函数。 - 计数器自增:
mov -0x4(%rbp),%eax; add $0x1,%eax; mov %eax,-0x4(%rbp)——i加1,跳回循环判断。
关于call *(%rax,%rcx,8)的寄存器变化问题
你观察到RAX和RCX在调用后变化,原因如下:
- RAX的变化:x86-64 System V调用约定中,RAX是函数返回值寄存器。被调用的函数执行完成后,会将返回值写入RAX,因此调用后RAX的值会被覆盖为函数返回结果,而非之前的数组基地址。
- RCX的变化:RCX是调用约定中的第4个参数寄存器,如果被调用的函数需要接收4个及以上参数,或者函数内部主动修改了RCX寄存器,都会导致其值变化。不过你的循环逻辑中,每次进入循环判断时都会重新从栈上的
i加载RCX,因此下一次循环时RCX会被重置为当前索引值,不会影响循环的正常执行。
无法设置断点的解决思路
如果无法使用调试器断点,可通过以下方式分析:
- 静态解析函数指针数组:用
objdump -s或readelf工具读取二进制文件中init_functions数组的内容,获取所有有效函数指针的地址,再对这些地址对应的代码进行反汇编(objdump -d),直接分析每个函数的逻辑。 - 静态分析函数行为:使用逆向工程工具(如Ghidra、IDA Pro的静态模式)加载二进制文件,定位到每个被调用函数的汇编代码,分析其对寄存器的修改、执行的操作。
- 二进制插桩追踪:用Frida等动态插桩工具,在
call *(%rax,%rcx,8)指令前后注入代码,打印调用前后RAX、RCX的值以及函数指针地址,记录每次调用的状态变化。 - 修改二进制验证逻辑:将
call指令替换为nop(空指令),运行程序观察行为变化,反向推断原函数的作用(需注意备份原二进制文件)。
内容的提问来源于stack exchange,提问作者LuX
相关产品推荐
相关产品推荐

