CSAPP(CMU 15-213):Lab2 BombLab详解

# 前言

本系列文章意在记录答主学习CSAPP Lab的过程,也旨在可以帮助后人一二,欢迎大家指正!


​ 使用 gdb 中的反汇编代码与 objdump 反编译出的汇编代码相比更容易阅读(地址跳跃等),并且在解迷的过程中需要用到 gdb ,故以下代码都使用 gdb 的反汇编。

phase_1(小试牛刀)

# phase_1 反汇编
   0x0000000000400ee0 <+0>:	    sub    $0x8,%rsp
   0x0000000000400ee4 <+4>:	    mov    $0x402400,%esi
   0x0000000000400ee9 <+9>:	    callq  0x401338 <strings_not_equal>
   0x0000000000400eee <+14>:	test   %eax,%eax
   0x0000000000400ef0 <+16>:	je     0x400ef7 <phase_1+23>
   0x0000000000400ef2 <+18>:	callq  0x40143a <explode_bomb>
   0x0000000000400ef7 <+23>:	add    $0x8,%rsp
   0x0000000000400efb <+27>:	retq   

​ 通过对以上代码简单的分析,这个 phase_1 就是一个热身的题目,它的作用是通过函数 strings_not_equal 对比输入的字符串(其地址存储在%rdi)与目的字符串(其地址为0x402400)是否相同,若相同,则返回0,即解除 phase_1。
​ 至于为何寄存器中存放的是地址请看下方关于 strings_not_equal 函数的分析哦~而且换句话说,实际上不分析这个函数也大概可以想出来的,代码中的字符串属于常量,一般会存放在内存空间的data区域,所以寄存器中存放的是地址。

# strings_not_equal 反汇编
   0x0000000000401338 <+0>:	    push   %r12
   0x000000000040133a <+2>:	    push   %rbp
   0x000000000040133b <+3>:	    push   %rbx
   0x000000000040133c <+4>:	    mov    %rdi,%rbx
   0x000000000040133f <+7>:	    mov    %rsi,%rbp
   0x0000000000401342 <+10>:	callq  0x40131b <string_length>
   0x0000000000401347 <+15>:	mov    %eax,%r12d
   0x000000000040134a <+18>:	mov    %rbp,%rdi
   0x000000000040134d <+21>:	callq  0x40131b <string_length>
   0x0000000000401352 <+26>:	mov    $0x1,%edx
   0x0000000000401357 <+31>:	cmp    %eax,%r12d

​ 对于 strings_not_equal 函数来说,我们无需全部分析,只需分析以上前部代码即可得知此函数的参数是两个分别放入 %rdi 和 %rsi 的字符串地址。

通过以上的分析可以得知,目标字符串就是地址0x402400所指的字符串哦~

(gdb) x/s 0x402400
0x402400:	"Border relations with Canada have never been better."

phase_2(for 循环)

# phase_2 反汇编(1)
   0x0000000000400efc <+0>:	    push   %rbp
   0x0000000000400efd <+1>:	    push   %rbx
   0x0000000000400efe <+2>:	    sub    $0x28,%rsp
   0x0000000000400f02 <+6>:	    mov    %rsp,%rsi
   0x0000000000400f05 <+9>:	    callq  0x40145c <read_six_numbers>

以上就是做了一些基本操作,主要分析下read_six_numbers函数,不过见名知义,先可以猜测这个字符串就是6个数字哈哈哈

# read_six_numbers 反汇编
   0x000000000040145c <+0>:	    sub    $0x18,%rsp
   0x0000000000401460 <+4>:	    mov    %rsi,%rdx
   0x0000000000401463 <+7>:	    lea    0x4(%rsi),%rcx
   0x0000000000401467 <+11>:	lea    0x14(%rsi),%rax
   0x000000000040146b <+15>:	mov    %rax,0x8(%rsp)
   0x0000000000401470 <+20>:	lea    0x10(%rsi),%rax
   0x0000000000401474 <+24>:	mov    %rax,(%rsp)
   0x0000000000401478 <+28>:	lea    0xc(%rsi),%r9
   0x000000000040147c <+32>:	lea    0x8(%rsi),%r8
   0x0000000000401480 <+36>:	mov    $0x4025c3,%esi
   0x0000000000401485 <+41>:	mov    $0x0,%eax
   0x000000000040148a <+46>:	callq  0x400bf0 <__isoc99_sscanf@plt>
   0x000000000040148f <+51>:	cmp    $0x5,%eax
   0x0000000000401492 <+54>:	jg     0x401499 <read_six_numbers+61>
   0x0000000000401494 <+56>:	callq  0x40143a <explode_bomb>
   0x0000000000401499 <+61>:	add    $0x18,%rsp
   0x000000000040149d <+65>:	retq   

对于这个sscanf函数,总共有8个参数,第一个参数(输入字符串的地址)放入%edi,第二个格式字符串的地址在%esi,然后就是剩余6个数字的存放地址(%rdx, %rcx, %r8, %r9以及read_six_numbers函数的栈桢中)

tips:注意这边在栈桢中参数是倒序存放哦~
# %esi中格式字符串
(gdb) x/s 0x4025c3
0x4025c3:	"%d %d %d %d %d %d"

​ 这个格式字符串的内容进一步验证输入是6个数字哦~
​ 下面是这两个函数的栈桢,仅供参考~

退出read_six_numbers函数之后可以打印栈桢信息验证一下哦~

# 栈桢
(gdb) info frame
Stack level 0, frame at 0x7fffffffe1b0:
 rip = 0x400f0a in phase_2; saved rip = 0x400e5b
 called by frame at 0x7fffffffe1c0
 Arglist at 0x7fffffffe170, args: 
 Locals at 0x7fffffffe170, Previous frame's sp is 0x7fffffffe1b0
 Saved registers:
  rbx at 0x7fffffffe198, rbp at 0x7fffffffe1a0, rip at 0x7fffffffe1a8

#phase_2栈桢中存放的6个数字   ,输入为 1 4 8 3 6 5
(gdb) x/3gx $rsp
0x7fffffffe170:	0x0000000400000001	0x0000000300000008
0x7fffffffe180:	0x0000000500000006

接下来通过<+14>说明x1的值为1,下面的代码是一个for循环,验证每一个x都是前一个x的两倍,故最终答案为 “1 2 4 8 16 32”

