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

二进制炸弹phase_2栈顶存输入原因及汇编逻辑疑问

二进制炸弹phase_2汇编逻辑答疑

首先先纠正一处前期理解偏差:你对mov QWORD PTR [rsp+0x18],rax指令的解读有误。此时rax里存的是刚从fs:0x28读取的栈金丝雀值,和read_line的返回值没有关系——read_line返回的输入字符串首地址在main函数里已经被存入rdi,作为参数传给phase_2,进入phase_2后rax第一次被赋值就是读取栈金丝雀,这条指令的作用就是把金丝雀值存到栈上预留的保护位置,用于函数返回前的栈溢出校验。


核心疑问解答:为什么栈顶位置会存放输入内容?

这是C语言函数局部变量的常规栈布局导致的,逻辑链路非常清晰:

  1. phase_2执行完函数序言后,通过sub rsp,0x28在栈上预留了40字节的空间,其中低地址侧的24字节(0x18字节)刚好可以存下6个4字节长度的int类型值,高地址8字节存栈金丝雀。
  2. 调用read_six_numbers前执行的mov rsi,rsp,是按照System V AMD64调用约定,把栈顶地址作为第二个参数传给read_six_numbers。
  3. 进入read_six_numbers后可以看到,它基于传入的rsi地址,依次计算了6个连续偏移的地址:
    mov    rdx,rsi          ; 第1个数字的存储地址 = rsi+0x0,作为sscanf第3个参数
    lea    rcx,[rsi+0x4]    ; 第2个数字的存储地址 = rsi+0x4,作为sscanf第4个参数
    lea    r9,[rsi+0xc]     ; 第4个数字的存储地址 = rsi+0xc,作为sscanf第5个参数
    lea    r8,[rsi+0x8]     ; 第3个数字的存储地址 = rsi+0x8,作为sscanf第6个参数
    push   rax              ; rax = rsi+0x14,第6个数字的存储地址,压栈作为sscanf第8个参数
    push   rax              ; rax = rsi+0x10,第5个数字的存储地址,压栈作为sscanf第7个参数
    lea    rsi,[rip+0x16ca] ; rsi改为指向"%d %d %d %d %d %d"格式串,作为sscanf第2个参数
    
    此时rdi仍然是phase_2收到的第一个参数:read_line返回的用户输入字符串首地址,也就是sscanf的第一个参数。
  4. 调用sscanf时,函数会按照格式串解析输入字符串,把解析出的6个整数依次写入传入的6个地址,也就是从phase_2栈顶开始的连续24字节栈空间。这本质就是C代码里int nums[6]; sscanf(input, "%d %d %d %d %d %d", &nums[0], &nums[1]...&nums[5]);的汇编实现,栈顶就是局部数组nums的起始地址,自然会存放解析后的输入内容。

其他逐点疑问解答

  • mov rbx,rsp把栈顶存入rbx的设计目的?
    rbx是System V约定中的被调用者保存寄存器,这里被用作数组遍历的游标指针,指向当前校验环节的基准元素。遍历数组时需要一个可以反复修改递增的指针,用rbx存储不需要频繁读写rsp,符合寄存器使用规范,也能避免后续栈操作(比如read_six_numbers里的push指令)修改rsp带来的地址计算错误。
  • lea rbp,[rsp+0x14]的作用?和栈金丝雀有关吗?
    这个地址就是6个int元素数组的末尾边界:6个int总长度为24字节(0x18),索引从0到5,最后一个元素的地址就是rsp + 5*4 = rsp+0x14,和存在rsp+0x18的栈金丝雀没有关系——金丝雀刚好放在数组后面的高地址位置,是编译器栈保护机制的常规布局,一旦数组发生越界写入会先覆盖金丝雀,返回前校验失败就会直接终止程序。
    选择rbp存储这个边界地址,同样是因为rbp是被调用者保存寄存器,适合存储整个函数生命周期内不变的常量值,作为循环终止的判断依据。
  • 循环中为什么比较[rbx+0x4](即array[i+1])和翻倍后的基准值?rbx的作用是什么?
    rbx始终指向当前校验的基准元素,整个循环的校验逻辑是后一个元素必须等于前一个元素的2倍:
    1. 初始时已经校验过第一个元素[rsp]必须为1,rbx初始指向数组首地址(第一个元素)
    2. 读取rbx指向的当前元素值,翻倍后和rbx后面紧邻的元素(rbx+0x4)比较,如果不相等直接引爆炸弹
    3. 校验通过后rbx加4,移到下一个元素位置,直到rbx等于rbp(指向最后一个元素),说明所有元素校验完成,循环结束。
      按照这个规则推导出的正确输入序列为:1 2 4 8 16 32。

内容的提问来源于stack exchange,提问作者Happy Jerry

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 22:01:06