CSAPP-BombLab


binary bombs

拆除“binary bombs”。一个“binary bombs”(二进制炸弹,下文将简称为炸弹)是一个Linux可执行程序,包含了6个阶段(或层次、关卡)。炸弹运行的每个阶段要求你输入一个特定字符串,你的输入符合程序预期的输入,该阶段的炸弹就被拆除引信即解除了,否则炸弹“爆炸”打印输出 “BOOM!!!”。实验的目标是拆除尽可能多的炸弹层次。 每个炸弹阶段考察了机器级程序语言的一个不同方面,难度逐级递增

另外还有一个隐藏阶段,只有在第4阶段的解后附加一特定字符串后才会出现

拆除“binary bombs”,增强对程序的机器级表示、汇编语言、调试器和逆向工程等方面原理与技能的掌握。

· 阶段1:字符串比较
· 阶段2:循环
· 阶段3:条件/分支
· 阶段4:递归调用和栈
· 阶段5:指针
· 阶段6:链表/指针/结构

构造自己的bomb

 

phase_1

首先调用objdump –d bomb > bomb_disas.txt 对bomb进行反汇编并将汇编源代码输出到boomb_disas.txt文本文件中。

查看该汇编源代码文件,可以在main函数中找到如下语句,从而得知第一关的处理程序包含在main()函数所调用的函数phase_1()中,判断的过程可以参照bomb.c文件源码。

在反汇编代码中寻找这个子函数phase_1:

接下来,使用gdb调试bomb二进制文件:gdb bomb后,运行break phase_1,也就是在phase_1函数处设置一个断点,然后运行run,开始调试。运行disas phase_1,得到如下信息:

可以看到%esi中装入的就是待比较的目标字符串地址:

接下来,去设置断点去检测这个答案是否正确,我们kill当前程序的调试。再在explode_bomb处设置断点break explode_bomb,然后run开始运行,按照提示输入这个字符串。

第一关解除。

phase_2

按照第一关的思路和步骤进行调试:

000000000400f0c <phase_2>:
  400f0c: 55                    push   %rbp//把数据压入栈
  400f0d: 53                    push   %rbx//把数据压入栈
  400f0e: 48 83 ec 28           sub    $0x28,%rsp//把栈指针减少40,提供局部变量空间
  400f12: 48 89 e6              mov    %rsp,%rsi//把栈指针状态存入%rsi中
  400f15: e8 e5 06 00 00        callq  4015ff <read_six_numbers>// 调用函数read_six_numbers,读取6个数字,此时(%rsp)已被赋值
  400f1a: 83 3c 24 00           cmpl   $0x0,(%rsp)//(%rsp)的值即输入的第一个参数的值,将(%rsp)的值与0比较,需要等于0来避免bomb。
  400f1e: 79 24                 jns    400f44 <phase_2+0x38>//若相等则跳转
  400f20: e8 a4 06 00 00        callq  4015c9 <explode_bomb>//否则爆炸
  400f25: eb 1d                 jmp    400f44 <phase_2+0x38>//jmp指令无条件跳转400f44
  400f27: 89 d8                 mov    %ebx,%eax    //%eax=ebx
  400f29: 03 45 fc              add    -0x4(%rbp),%eax    //%eax=%eax+num1。
  400f2c: 39 45 00              cmp    %eax,0x0(%rbp) //比较%eax和num2
  400f2f: 74 05                 je     400f36 <phase_2+0x2a> //相等
  400f31: e8 93 06 00 00        callq  4015c9 <explode_bomb>  //不相等
  400f36: 83 c3 01              add    $0x1,%ebx //%ebx+1
  400f39: 48 83 c5 04           add    $0x4,%rbp //下一个num地址
  400f3d: 83 fb 06              cmp    $0x6,%ebx //比较
  400f40: 75 e5                 jne    400f27 <phase_2+0x1b> //不相等
  400f42: eb 0c                 jmp    400f50 <phase_2+0x44> //相等
  400f44: 48 8d 6c 24 04        lea    0x4(%rsp),%rbp//将%rsp+4代表的地址移入%rbx,即第2个输入数的地址
  400f49: bb 01 00 00 00        mov    $0x1,%ebx //%ebx=1
  400f4e: eb d7                 jmp    400f27 <phase_2+0x1b>//跳转
  400f50: 48 83 c4 28           add    $0x28,%rsp //释放空间
  400f54: 5b                    pop    %rbx
  400f55: 5d                    pop    %rbp
  400f56: c3                    retq   