# phase_2 反汇编(2)

   0x0000000000400f0a <+14>:	cmpl   $0x1,(%rsp)
   0x0000000000400f0e <+18>:	je     0x400f30 <phase_2+52>
   0x0000000000400f10 <+20>:	callq  0x40143a <explode_bomb>
   0x0000000000400f15 <+25>:	jmp    0x400f30 <phase_2+52>
   0x0000000000400f17 <+27>:	mov    -0x4(%rbx),%eax
   0x0000000000400f1a <+30>:	add    %eax,%eax
   0x0000000000400f1c <+32>:	cmp    %eax,(%rbx)
   0x0000000000400f1e <+34>:	je     0x400f25 <phase_2+41>
   0x0000000000400f20 <+36>:	callq  0x40143a <explode_bomb>
   0x0000000000400f25 <+41>:	add    $0x4,%rbx
   0x0000000000400f29 <+45>:	cmp    %rbp,%rbx
   0x0000000000400f2c <+48>:	jne    0x400f17 <phase_2+27>
   0x0000000000400f2e <+50>:	jmp    0x400f3c <phase_2+64>
   0x0000000000400f30 <+52>:	lea    0x4(%rsp),%rbx
   0x0000000000400f35 <+57>:	lea    0x18(%rsp),%rbp
   0x0000000000400f3a <+62>:	jmp    0x400f17 <phase_2+27>
   0x0000000000400f3c <+64>:	add    $0x28,%rsp
   0x0000000000400f40 <+68>:	pop    %rbx
   0x0000000000400f41 <+69>:	pop    %rbp
   0x0000000000400f42 <+70>:	retq   

phase_3(switch 语句)

# phase_3 反汇编(1)
   0x0000000000400f43 <+0>:	    sub    $0x18,%rsp
   0x0000000000400f47 <+4>:	    lea    0xc(%rsp),%rcx        # x_2
   0x0000000000400f4c <+9>:	    lea    0x8(%rsp),%rdx        # x_1
   0x0000000000400f51 <+14>:	mov    $0x4025cf,%esi        # 0x4025cf is "%d %d"
   0x0000000000400f56 <+19>:	mov    $0x0,%eax
   0x0000000000400f5b <+24>:	callq  0x400bf0 <__isoc99_sscanf@plt>

​ 此时这个函数调用了 sscanf 函数,查看一下地址 0x4025cf 处的字符串,可以得到最初的信息,这个密码为两个数字组成。

(gdb) x /s 0x4025cf
0x4025cf:       "%d %d"

​ 并且由于 %rdx%rcx 存放第三四个参数,所以可以推测两个数字分别存放在这两个栈中的位置,此处就不再验证。

# phase_3 反汇编(2)
   0x0000000000400f60 <+29>:	cmp    $0x1,%eax
   0x0000000000400f63 <+32>:	jg     0x400f6a <phase_3+39>
   0x0000000000400f65 <+34>:	callq  0x40143a <explode_bomb>
   0x0000000000400f6a <+39>:	cmpl   $0x7,0x8(%rsp)
   0x0000000000400f6f <+44>:	ja     0x400fad <phase_3+106>          # ja  超过(无符号 >)
   0x0000000000400f71 <+46>:	mov    0x8(%rsp),%eax
   0x0000000000400f75 <+50>:	jmpq   *0x402470(,%rax,8)              # 跳转表地址跳转
   # begin  switch table
   0x0000000000400f7c <+57>:	mov    $0xcf,%eax
   0x0000000000400f81 <+62>:	jmp    0x400fbe <phase_3+123>
   0x0000000000400f83 <+64>:	mov    $0x2c3,%eax
   0x0000000000400f88 <+69>:	jmp    0x400fbe <phase_3+123>
   0x0000000000400f8a <+71>:	mov    $0x100,%eax
   0x0000000000400f8f <+76>:	jmp    0x400fbe <phase_3+123>
   0x0000000000400f91 <+78>:	mov    $0x185,%eax
   0x0000000000400f96 <+83>:	jmp    0x400fbe <phase_3+123>
   0x0000000000400f98 <+85>:	mov    $0xce,%eax
   0x0000000000400f9d <+90>:	jmp    0x400fbe <phase_3+123>
   0x0000000000400f9f <+92>:	mov    $0x2aa,%eax
   0x0000000000400fa4 <+97>:	jmp    0x400fbe <phase_3+123>
   0x0000000000400fa6 <+99>:	mov    $0x147,%eax
   0x0000000000400fab <+104>:	jmp    0x400fbe <phase_3+123>
   0x0000000000400fad <+106>:	callq  0x40143a <explode_bomb>
   0x0000000000400fb2 <+111>:	mov    $0x0,%eax
   0x0000000000400fb7 <+116>:	jmp    0x400fbe <phase_3+123>
   0x0000000000400fb9 <+118>:	mov    $0x137,%eax
   # end  switch table
   0x0000000000400fbe <+123>:	cmp    0xc(%rsp),%eax
   0x0000000000400fc2 <+127>:	je     0x400fc9 <phase_3+134>
   0x0000000000400fc4 <+129>:	callq  0x40143a <explode_bomb>
   0x0000000000400fc9 <+134>:	add    $0x18,%rsp
   0x0000000000400fcd <+138>:	retq   

​ 继续观察以上代码,会发现此处是一个 switch 语句,其跳转表存放在 0x402470 处,根据语句 jmpq *0x402470(,%rax,8) 可以知道其跳转表范围由 %rax 决定,而由前面的判断语句可知,其值 x 1 x_1 x1应小于等于7,即 0 ≤ x 1 ≤ 7 0 \le x_1 \le 7 0x17 。所以我们打印跳转表处的数据。

(gdb) x /8gx 0x402470
0x402470:       0x0000000000400f7c      0x0000000000400fb9
0x402480:       0x0000000000400f83      0x0000000000400f8a
0x402490:       0x0000000000400f91      0x0000000000400f98
0x4024a0:       0x0000000000400f9f      0x0000000000400fa6

​ 通关条件为跳转处所得的 %eax 看要与 x 2 x_2 x2 相同,即我们所需的答案 x 1 x_1 x1 x 2 x_2 x2 是有关系的。

​ 若 x 1 = 0 x_1 = 0 x1=0, 则跳转至0x0000000000400f7c,其要求 x 2 x_2 x2 要为0xcf = 207。这只是其中的一个答案,其他几个答案也都可以通过哦~

phase_4(递归函数(简单))

# phase_4 反汇编(1)
   0x000000000040100c <+0>:     sub    $0x18,%rsp
   0x0000000000401010 <+4>:     lea    0xc(%rsp),%rcx       # x_2
   0x0000000000401015 <+9>:     lea    0x8(%rsp),%rdx       # x_1
   0x000000000040101a <+14>:    mov    $0x4025cf,%esi       # 0x4025cf is "%d %d"
   0x000000000040101f <+19>:    mov    $0x0,%eax
   0x0000000000401024 <+24>:    callq  0x400bf0 <__isoc99_sscanf@plt>
   0x0000000000401029 <+29>:    cmp    $0x2,%eax
   0x000000000040102c <+32>:    jne    0x401035 <phase_4+41>         # jne  不相等/非零
   0x000000000040102e <+34>:    cmpl   $0xe,0x8(%rsp)                # 0x8(%rsp)(x_1) : 0xe
   0x0000000000401033 <+39>:    jbe    0x40103a <phase_4+46>         # jbe   低于或相等(无符号<=)
   0x0000000000401035 <+41>:    callq  0x40143a <explode_bomb>
   0x000000000040103a <+46>:    mov    $0xe,%edx                 # third argument
   0x000000000040103f <+51>:    mov    $0x0,%esi                 # second argument
   0x0000000000401044 <+56>:    mov    0x8(%rsp),%edi            # first argument   x_1
   0x0000000000401048 <+60>:    callq  0x400fce <func4>

