ICS计算机系统实验--bomblab

实验内容及操作步骤:

  • 实验步骤

1.实验题目分析和准备

首先分析bomb.c的C语言代码,我们可以得知本实验共分为六个部分外加一个隐藏关卡,每一部分都要求输入一个字符串,如果输入的字符串不正确则会触发炸弹爆炸实验失败,因此本实验的主要目标就是找到这几个合法的字符串,但是我们会发现无法直接阅读C语言的函数,不能从C语言代码中获得信息。但是题目中还有一个可执行文件,因此我们能想到可以反汇编可执行文件从反汇编出的汇编代码中获得操作的信息从而解决问题。

本实验在linux环境下进行,方便使用用gdb调试方法进行实验,因为本次实验反汇编出的汇编代码很长,在命令行中有时会显示不全,因此我们可以通过指令objdump -d bomb > bomb.txt 可以将反汇编出的汇编代码导入到一个txt文件,然后进行分析。

 

2.实验步骤和分析过程

首先,我们分析主函数的汇编代码发现,在主函数中依次调用了phase1到phase6这几个函数,由此我们可以推断,这六个函数应该是和我们要找的六个字符串相关,因此我们要分析这六个函数的汇编代码。

    接下来进行这六个函数的汇编代码的分析:

Phase1

        08048b50 :

       8048b50: 83 ec 1c             sub    $0x1c,%esp

       8048b53: c7 44 24 04 8c a1 04      movl   $0x804a18c,0x4(%esp)

       8048b5a: 08

       8048b5b: 8b 44 24 20          mov    0x20(%esp),%eax

       8048b5f: 89 04 24                     mov    %eax,(%esp)

       8048b62: e8 5d 04 00 00         call   8048fc4

       8048b67: 85 c0                        test   %eax,%eax

       8048b69: 74 05                       je     8048b70

       8048b6b: e8 66 05 00 00         call   80490d6

       8048b70: 83 c4 1c             add    $0x1c,%esp

       8048b73: c3                          ret  

分析:

        分析汇编代码我们可以发现当调用了 函数之后的返回值%eax不是0的时候就会执行函数,引爆炸弹,因此传入 函数的两个字符串必须相等才行。通过对前面的汇编代码分析发现代码movl $0x804a18c,0x4(%esp)中有一个类似地址的立即数,分析得出此处可能是将地址作为这个函数的参数。

那么可以利用gdb调试来查看0x804a18c这个地址内的内容:

        查看到果然是一个字符串:

        When a problem comes along, you must zip it!

        再看前面的代码mov    0x20(%esp),%eax     mov    %eax,(%esp)两部分是吧我们输入的字符串作为参数传递给 函数,因此可以得到,我们输入的字符串必须和这个地址内的字符串相等,炸弹才不会爆炸。

        因此这一题我们输入的字符串应该是:When a problem comes along, you must zip it!

验证:(正确进入下一关!)

 

Phase2

08048b74 :

 8048b74:     56                       push   %esi

 8048b75:     53                       push   %ebx

 8048b76:     83 ec 34                   sub    $0x34,%esp

 8048b79:     8d 44 24 18                lea    0x18(%esp),%eax

 8048b7d:     89 44 24 04                mov    %eax,0x4(%esp)

 8048b81:     8b 44 24 40                mov    0x40(%esp),%eax

 8048b85:     89 04 24                  mov    %eax,(%esp)

 8048b88:     e8 7e 06 00 00       call   804920b

 8048b8d:     83 7c 24 18 01       cmpl   $0x1,0x18(%esp)

 8048b92:     74 05                   je     8048b99

 8048b94:     e8 3d 05 00 00       call   80490d6

 8048b99:     8d 5c 24 1c                 lea    0x1c(%esp),%ebx

 8048b9d:     8d 74 24 30                lea    0x30(%esp),%esi

 8048ba1:      8b 43 fc                   mov    -0x4(%ebx),%eax

 8048ba4:      01 c0                     add    %eax,%eax

 8048ba6:      39 03                     cmp    %eax,(%ebx)

 8048ba8:      74 05                   je     8048baf

 8048baa:      e8 27 05 00 00       call   80490d6

 8048baf:      83 c3 04                   add    $0x4,%ebx

 8048bb2:     39 f3                     cmp    %esi,%ebx

 8048bb4:     75 eb                   jne    8048ba1

 8048bb6:     83 c4 34                   add    $0x34,%esp

 8048bb9:     5b                       pop    %ebx

 8048bba:      5e                       pop    %esi

 8048bbb:     c3                       ret 