分析汇编代码:

和%rsp与$0x0比较可知道:

我们需要输入6个数,并且第一个数(num1=0)需要等于0来避免bomb。

接下来跳转到0x400f44地址处的指令,%rbp=(%rsp)地址的偏移4处的值,也就是num2。%ebx=1。跳转到0x400f27,开始循环。

%eax=ebx=1。%eax=%eax+num1。比较%eax和num2,相等不会bomb,所以num2=1。接下来add指令,%ebx+=1,%rbp+=4,也就是%rbp=num3。跳转到循环,直到%ebx>6。

第二次循环,%eax=ebx=2。%eax=%eax+num2=3。比较%eax和num3。避免bomb,所以num3=3。

同理,推得这六个数:0 1 3 6 10 15

接下来,去设置断点去检测这个答案是否正确,我们kill当前程序的调试。再在explode_bomb处设置断点break explode_bomb,然后run开始运行,按照提示输入这六个数。

第二关解除。

phase_3

0000000000400f57 <phase_3>:
  400f57: 48 83 ec 18           sub    $0x18,%rsp//把栈指针减少24,提供局部变量空间
  400f5b: 48 8d 4c 24 08        lea    0x8(%rsp),%rcx//加载地址,将0x8(%rsp)设为num2
  400f60: 48 8d 54 24 0c        lea    0xc(%rsp),%rdx//加载地址,将0xc(%rsp)设为num1
  400f65: be 21 28 40 00        mov    $0x402821,%esi//经测试,0x4025cf对应字符串"%d %d"
  400f6a: b8 00 00 00 00        mov    $0x0,%eax//初始化%eax
  400f6f: e8 bc fc ff ff        callq  400c30 <__isoc99_sscanf@plt>
  400f74: 83 f8 01              cmp    $0x1,%eax
  400f77: 7f 05                 jg     400f7e <phase_3+0x27>//jg:有符号大于则跳转,说明scanf输入数据的个数必须大于1
  400f79: e8 4b 06 00 00        callq  4015c9 <explode_bomb>//否则爆炸
  400f7e: 83 7c 24 0c 07        cmpl   $0x7,0xc(%rsp)
  400f83: 77 66                 ja     400feb <phase_3+0x94>//ja:无符号大于则跳转,至爆炸,说明num1为无符号数,大于0且需要小于等于7,所以num1=[0,7]
  400f85: 8b 44 24 0c           mov    0xc(%rsp),%eax  //将num1储存到%eax中
  400f89: ff 24 c5 80 25 40 00 jmpq   *0x402580(,%rax,8) //根据该指令和后续的指令格式,很容易判断此处是switch语句的跳转表
  400f90: b8 00 00 00 00        mov    $0x0,%eax  //1
  400f95: eb 05                 jmp    400f9c <phase_3+0x45>
  400f97: b8 aa 00 00 00        mov    $0xaa,%eax  //0
  400f9c: 2d e2 02 00 00        sub    $0x2e2,%eax
  400fa1: eb 05                 jmp    400fa8 <phase_3+0x51>
  400fa3: b8 00 00 00 00        mov    $0x0,%eax  //2
  400fa8: 05 e2 01 00 00        add    $0x1e2,%eax
  400fad: eb 05                 jmp    400fb4 <phase_3+0x5d>
  400faf: b8 00 00 00 00        mov    $0x0,%eax  //3
  400fb4: 2d 6b 01 00 00        sub    $0x16b,%eax
  400fb9: eb 05                 jmp    400fc0 <phase_3+0x69>
  400fbb: b8 00 00 00 00        mov    $0x0,%eax  // 4
  400fc0: 05 6b 01 00 00        add    $0x16b,%eax
  400fc5: eb 05                 jmp    400fcc <phase_3+0x75>
  400fc7: b8 00 00 00 00        mov    $0x0,%eax // 5
  400fcc: 2d 6b 01 00 00        sub    $0x16b,%eax
  400fd1: eb 05                 jmp    400fd8 <phase_3+0x81>
  400fd3: b8 00 00 00 00        mov    $0x0,%eax  //6
  400fd8: 05 6b 01 00 00        add    $0x16b,%eax
  400fdd: eb 05                 jmp    400fe4 <phase_3+0x8d>
  400fdf: b8 00 00 00 00        mov    $0x0,%eax //7
  400fe4: 2d 6b 01 00 00        sub    $0x16b,%eax
  400fe9: eb 0a                 jmp    400ff5 <phase_3+0x9e>
  400feb: e8 d9 05 00 00        callq  4015c9 <explode_bomb>
  400ff0: b8 00 00 00 00        mov    $0x0,%eax
  400ff5: 83 7c 24 0c 05        cmpl   $0x5,0xc(%rsp) //比较num1和5
  400ffa: 7f 06                 jg     401002 <phase_3+0xab> //不相等就爆炸
  400ffc: 3b 44 24 08           cmp    0x8(%rsp),%eax  //比较%eax的值和num2
  401000: 74 05                 je     401007 <phase_3+0xb0>//je:若相等则跳转至结束
  401002: e8 c2 05 00 00        callq  4015c9 <explode_bomb>
  401007: 48 83 c4 18           add    $0x18,%rsp //释放空间
  40100b: c3                    retq   