​ 由以上的信息可知: x 1 ≤ 0 x e = 14 x_1 \leq 0xe = 14 x10xe=14,以及一些赋值参数的信息。

# phase_4反汇编(2)
   0x000000000040104d <+65>:    test   %eax,%eax
   0x000000000040104f <+67>:    jne    0x401058 <phase_4+76>
   0x0000000000401051 <+69>:    cmpl   $0x0,0xc(%rsp)           #  X_2 = 0
   0x0000000000401056 <+74>:    je     0x40105d <phase_4+81>
   0x0000000000401058 <+76>:    callq  0x40143a <explode_bomb>
   0x000000000040105d <+81>:    add    $0x18,%rsp
   0x0000000000401061 <+85>:    retq   

​ 先跳过 func4 看一下后面的代码。可以推断出我们需要 func4 的返回值为0,并且 x 2 = 0 x_2 = 0 x2=0

​ 我们使用(gdb) layout regs 来显示寄存器的窗口。方便检查。

# func4 反汇编
   0x0000000000400fce <+0>:     sub    $0x8,%rsp
   0x0000000000400fd2 <+4>:     mov    %edx,%eax     #  0xe
   0x0000000000400fd4 <+6>:     sub    %esi,%eax
   0x0000000000400fd6 <+8>:     mov    %eax,%ecx
   0x0000000000400fd8 <+10>:    shr    $0x1f,%ecx    # shr 逻辑右移31位
   0x0000000000400fdb <+13>:    add    %ecx,%eax
   0x0000000000400fdd <+15>:    sar    %eax         # sar 算术右移   (%eax + %eax >>L 31) >>A 1  = 0x7 = %eax
   0x0000000000400fdf <+17>:    lea    (%rax,%rsi,1),%ecx
   0x0000000000400fe2 <+20>:    cmp    %edi,%ecx           #  %edi is x_1
   0x0000000000400fe4 <+22>:    jle    0x400ff2 <func4+36>
   0x0000000000400fe6 <+24>:    lea    -0x1(%rcx),%edx
   0x0000000000400fe9 <+27>:    callq  0x400fce <func4>
   0x0000000000400fee <+32>:    add    %eax,%eax
   0x0000000000400ff0 <+34>:    jmp    0x401007 <func4+57>   # 出口
   0x0000000000400ff2 <+36>:    mov    $0x0,%eax
   0x0000000000400ff7 <+41>:    cmp    %edi,%ecx
   0x0000000000400ff9 <+43>:    jge    0x401007 <func4+57>   # 出口
   0x0000000000400ffb <+45>:    lea    0x1(%rcx),%esi
   0x0000000000400ffe <+48>:    callq  0x400fce <func4>
   0x0000000000401003 <+53>:    lea    0x1(%rax,%rax,1),%eax
   0x0000000000401007 <+57>:    add    $0x8,%rsp
   0x000000000040100b <+61>:    retq   

​ 可以很明显地看出我们要到 <+34><+43> 才可出循环,又从前已知我们需要返回值为0,即执行<+36>: mov $0x0,%eax ,故让 x 1 = 7 x_1 = 7 x1=7 ,即可达到目的,以返回值为0退出循环。

​ 📓 如果我们完全分析这个递归函数还是比较麻烦的,可见为了解题还是降低了难度的。

​ 所以综上可知输入字符串应为"7 0"

phase_5(寄存器低位操作)

全汇编代码

# phase_5 反汇编
   0x0000000000401062 <+0>:     push   %rbx
   0x0000000000401063 <+1>:     sub    $0x20,%rsp
   0x0000000000401067 <+5>:     mov    %rdi,%rbx
   0x000000000040106a <+8>:     mov    %fs:0x28,%rax
   0x0000000000401073 <+17>:    mov    %rax,0x18(%rsp)
   0x0000000000401078 <+22>:    xor    %eax,%eax
   0x000000000040107a <+24>:    callq  0x40131b <string_length>
   0x000000000040107f <+29>:    cmp    $0x6,%eax
   0x0000000000401082 <+32>:    je     0x4010d2 <phase_5+112>
   0x0000000000401084 <+34>:    callq  0x40143a <explode_bomb>
   0x0000000000401089 <+39>:    jmp    0x4010d2 <phase_5+112>
   0x000000000040108b <+41>:    movzbl (%rbx,%rax,1),%ecx
   0x000000000040108f <+45>:    mov    %cl,(%rsp)
   0x0000000000401092 <+48>:    mov    (%rsp),%rdx
   0x0000000000401096 <+52>:    and    $0xf,%edx
   0x0000000000401099 <+55>:    movzbl 0x4024b0(%rdx),%edx
   0x00000000004010a0 <+62>:    mov    %dl,0x10(%rsp,%rax,1)
   0x00000000004010a4 <+66>:    add    $0x1,%rax
   0x00000000004010a8 <+70>:    cmp    $0x6,%rax
   0x00000000004010ac <+74>:    jne    0x40108b <phase_5+41>
   0x00000000004010ae <+76>:    movb   $0x0,0x16(%rsp)
   0x00000000004010b3 <+81>:    mov    $0x40245e,%esi
   0x00000000004010b8 <+86>:    lea    0x10(%rsp),%rdi
   0x00000000004010bd <+91>:    callq  0x401338 <strings_not_equal>
   0x00000000004010c2 <+96>:    test   %eax,%eax
   0x00000000004010c4 <+98>:    je     0x4010d9 <phase_5+119>
   0x00000000004010c6 <+100>:   callq  0x40143a <explode_bomb>
   0x00000000004010cb <+105>:   nopl   0x0(%rax,%rax,1)
   0x00000000004010d0 <+110>:   jmp    0x4010d9 <phase_5+119>
   0x00000000004010d2 <+112>:   mov    $0x0,%eax
   0x00000000004010d7 <+117>:   jmp    0x40108b <phase_5+41>
   0x00000000004010d9 <+119>:   mov    0x18(%rsp),%rax
   0x00000000004010de <+124>:   xor    %fs:0x28,%rax
   0x00000000004010e7 <+133>:   je     0x4010ee <phase_5+140>
   0x00000000004010e9 <+135>:   callq  0x400b30 <__stack_chk_fail@plt>
   0x00000000004010ee <+140>:   add    $0x20,%rsp
   0x00000000004010f2 <+144>:   pop    %rbx

