CSAPP Lab2 bomblab 炸弹实验 6+1

目录

一,实验前准备

1.1 汇编代码的准备

1.2 gdb关键指令的预备

二,实验过程

2.1 phase_1 字符串比较

2.2 phase_2 循环

2.3 phase_3 条件/分支

2.4 phase_4 递归调用和栈

2.5 phase_5 指针

2.6 phase_6 链表/指针/结构

2.7 secret_phase

2.7.1 秘密的发现

2.7.2 秘密的答案

三,实验测试


一,实验前准备

1.1 汇编代码的准备

         便于整体的观察我们一开始可以将整个汇编代码导出,我们用到以下指令并将其导出至asm.txt文件。

objdump -d bomb > asm.txt

        在vscode中我们主要通过ctrl+Fasm.txt中搜索我们感兴趣的类容,比如我们需要找到phase_1的断点位置,我们可以直接搜索phase_1。可得到以下结果。

0000000000400ee0 <phase_1>:
  400ee0:	48 83 ec 08          	sub    $0x8,%rsp
  400ee4:	be 00 24 40 00       	mov    $0x402400,%esi
  400ee9:	e8 4a 04 00 00       	callq  401338 <strings_not_equal>
  400eee:	85 c0                	test   %eax,%eax
  400ef0:	74 05                	je     400ef7 <phase_1+0x17>
  400ef2:	e8 43 05 00 00       	callq  40143a <explode_bomb>
  400ef7:	48 83 c4 08          	add    $0x8,%rsp
  400efb:	c3                   	retq   

1.2 gdb关键指令的预备

  • b (breakpoint):设置断点。

    phase_1函数前设置断点:b phase_1

    0x400100行设置断点:b *0x400100

  • r (run):执行,直到第一个断点处,若无断点,则一直执行直到程序结束。

  • ni (next/step instructor):单步执行机器指令。

  • x:显示内存内容

    基本用法:

    以十六进制的形式显示明码地址处开头的20个字节的内容:

    (gdb) x/20x <明码地址>

    显示明码地址处开头处的2个字符串

    (gdb) x/2s <明码地址>

  • info reg:将当前寄存器中的全部变量的内容打印出来

二,实验过程

2.1 phase_1 字符串比较

0000000000400ee0 <phase_1>:
  400ee0:	48 83 ec 08          	sub    $0x8,%rsp
  #入栈👆
  400ee4:	be 00 24 40 00       	mov    $0x402400,%esi	#出现0x402400明码。
  400ee9:	e8 4a 04 00 00       	callq  401338 <strings_not_equal>	#调用函数
  400eee:	85 c0                	test   %eax,%eax	#按位与
  400ef0:	74 05                	je     400ef7 <phase_1+0x17>	#返回非0则爆炸💣
  400ef2:	e8 43 05 00 00       	callq  40143a <explode_bomb>	#💣💥
  #出栈👇
  400ef7:	48 83 c4 08          	add    $0x8,%rsp
  400efb:	c3                   	retq   

        通过上面注释我们大致了解了phase_1的工作原理,这时候我们观察反汇编注释大致了解到400ee9调用函数用来判断字符串是否相等并将其移至%eax,然后通过je跳转,若相等则直接释放空间,完成该函数,若不相等则直接触发爆炸。

        下面知道该思路后我们通过复制$0x402400明码,通过gdb调试追踪该明码对应的字符串,该字符串即为phase_1的答案。

#参考1.2
(gdb) x/1s 0x402400
0x402400:       "Border relations with Canada have never been better."

2.2 phase_2 循环

0000000000400efc <phase_2>:
  400efc:	55                   	push   %rbp
  400efd:	53                   	push   %rbx
  400efe:	48 83 ec 28          	sub    $0x28,%rsp
  #申请空间👆
  400f02:	48 89 e6             	mov    %rsp,%rsi	#传值
  400f05:	e8 52 05 00 00       	callq  40145c <read_six_numbers>	#读入 6 个数字
  400f0a:	83 3c 24 01          	cmpl   $0x1,(%rsp)	#与 1 做比较
  400f0e:	74 20                	je     400f30 <phase_2+0x34>	#若相等则相应跳转至400f30
  400f10:	e8 25 05 00 00       	callq  40143a <explode_bomb>	#💣💥
  400f15:	eb 19                	jmp    400f30 <phase_2+0x34>	#直接跳转至400f30
  400f17:	8b 43 fc             	mov    -0x4(%rbx),%eax	
  400f1a:	01 c0                	add    %eax,%eax	#%eax自增
  400f1c:	39 03                	cmp    %eax,(%rbx)	#比较%eax的值和此时%rbx对应的内存的值
  400f1e:	74 05                	je     400f25 <phase_2+0x29>	#相等的跳转400f25
  400f20:	e8 15 05 00 00       	callq  40143a <explode_bomb>	#💣💥
  400f25:	48 83 c3 04          	add    $0x4,%rbx	#%rbx的值加4
  400f29:	48 39 eb             	cmp    %rbp,%rbx	#比较两个寄存器的值
  400f2c:	75 e9                	jne    400f17 <phase_2+0x1b>	
  400f2e:	eb 0c                	jmp    400f3c <phase_2+0x40>
  400f30:	48 8d 5c 24 04       	lea    0x4(%rsp),%rbx	#将%rsp的值加4的地址移入%rbx
  400f35:	48 8d 6c 24 18       	lea    0x18(%rsp),%rbp	#将%rsp的值加18的地址移入%rbp
  400f3a:	eb db                	jmp    400f17 <phase_2+0x1b>	#直接跳转至400f17
  #释放空间👇
  400f3c:	48 83 c4 28          	add    $0x28,%rsp
  400f40:	5b                   	pop    %rbx
  400f41:	5d                   	pop    %rbp
  400f42:	c3                   	retq 
  • 下表记录了上述程序中各个寄存器的变化(随着循环逐行变化,跳转分支跳转至不爆炸)