分析汇编代码:

得知输入了两个数,推测%rcx=num2,%rdx=num1。输入数的个数必须大于1,否则bomb。如果num1>0x7,bomb。

由jmpq 0x402580(,%rax,8)可知,间接跳转,再根据后面一系列jmp,推测此次是switch语句。查找跳转表的存储地址:

在switch case出来后,计算出的%eax=num2,否则bomb。

Cmpl $0x5,0xc(%rsp) 所以num1=5,否则bomb。

将num1=5代入,得%eax=-363。

接下来,去设置断点去检测这个答案是否正确,我们kill当前程序的调试。再在explode_bomb处设置断点break explode_bomb,然后run开始运行,按照提示输入5 -363。

第三关解除。

phase_4

000000000040103f <phase_4>:
  40103f: 48 83 ec 18           sub    $0x18,%rsp//开辟空间
  401043: 48 8d 4c 24 08        lea    0x8(%rsp),%rcx//加载地址,将0x8(%rsp)设为num2
  401048: 48 8d 54 24 0c        lea    0xc(%rsp),%rdx//加载地址,将0xc(%rsp)设为num1
  40104d: be 21 28 40 00        mov    $0x402821,%esi //scanf函数输入格式, %d %d
  401052: b8 00 00 00 00        mov    $0x0,%eax
  401057: e8 d4 fb ff ff        callq  400c30 <__isoc99_sscanf@plt>
  40105c: 83 f8 02              cmp    $0x2,%eax //当scanf输入数据个数不等于2时,跳转至爆炸
  40105f: 75 07                 jne    401068 <phase_4+0x29>
  401061: 83 7c 24 0c 0e        cmpl   $0xe,0xc(%rsp)//jdb:无符号小于等于跳转,当num1小于等于14时,跳转至40106d,否则爆炸,所以num1的限制条件为[0,14]
  401066: 76 05                 jbe    40106d <phase_4+0x2e>
  401068: e8 5c 05 00 00        callq  4015c9 <explode_bomb>
  40106d: ba 0e 00 00 00        mov    $0xe,%edx //func4参数z
  401072: be 00 00 00 00        mov    $0x0,%esi//func4参数y
  401077: 8b 7c 24 0c           mov    0xc(%rsp),%edi//func4参数x
  40107b: e8 8c ff ff ff        callq  40100c <func4> // 传参x=num1,y=0,z=14
  401080: 83 f8 15              cmp    $0x15,%eax //返回值比较
  401083: 75 07                 jne    40108c <phase_4+0x4d> //不等于21就爆炸
  401085: 83 7c 24 08 15        cmpl   $0x15,0x8(%rsp)
  40108a: 74 05                 je     401091 <phase_4+0x52>
  40108c: e8 38 05 00 00        callq  4015c9 <explode_bomb>
  401091: 48 83 c4 18           add    $0x18,%rsp //释放空间
  401095: c3                    retq   