逐步分析

# phase_5 反汇编(1)    涉及金丝雀(canary)值来栈破坏检测
   # canary value
   0x0000000000401062 <+0>:     push   %rbx
   0x0000000000401063 <+1>:     sub    $0x20,%rsp
   0x0000000000401067 <+5>:     mov    %rdi,%rbx       # %rdi is first arg (input_string)
   0x000000000040106a <+8>:     mov    %fs:0x28,%rax
   0x0000000000401073 <+17>:    mov    %rax,0x18(%rsp)
   0x0000000000401078 <+22>:    xor    %eax,%eax

# phase_5 反汇编(2)   
   0x000000000040107a <+24>:    callq  0x40131b <string_length>    # return string_length
   0x000000000040107f <+29>:    cmp    $0x6,%eax
   0x0000000000401082 <+32>:    je     0x4010d2 <phase_5+112>
   0x0000000000401084 <+34>:    callq  0x40143a <explode_bomb>
   0x0000000000401089 <+39>:    jmp    0x4010d2 <phase_5+112>
   0x000000000040108b <+41>:    movzbl (%rbx,%rax,1),%ecx
   0x000000000040108f <+45>:    mov    %cl,(%rsp)                #  %cl(%rcx) has 8 bits
   0x0000000000401092 <+48>:    mov    (%rsp),%rdx
   0x0000000000401096 <+52>:    and    $0xf,%edx                 # remain low 4 bits(char of input string)
   0x0000000000401099 <+55>:    movzbl 0x4024b0(%rdx),%edx
   0x00000000004010a0 <+62>:    mov    %dl,0x10(%rsp,%rax,1)     #  %dl(%rdx) has 8 bits
   0x00000000004010a4 <+66>:    add    $0x1,%rax                 # remain low 4 bits(existed stirng)
   0x00000000004010a8 <+70>:    cmp    $0x6,%rax
   0x00000000004010ac <+74>:    jne    0x40108b <phase_5+41> 

打印其中必要的信息:

#  input string
(gdb) x /s $rdi
0x6038c0 <input_strings+320>:   "tesadf"
(gdb) x /gx $rdi
0x6038c0 <input_strings+320>:   0x0000666461736574

# 地址0x4024b0处的string     existed string
(gdb) x /s 0x4024b0
0x4024b0 <array.3449>:  "maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?"

📓 第二部分汇编:根据每个输入字符 ascii 后四位作为索引来寻找已有字符中对应字母依次放入 0x10(%rsp) 前6个字节的位置。

# phase_5 反汇编(3) 
   0x00000000004010ae <+76>:    movb   $0x0,0x16(%rsp)
   0x00000000004010b3 <+81>:    mov    $0x40245e,%esi           #   "flyers"
   0x00000000004010b8 <+86>:    lea    0x10(%rsp),%rdi
   0x00000000004010bd <+91>:    callq  0x401338 <strings_not_equal>
   0x00000000004010c2 <+96>:    test   %eax,%eax
   0x00000000004010c4 <+98>:    je     0x4010d9 <phase_5+119>
   0x00000000004010c6 <+100>:   callq  0x40143a <explode_bomb>
   0x00000000004010cb <+105>:   nopl   0x0(%rax,%rax,1)
   0x00000000004010d0 <+110>:   jmp    0x4010d9 <phase_5+119>
   0x00000000004010d2 <+112>:   mov    $0x0,%eax
   0x00000000004010d7 <+117>:   jmp    0x40108b <phase_5+41>

​ 打印必要的信息:

(gdb) x /s 0x40245e
0x40245e:       "flyers"

📓 第三部分汇编: 由前面得到的字符与 "flyers" 对比是否相同,相同则通关。

​ 故我们在 0x4024b0 处前16位字母中寻找 "flyers" 对应的索引如下。

charflyers
index915(f)14(e)567

​ 所以我们只要在 ASCII 中寻找6个字符,它们的低4位分别为9fe567即可。

​ 可选择的答案有:9?>567yoNuVw, ionijk,等。

# phase_5 反汇编(4) 
   # canary value
   0x00000000004010d9 <+119>:   mov    0x18(%rsp),%rax
   0x00000000004010de <+124>:   xor    %fs:0x28,%rax
   0x00000000004010e7 <+133>:   je     0x4010ee <phase_5+140>
   0x00000000004010e9 <+135>:   callq  0x400b30 <__stack_chk_fail@plt>
   0x00000000004010ee <+140>:   add    $0x20,%rsp
   0x00000000004010f2 <+144>:   pop    %rbx
   0x00000000004010f3 <+145>:   retq   

phase_6(链表)