%rsp%rbx%rbp%eax
&1%rsp+4%rsp+241
%rsp+82
%rsp+124
%rsp+168
%rsp+2016
%rsp+2432

        依据备注我们注意到400f30400f35两行,这两行主要通过400f25不断加4直至相等即可跳出循环(参考表格第二列),那么在这多次跳转中不断变化的就是另一个%eax的值,我们可以推断%eax的变化就是答案。

2.3 phase_3 条件/分支

0000000000400f43 <phase_3>:
  400f43:   48 83 ec 18             sub    $0x18,%rsp
  #申请空间👆
  400f47:   48 8d 4c 24 0c          lea    0xc(%rsp),%rcx
  400f4c:   48 8d 54 24 08          lea    0x8(%rsp),%rdx
  400f51:   be cf 25 40 00          mov    $0x4025cf,%esi
  400f56:   b8 00 00 00 00          mov    $0x0,%eax
  400f5b:   e8 90 fc ff ff          callq  400bf0 <__isoc99_sscanf@plt>
  400f60:   83 f8 01                cmp    $0x1,%eax
  400f63:   7f 05                   jg     400f6a <phase_3+0x27>    #大于则跳转(有符号)
  400f65:   e8 d0 04 00 00          callq  40143a <explode_bomb>    #💣💥
  #以上对参数进行操作👆
  400f6a:   83 7c 24 08 07          cmpl   $0x7,0x8(%rsp)   #7与M[%rdx]比较并设置条件码
  400f6f:   77 3c                   ja     400fad <phase_3+0x6a>    #大于则跳转至爆炸💣(无符号)
  400f71:   8b 44 24 08             mov    0x8(%rsp),%eax   #%eax=M[%rdx]
  #条件分支
  400f75:   ff 24 c5 70 24 40 00    jmpq   *0x402470(,%rax,8)   #0x402470+%rax*8
  400f7c:   b8 cf 00 00 00          mov    $0xcf,%eax
  400f81:   eb 3b                   jmp    400fbe <phase_3+0x7b>
  400f83:   b8 c3 02 00 00          mov    $0x2c3,%eax
  400f88:   eb 34                   jmp    400fbe <phase_3+0x7b>
  400f8a:   b8 00 01 00 00          mov    $0x100,%eax
  400f8f:   eb 2d                   jmp    400fbe <phase_3+0x7b>
  400f91:   b8 85 01 00 00          mov    $0x185,%eax
  400f96:   eb 26                   jmp    400fbe <phase_3+0x7b>
  400f98:   b8 ce 00 00 00          mov    $0xce,%eax
  400f9d:   eb 1f                   jmp    400fbe <phase_3+0x7b>
  400f9f:   b8 aa 02 00 00          mov    $0x2aa,%eax
  400fa4:   eb 18                   jmp    400fbe <phase_3+0x7b>
  400fa6:   b8 47 01 00 00          mov    $0x147,%eax
  400fab:   eb 11                   jmp    400fbe <phase_3+0x7b>
  400fad:   e8 88 04 00 00          callq  40143a <explode_bomb>    #💣💥
  400fb2:   b8 00 00 00 00          mov    $0x0,%eax
  400fb7:   eb 05                   jmp    400fbe <phase_3+0x7b>
  400fb9:   b8 37 01 00 00          mov    $0x137,%eax
  #分支结束
  400fbe:   3b 44 24 0c             cmp    0xc(%rsp),%eax
  400fc2:   74 05                   je     400fc9 <phase_3+0x86>    #相等则结束
  400fc4:   e8 71 04 00 00          callq  40143a <explode_bomb>    #💣💥
  #释放空间👇
  400fc9:   48 83 c4 18             add    $0x18,%rsp
  400fcd:   c3                      retq

        我们通过400f51的明码0x4025cf的知需要键入2个数字,参考以下命令行(其中text.txt为答案文本)