分析:

        代码的开头部分是栈帧的创建过程和参数传递过程,我们可以注意到开头调用了函数,可以猜测出这一关的字符应该是要输入六个数字。紧接着由cmpl   $0x1,0x18(%esp)指令我们可以看出我们要输入的第一个数字必须是 1 ,否则的话就会调用函数,引爆炸弹。

       接下来的lea    0x1c(%esp),%ebx和mov    -0x4(%ebx),%eax两条指令先把第二个数的地址存放进ebx,然后又把第一个数放进eax,接着add    %eax,%eax指令相当于把,eax中的数据乘2,cmp    %eax,(%ebx)和ebx的地址中的数据比较,也就是和输入的第2个数比较,如果不相等的话就会调用函数引爆炸弹。。如果相等,那么ebx的地址加4,也就是指向下一个数,也就是第3个数,cmp    %esi,%ebx这条指令比较当前的地址是不是已经超过了我们输入的6个数的存储地址,如果超过了那就停止循环,如果没有,就跳转到mov    -0x4(%ebx),%eax指令继续进行循环。这个循环内进行的操作就是将前一个数乘2和当前的数比较。

       因此如果不想引爆炸弹,我们输入的6个数中,后一个数必须是前一个数的2倍,并且第一个数是1,那么很容易就能得到了6个数分别是:1  2  4  8  16  32

       因此这一题的答案是: 1  2  4  8  16  32

验证:(正确进入下一关!)

 

 

Phase3

总的代码部分太长省略粘贴,只粘贴了重要的部分。

分析:

8048bbf: 8d 44 24 1c          lea    0x1c(%esp),%eax

 8048bc3:       89 44 24 0c          mov    %eax,0xc(%esp)

 8048bc7:       8d 44 24 18          lea    0x18(%esp),%eax

 8048bcb:       89 44 24 08          mov    %eax,0x8(%esp)

 8048bcf:        c7 44 24 04 7f a3 04      movl   $0x804a37f,0x4(%esp)

 8048bd6:       08

 8048bd7:       8b 44 24 30          mov    0x30(%esp),%eax

 8048bdb:       89 04 24                     mov    %eax,(%esp)

 8048bde:       e8 8d fc ff ff           call   8048870 <__isoc99_sscanf@plt>

 8048be3:       83 f8 01             cmp    $0x1,%eax

 8048be6:       7f 05                        jg     8048bed

 8048be8:       e8 e9 04 00 00          call   80490d6

 8048bed:       83 7c 24 18 07         cmpl   $0x7,0x18(%esp)

 8048bf2:       77 3c                        ja     8048c30

        在汇编代码的开头部分,有一个指令movl $0x804a37f,0x4(%esp),我们在命令行gdb调试中输入指令x/s $0x804a37f查看,可以得到"%d %d",由此显示出这一题我们输入的应该是两个数字。开头的地址传送部分都是给函数<__isoc99_sscanf@plt>传送参数,把我们输入的数据写入栈帧,函数在调用<__isoc99_sscanf@plt>函数后,接着函数调用了而cmp $0x1,%eax表明输入参数多于1个。

并且cmpl   $0x7,0x18(%esp)指令也告诉我们,我们输入的第一个数字必须小于等于7,否则就会引爆炸弹。

 