000000000040100c <func4>:
  40100c: 53                    push   %rbx
  40100d: 89 d0                 mov    %edx,%eax
  40100f: 29 f0                 sub    %esi,%eax
  401011: 89 c3                 mov    %eax,%ebx
  401013: c1 eb 1f              shr    $0x1f,%ebx
  401016: 01 d8                 add    %ebx,%eax
  401018: d1 f8                 sar    %eax
  40101a: 8d 1c 30              lea    (%rax,%rsi,1),%ebx
  40101d: 39 fb                 cmp    %edi,%ebx
  40101f: 7e 0c                 jle    40102d <func4+0x21>
  401021: 8d 53 ff              lea    -0x1(%rbx),%edx
  401024: e8 e3 ff ff ff        callq  40100c <func4>
  401029: 01 d8                 add    %ebx,%eax
  40102b: eb 10                 jmp    40103d <func4+0x31>
  40102d: 89 d8                 mov    %ebx,%eax
  40102f: 39 fb                 cmp    %edi,%ebx
  401031: 7d 0a                 jge    40103d <func4+0x31>
  401033: 8d 73 01              lea    0x1(%rbx),%esi
  401036: e8 d1 ff ff ff        callq  40100c <func4>
  40103b: 01 d8                 add    %ebx,%eax
  40103d: 5b                    pop    %rbx
  40103e: c3                    retq   

分析汇编代码:

我们需要输入两个数。

cmpl $0xe ,0xc(%rsp) 得知:num1小于等于14。

%edx = 14,%esi = 0 ,%edi = num1 作为参数执行func4。Func4的返回值%eax=21,否则bomb。比较num2和21,num2=21,否则bomb。

分析func4:

要使返回值t=21,传入的num1=x=6。

所以num1=6,num2=21。

接下来,去设置断点去检测这个答案是否正确,我们kill当前程序的调试。再在explode_bomb处设置断点break explode_bomb,然后run开始运行,按照提示输入6 21。

第四关解除。

phase_5