root@Nava9:~/Linux_lab/lab2# gdb bomb
(gdb) b phase_3
Breakpoint 1 at 0x400f43
(gdb) r test.txt 
Starting program: /root/Linux_lab/lab2/bomb test.txt
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!
0
​
Breakpoint 1, 0x0000000000400f43 in phase_3 ()
(gdb) x/3s 0x4025cf
0x4025cf:       "%d %d"             #从这里发现需要键入2个数字
0x4025d5:       "Error: Premature EOF on stdin"
0x4025f3:       "GRADE_BOMB"

        通过查询,可以知道%rcx%rdx为第三第四个参数,然后根据400f6a行以及400f6f行(无符号比较)大致知道第一个参数要小于7,我们这里随便设置一个值(例如 2 )并调试。

        设置断点b phase_3,运行r text.txt然后不断使用ni指令直到运行至400fc2行我们继续使用info reg指令,此时可以看到第二个值的答案(因为通过分支语句已经得到%eax中的值,我们这里直接取出即可,如果输入2那么就是case2,得到第二个参数的答案为707)。

2.4 phase_4 递归调用和栈

000000000040100c <phase_4>:
  40100c:   48 83 ec 18             sub    $0x18,%rsp
  #申请空间👆
  401010:   48 8d 4c 24 0c          lea    0xc(%rsp),%rcx   #第四个参数
  401015:   48 8d 54 24 08          lea    0x8(%rsp),%rdx   #第三个参数
  40101a:   be cf 25 40 00          mov    $0x4025cf,%esi
  40101f:   b8 00 00 00 00          mov    $0x0,%eax
  401024:   e8 c7 fb ff ff          callq  400bf0 <__isoc99_sscanf@plt>
  401029:   83 f8 02                cmp    $0x2,%eax
  40102c:   75 07                   jne    401035 <phase_4+0x29>    #不相等则爆炸💣
  #参数处理👆
  40102e:   83 7c 24 08 0e          cmpl   $0xe,0x8(%rsp)   
  401033:   76 05                   jbe    40103a <phase_4+0x2e>    #无符号<=
  401035:   e8 00 04 00 00          callq  40143a <explode_bomb>    #💣💥
  40103a:   ba 0e 00 00 00          mov    $0xe,%edx    #传入第三个参数 z
  40103f:   be 00 00 00 00          mov    $0x0,%esi    #传入第二个参数 y
  401044:   8b 7c 24 08             mov    0x8(%rsp),%edi   #传入第一个参数 x
  401048:   e8 81 ff ff ff          callq  400fce <func4>   #调用函数
  40104d:   85 c0                   test   %eax,%eax    
  40104f:   75 07                   jne    401058 <phase_4+0x4c>    #返回非0则爆炸
  401051:   83 7c 24 0c 00          cmpl   $0x0,0xc(%rsp)   
  401056:   74 05                   je     40105d <phase_4+0x51>    #相等则结束
  401058:   e8 dd 03 00 00          callq  40143a <explode_bomb>    #💣💥
  #释放空间👇
  40105d:   48 83 c4 18             add    $0x18,%rsp
  401061:   c3                      retq   

        同2.4开头代码,我们尝试通过明码发现该部分同样只需要2个参数。然后通过第401051行我们可以得知第二个参数为0。

        并且通过40102e行和401033行我们任然可以知道%rdx需要小于等于14,并且直到401048行为止我们都能很好的理解,下面为func4函数的汇编代码(不妨设%edi为x,%esi为y,%edx为z)。

#z = 14, y = 0
0000000000400fce <func4>:
  400fce:   48 83 ec 08             sub    $0x8,%rsp
  #申请空间👆
  400fd2:   89 d0                   mov    %edx,%eax
  400fd4:   29 f0                   sub    %esi,%eax    #%eax = z-y
  400fd6:   89 c1                   mov    %eax,%ecx    #%ecx = %eax = z-y
  400fd8:   c1 e9 1f                shr    $0x1f,%ecx   #%ecx = (z-y)/2^31 = 0 (我们这里可以认为其为0)
  400fdb:   01 c8                   add    %ecx,%eax    #%eax = z-y
  400fdd:   d1 f8                   sar    %eax #%eax = (z-y)/2
  400fdf:   8d 0c 30                lea    (%rax,%rsi,1),%ecx   #%ecx = (z+y)/2
  400fe2:   39 f9                   cmp    %edi,%ecx    #if(x >= (z+y)/2)
  400fe4:   7e 0c                   jle    400ff2 <func4+0x24>  #有符号<= 跳转
  400fe6:   8d 51 ff                lea    -0x1(%rcx),%edx  #else z = (z+y)/2 - 1
  400fe9:   e8 e0 ff ff ff          callq  400fce <func4>   #调用函数自身
  400fee:   01 c0                   add    %eax,%eax    #%eax = z-y
  400ff0:   eb 15                   jmp    401007 <func4+0x39>  #递归出口
  400ff2:   b8 00 00 00 00          mov    $0x0,%eax    #%eax = 0
  400ff7:   39 f9                   cmp    %edi,%ecx    #if(x <= %ecx)
  400ff9:   7d 0c                   jge    401007 <func4+0x39>  #有符号>= 递归出口
  400ffb:   8d 71 01                lea    0x1(%rcx),%esi
  400ffe:   e8 cb ff ff ff          callq  400fce <func4>   #调用函数自身
  401003:   8d 44 00 01             lea    0x1(%rax,%rax,1),%eax
  #释放空间👇
  401007:   48 83 c4 08             add    $0x8,%rsp
  40100b:   c3                      retq 

        通过上面注释,我们可以大致完成以下C语言伪代码:

int func4(int x, int y, int z)
{
    int mid = (y + z) / 2;
    if(x >= mid)
    {
        if(x <= mid)
            return 0;
        else
        {
            y = mid + 1;
            return 2 * func4(x, y, z) + 1;
        }
    }
    else
    {
        z = mid - 1;
        return 2 * func4(x, y, z);
    }
}

        再通过该伪代码,我们很容易可以得知第一个参数为0,1,3,7其中一个,因此第4关的答案是7 0(我们选择7)。

2.5 phase_5 指针

以下为phase_5的汇编代码部分

0000000000401062 <phase_5>:
  401062:   53                      push   %rbx
  401063:   48 83 ec 20             sub    $0x20,%rsp
  #申请空间👆
  401067:   48 89 fb                mov    %rdi,%rbx
  40106a:   64 48 8b 04 25 28 00    mov    %fs:0x28,%rax
  401071:   00 00 
  401073:   48 89 44 24 18          mov    %rax,0x18(%rsp)
  401078:   31 c0                   xor    %eax,%eax    #返回值置零
  40107a:   e8 9c 02 00 00          callq  40131b <string_length>
  40107f:   83 f8 06                cmp    $0x6,%eax    #该行与上一行明显表示此次输入字符串应该等于6个字符
  401082:   74 4e                   je     4010d2 <phase_5+0x70>
  401084:   e8 b1 03 00 00          callq  40143a <explode_bomb>    #💣💥
  401089:   eb 47                   jmp    4010d2 <phase_5+0x70>
  
  40108b:   0f b6 0c 03             movzbl (%rbx,%rax,1),%ecx   #载入输入值
  40108f:   88 0c 24                mov    %cl,(%rsp)   #取字符最后一字节
  401092:   48 8b 14 24             mov    (%rsp),%rdx
  401096:   83 e2 0f                and    $0xf,%edx    #通过与运算在获得最后一字节的最后4位
  401099:   0f b6 92 b0 24 40 00    movzbl 0x4024b0(%rdx),%edx  #发现关键信息,参考下文
  4010a0:   88 54 04 10             mov    %dl,0x10(%rsp,%rax,1)    #入栈
  4010a4:   48 83 c0 01             add    $0x1,%rax
  4010a8:   48 83 f8 06             cmp    $0x6,%rax
  4010ac:   75 dd                   jne    40108b <phase_5+0x29>    #依据该行与上列三行可知这里通过40108b入口循环了6次(即对这六个字符进行操作)
  4010ae:   c6 44 24 16 00          movb   $0x0,0x16(%rsp)
  4010b3:   be 5e 24 40 00          mov    $0x40245e,%esi
  4010b8:   48 8d 7c 24 10          lea    0x10(%rsp),%rdi
  4010bd:   e8 76 02 00 00          callq  401338 <strings_not_equal>
  4010c2:   85 c0                   test   %eax,%eax
  4010c4:   74 13                   je     4010d9 <phase_5+0x77>    #以上比较处理后的字符串
  4010c6:   e8 6f 03 00 00          callq  40143a <explode_bomb>    #💣💥
  4010cb:   0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)
  4010d0:   eb 07                   jmp    4010d9 <phase_5+0x77>
  4010d2:   b8 00 00 00 00          mov    $0x0,%eax    #返回值置零
  4010d7:   eb b2                   jmp    40108b <phase_5+0x29>
  4010d9:   48 8b 44 24 18          mov    0x18(%rsp),%rax
  4010de:   64 48 33 04 25 28 00    xor    %fs:0x28,%rax
  4010e5:   00 00 
  4010e7:   74 05                   je     4010ee <phase_5+0x8c>
  4010e9:   e8 42 fa ff ff          callq  400b30 <__stack_chk_fail@plt>
  #释放空间👇
  4010ee:   48 83 c4 20             add    $0x20,%rsp
  4010f2:   5b                      pop    %rbx
  4010f3:   c3                      retq 

        观察代码我们很明显知道需要输入6个字符,调试过程中,我们随便输入6个,例如asdfgh。下面我们不断调试至第一个明码地址,即(%rbx,%rax,1)。观察其中的值发现就是我们输入的asdfgh

然后调试至401099行我们再次观察该明码地址得到如下信息

(gdb) x/3s 0x4024b0
0x4024b0 <array.3449>:  "maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?"
0x4024f8:       "Curses, you've found the secret phase!"
0x40251f:       ""

        在这里我们执行了6次循环,依次在寄存器%rcx中存入我们所输入的6个字符,并且取这6个字符的后4位码,对这4位码使用上面字符串前十六位字符偏移的处理,即maduiersnfotvbyl这十六个字符。然后将值再移至栈偏移的0x100x15

        紧接着向下调试循环6次后来到了与第一关相似的部分,不妨再查查4010b3行的明码

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

        通过这个值我们可以很清楚的知道我们需要输入一个值并通过上面循环的处理的到该字符串,我们查看ASCII表通过如下文本对应找到答案