全汇编代码

   0x00000000004010f4 <+0>:     push   %r14
   0x00000000004010f6 <+2>:     push   %r13
   0x00000000004010f8 <+4>:     push   %r12
   0x00000000004010fa <+6>:     push   %rbp
   0x00000000004010fb <+7>:     push   %rbx
   0x00000000004010fc <+8>:     sub    $0x50,%rsp
   0x0000000000401100 <+12>:    mov    %rsp,%r13
   0x0000000000401103 <+15>:    mov    %rsp,%rsi
   0x0000000000401106 <+18>:    callq  0x40145c <read_six_numbers>
   0x000000000040110b <+23>:    mov    %rsp,%r14
   0x000000000040110e <+26>:    mov    $0x0,%r12d
   0x0000000000401114 <+32>:    mov    %r13,%rbp
   0x0000000000401117 <+35>:    mov    0x0(%r13),%eax
   0x000000000040111b <+39>:    sub    $0x1,%eax
   0x000000000040111e <+42>:    cmp    $0x5,%eax
   0x0000000000401121 <+45>:    jbe    0x401128 <phase_6+52>
   0x0000000000401123 <+47>:    callq  0x40143a <explode_bomb>
   0x0000000000401128 <+52>:    add    $0x1,%r12d
   0x000000000040112c <+56>:    cmp    $0x6,%r12d
   0x0000000000401130 <+60>:    je     0x401153 <phase_6+95>
   0x0000000000401132 <+62>:    mov    %r12d,%ebx
   0x0000000000401135 <+65>:    movslq %ebx,%rax
   0x0000000000401138 <+68>:    mov    (%rsp,%rax,4),%eax
   0x000000000040113b <+71>:    cmp    %eax,0x0(%rbp)
   0x000000000040113e <+74>:    jne    0x401145 <phase_6+81>
   0x0000000000401140 <+76>:    callq  0x40143a <explode_bomb>
   0x0000000000401145 <+81>:    add    $0x1,%ebx
   0x0000000000401148 <+84>:    cmp    $0x5,%ebx
   0x000000000040114b <+87>:    jle    0x401135 <phase_6+65>
   0x000000000040114d <+89>:    add    $0x4,%r13
   0x0000000000401151 <+93>:    jmp    0x401114 <phase_6+32>
   0x0000000000401153 <+95>:    lea    0x18(%rsp),%rsi
   0x0000000000401158 <+100>:   mov    %r14,%rax
   0x000000000040115b <+103>:   mov    $0x7,%ecx
   0x0000000000401160 <+108>:   mov    %ecx,%edx
   0x0000000000401162 <+110>:   sub    (%rax),%edx
   0x0000000000401164 <+112>:   mov    %edx,(%rax)
   0x0000000000401166 <+114>:   add    $0x4,%rax
   0x000000000040116a <+118>:   cmp    %rsi,%rax
   0x000000000040116d <+121>:   jne    0x401160 <phase_6+108>
   0x000000000040116f <+123>:   mov    $0x0,%esi
   0x0000000000401174 <+128>:   jmp    0x401197 <phase_6+163>
   0x0000000000401176 <+130>:   mov    0x8(%rdx),%rdx
   0x000000000040117a <+134>:   add    $0x1,%eax
   0x000000000040117d <+137>:   cmp    %ecx,%eax
   0x000000000040117f <+139>:   jne    0x401176 <phase_6+130>
   0x0000000000401181 <+141>:   jmp    0x401188 <phase_6+148>
   0x0000000000401183 <+143>:   mov    $0x6032d0,%edx
   0x0000000000401188 <+148>:   mov    %rdx,0x20(%rsp,%rsi,2)
   0x000000000040118d <+153>:   add    $0x4,%rsi
   0x0000000000401191 <+157>:   cmp    $0x18,%rsi
   0x0000000000401195 <+161>:   je     0x4011ab <phase_6+183>
   0x0000000000401197 <+163>:   mov    (%rsp,%rsi,1),%ecx
   0x000000000040119a <+166>:   cmp    $0x1,%ecx
   0x000000000040119d <+169>:   jle    0x401183 <phase_6+143>
   0x000000000040119f <+171>:   mov    $0x1,%eax
   0x00000000004011a4 <+176>:   mov    $0x6032d0,%edx
   0x00000000004011a9 <+181>:   jmp    0x401176 <phase_6+130>
   0x00000000004011ab <+183>:   mov    0x20(%rsp),%rbx
   0x00000000004011b0 <+188>:   lea    0x28(%rsp),%rax
   0x00000000004011b5 <+193>:   lea    0x50(%rsp),%rsi
   0x00000000004011ba <+198>:   mov    %rbx,%rcx
   0x00000000004011bd <+201>:   mov    (%rax),%rdx
   0x00000000004011c0 <+204>:   mov    %rdx,0x8(%rcx)
   0x00000000004011c4 <+208>:   add    $0x8,%rax
   0x00000000004011c8 <+212>:   cmp    %rsi,%rax
   0x00000000004011cb <+215>:   je     0x4011d2 <phase_6+222>
   0x00000000004011cd <+217>:   mov    %rdx,%rcx
   0x00000000004011d0 <+220>:   jmp    0x4011bd <phase_6+201>
   0x00000000004011d2 <+222>:   movq   $0x0,0x8(%rdx)
   0x00000000004011da <+230>:   mov    $0x5,%ebp
   0x00000000004011df <+235>:   mov    0x8(%rbx),%rax
   0x00000000004011e3 <+239>:   mov    (%rax),%eax
   0x00000000004011e5 <+241>:   cmp    %eax,(%rbx)
   0x00000000004011e7 <+243>:   jge    0x4011ee <phase_6+250>
   0x00000000004011e9 <+245>:   callq  0x40143a <explode_bomb>
   0x00000000004011ee <+250>:   mov    0x8(%rbx),%rbx
   0x00000000004011f2 <+254>:   sub    $0x1,%ebp
   0x00000000004011f5 <+257>:   jne    0x4011df <phase_6+235>
   0x00000000004011f7 <+259>:   add    $0x50,%rsp
   0x00000000004011fb <+263>:   pop    %rbx
   0x00000000004011fc <+264>:   pop    %rbp
   0x00000000004011fd <+265>:   pop    %r12
   0x00000000004011ff <+267>:   pop    %r13
   0x0000000000401201 <+269>:   pop    %r14
   0x0000000000401203 <+271>:   retq   

分析

# phase_6 反汇编(1) 
   #五个被调用者保存寄存器
   0x00000000004010f4 <+0>:     push   %r14
   0x00000000004010f6 <+2>:     push   %r13
   0x00000000004010f8 <+4>:     push   %r12
   0x00000000004010fa <+6>:     push   %rbp
   0x00000000004010fb <+7>:     push   %rbx

   0x00000000004010fc <+8>:     sub    $0x50,%rsp
   0x0000000000401100 <+12>:    mov    %rsp,%r13
   0x0000000000401103 <+15>:    mov    %rsp,%rsi              # put %rsp as second arg, store 6 numbers  
   0x0000000000401106 <+18>:    callq  0x40145c <read_six_numbers>
   0x000000000040110b <+23>:    mov    %rsp,%r14
   0x000000000040110e <+26>:    mov    $0x0,%r12d              # %r12d  has 32 bits

# phase_6 反汇编(2) 
   # 双重 for 循环
   0x0000000000401114 <+32>:    mov    %r13,%rbp
   0x0000000000401117 <+35>:    mov    0x0(%r13),%eax          # put x_1 to %eax
   0x000000000040111b <+39>:    sub    $0x1,%eax
   0x000000000040111e <+42>:    cmp    $0x5,%eax
   0x0000000000401121 <+45>:    jbe    0x401128 <phase_6+52>    # jbe 无符号<=    0 <= x_1 - 1 <= 5
   0x0000000000401123 <+47>:    callq  0x40143a <explode_bomb>
   
   0x0000000000401128 <+52>:    add    $0x1,%r12d
   0x000000000040112c <+56>:    cmp    $0x6,%r12d
   0x0000000000401130 <+60>:    je     0x401153 <phase_6+95>
   0x0000000000401132 <+62>:    mov    %r12d,%ebx
   0x0000000000401135 <+65>:    movslq %ebx,%rax
   0x0000000000401138 <+68>:    mov    (%rsp,%rax,4),%eax
   0x000000000040113b <+71>:    cmp    %eax,0x0(%rbp)
   0x000000000040113e <+74>:    jne    0x401145 <phase_6+81>    #   x_1 != x_2, x_3, x_4, x_5
   0x0000000000401140 <+76>:    callq  0x40143a <explode_bomb>
   0x0000000000401145 <+81>:    add    $0x1,%ebx
   0x0000000000401148 <+84>:    cmp    $0x5,%ebx
   0x000000000040114b <+87>:    jle    0x401135 <phase_6+65>
   0x000000000040114d <+89>:    add    $0x4,%r13
   0x0000000000401151 <+93>:    jmp    0x401114 <phase_6+32>