0000000000401096 <phase_5>:
  401096: 48 83 ec 18           sub    $0x18,%rsp //开辟空间
  40109a: 48 8d 4c 24 08        lea    0x8(%rsp),%rcx//加载地址,将0x8(%rsp)设为num2
  40109f: 48 8d 54 24 0c        lea    0xc(%rsp),%rdx//加载地址,将0xc(%rsp)设为num1
  4010a4: be 21 28 40 00        mov    $0x402821,%esi //d d格式,需要输入两个数
  4010a9: b8 00 00 00 00        mov    $0x0,%eax //初始化%eax
  4010ae: e8 7d fb ff ff        callq  400c30 <__isoc99_sscanf@plt>
  4010b3: 83 f8 01              cmp    $0x1,%eax // 输入个数大于1,否则跳转到爆炸
  4010b6: 7f 05                 jg     4010bd <phase_5+0x27>
  4010b8: e8 0c 05 00 00        callq  4015c9 <explode_bomb>
  4010bd: 8b 44 24 0c           mov    0xc(%rsp),%eax //%eax=num1(结合后面代码来看,num1是第一个取数的下标)
  4010c1: 83 e0 0f              and    $0xf,%eax //取出来的num1的低四位与0xf作比较,若相等,则跳转到爆炸函数,否则,执行下一步,由此可见,num1的低四位不为1111。
  4010c4: 89 44 24 0c           mov    %eax,0xc(%rsp)
  4010c8: 83 f8 0f              cmp    $0xf,%eax
  4010cb: 74 2c                 je     4010f9 <phase_5+0x63>
  4010cd: b9 00 00 00 00        mov    $0x0,%ecx // 赋值,%ecx=0,%edx=0,%edx+=1。
  4010d2: ba 00 00 00 00        mov    $0x0,%edx
  4010d7: 83 c2 01              add    $0x1,%edx //计数器+1
  4010da: 48 98                 cltq   
  4010dc: 8b 04 85 c0 25 40 00 mov    0x4025c0(,%rax,4),%eax //eax=*(4rax+0x4025c0),此处以4*eax为步长,推测0x4025c0是一个数组的首地址,rax是上次取的数组里的数
  4010e3: 01 c1                 add    %eax,%ecx //%ecx存储累加结果
  4010e5: 83 f8 0f              cmp    $0xf,%eax //比较15和 数组取出来的数
  4010e8: 75 ed                 jne    4010d7 <phase_5+0x41> //不相等继续循环,所以最后取出来的数是15
  4010ea: 89 44 24 0c           mov    %eax,0xc(%rsp)
  4010ee: 83 fa 07              cmp    $0x7,%edx //与7比较,不相等就爆炸。所以取七个数,最后一个数是15
  4010f1: 75 06                 jne    4010f9 <phase_5+0x63>
  4010f3: 3b 4c 24 08           cmp    0x8(%rsp),%ecx //累计结果和num2比较,不相等就爆炸
  4010f7: 74 05                 je     4010fe <phase_5+0x68>
  4010f9: e8 cb 04 00 00        callq  4015c9 <explode_bomb>
  4010fe: 48 83 c4 18           add    $0x18,%rsp
  401102: c3                    retq   

同样需要输入两个数,输入的参数个数必须大于1个。%rdx=num1,%rcx=num2

把取出来的num1的低四位与0xf作比较,若相等,则跳转到爆炸函数,否则,执行下一步,由此可见,参数1的低四位不为1111。

赋值,%ecx=0,%edx=0,%edx+=1。

而后执行操作得到eax=(4rax+0x4025c0),此处以4eax为步长,推测0x4025c0是一个数组的首地址,接着把存储在地址*(0x4025c0+4eax)处的值与%ecx相加存储在%ecx中。

接着把%eax存储的值(应该是数组的序号)与0xf也就是15作比较,若不等于,则跳到<phase_5+65>处继续循环,否则,接着执行后续代码,由此可知,<phase_5+65>处开始应该是一个循环,每次循环取出数组中的一个值累加存储到%ecx中,最后一次取出的值应该是15,接着把%edx与7作比较,不相等则爆炸,反之,执行后续代码。

我们打印出数组连续的16位:

所以我们最后一个取到的数是15,一共取7个数。初次%rax=num1,取数组里面的A[num1],第二次取A[ %rax ],此时%rax = %eax =A[num1]。

数组序列:

取数顺序:

逆推,取七个数,也就是A[num1] = 0,num1=8。%ecx = 0+10+1+2+14+6+15=48

而num2=%ecx=48,否则爆炸。

所以num1 = 8,num2=48。

接下来,去设置断点去检测这个答案是否正确,我们kill当前程序的调试。再在explode_bomb处设置断点break explode_bomb,然后run开始运行,按照提示输入8 48。

第五关解除。

phase_6