偏移码:        maduiersnfotvbyl
偏移量:        0123456789ABCDEF
​
目标:          f  l  y  e  r  s
偏移值:        9  F  E  5  6  7
​
反推:          69 6F 6E 65 66 67 (答案有多个前四位的值不单单可以是6)
答案:          i  o  n  e  f  g

2.6 phase_6 链表/指针/结构

以下为phase_6的汇编代码部分

00000000004010f4 <phase_6>:
  4010f4:   41 56                   push   %r14
  4010f6:   41 55                   push   %r13
  4010f8:   41 54                   push   %r12
  4010fa:   55                      push   %rbp
  4010fb:   53                      push   %rbx
  4010fc:   48 83 ec 50             sub    $0x50,%rsp
  #申请空间👆
  401100:   49 89 e5                mov    %rsp,%r13
  401103:   48 89 e6                mov    %rsp,%rsi
  401106:   e8 51 03 00 00          callq  40145c <read_six_numbers>    #读入入6个字
  40110b:   49 89 e6                mov    %rsp,%r14
  40110e:   41 bc 00 00 00 00       mov    $0x0,%r12d   #类似 i = 0
  #循环入口(第一部分循环),类似for循环,参考伪代码👇
  401114:   4c 89 ed                mov    %r13,%rbp
  401117:   41 8b 45 00             mov    0x0(%r13),%eax
  40111b:   83 e8 01                sub    $0x1,%eax
  40111e:   83 f8 05                cmp    $0x5,%eax
  401121:   76 05                   jbe    401128 <phase_6+0x34>    #无符号<=,此处判断输入数字大小
  401123:   e8 12 03 00 00          callq  40143a <explode_bomb>    #💣💥
  401128:   41 83 c4 01             add    $0x1,%r12d
  40112c:   41 83 fc 06             cmp    $0x6,%r12d
  401130:   74 21                   je     401153 <phase_6+0x5f>
  401132:   44 89 e3                mov    %r12d,%ebx
  #大循环中嵌套的小循环👇
  401135:   48 63 c3                movslq %ebx,%rax
  401138:   8b 04 84                mov    (%rsp,%rax,4),%eax   #类似于 j = i + 1
  40113b:   39 45 00                cmp    %eax,0x0(%rbp)
  40113e:   75 05                   jne    401145 <phase_6+0x51>
  401140:   e8 f5 02 00 00          callq  40143a <explode_bomb>    #💣💥
  401145:   83 c3 01                add    $0x1,%ebx
  401148:   83 fb 05                cmp    $0x5,%ebx
  40114b:   7e e8                   jle    401135 <phase_6+0x41>    #有符号<=
  #小循环出口👆
  40114d:   49 83 c5 04             add    $0x4,%r13
  401151:   eb c1                   jmp    401114 <phase_6+0x20>
  #循环出口👆
  #循环入口(第二部分循环)👇
  401153:   48 8d 74 24 18          lea    0x18(%rsp),%rsi
  401158:   4c 89 f0                mov    %r14,%rax
  40115b:   b9 07 00 00 00          mov    $0x7,%ecx
  401160:   89 ca                   mov    %ecx,%edx
  401162:   2b 10                   sub    (%rax),%edx
  401164:   89 10                   mov    %edx,(%rax)
  401166:   48 83 c0 04             add    $0x4,%rax
  40116a:   48 39 f0                cmp    %rsi,%rax    #类似while中循环到数组最后一个数字
  40116d:   75 f1                   jne    401160 <phase_6+0x6c>
  #循环出口👆
  #循环入口(第三部分循环)👇
  40116f:   be 00 00 00 00          mov    $0x0,%esi    #%esi可理解为链表尾部指针,同时也代表了循环出口
  401174:   eb 21                   jmp    401197 <phase_6+0xa3>
  401176:   48 8b 52 08             mov    0x8(%rdx),%rdx   #加上一个8字节的空间
  40117a:   83 c0 01                add    $0x1,%eax
  40117d:   39 c8                   cmp    %ecx,%eax
  40117f:   75 f5                   jne    401176 <phase_6+0x82>
  401181:   eb 05                   jmp    401188 <phase_6+0x94>
  401183:   ba d0 32 60 00          mov    $0x6032d0,%edx
  401188:   48 89 54 74 20          mov    %rdx,0x20(%rsp,%rsi,2)
  40118d:   48 83 c6 04             add    $0x4,%rsi
  401191:   48 83 fe 18             cmp    $0x18,%rsi
  401195:   74 14                   je     4011ab <phase_6+0xb7>
  401197:   8b 0c 34                mov    (%rsp,%rsi,1),%ecx   #%ecx存入第i个数
  40119a:   83 f9 01                cmp    $0x1,%ecx
  40119d:   7e e4                   jle    401183 <phase_6+0x8f>    #有符号<=
  40119f:   b8 01 00 00 00          mov    $0x1,%eax
  4011a4:   ba d0 32 60 00          mov    $0x6032d0,%edx
  4011a9:   eb cb                   jmp    401176 <phase_6+0x82>    #跳至401176行
  #循环出口👆
  4011ab:   48 8b 5c 24 20          mov    0x20(%rsp),%rbx
  4011b0:   48 8d 44 24 28          lea    0x28(%rsp),%rax
  4011b5:   48 8d 74 24 50          lea    0x50(%rsp),%rsi
  4011ba:   48 89 d9                mov    %rbx,%rcx
  4011bd:   48 8b 10                mov    (%rax),%rdx
  4011c0:   48 89 51 08             mov    %rdx,0x8(%rcx)
  4011c4:   48 83 c0 08             add    $0x8,%rax
  4011c8:   48 39 f0                cmp    %rsi,%rax
  4011cb:   74 05                   je     4011d2 <phase_6+0xde>
  4011cd:   48 89 d1                mov    %rdx,%rcx
  4011d0:   eb eb                   jmp    4011bd <phase_6+0xc9>
  4011d2:   48 c7 42 08 00 00 00    movq   $0x0,0x8(%rdx)
  4011d9:   00 
  4011da:   bd 05 00 00 00          mov    $0x5,%ebp
  4011df:   48 8b 43 08             mov    0x8(%rbx),%rax
  4011e3:   8b 00                   mov    (%rax),%eax
  4011e5:   39 03                   cmp    %eax,(%rbx)  #于下一个节点的val1比较
  4011e7:   7d 05                   jge    4011ee <phase_6+0xfa>    #有符号>=
  4011e9:   e8 4c 02 00 00          callq  40143a <explode_bomb>    #💣💥
  4011ee:   48 8b 5b 08             mov    0x8(%rbx),%rbx
  4011f2:   83 ed 01                sub    $0x1,%ebp
  4011f5:   75 e8                   jne    4011df <phase_6+0xeb>
  #释放空间👇
  4011f7:   48 83 c4 50             add    $0x50,%rsp
  4011fb:   5b                      pop    %rbx
  4011fc:   5d                      pop    %rbp
  4011fd:   41 5c                   pop    %r12
  4011ff:   41 5d                   pop    %r13
  401201:   41 5e                   pop    %r14
  401203:   c3                      retq   

        第一部分和第二部分循环我们可以编辑如下C语言伪代码:

//假设我们输入的6个值存入x[6]数组中,
for(int i = 0; i < 6; i++)
{
    unsigned int temp = x[i];
    if(temp - 1 <= 5)
        explode_bomb();
    for(int j = i + 1; j <= 5; j++)
        if(temp == x[j])
            explode_bomb();
}
//在汇编中该循环实际并不像for,而更像while循环,但是为了方便起见,我们这里把伪代码形式写成for循环
for(int i = 0; i < 6; i++)  
    x[i] = 7 - x[i];

        显然这个循环在判断我们输入的6个数字必须是不同且小于6(由于是无符号数,故我们也必须大于0),并且第二个循环表示再将输入的数字逐个被7减。

        我们接着调试下面代码,找到一个明码地址$0x6032d0尝试打印该值如下

(gdb) x/3s 0x6032d0
0x6032d0 <node1>:       "L\001"
0x6032d3 <node1+3>:     ""
0x6032d4 <node1+4>:     "\001"

        其中发现该值并非我们能理解的,但是node这个单词提示了我们这是一个结构体,那么我们换一种打印方式得到如下多个结果(通过尝试我们打印了所有节点)

(gdb) x/24x 0x6032d0
0x6032d0 <node1>:       0x0000014c      0x00000001      0x006032e0     0x00000000
0x6032e0 <node2>:       0x000000a8      0x00000002      0x006032f0     0x00000000
0x6032f0 <node3>:       0x0000039c      0x00000003      0x00603300     0x00000000
0x603300 <node4>:       0x000002b3      0x00000004      0x00603310     0x00000000
0x603310 <node5>:       0x000001dd      0x00000005      0x00603320     0x00000000
0x603320 <node6>:       0x000001bb      0x00000006      0x00000000     0x00000000

        根据上文,我们可以很容易的到如下结构体,并且能完成第三个循环的伪代码

typedef struct node
{
    int val1;
    int val2;
    node *next;
}Node;
//node[i]表示上文第i个node,为方便起见,node[0]不表示。
for(int i = 0; i < 6; i++)
{
    int temp = x[i]
    if(temp <= 1)
        node[i] = node[temp];
    else
        node[i] = node[temp];
}

        很明显这是按照x[i]的序列对node[i]进行排序,到这里我们很容易猜到,按照某种排序,可以使node.val1的值按大小顺序排列,那么这个排列的x[i]即是我们想要的答案。

        我们继续向下调试并阅读代码,观察4011e5行以及4011e7行我们可以很明显的感受到序列应该是从大到小,于是我们的到序列应该是3 4 5 6 1 2,由于有第二个循环的存在,我们应该逐个被7减去,得到序列4 3 2 1 6 5,经过测试,答案正确。

2.7 secret_phase