​ 📔 此为二重循环,注释中为第一重选择基底 x 1 x_1 x1 的情况。以下注释同理。

​ 故最终结果为 0 ≤ x 1 , x 2 , x 3 , x 4 , x 5 , x 6 ≤ 6 0 \leq x_1, x_2, x_3, x_4, x_5, x_6 \leq 6 0x1,x2,x3,x4,x5,x66 x 1 ≠ x 2 ≠ x 3 ≠ x 4 ≠ x 5 ≠ x 6 x_1 \ne x_2 \ne x_3 \ne x_4 \ne x_5 \ne x_6 x1=x2=x3=x4=x5=x6

​ 即六个数是0到6之间互不相同的数字。

# phase_6 反汇编(3) 
# 翻转6个数字  x = (7 - x)
   0x0000000000401153 <+95>:    lea    0x18(%rsp),%rsi     # 
   0x0000000000401158 <+100>:   mov    %r14,%rax           # %r14 value is %rsp
   0x000000000040115b <+103>:   mov    $0x7,%ecx
   0x0000000000401160 <+108>:   mov    %ecx,%edx
   0x0000000000401162 <+110>:   sub    (%rax),%edx         # 7 - x_1
   0x0000000000401164 <+112>:   mov    %edx,(%rax)         # x_1 = 7 - x_1
   0x0000000000401166 <+114>:   add    $0x4,%rax
   0x000000000040116a <+118>:   cmp    %rsi,%rax
   0x000000000040116d <+121>:   jne    0x401160 <phase_6+108>

将六个数字都用7减去,即 $x_1 = 7 - x_1 $ ,其他5个数字也同样操作。

💁‍♂ 以下我们所讨论的 x 都为翻转过后的 x ,最后判断结果时再处理

# phase_6 反汇编(4)   输入为    2 1 5 4 3 6
   # 将链表结点的地址按照输入值顺序依次放入栈中待用
   0x000000000040116f <+123>:   mov    $0x0,%esi
   0x0000000000401174 <+128>:   jmp    0x401197 <phase_6+163>
   0x0000000000401176 <+130>:   mov    0x8(%rdx),%rdx     # next node's address
   0x000000000040117a <+134>:   add    $0x1,%eax
   0x000000000040117d <+137>:   cmp    %ecx,%eax
   0x000000000040117f <+139>:   jne    0x401176 <phase_6+130>    # 找到第X_1个结点的地址,存放至%rdx中
   0x0000000000401181 <+141>:   jmp    0x401188 <phase_6+148>
   0x0000000000401183 <+143>:   mov    $0x6032d0,%edx
   0x0000000000401188 <+148>:   mov    %rdx,0x20(%rsp,%rsi,2)    #将第X_1个结点的地址存放至 0x20(%rsp)
   0x000000000040118d <+153>:   add    $0x4,%rsi
   0x0000000000401191 <+157>:   cmp    $0x18,%rsi
   0x0000000000401195 <+161>:   je     0x4011ab <phase_6+183>
   0x0000000000401197 <+163>:   mov    (%rsp,%rsi,1),%ecx         # x_1  x_2
   0x000000000040119a <+166>:   cmp    $0x1,%ecx              
   0x000000000040119d <+169>:   jle    0x401183 <phase_6+143>     # if x_2 = 1
   0x000000000040119f <+171>:   mov    $0x1,%eax
   0x00000000004011a4 <+176>:   mov    $0x6032d0,%edx
   0x00000000004011a9 <+181>:   jmp    0x401176 <phase_6+130>

​ 先打印其中出现的地址值,发现显示为<node1>,继续打印。这时可以断定此处存放是6个链表结点。

(gdb) x /x  0x6032d0 
0x6032d0 <node1>:       0x4c
(gdb) x /12gx  0x6032d0               # 此时输入为   2 1 5 4 3 6,并且未修改链表处值
0x6032d0 <node1>:       0x000000010000014c      0x00000000006032e0
0x6032e0 <node2>:       0x00000002000000a8      0x00000000006032f0
0x6032f0 <node3>:       0x000000030000039c      0x0000000000603300
0x603300 <node4>:       0x00000004000002b3      0x0000000000603310
0x603310 <node5>:       0x00000005000001dd      0x0000000000603320
0x603320 <node6>:       0x00000006000001bb      0x0000000000000000

​ 根据上述的汇编代码,其功能为将 x 的值作为索引,找到指向索引结点的地址依次(按照 x 的顺序)放入 0x20(%rsp) 中,即

数据栈中地址
指向第 x 6 x_6 x6 个结点的地址0x20(%rsp, 5, 8)
指向第 x 5 x_5 x5 个结点的地址0x20(%rsp, 4, 8)
指向第 x 4 x_4 x4 个结点的地址0x20(%rsp, 3, 8)
指向第 x 3 x_3 x3 个结点的地址0x20(%rsp, 2, 8)
指向第 x 2 x_2 x2 个结点的地址0x20(%rsp, 1, 8)
指向第 x 1 x_1 x1 个结点的地址0x20(%rsp, 0, 8)
# phase_6 反汇编(5)
   0x00000000004011ab <+183>:   mov    0x20(%rsp),%rbx    # point to x_1-th node
   0x00000000004011b0 <+188>:   lea    0x28(%rsp),%rax    
   0x00000000004011b5 <+193>:   lea    0x50(%rsp),%rsi    # 循环的边界
   0x00000000004011ba <+198>:   mov    %rbx,%rcx          # point to x_1-th node
   0x00000000004011bd <+201>:   mov    (%rax),%rdx        # point to x_2-th node
   0x00000000004011c0 <+204>:   mov    %rdx,0x8(%rcx)     # make x_1-th node's next node is x_2-th
   0x00000000004011c4 <+208>:   add    $0x8,%rax
   0x00000000004011c8 <+212>:   cmp    %rsi,%rax
   0x00000000004011cb <+215>:   je     0x4011d2 <phase_6+222>
   0x00000000004011cd <+217>:   mov    %rdx,%rcx
   0x00000000004011d0 <+220>:   jmp    0x4011bd <phase_6+201>

使得在栈桢中排序的结点按序链接起来,即根据 x 的值重新排序结点。