0000000000401103 <phase_6>:
  401103: 41 55                 push   %r13
  401105: 41 54                 push   %r12
  401107: 55                    push   %rbp
  401108: 53                    push   %rbx
  401109: 48 83 ec 58           sub    $0x58,%rsp // 开辟空间
  40110d: 48 8d 74 24 30        lea    0x30(%rsp),%rsi
  401112: e8 e8 04 00 00        callq  4015ff <read_six_numbers> // 读取输入的六个数
  401117: 4c 8d 6c 24 30        lea    0x30(%rsp),%r13
  40111c: 41 bc 00 00 00 00     mov    $0x0,%r12d
  401122: 4c 89 ed              mov    %r13,%rbp  //初始 %rbp=%r13=%rsp
  401125: 41 8b 45 00           mov    0x0(%r13),%eax //%eax=num[i]
  401129: 83 e8 01              sub    $0x1,%eax
  40112c: 83 f8 05              cmp    $0x5,%eax
  40112f: 76 05                 jbe    401136 <phase_6+0x33>//无符号数比较,说明num为无符号数,即大于等于0,401129~40112f num[i]-1<=5,所以num[i]<=6
  401131: e8 93 04 00 00        callq  4015c9 <explode_bomb>
  401136: 41 83 c4 01           add    $0x1,%r12d
  40113a: 41 83 fc 06           cmp    $0x6,%r12d
  40113e: 75 07                 jne    401147 <phase_6+0x44>
  401140: be 00 00 00 00        mov    $0x0,%esi
  401145: eb 42                 jmp    401189 <phase_6+0x86>
  401147: 44 89 e3              mov    %r12d,%ebx //401136~401147 退出大循环的条件:6个数字全部遍历到
  40114a: 48 63 c3              movslq %ebx,%rax
  40114d: 8b 44 84 30           mov    0x30(%rsp,%rax,4),%eax
  401151: 39 45 00              cmp    %eax,0x0(%rbp)
  401154: 75 05                 jne    40115b <phase_6+0x58>
  401156: e8 6e 04 00 00        callq  4015c9 <explode_bomb>
  40115b: 83 c3 01              add    $0x1,%ebx
  40115e: 83 fb 05              cmp    $0x5,%ebx
  401161: 7e e7                 jle    40114a <phase_6+0x47>//小循环,判断数组元素是否相等
  401163: 49 83 c5 04           add    $0x4,%r13
  401167: eb b9                 jmp    401122 <phase_6+0x1f>//大循环,每次将%r13加4,之后回到401122,%r13赋给了%eax

这部分伪代码:两层循环,说明输入的每个数字要求不大于6,且互不相同

  401169: 48 8b 52 08           mov    0x8(%rdx),%rdx
  40116d: 83 c0 01              add    $0x1,%eax
  401170: 39 c8                 cmp    %ecx,%eax
  401172: 75 f5                 jne    401169 <phase_6+0x66>
  401174: eb 05                 jmp    40117b <phase_6+0x78>
  401176: ba f0 42 60 00        mov    $0x6042f0,%edx
  40117b: 48 89 14 74           mov    %rdx,(%rsp,%rsi,2)
  40117f: 48 83 c6 04           add    $0x4,%rsi
  401183: 48 83 fe 18           cmp    $0x18,%rsi
  401187: 74 15                 je     40119e <phase_6+0x9b>
  401189: 8b 4c 34 30           mov    0x30(%rsp,%rsi,1),%ecx
  40118d: 83 f9 01              cmp    $0x1,%ecx
  401190: 7e e4                 jle    401176 <phase_6+0x73>
  401192: b8 01 00 00 00        mov    $0x1,%eax
  401197: ba f0 42 60 00        mov    $0x6042f0,%edx
  40119c: eb cb                 jmp    401169 <phase_6+0x66>
  40119e: 48 8b 1c 24           mov    (%rsp),%rbx
  4011a2: 48 8d 44 24 08        lea    0x8(%rsp),%rax
  4011a7: 48 8d 74 24 30        lea    0x30(%rsp),%rsi
  4011ac: 48 89 d9              mov    %rbx,%rcx
  4011af: 48 8b 10              mov    (%rax),%rdx
  4011b2: 48 89 51 08           mov    %rdx,0x8(%rcx)
  4011b6: 48 83 c0 08           add    $0x8,%rax
  4011ba: 48 39 f0              cmp    %rsi,%rax
  4011bd: 74 05                 je     4011c4 <phase_6+0xc1>