2.7.1 秘密的发现

        首次发现有秘密关卡的时候是在2.5偏移码下面一句,但是知道第6关完成我们也没找到秘密,因此有了下面的分析。

        第一次拿到代码我们肯定会看bomb.c这个文件,我们观察到每个phase都是如下结构

    input = read_line(); 
    phase_1(input);               
    phase_defused();  

        当我们做完题目会发现似乎每一个phase下面会有phase_defused(),故我们应该尝试取寻找该函数的汇编码,如下

00000000004015c4 <phase_defused>:
  4015c4:   48 83 ec 78             sub    $0x78,%rsp
  4015c8:   64 48 8b 04 25 28 00    mov    %fs:0x28,%rax
  4015cf:   00 00 
  4015d1:   48 89 44 24 68          mov    %rax,0x68(%rsp)
  4015d6:   31 c0                   xor    %eax,%eax
  4015d8:   83 3d 81 21 20 00 06    cmpl   $0x6,0x202181(%rip)        # 累计输入6次正确答案
  4015df:   75 5e                   jne    40163f <phase_defused+0x7b>
  4015e1:   4c 8d 44 24 10          lea    0x10(%rsp),%r8
  4015e6:   48 8d 4c 24 0c          lea    0xc(%rsp),%rcx
  4015eb:   48 8d 54 24 08          lea    0x8(%rsp),%rdx
  4015f0:   be 19 26 40 00          mov    $0x402619,%esi
  4015f5:   bf 70 38 60 00          mov    $0x603870,%edi
  4015fa:   e8 f1 f5 ff ff          callq  400bf0 <__isoc99_sscanf@plt>
  4015ff:   83 f8 03                cmp    $0x3,%eax
  401602:   75 31                   jne    401635 <phase_defused+0x71>
  401604:   be 22 26 40 00          mov    $0x402622,%esi   #0x402622行是我们进入秘密的入口
  401609:   48 8d 7c 24 10          lea    0x10(%rsp),%rdi
  40160e:   e8 25 fd ff ff          callq  401338 <strings_not_equal>
  401613:   85 c0                   test   %eax,%eax
  401615:   75 1e                   jne    401635 <phase_defused+0x71>
  401617:   bf f8 24 40 00          mov    $0x4024f8,%edi
  40161c:   e8 ef f4 ff ff          callq  400b10 <puts@plt>
  401621:   bf 20 25 40 00          mov    $0x402520,%edi
  401626:   e8 e5 f4 ff ff          callq  400b10 <puts@plt>
  40162b:   b8 00 00 00 00          mov    $0x0,%eax
  401630:   e8 0d fc ff ff          callq  401242 <secret_phase>
  401635:   bf 58 25 40 00          mov    $0x402558,%edi
  40163a:   e8 d1 f4 ff ff          callq  400b10 <puts@plt>
  40163f:   48 8b 44 24 68          mov    0x68(%rsp),%rax
  401644:   64 48 33 04 25 28 00    xor    %fs:0x28,%rax
  40164b:   00 00 
  40164d:   74 05                   je     401654 <phase_defused+0x90>
  40164f:   e8 dc f4 ff ff          callq  400b30 <__stack_chk_fail@plt>
  401654:   48 83 c4 78             add    $0x78,%rsp
  401658:   c3                      retq   
  401659:   90                      nop
  40165a:   90                      nop
  40165b:   90                      nop
  40165c:   90                      nop
  40165d:   90                      nop
  40165e:   90                      nop
  40165f:   90                      nop

        我们试着输入明码地址的内容,得到如下

(gdb) x/3s 0x402619
0x402619:       "%d %d %s"
0x402622:       "DrEvil"
0x402629:       "greatwhite.ics.cs.cmu.edu"

        我们通过设置4015f5行的断点并调试到这里发现这一行是第四个问题的输入

Breakpoint 2, 0x00000000004015fa in phase_defused ()
(gdb) x/2s 0x603870
0x603870 <input_strings+240>:   "7 0 DrEvil"
0x60387b <input_strings+251>:   ""

        故在第四个答案后面加上DrEvil则进入隐藏关卡。

2.7.2 秘密的答案

        我们先调出secret_phase的汇编