# phase_6 反汇编(6)
   0x00000000004011d2 <+222>:   movq   $0x0,0x8(%rdx)     # final node's next node address is 0x0
   0x00000000004011da <+230>:   mov    $0x5,%ebp
   0x00000000004011df <+235>:   mov    0x8(%rbx),%rax     # %rbx is 0x20(%rsp)
   0x00000000004011e3 <+239>:   mov    (%rax),%eax        # deliver 32 bits from node data
   0x00000000004011e5 <+241>:   cmp    %eax,(%rbx)
   0x00000000004011e7 <+243>:   jge    0x4011ee <phase_6+250>
   0x00000000004011e9 <+245>:   callq  0x40143a <explode_bomb>
   0x00000000004011ee <+250>:   mov    0x8(%rbx),%rbx
   0x00000000004011f2 <+254>:   sub    $0x1,%ebp
   0x00000000004011f5 <+257>:   jne    0x4011df <phase_6+235>
   
   0x00000000004011f7 <+259>:   add    $0x50,%rsp
   0x00000000004011fb <+263>:   pop    %rbx
   0x00000000004011fc <+264>:   pop    %rbp
   0x00000000004011fd <+265>:   pop    %r12
   0x00000000004011ff <+267>:   pop    %r13
   0x0000000000401201 <+269>:   pop    %r14
   0x0000000000401203 <+271>:   retq   

​ 判断新序链表中的结点数据大小(低32位)是否按照从小到大排序,若是,则解密成功。

原链表顺序排序后链表顺序(从大到小)
0x000000010000014c0x000000030000039c
0x00000002000000a80x00000004000002b3
0x000000030000039c0x00000005000001dd
0x00000004000002b30x00000006000001bb
0x00000005000001dd0x000000010000014c
0x00000006000001bb0x00000002000000a8

​ 故从大到小的结点排序为3 4 5 6 1 2

​ 因为前面对结点数字做过翻转,故传入字符串为"4 3 2 1 6 5"

前6 phase 输入输出

输入

Border relations with Canada have never been better.
1 2 4 8 16 32
0 207
7 0
9?>567
4 3 2 1 6 5

输出

Welcome to my fiendish little bomb. You have 6 phases with
which to blow yourself up. Have a nice day!
Phase 1 defused. How about the next one?
That's number 2.  Keep going!
Halfway there!
So you got that one.  Try this one.
Good work!  On to the next...
Congratulations! You've defused the bomb!

secret phase(链表+递归函数(复杂))

​ 在 bomb.c 中有提示可能还有隐匿的 phase ,接着查看 bomb.d 中发现还有一部分 <secret_phase>

​ 直接在 gdb 中对 secret_phase 打断点运行的话是过不去的,直接运行结束 ,故想进入此 phase 需要有些条件,我们在反汇编中查看(objdump -d bomb)并搜索 secret_phase 。发现在 <phase_defused> 模块中另有乾坤。

# phase_defused:
   0x00000000004015c4 <+0>:     sub    $0x78,%rsp
   0x00000000004015c8 <+4>:     mov    %fs:0x28,%rax
   0x00000000004015d1 <+13>:    mov    %rax,0x68(%rsp)
   0x00000000004015d6 <+18>:    xor    %eax,%eax
   
   0x00000000004015d8 <+20>:    cmpl   $0x6,0x202181(%rip)     # 0x603760 <num_input_strings> 对应6个phase
   0x00000000004015df <+27>:    jne    0x40163f <phase_defused+123>
   0x00000000004015e1 <+29>:    lea    0x10(%rsp),%r8
   0x00000000004015e6 <+34>:    lea    0xc(%rsp),%rcx
   0x00000000004015eb <+39>:    lea    0x8(%rsp),%rdx
   0x00000000004015f0 <+44>:    mov    $0x402619,%esi      # "%d %d %s"
   0x00000000004015f5 <+49>:    mov    $0x603870,%edi      # "7 0"   is 4-th phase
   0x00000000004015fa <+54>:    callq  0x400bf0 <__isoc99_sscanf@plt>
   0x00000000004015ff <+59>:    cmp    $0x3,%eax
   0x0000000000401602 <+62>:    jne    0x401635 <phase_defused+113>
   0x0000000000401604 <+64>:    mov    $0x402622,%esi     # "DrEvil"
   0x0000000000401609 <+69>:    lea    0x10(%rsp),%rdi
   0x000000000040160e <+74>:    callq  0x401338 <strings_not_equal>
   0x0000000000401613 <+79>:    test   %eax,%eax
   0x0000000000401615 <+81>:    jne    0x401635 <phase_defused+113>
   0x0000000000401617 <+83>:    mov    $0x4024f8,%edi    # "Curses, you've found the secret phase!"
   0x000000000040161c <+88>:    callq  0x400b10 <puts@plt>
   0x0000000000401621 <+93>:    mov    $0x402520,%edi    # "But finding it and solving it are quite different..."
   0x0000000000401626 <+98>:    callq  0x400b10 <puts@plt>
   0x000000000040162b <+103>:   mov    $0x0,%eax
   0x0000000000401630 <+108>:   callq  0x401242 <secret_phase>    # callq secret_phase
   0x0000000000401635 <+113>:   mov    $0x402558,%edi    # "Congratulations! You've defused the bomb!"
   0x000000000040163a <+118>:   callq  0x400b10 <puts@plt>
   
   0x000000000040163f <+123>:   mov    0x68(%rsp),%rax
   0x0000000000401644 <+128>:   xor    %fs:0x28,%rax
   0x000000000040164d <+137>:   je     0x401654 <phase_defused+144>
   0x000000000040164f <+139>:   callq  0x400b30 <__stack_chk_fail@plt>
   0x0000000000401654 <+144>:   add    $0x78,%rsp
   0x0000000000401658 <+148>:   retq   

​ 根据以上的分析,我们发现只要在第四个 phase 的字符串后添加 "DrEvil" 即可进入 secret_phase

# secret_phase
   0x0000000000401242 <+0>:     push   %rbx
   0x0000000000401243 <+1>:     callq  0x40149e <read_line>    # answer of secret_phase
   0x0000000000401248 <+6>:     mov    $0xa,%edx               # third_arg: base is 10
   0x000000000040124d <+11>:    mov    $0x0,%esi               # second_arg: char **endptr,具体看此函数原型
   0x0000000000401252 <+16>:    mov    %rax,%rdi               # first_arg: string
   0x0000000000401255 <+19>:    callq  0x400bd0 <strtol@plt>   # convert string to long int.
   0x000000000040125a <+24>:    mov    %rax,%rbx
   0x000000000040125d <+27>:    lea    -0x1(%rax),%eax
   0x0000000000401260 <+30>:    cmp    $0x3e8,%eax                   # answer - 1 <= 0x3e8
   0x0000000000401265 <+35>:    jbe    0x40126c <secret_phase+42>    # jbe 无符号 <=
   0x0000000000401267 <+37>:    callq  0x40143a <explode_bomb>
   0x000000000040126c <+42>:    mov    %ebx,%esi               # answer
   0x000000000040126e <+44>:    mov    $0x6030f0,%edi
   0x0000000000401273 <+49>:    callq  0x401204 <fun7>
   0x0000000000401278 <+54>:    cmp    $0x2,%eax               # fun7's return is 2
   0x000000000040127b <+57>:    je     0x401282 <secret_phase+64>
   0x000000000040127d <+59>:    callq  0x40143a <explode_bomb>
   0x0000000000401282 <+64>:    mov    $0x402438,%edi    # "Wow! You've defused the secret stage!"
   0x0000000000401287 <+69>:    callq  0x400b10 <puts@plt>
   0x000000000040128c <+74>:    callq  0x4015c4 <phase_defused>
   0x0000000000401291 <+79>:    pop    %rbx
   0x0000000000401292 <+80>:    retq   