这部分伪代码:

如果把链表中编号为id的节点地址叫做L[id],那么这一部分干的事情就是P[i] = L[num[i]]

  4011bf: 48 89 d1              mov    %rdx,%rcx
  4011c2: eb eb                 jmp    4011af <phase_6+0xac>
  4011c4: 48 c7 42 08 00 00 00 movq   $0x0,0x8(%rdx)
  4011cb: 00
  4011cc: bd 05 00 00 00        mov    $0x5,%ebp
  4011d1: 48 8b 43 08           mov    0x8(%rbx),%rax
  4011d5: 8b 00                 mov    (%rax),%eax
  4011d7: 39 03                 cmp    %eax,(%rbx)
  4011d9: 7e 05                 jle    4011e0 <phase_6+0xdd>
  4011db: e8 e9 03 00 00        callq  4015c9 <explode_bomb>
  4011e0: 48 8b 5b 08           mov    0x8(%rbx),%rbx
  4011e4: 83 ed 01              sub    $0x1,%ebp
  4011e7: 75 e8                 jne    4011d1 <phase_6+0xce>
  4011e9: 48 83 c4 58           add    $0x58,%rsp
  4011ed: 5b                    pop    %rbx
  4011ee: 5d                    pop    %rbp
  4011ef: 41 5c                 pop    %r12
  4011f1: 41 5d                 pop    %r13
  4011f3: c3                    retq   

这部分伪代码:是按照L[num[0]]->L[num[1]]->…->L[num[5]]的顺序重排链表

发现它访问了node前4字节的值,就叫它val

这一部分,所做的事情是检查是否满足P[0]->val > … > P[5]->val

分析结构体:

偏移4字节为结点编号,偏移8字节为下一个结构体地址,所以是链表。

struct node{
    int value;
    int number;
node* next;
}

最后打印各个结点的val值,排序

因此得出序列:1 5 6 4 3 2

接下来,去设置断点去检测这个答案是否正确,我们kill当前程序的调试。再在explode_bomb处设置断点break explode_bomb,然后run开始运行,按照提示输入1 5 6 4 3 2。

第六关解除。

secret_phase

入口寻找

在第六关汇编代码下面还有一个fun7函数以及secret_phase部分,这很明显就说明了有隐藏关卡。我们观察给出的c语言代码可以发现每个关卡函数里面都有一个函数调用 phase_defused()。

找到该函数汇编代码:

分析可知当输入两个数字和一个字符串的组合时就调用了隐藏关卡,第四关是输入两个数字的,所以我们需要在后面再输入某个字符串,隐藏关卡是在结束第六关才会出现的,所以字符的密码就隐藏在第六关,设置断点,查看需要传入的字符串,是 DrEvil,在第四关输入两个数字之后输入DrEvil,结束第六关之后,成功跳出隐藏关卡。

分析汇编代码:

输入并读取一行字符,将字符转换为长整型,并规定了长整型的大小。

调用fun7函数,并且返回值必须是5.

这是一个递归调用函数,其结构类似于二叉树,其中首次传进去的这一个地址0x 604110就是根节点地址。

查看树结点,完整二叉树:

要得到fun7的返回值为5,需要右左右。(((20+1)2)*2)+1=5

所以是47。

接下来,去设置断点去检测这个答案是否正确,我们kill当前程序的调试。再在explode_bomb处设置断点break explode_bomb,然后run开始运行,按照提示输入47。

隐藏关解除。


文章作者: Aiaa
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Aiaa !
  目录