0000000000401242 <secret_phase>:
  401242:   53                      push   %rbx
  401243:   e8 56 02 00 00          callq  40149e <read_line>
  401248:   ba 0a 00 00 00          mov    $0xa,%edx
  40124d:   be 00 00 00 00          mov    $0x0,%esi
  401252:   48 89 c7                mov    %rax,%rdi    #经调试,%rax中存放我们输入值的地址
  401255:   e8 76 f9 ff ff          callq  400bd0 <strtol@plt>
  40125a:   48 89 c3                mov    %rax,%rbx    #再经调试,%rax中存放我们输入值
  40125d:   8d 40 ff                lea    -0x1(%rax),%eax  #输入值减一
  401260:   3d e8 03 00 00          cmp    $0x3e8,%eax  #与1000比较,
  401265:   76 05                   jbe    40126c <secret_phase+0x2a>   #无符号<=
  401267:   e8 ce 01 00 00          callq  40143a <explode_bomb>    #💣💥
  40126c:   89 de                   mov    %ebx,%esi
  40126e:   bf f0 30 60 00          mov    $0x6030f0,%edi
  #调试操作至此行我们得知我们需要调用func7这个函数,并且输入第一个参数是一个指针,第二个参数是我们输入值是否使用其他参数需要我们后续观察func7汇编时了解下面我们调用func7。
  401273:   e8 8c ff ff ff          callq  401204 <fun7>
  401278:   83 f8 02                cmp    $0x2,%eax    #不等于2就会爆炸否则我们运行成功
  40127b:   74 05                   je     401282 <secret_phase+0x40>
  40127d:   e8 b8 01 00 00          callq  40143a <explode_bomb>    #💣💥
  401282:   bf 38 24 40 00          mov    $0x402438,%edi   #明码显示我们成功解决隐藏关卡
  401287:   e8 84 f8 ff ff          callq  400b10 <puts@plt>
  40128c:   e8 33 03 00 00          callq  4015c4 <phase_defused>
  401291:   5b                      pop    %rbx
  401292:   c3                      retq   
  #无操作
  401293:   90                      nop
  401294:   90                      nop
  401295:   90                      nop
  401296:   90                      nop
  401297:   90                      nop
  401298:   90                      nop
  401299:   90                      nop
  40129a:   90                      nop
  40129b:   90                      nop
  40129c:   90                      nop
  40129d:   90                      nop
  40129e:   90                      nop
  40129f:   90                      nop

        调试至401267行我们可以大致了解到,我们输入的值必须小于1000。

        调试至401273行时我们需要调出函数汇编代码,然后进行下一步

#粗略观察该函数,我们得知这肯定是递归调用
#回到secret_phase汇编段我们得知此次调用需要返回2,否则失败
0000000000401204 <fun7>:
  401204:   48 83 ec 08             sub    $0x8,%rsp
  #入栈👆
  401208:   48 85 ff                test   %rdi,%rdi
  40120b:   74 2b                   je     401238 <fun7+0x34>   #空指针返回-1
  40120d:   8b 17                   mov    (%rdi),%edx  #经调试p指针会指向0x24
  40120f:   39 f2                   cmp    %esi,%edx
  401211:   7e 0d                   jle    401220 <fun7+0x1c>   #有符号<=
  401213:   48 8b 7f 08             mov    0x8(%rdi),%rdi
  401217:   e8 e8 ff ff ff          callq  401204 <fun7>
  40121c:   01 c0                   add    %eax,%eax
  40121e:   eb 1d                   jmp    40123d <fun7+0x39>
  401220:   b8 00 00 00 00          mov    $0x0,%eax
  401225:   39 f2                   cmp    %esi,%edx
  401227:   74 14                   je     40123d <fun7+0x39>
  401229:   48 8b 7f 10             mov    0x10(%rdi),%rdi
  40122d:   e8 d2 ff ff ff          callq  401204 <fun7>
  401232:   8d 44 00 01             lea    0x1(%rax,%rax,1),%eax
  401236:   eb 05                   jmp    40123d <fun7+0x39>
  401238:   b8 ff ff ff ff          mov    $0xffffffff,%eax
  #出栈👇
  40123d:   48 83 c4 08             add    $0x8,%rsp
  401241:   c3                      retq  

        我们尝试对func7编写伪码

//我们无法确认指针指向的内容,故此处用Type代替
int func7(Type *p, int input)
{
    if(p == NULL)
        return -1;
    if(&p <= input)
    {
        if(&p == input)
            return 0;
        else
        {
            p = p + 0x10;
            int n = func7(p, input);
            return 2 * n + 1;
        }
    }
    else
    {
        p = p + 0x8;
        int n = func7(p, input);
        return 2 * n;
    }
}

        显而易见,p结构内由于存在两个与自己相似的结构,故p所指向的数据结构是树(而且是二叉搜索树)。考虑左右节点的返回值我们知道需要先返回一次0(即找到该点)然后返回一次2 * n + 1,这样就变成了1,返回返回一次2 * n这样就得到我们想要的答案2,依据这个思路寻找我们有如下命令行,即可查阅到答案。当然,该树的结构我们很容易想到p = p + 0x10是加载右节点,p = p + 0x8是加载左节点。参考下图返回路径(红线)。

 

0x6031b0 <n34>: 0x0000006b
(gdb) x/x 0x6030f8
0x6030f8 <n1+8>:        0x00603110
(gdb) x/x 0x603120
0x603120 <n21+16>:      0x00603150
(gdb) x/x 0x603150
0x603150 <n32>: 0x00000016  #0x16 = 22

三,实验测试

我们输入以下答案文本

Border relations with Canada have never been better.
1 2 4 8 16 32
2 707 
7 0 DrEvil
ionefg
4 3 2 1 6 5
22

        命名为text.txt,然后输入以下命令行

./bomb text.txt

        得到如下结构

root@Nava9:~/Linux_lab/lab2# ./bomb test.txt 
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!

成功完成本次实验

  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值