​ 打印传入 <func7> 的第一个参数。

#  multi link list  以下的no.字样人为添加
(gdb) x /60gx 0x6030f0     (8-byte)    
0x6030f0 <n1>:          0x0000000000000024      0x0000000000603110		no.1(0x6030f0)
0x603100 <n1+16>:       0x0000000000603130      0x0000000000000000
0x603110 <n21>:         0x0000000000000008      0x0000000000603190		no.1
0x603120 <n21+16>:      0x0000000000603150      0x0000000000000000
0x603130 <n22>:         0x0000000000000032      0x0000000000603170		no.2(0x603130)
0x603140 <n22+16>:      0x00000000006031b0      0x0000000000000000
0x603150 <n32>:         0x0000000000000016      0x0000000000603270		no.3(0x603150)
0x603160 <n32+16>:      0x0000000000603230      0x0000000000000000
0x603170 <n33>:         0x000000000000002d      0x00000000006031d0		no.2
0x603180 <n33+16>:      0x0000000000603290      0x0000000000000000
0x603190 <n31>:         0x0000000000000006      0x00000000006031f0		no.1
0x6031a0 <n31+16>:      0x0000000000603250      0x0000000000000000
0x6031b0 <n34>:         0x000000000000006b      0x0000000000603210
0x6031c0 <n34+16>:      0x00000000006032b0      0x0000000000000000
0x6031d0 <n45>:         0x0000000000000028      0x0000000000000000		no.2
0x6031e0 <n45+16>:      0x0000000000000000      0x0000000000000000
0x6031f0 <n41>:         0x0000000000000001      0x0000000000000000		no.1
0x603200 <n41+16>:      0x0000000000000000      0x0000000000000000
0x603210 <n47>:         0x0000000000000063      0x0000000000000000
0x603220 <n47+16>:      0x0000000000000000      0x0000000000000000
0x603230 <n44>:         0x0000000000000023      0x0000000000000000
0x603240 <n44+16>:      0x0000000000000000      0x0000000000000000
0x603250 <n42>:         0x0000000000000007      0x0000000000000000
0x603260 <n42+16>:      0x0000000000000000      0x0000000000000000
0x603270 <n43>:         0x0000000000000014      0x0000000000000000		no.3
0x603280 <n43+16>:      0x0000000000000000      0x0000000000000000
0x603290 <n46>:         0x000000000000002f      0x0000000000000000
0x6032a0 <n46+16>:      0x0000000000000000      0x0000000000000000
0x6032b0 <n48>:         0x00000000000003e9      0x0000000000000000
0x6032c0 <n48+16>:      0x0000000000000000      0x0000000000000000
# func7 反汇编  递归函数     进入前%rax = ans - 1
   #  找到链表结点的值(store in %edx) <= x  第一条链表: 0x24 0x8 0x6 0x1
   #									     no.2:  0x32 0x2d 0x28
   #									     no.3:  0x16 0x14
   0x0000000000401204 <+0>:     sub    $0x8,%rsp
   0x0000000000401208 <+4>:     test   %rdi,%rdi
   0x000000000040120b <+7>:     je     0x401238 <fun7+52>      # je 相等/零   不可跳转,会造成错误的%rax
   0x000000000040120d <+9>:     mov    (%rdi),%edx             # n1 = 0x24 = 36
   0x000000000040120f <+11>:    cmp    %esi,%edx               # %esi is secret_ans
   0x0000000000401211 <+13>:    jle    0x401220 <fun7+28>      # 36 <= x
   0x0000000000401213 <+15>:    mov    0x8(%rdi),%rdi
   0x0000000000401217 <+19>:    callq  0x401204 <fun7>
   
   0x000000000040121c <+24>:    add    %eax,%eax
   0x000000000040121e <+26>:    jmp    0x40123d <fun7+57>
   0x0000000000401220 <+28>:    mov    $0x0,%eax
   0x0000000000401225 <+33>:    cmp    %esi,%edx
   0x0000000000401227 <+35>:    je     0x40123d <fun7+57>    
   0x0000000000401229 <+37>:    mov    0x10(%rdi),%rdi     #  换链,继续寻找链表结点=x的结点
   0x000000000040122d <+41>:    callq  0x401204 <fun7>
   0x0000000000401232 <+46>:    lea    0x1(%rax,%rax,1),%eax
   0x0000000000401236 <+50>:    jmp    0x40123d <fun7+57>
   
   0x0000000000401238 <+52>:    mov    $0xffffffff,%eax
   0x000000000040123d <+57>:    add    $0x8,%rsp
   0x0000000000401241 <+61>:    retq   

​ 通过粗略地观察,如果要其返回值为2,则必须依次执行以下语句:

   0x0000000000401220 <+28>:    mov    $0x0,%eax
   0x0000000000401232 <+46>:    lea    0x1(%rax,%rax,1),%eax
   0x000000000040121c <+24>:    add    %eax,%eax
   # exit
   0x000000000040121e <+26>:    jmp    0x40123d <fun7+57>

​ 所以函数调用顺序为:(由于递归的性质,所以与上相反)

   0x0000000000401217 <+19>:    callq  0x401204 <fun7>      # ans < 0x24 = 36(no.1)
   0x000000000040122d <+41>:    callq  0x401204 <fun7>      # ans > 0x8  = 8(no.1)
   # 最深层调用
   0x0000000000401211 <+13>:    jle    0x401220 <fun7+28>   # ans >= 0x16 = 22(no.3)
   0x0000000000401227 <+35>:    je     0x40123d <fun7+57>   # ans = 0x16

​ 所以最后的答案为22。

最终输入输出

输入

Border relations with Canada have never been better.
1 2 4 8 16 32
0 207
7 0 DrEvil
9?>567
4 3 2 1 6 5
22

输出

Welcome to my fiendish little bomb. You have 6 phases with
which to blow yourself up. Have a nice day!
Phase 1 defused. How about the next one?
That's number 2.  Keep going!
Halfway there!
So you got that one.  Try this one.
Good work!  On to the next...
Curses, you've found the secret phase!
But finding it and solving it are quite different...
Wow! You've defused the secret stage!
Congratulations! You've defused the bomb!
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值