8048bf4: 8b 44 24 18          mov    0x18(%esp),%eax

 8048bf8:       ff 24 85 ec a1 04 08       jmp    *0x804a1ec(,%eax,4)

 8048bff: b8 dc 03 00 00         mov    $0x3dc,%eax

 8048c04:       eb 3b                        jmp    8048c41

 8048c06:       b8 ab 03 00 00         mov    $0x3ab,%eax

 8048c0b:       eb 34                        jmp    8048c41

 8048c0d:       b8 76 03 00 00         mov    $0x376,%eax

 8048c12:       eb 2d                        jmp    8048c41

 8048c14:       b8 71 03 00 00         mov    $0x371,%eax

 8048c19:       eb 26                        jmp    8048c41

 8048c1b:       b8 eb 00 00 00         mov    $0xeb,%eax

 8048c20:       eb 1f                        jmp    8048c41

 8048c22:       b8 ac 02 00 00          mov    $0x2ac,%eax

 8048c27:       eb 18                        jmp    8048c41

 8048c29:       b8 75 03 00 00         mov    $0x375,%eax

 8048c2e:       eb 11                        jmp    8048c41

 8048c30:       e8 a1 04 00 00          call   80490d6

 8048c35:       b8 00 00 00 00         mov    $0x0,%eax

 8048c3a:       eb 05                        jmp    8048c41

 8048c3c:       b8 c1 00 00 00         mov    $0xc1,%eax

 8048c41:       3b 44 24 1c          cmp    0x1c(%esp),%eax

 8048c45:       74 05                       je     8048c4c

        接着向下查看代码,    mov    0x18(%esp),%eax把我们输入的第一个参数传递给eax;然后看到jmp    *0x804a1ec(,%eax,4),这是switch跳转语句,即跳转到以地址*0x804a1ec为基址的跳转表中。我们可以查看这个跳转表中的地址元素,在gdb调试中输入x/8x 0x804a1ec可以查看到其中储存的地址。

我们可以对应这其中储存的地址找到代码中相应的指令,例如当eax为0时,指向的地址是0x8048bff,在代码中对应指令mov    $0x3dc,%eax;jmp    8048c41 ;以及最后跳转到的代码指令cmp    0x1c(%esp),%eax也就是将0x3dc存放进eax和我们输入的第2个数作比较,相等时才算成功。因此我们输入的第二个数也就是0x3dc对应的十进制的988。

        当然这道题的答案不唯一,从0到7一共有8种可能的答案,分别对应着跳转表中的8个地址,8个条指令,也对应着8组数据。

(0  988(1  193(2  939(3  886(4  881(5  235(6  684(7  885

        在此我们就验证第一个答案:0  988

验证:(正确进入下一关!)

 

Phase4

总的代码部分太长省略粘贴,只粘贴了重要的部分。

    Phase4的代码前面部分和phase3很相似,也是调用了<__isoc99_sscanf@plt>函数传送参数,并且通过查看0x804a37f和指令cmp    $0x2,%eax,可以知道也是输入了两个整数。

8048cd9:       8b 44 24 18          mov    0x18(%esp),%eax

 8048cdd:       83 f8 01             cmp    $0x1,%eax

 8048ce0:       7e 05                        jle    8048ce7

 8048ce2:       83 f8 04             cmp    $0x4,%eax

 8048ce5:       7e 05                        jle    8048cec

        从这部分代码我们可以知道输入的其中一个数必须大于1小于等于4.

8048cec:        8b 44 24 18          mov    0x18(%esp),%eax

 8048cf0:        89 44 24 04          mov    %eax,0x4(%esp)

 8048cf4:        c7 04 24 09 00 00 00     movl   $0x9,(%esp)

 8048cfb:        e8 50 ff ff ff              call   8048c50

        这几行代码显示了在调用func4函数前进行的参数传递过程,将输入的一个数字和数字9传递给func4,进入func4函数。最后将函数的返回值和我们输入的第一个数进行比较,正确时才成功。

08048c50 :

 8048c50:       83 ec 1c             sub    $0x1c,%esp

 8048c53:       89 5c 24 10          mov    %ebx,0x10(%esp)

 8048c57:       89 74 24 14          mov    %esi,0x14(%esp)

 8048c5b:       89 7c 24 18          mov    %edi,0x18(%esp)

 8048c5f:        8b 74 24 20          mov    0x20(%esp),%esi

 8048c63:       8b 5c 24 24          mov    0x24(%esp),%ebx

 8048c67:       85 f6                        test   %esi,%esi

 8048c69:       7e 2b                        jle    8048c96

 8048c6b:       83 fe 01             cmp    $0x1,%esi

 8048c6e:       74 2b                       je     8048c9b

 8048c70:       89 5c 24 04          mov    %ebx,0x4(%esp)

 8048c74:       8d 46 ff             lea    -0x1(%esi),%eax

 8048c77:       89 04 24                     mov    %eax,(%esp)

 8048c7a:       e8 d1 ff ff ff              call   8048c50

 8048c7f:        8d 3c 18             lea    (%eax,%ebx,1),%edi

 8048c82:       89 5c 24 04          mov    %ebx,0x4(%esp)

 8048c86:       83 ee 02             sub    $0x2,%esi

 8048c89:       89 34 24                     mov    %esi,(%esp)

 8048c8c:       e8 bf ff ff ff              call   8048c50

 8048c91:       8d 1c 07             lea    (%edi,%eax,1),%ebx

 8048c94:       eb 05                        jmp    8048c9b

 8048c96:       bb 00 00 00 00         mov    $0x0,%ebx

 8048c9b:       89 d8                       mov    %ebx,%eax

 8048c9d:       8b 5c 24 10          mov    0x10(%esp),%ebx

 8048ca1:       8b 74 24 14          mov    0x14(%esp),%esi

 8048ca5:       8b 7c 24 18          mov    0x18(%esp),%edi

 8048ca9:       83 c4 1c             add    $0x1c,%esp

8048cac:        c3                          ret 

      首先看func4部分的代码我们可以发现它在函数内部又调用了它本身,因此这是个递归函数。接着来判断这个函数的内部结构。我们对传入的两个参数暂时称作 x和y第一次进入时,x=9,y=a(也就是我们输入的数)。

        从指令test   %esi,%esi;jle    8048c96 我们可以看出当x为0时,则返回0;根据指令cmp    $0x1,%esi;je     8048c9b 得出当x为1时,返回另一个参数y,也就是我们输入的参数a。

        剩下的部分也就是参数x大于1的时候,首先指令lea    -0x1(%esi),%eax以及后面的指令我们可以看出,参数x把自己减1后又作为一个参数x-1和y传入下一层递归func4,并把返回值和y相加,传到edi,接着后面sub    $0x2,%esi指令显示参数x把自己减2后又作为一个参数x-2和y传入下一层递归func4,并把返回值和之前的edi中储存的值相加传给ebx然后作为返回值返回。

        以上就是这个func4函数递归的整个过程。整理一下我们可以用c语言来表示这个过程。

       ICS计算机系统实验--bomblab_第1张图片

        这就是这个函数递归的过程。由此可以很容易的写出对应的解,同样本题也不只有一个答案,这里我们验证的解是:176   2(只要第二个参数在1到4的范围内通过函数我们就能得到第一个参数也就能得到正确的解)

验证:(正确进入下一关!)(在这一关还有一个隐藏关卡的开启按钮后面会提到)

 

Phase5

总的代码部分太长省略粘贴,只粘贴了重要的部分。

分析:

8048d0f:       53                         push   %ebx

 8048d10:       83 ec 28             sub    $0x28,%esp

 8048d13:       8b 5c 24 30          mov    0x30(%esp),%ebx

 8048d17:       65 a1 14 00 00 00       mov    %gs:0x14,%eax

 8048d1d:       89 44 24 1c          mov    %eax,0x1c(%esp)

 8048d21:       31 c0                        xor    %eax,%eax

 8048d23:       89 1c 24             mov    %ebx,(%esp)

 8048d26:       e8 80 02 00 00         call   8048fab

 8048d2b:       83 f8 06             cmp    $0x6,%eax

 8048d2e:       74 05                       je     8048d35
        代码的开头部分还是栈帧的准备阶段和输入函数的调用。从call   8048fab 和cmp    $0x6,%eax两条指令可以看出要输入的是一个长度为6的字符串。

 

8048d35:       b8 00 00 00 00         mov    $0x0,%eax

 8048d3a:       0f be 14 03            movsbl (%ebx,%eax,1),%edx

 8048d3e:       83 e2 0f             and    $0xf,%edx

 8048d41:       0f b6 92 0c a2 04 08      movzbl 0x804a20c(%edx),%edx

 8048d48:       88 54 04 15          mov    %dl,0x15(%esp,%eax,1)

 8048d4c:       83 c0 01             add    $0x1,%eax

 8048d4f:       83 f8 06             cmp    $0x6,%eax

 8048d52:       75 e6                        jne    8048d3a

        这一段的代码是一个循环,首先对第一个字符and    $0xf,%edx取出来它的二进制表示的低4位,然后用基址变址寻址从0x804a20c(%edx)中找到相应的字符传到edx,在取低8位也就是正好一个char类型的字符传到0x15(%esp,%eax,1)这样的一个地址中去。然后循环对6个字符都进行这个操作。至此我们可能还看不出什么头绪,但是得到了一个字符串。往下继续看。

8048d54:       c6 44 24 1b 00         movb   $0x0,0x1b(%esp)

 8048d59:       c7 44 24 04 e2 a1 04      movl   $0x804a1e2,0x4(%esp)

 8048d60:       08

 8048d61:       8d 44 24 15          lea    0x15(%esp),%eax

 8048d65:       89 04 24                     mov    %eax,(%esp)

 8048d68:       e8 57 02 00 00         call   8048fc4

 8048d6d:       85 c0                        test   %eax,%eax

 8048d6f:       74 05                       je     8048d76

        这一部分调用了函数,并传入两个字符串,其中一个是刚才得到的,另一个储存在地址 $0x804a1e2 中,我们可以查看这个地址中的内容。

        这个内存中储存的字符串是 flames 也就是说前面通过计算变换得到的字符串也应该是flames,因此我们可以到这推回去,推测我们输入的字符串是什么,首先查看地址0x804a20c 里面存放的数据

        可以看到前面有一串字符,因为flames是通过基址变址寻址得到的,因此通过这个字符串我们就能得到他们对应的偏移分别是9 15 1 0 5 7,而这些偏移正是通过我们输入的字符的ASCII码的后四位得到的,因此他们对应的二进制分别是1001  1111  0001  0000  0101  0111,然后从ASCII码表中找出对应的低位能对应上的字母即可,同样这一题的答案也是不唯一的。

        在此我选取验证的字符串是:yoapuw

验证:(正确进入下一关!)

 

Phase6

总的代码部分太长省略粘贴,只粘贴了重要的部分。

分析:

开始部分依然是栈帧的准备阶段,从函数我们可以看出这次输入的数据是6个数字,而且

8048dab:       8b 44 b4 10          mov    0x10(%esp,%esi,4),%eax

 8048daf:        83 e8 01             sub    $0x1,%eax

 8048db2:       83 f8 05             cmp    $0x5,%eax

从这几行可以看出输入的数字小于等于6,接着是一段循环代码:

8048dbc:       83 c6 01             add    $0x1,%esi

 8048dbf:       83 fe 06             cmp    $0x6,%esi

 8048dc2:       74 33                       je     8048df7

 8048dc4:       89 f3                        mov    %esi,%ebx

 8048dc6:       8b 44 9c 10          mov    0x10(%esp,%ebx,4),%eax

 8048dca:       39 44 b4 0c          cmp    %eax,0xc(%esp,%esi,4)

 8048dce:       75 05                       jne    8048dd5

 8048dd0:       e8 01 03 00 00         call   80490d6

 8048dd5:       83 c3 01             add    $0x1,%ebx

 8048dd8:       83 fb 05             cmp    $0x5,%ebx

 8048ddb:       7e e9                        jle    8048dc6

 8048ddd:       eb cc                        jmp    8048dab

        这一段代码的功能就是通过循环判断这6个数字都是相互不相等的,        mov    0x10(%esp,%ebx,4),%eax和 cmp    %eax,0xc(%esp,%esi,4)两条指令通过不断地循环,把当前的数和之前的数进行比较必须都不相同才不会爆炸,经过6次循环之后,可以判断这六个数都互不相同。

8048ddf:       8b 52 08                     mov    0x8(%edx),%edx

 8048de2:       83 c0 01             add    $0x1,%eax

 8048de5:       39 c8                        cmp    %ecx,%eax

 8048de7:       75 f6                        jne    8048ddf

 8048de9:       89 54 b4 28          mov    %edx,0x28(%esp,%esi,4)

 8048ded:       83 c3 01              add    $0x1,%ebx

 8048df0:       83 fb 06             cmp    $0x6,%ebx

 8048df3:       75 07                       jne    8048dfc

 8048df5:       eb 1c                        jmp    8048e13

 8048df7:       bb 00 00 00 00         mov    $0x0,%ebx

 8048dfc:        89 de                        mov    %ebx,%esi

 8048dfe:        8b 4c 9c 10            mov    0x10(%esp,%ebx,4),%ecx

 8048e02:       b8 01 00 00 00         mov    $0x1,%eax

 8048e07:       ba 3c c1 04 08          mov    $0x804c13c,%edx

 8048e0c:       83 f9 01             cmp    $0x1,%ecx

 8048e0f:        7f ce                jg     8048ddf

 8048e11:       eb d6                        jmp    8048de9

        这一部分代码也是一个循环,根据我们输入的6个数字,从首地址$0x804c13c开始,根据输入的数字的不同在内存中查找相应的存储单元中的数据存到栈空间内,通过查看内存的内容我们可以发现,这些单元的数据全部都是地址形式的。其实循环里面对于%ebx的迭代操作,很像链表的操作,把最初的那个赋给%edx的值看作是链表的头节点的地址,然后每一个节点都有一个指针域指向下一个节点,那么这个迭代过程就是在节点之间移动。将这六个节点的地址按照我们输入的数字的顺序,已经存入栈帧中间了。

ICS计算机系统实验--bomblab_第2张图片

8048e13:       8b 5c 24 28          mov    0x28(%esp),%ebx

 8048e17:       8b 44 24 2c          mov    0x2c(%esp),%eax

 8048e1b:       89 43 08                     mov    %eax,0x8(%ebx)

 8048e1e:       8b 54 24 30          mov    0x30(%esp),%edx

 8048e22:       89 50 08                     mov    %edx,0x8(%eax)

 8048e25:       8b 44 24 34          mov    0x34(%esp),%eax

 8048e29:       89 42 08                     mov    %eax,0x8(%edx)

 8048e2c:       8b 54 24 38          mov    0x38(%esp),%edx

 8048e30:       89 50 08                     mov    %edx,0x8(%eax)

 8048e33:       8b 44 24 3c          mov    0x3c(%esp),%eax

 8048e37:       89 42 08                     mov    %eax,0x8(%edx)

 8048e3a:       c7 40 08 00 00 00 00     movl   $0x0,0x8(%eax)

        这部分的代码是把节点中的数据和地址都按照我们输入的数据存放在了栈帧中。例如我们输入的第一个数字如果是4 那么此时栈帧中的第一个节点就是值为390的节点。

8048e41:       be 05 00 00 00         mov    $0x5,%esi

 8048e46:       8b 43 08                     mov    0x8(%ebx),%eax

 8048e49:       8b 10                       mov    (%eax),%edx

 8048e4b:       39 13                       cmp    %edx,(%ebx)

 8048e4d:       7d 05                       jge    8048e54

 8048e4f:        e8 82 02 00 00         call   80490d6

 8048e54:       8b 5b 08                     mov    0x8(%ebx),%ebx

 8048e57:       83 ee 01             sub    $0x1,%esi

 8048e5a:       75 ea                        jne    8048e46

        这部分代码是一个比较排序的循环,mov    0x8(%ebx),%eax;mov    (%eax),%edx;cmp    %edx,(%ebx) 这三个指令可以看出每次都取前一个节点的值和后一个比较,前一个节点的值必须大于后一个节点才不会爆炸。也就是说我们前面在输入6个数字进行排序的时候,内存中存储的值大的必须排在前面。通过查看内存的数据我们知道,6个值分别是16进制的67  1b9  12e  390  136  2b5 ,因此在输入数据时必须保证大的在前面,所以输入的顺序为4  6  2  5  3  1

        这也就是这一题的答案,输入数字为:4  6  2  5  3  1

验证:(通过!)

 

至此前面的六关已经全部通过:

 

隐藏的关卡

Phase_defused

因为当六关过了之后就直接退出了程序,没有进入其他关卡,的所以证明这个隐藏的关卡一定还有其他的触发机关才能触发,首先我们发现,在C语言的代码中每一个关卡后面都有一个phase_defused函数,但是在之前的解题中并没有使用到,因此我们查看这个函数的代码,发现其中有一个函数这就验证了我的猜想,这个函数应该是能触发隐藏关卡的一个入口。于是分析这个汇编代码:

0804925b :

 804925b:       81 ec 8c 00 00 00        sub    $0x8c,%esp

 8049261:       65 a1 14 00 00 00       mov    %gs:0x14,%eax

 8049267:       89 44 24 7c          mov    %eax,0x7c(%esp)

 804926b:       31 c0                        xor    %eax,%eax

 804926d:       83 3d cc c3 04 08 06      cmpl   $0x6,0x804c3cc

 8049274:       75 72                       jne    80492e8

通过这一段代码我们可以看出出现了一个cmpl   $0x6,0x804c3cc指令,因为正好是6,推测可能是和我们通过的关卡数有关,如果没有通过6关应该不会触发这个隐藏关卡。

 

 8049276:       8d 44 24 2c          lea    0x2c(%esp),%eax

 804927a:       89 44 24 10          mov    %eax,0x10(%esp)

 804927e:       8d 44 24 28          lea    0x28(%esp),%eax

 8049282:       89 44 24 0c          mov    %eax,0xc(%esp)

 8049286:       8d 44 24 24          lea    0x24(%esp),%eax

 804928a:       89 44 24 08          mov    %eax,0x8(%esp)

 804928e:       c7 44 24 04 85 a3 04      movl   $0x804a385,0x4(%esp)

 8049295:       08

 8049296:       c7 04 24 d0 c4 04 08      movl   $0x804c4d0,(%esp)

 804929d:       e8 ce f5 ff ff           call   8048870 <__isoc99_sscanf@plt>

 80492a2:       83 f8 03             cmp    $0x3,%eax

 80492a5:       75 35                       jne    80492dc

前面这一部分代码传递了两个立即数地址给<__isoc99_sscanf@plt>函数,查看第一个地址中存放的

由此可以看出我们输入的数据应该是两个整数和一个字符串。

第二个地址提供了输入数据的地址位置,我们推测应该是在之前的某一个关卡中触发的,因此这个输入的地址应该和其中一个关卡的数据输入地址相同 ,于是我们查找每一个关卡数据输入的地址,正好发现第四关的数据输入的地址和这个地址相同。都是0x804c4d0.由此可以判断这个关卡在第四关触发。

ICS计算机系统实验--bomblab_第3张图片

 80492a7:       c7 44 24 04 8e a3 04      movl   $0x804a38e,0x4(%esp)

 80492ae:       08

 80492af:        8d 44 24 2c          lea    0x2c(%esp),%eax

 80492b3:       89 04 24                     mov    %eax,(%esp)

 80492b6:       e8 09 fd ff ff             call   8048fc4

 80492bb:       85 c0                        test   %eax,%eax

 80492bd:       75 1d                       jne    80492dc

 80492bf:       c7 04 24 54 a2 04 08      movl   $0x804a254,(%esp)

 80492c6:       e8 35 f5 ff ff             call   8048800

 80492cb:       c7 04 24 7c a2 04 08      movl   $0x804a27c,(%esp)

 80492d2:       e8 29 f5 ff ff             call   8048800

 80492d7:       e8 d7 fb ff ff             call   8048eb3

这一段代码显示程序从地址0x804a38e中取了一个字符串和我们输入的字符串比较,相同时才能触发隐藏关卡,于是查看内存0x804a38e中的值是 DrEvil

因此在第四关的答案后输入 DrEvil 就可在最后进入隐藏关卡,经过验证正确

ICS计算机系统实验--bomblab_第4张图片

 

 

secret_phase

总的代码部分太长省略粘贴,只粘贴了重要的部分。

08048eb3 :

 8048eb3:       53                         push   %ebx

 8048eb4:       83 ec 18             sub    $0x18,%esp

 8048eb7:       e8 41 02 00 00         call   80490fd

 8048ebc:       c7 44 24 08 0a 00 00      movl   $0xa,0x8(%esp)

 8048ec3:       00

 8048ec4:       c7 44 24 04 00 00 00     movl   $0x0,0x4(%esp)

 8048ecb:       00

 8048ecc:        89 04 24                     mov    %eax,(%esp)

 8048ecf: e8 0c fa ff ff             call   80488e0

前面是栈帧准备的阶段,然后是给函数函数传参,函数调用时写为:strtol(char *ch1,char *ch2,int base){},后面的指令时是把字符串*ch1当作一个base进制的整数并返回,这里的base=0xa,说明为10进制,调用strtol,说明把你输入的字符串转化为一个当作一个10进制的整数来使用。

 8048ed4:       89 c3                        mov    %eax,%ebx

 8048ed6:       8d 40 ff             lea    -0x1(%eax),%eax

 8048ed9:       3d e8 03 00 00         cmp    $0x3e8,%eax

 8048ede:       76 05                       jbe    8048ee5

 8048ee0:       e8 f1 01 00 00          call   80490d6

 8048ee5:       89 5c 24 04          mov    %ebx,0x4(%esp)

 8048ee9:       c7 04 24 88 c0 04 08      movl   $0x804c088,(%esp)

 8048ef0:        e8 6d ff ff ff              call   8048e62

 8048ef5:        83 f8 03             cmp    $0x3,%eax

 8048ef8:        74 05                       je     8048eff

 8048efa: e8 d7 01 00 00         call   80490d6

这一部分代码可以看出strtol函数的返回值不能大于0x3e8,否则就会爆炸。然后把0x804c088和刚才输入的数当做参数传送给func7,进入func7函数。而且返回值要等于3.

 

08048e62 :

 8048e62:       53                         push   %ebx

 8048e63:       83 ec 18             sub    $0x18,%esp

 8048e66:       8b 54 24 20          mov    0x20(%esp),%edx

 8048e6a:       8b 4c 24 24          mov    0x24(%esp),%ecx

 8048e6e:       85 d2                       test   %edx,%edx

 8048e70:       74 37                       je     8048ea9

 8048e72:       8b 1a                        mov    (%edx),%ebx

 8048e74:       39 cb                        cmp    %ecx,%ebx

 8048e76:       7e 13                        jle    8048e8b

 8048e78:       89 4c 24 04          mov    %ecx,0x4(%esp)

 8048e7c:       8b 42 04                     mov    0x4(%edx),%eax

 8048e7f:        89 04 24                     mov    %eax,(%esp)

 8048e82:       e8 db ff ff ff              call   8048e62

 

 8048e87:       01 c0                        add    %eax,%eax

 8048e89:       eb 23                        jmp    8048eae

 8048e8b:       b8 00 00 00 00         mov    $0x0,%eax

 8048e90:       39 cb                        cmp    %ecx,%ebx

 8048e92:       74 1a                        je     8048eae

 8048e94:       89 4c 24 04          mov    %ecx,0x4(%esp)

 8048e98:       8b 42 08                     mov    0x8(%edx),%eax

 8048e9b:       89 04 24                     mov    %eax,(%esp)

 8048e9e:       e8 bf ff ff ff              call   8048e62

 8048ea3:       8d 44 00 01          lea    0x1(%eax,%eax,1),%eax

 8048ea7:       eb 05                        jmp    8048eae

 8048ea9:       b8 ff ff ff ff               mov    $0xffffffff,%eax

 8048eae:        83 c4 18             add    $0x18,%esp

 8048eb1:       5b                         pop    %ebx

 8048eb2:       c3                          ret   

分析:

        这又是一个递归函数,可以看出当我们传入的值和传入的地址储存的值相同时返回的是0;当地址内的值大于输入的数时,将当前的地加4,里面储存的地址作为一个新的地址,和输入的值一同传入下一个func7函数,并返回他的2倍;当地址内的值小于输入的数时,将当前的地加8,里面储存的地址作为一个新的地址,和输入的值一同传入下一个func7函数,并返回他的2倍加1。

等价的C语言代码如下:

类似于一棵搜索二叉树,因为最终要返回的值是3,所以第二层递归时一定是2*1+1的情况,因此通过gdb调试查看,得到第二次的地址为0x0804c0a8,然后第三层同理应该是2*0+1,所以第三层的地址为0x0804c0d0,而且因为这一层返回值为0,所以这个地址单元内的值就是,我们输入的那个数,gdb查看得到0x6b转化为十进制是107,因此这个关卡输入的数据是   107

ICS计算机系统实验--bomblab_第5张图片

经过验证正确:

ICS计算机系统实验--bomblab_第6张图片

至此,本实验所有的关卡及隐藏关卡都已经完成!

 

收获与体会

    通过这次实验,对于Linux系统的一些操作命令有了一些了解和掌握,学习了如何使用gdb这个强大的工具进行调试,以及加深了对于汇编语言的熟悉。

    本次实验花费的时间较长,明显能感觉到对汇编语言的阅读能力和要求很高,难度也是循序渐进的,在做的时候必须思路清晰不能混乱,尤其是到了后面比较复杂的时候更不能乱。

    比较有效的方法就是一步一步的来,最好能列一个表对寄存器的数据实时更新,这样看起来方便,也能对分析汇编代码有帮助。

    本次实验遇到困难主要是在递归的部分,可能还是对递归的机器及表达不够熟悉,在phase4上花费了一些时间,最终还是克服了,也能写出它的C语言代码,还是很有成就感的。

    经过这几个题目的洗礼对汇编语言也有了更深的理解,在阅读的时候也能更加的流畅,加深了对程序底层的理解,也能熟练地运用gdb调试工具进行代码的调试,内存数据和寄存器数据的查看,收获很大

你可能感兴趣的:(反汇编)