CSAPP bomblab

来源:互联网 发布:林忆莲 都市触觉 知乎 编辑:程序博客网 时间:2024/06/10 09:58

Bomblab——二进制炸弹实验

学校:湖南大学

班级:智能

姓名:诸神

 

【实验目的】

理解汇编语言,学会使用gdb调试。

【实验原理】

二进制炸弹是作为一个目标代码文件提供给学生们的程序,运行时,它提示用户输入6个不同的字符串。如果其中任何一个不正确,炸弹就会“爆炸”:打印出一条错误信息。学生通过反汇编和逆向工程来确定是哪六个字符串,从而解除它们。

【实验过程】

一、反汇编bomb,并输出到txt文件。

打开终端,用objdump –d bomb > 1.txt反汇编bomb并且将汇编代码储存到文件1.txt中。

二、拆炸弹

1、 首先大致浏览代码,发现它有一个main函数,phase_1到phase_6六个函数,一个secret_phase函数,表明我们要从phase_1到phase_6的代码中得到答案,除了六个关卡之外还有一个隐藏关卡。

2、 Phase_1

我们从代码中可以看到当<strings_not_equal>返回值%eax!=0时,炸弹会炸。再向上看,有立即数$0x804a15c,这里是当做地址用。故此地址储存的字符串即为通关密码。在gdb调试中调用指令查看,得到通关密码:


3、 phase_2

首先注意到phase_2调用了函数<read_six_numbers>读入六个数。紧接着5条指令:

8048d84: 83 7d e0 00                cmpl  $0x0,-0x20(%ebp)

8048d88: 75 06                        jne   8048d90 <phase_2+0x26>

8048d8a: 83 7d e4 01                    cmpl  $0x1,-0x1c(%ebp)

8048d8e: 74 05                        je    8048d95 <phase_2+0x2b>

8048d90: e8 3c 03 00 00                 call  80490d1 <explode_bomb>

是说第一个数为0,第二个数为1,否则bomb。接下来是一个循环,主要指令是add  -0x8(%ebx),%eax,就是后一个数时它前两个数之和,很容易联想到斐波切纳数列。即输入斐波切纳数列前六个数即可:0 1 1 2 3 5。

4、 phase_3

注意到代码movl   $0x804a23e,0x4(%esp),用gdb查看:x/s 0x804a23e,得到"%d %d",

即要输入两个数。而cmp $0x1,%eax表明输入参数必须多于1个。再往下到达cmpl  $0x7,-0xc(%ebp),即输入的第一个参数值必须小于等于7。然后看到jmp   *0x8049920(,%eax,4),这是典型的switch跳转语句,即跳转到以地址*0x8049920为基址的跳转表中。在结束指令之前发现cmpl   $0x5,-0xc(%ebp),表明输入的第一个参数值必须要小于等于5。用gdb查看跳转表:


按照跳转表计算,得到共计6个答案:0  147,1  -641,2  217,3  -534,4  0,5  -534。

5、 phase_4

同样由movl   $0x804a23e,0x4(%esp)知道要输入两个数。由test   %eax,%eax知输入的第一个数为非负数,由cmp    $0xe,%eax知这个数还小于14。将这个数还有0,e作为func4的初始参数进行递归运算。由cmp   $0x1,%eax知返回值%eax要等于1。由cmpl   $0x1,-0x10(%ebp)知输入的第二个数为1。

<func4>代码:

08048b60<func4>:

 8048b60:        55                   push   %ebp

 8048b61:        89e5                  mov    %esp,%ebp

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

 8048b66:        895d f8             mov    %ebx,-0x8(%ebp)

 8048b69:        8975 fc               mov    %esi,-0x4(%ebp)

 8048b6c:        8b55 08               mov    0x8(%ebp),%edx   //第一个参数A

 8048b6f:         8b45 0c             mov    0xc(%ebp),%eax   //第二个参数B

 8048b72:        8b5d 10               mov    0x10(%ebp),%ebx  //第三个参数C

 8048b75:        89d9                mov    %ebx,%ecx

 8048b77:        29c1                 sub    %eax,%ecx

 8048b79:        89ce                 mov    %ecx,%esi

 8048b7b:        c1ee 1f               shr    $0x1f,%esi

 8048b7e:        8d0c 0e             lea    (%esi,%ecx,1),%ecx

 8048b81:        d1f9                 sar    %ecx    //C/2

 8048b83:        01c1                 add    %eax,%ecx //在此处将递归返回值加倍

 8048b85:        39d1                cmp    %edx,%ecx

 8048b87:        7e17                jle    8048ba0 <func4+0x40>

//若C>A,C=C-1,进入递归

 8048b89:        83e9 01             sub    $0x1,%ecx

 8048b8c:        894c 24 08            mov    %ecx,0x8(%esp)

 8048b90:        8944 24 04            mov    %eax,0x4(%esp)

 8048b94:        8914 24             mov    %edx,(%esp)

 8048b97:        e8c4 ff ff ff                call   8048b60<func4>

 8048b9c:        01c0                 add    %eax,%eax

 8048b9e:        eb20                jmp    8048bc0 <func4+0x60>

 8048ba0:        b800 00 00 00          mov    $0x0,%eax    //B=0

 8048ba5:        39d1                cmp    %edx,%ecx

 8048ba7:        7d17                jge    8048bc0 <func4+0x60>

//若C<A,则B=C+1,C=14,进入递归

 8048ba9:        895c 24 08            mov    %ebx,0x8(%esp)

 8048bad:        83c1 01             add    $0x1,%ecx

 8048bb0:        894c 24 04            mov    %ecx,0x4(%esp)

 8048bb4:        8914 24             mov    %edx,(%esp)

 8048bb7:        e8a4 ff ff ff                call   8048b60 <func4>

//在此处将递归返回值加倍后再加1

 8048bbc:        8d44 00 01            lea    0x1(%eax,%eax,1),%eax

//若C=A,则递归终止

 8048bc0:        8b5d f8             mov    -0x8(%ebp),%ebx

 8048bc3:        8b75 fc               mov    -0x4(%ebp),%esi

 8048bc6:        89ec                 mov    %ebp,%esp

 8048bc8:        5d                   pop    %ebp

 8048bc9:        c3                   ret

因为递归返回值为1,故最外层返回值为1,它里面一返回值为0,进行倒推,可得出3个答案:8  1,9  1,11  1。

6、 phase_5

同样由movl   $0x804a23e,0x4(%esp)知道要输入两个数。由cmp    $0xf,%eax知第一个数小于15。然后有一个立即数$0x804a1c0。再往下是个循环,在其中看到mov  (%ebx,%eax,

4),%eax,这是典型的数组的使用,把上次循环读取的值做为这次循环元素的下标。用指令add    %eax,%ecx实现每次读取的值的累加,由cmp  $0xf,%eax知最后取出的数应该为15。mov   %eax,-0xc(%ebp),累加值应于输入的第二个数相等。cmp    $0xf,%edx,知循环15次。

用gdb查看地址中储存的数组:


倒推15次得到12,故应该输入5,累加值为115,故答案为5  115.

7、 phase_6

首先我们注意到它先调用了函数<read_six_numbers>,要我们输入六个数。紧接着是一个循环,其中sub    $0x1,%eax,cmp   $0x5,%eax对其进行判断,即6个数都不大于6。再往下走,我们看到立即数$0x804c0c4,这里当做地址使用。上面的指令也是对储存在地址里的值进行赋值。可用gdb查看其中储存的值:


这个是很典型的链表的使用,,每个节点储存了两个int型,还有指向下一个节点的指针,即地址。所以题目是对链表的节点进行的操作。

再往下一段,是地址的传递,不改变我们输入的值,先跳过。再往下又是一个循环,其中关键指令mov  0x8(%ebx),%eax,mov   (%ebx),%edx,cmp    (%eax),%edx,知前一个值要小于等于后一个值。这样我们就知道题目的大概要求了,通过我们输入的六个值对对链表的值进行排序,使他们呈递减数列。

接着用gdb查看链表储存的值:


得到所有的值:1a7、6c、155、187、3bd、255,按从大到小排序:3bd>255>1a7>187>155>6c。那么我们就能得到答案:5 6 1 4 3 2。

8、 secret_phase

1) 入口

首先我们找调用<secret_phase>函数的地方,发现它在<phase_defused>函数里。查看函数<phase_defused>的代码,首先注意到cmpl   $0x6,0x804c3d0,若不等于6则直接离开,猜测是判断你是否完成了6道题。当6道题全部完成时,才会继续执行。再往下,到调用函数<secret_phase>之前发现一系列立即数:$0x804a200、$0x804a209、$0x804a2dc、$0x804a304。用gdb查看其内容:


首先我们看到%d %d %s,表示输入两个整数一个字符串,接着是DrEvil。我们注意到在使用$0x804a209之后调用了<strings_not_equal>函数,相同则继续执行。让我们自然的想到我们要在3、4、5关其中某一关的答案后加上字符串DrEvil。在调用<strings_not_equal>函数之前 我们发现指令lea   -0x5c(%ebp),%eax,其中0x5c(%ebp)即是我们要输入DrEvil的地址。我们可以经过计算知道应在第四关输入,不过太麻烦我没有使用。我是使用了简单无脑的方法,依次在3、4、5关输入DrEvil看其是否能进入隐藏关从而知到入口在第四关。

2) 解题

    <secret_phase>代码:

08048c1b <secret_phase>:

 8048c1b: 55                   push   %ebp

 8048c1c: 89e5                mov    %esp,%ebp

 8048c1e: 53                   push   %ebx

 8048c1f: 83ec 14             sub    $0x14,%esp

 8048c22: e8df 05 00 00           call   8049206<read_line>

 8048c27: c744 24 08 0a 00 00       movl   $0xa,0x8(%esp)

 8048c2e: 00

 8048c2f: c744 24 04 00 00 00       movl   $0x0,0x4(%esp)

 8048c36: 00

 8048c37: 8904 24             mov    %eax,(%esp)

 8048c3a: e871 fc ff ff               call   80488b0<strtol@plt>

 8048c3f: 89c3                  mov    %eax,%ebx

 8048c41: 8d40 ff               lea    -0x1(%eax),%eax

 8048c44: 3de8 03 00 00          cmp    $0x3e8,%eax

 8048c49: 7605                jbe    8048c50 <secret_phase+0x35>

 8048c4b: e881 04 00 00          call   80490d1<explode_bomb>

 8048c50: 895c 24 04            mov    %ebx,0x4(%esp)

 8048c54: c704 24 78 c1 04 08       movl   $0x804c178,(%esp)

 8048c5b: e86a ff ff ff                call   8048bca <fun7>

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

 8048c63: 7405                je     8048c6a <secret_phase+0x4f>

 8048c65: e867 04 00 00          call   80490d1<explode_bomb>

 8048c6a: c744 24 04 34 a1 04       movl   $0x804a134,0x4(%esp)

 8048c71: 08

 8048c72: c7 04 24 01 00 00 00       movl   $0x1,(%esp)

 8048c79: e8f2 fb ff ff                call   8048870<__printf_chk@plt>

 8048c7e: e891 03 00 00          call   8049014<phase_defused>

 8048c83: 83c4 14             add    $0x14,%esp

 8048c86: 5b                   pop   %ebx

 8048c87: 5d                   pop    %ebp

 8048c88: c3                   ret

首先一句call  8049206 <read_line>,表明程序先读入一行,随后返回值%eax作为函数<strtol@plt>的参数之一,另外两个参数分别是0xa和0x0,也就是我们需要输入一个十进制数。由lea -0x1(%eax),%eax 和cmp $0x3e8,%eax 这两句知输入的十进制数要小于等于1001。随后将所输入的数作为<fun7> 的参数之一。另外一个参数来自 0x804c178,查看为0x24。

<fun7>代码:

08048bca <fun7>:

 8048bca:        55                   push   %ebp

 8048bcb:        89e5                mov    %esp,%ebp

 8048bcd:        53                   push   %ebx

 8048bce:        83ec 14             sub    $0x14,%esp

 8048bd1:        8b55 08               mov    0x8(%ebp),%edx  //第一个参数A

 8048bd4:        8b4d 0c             mov    0xc(%ebp),%ecx  //第二个参数B 即输入

 8048bd7:        b8ff ff ff ff                 mov   $0xffffffff,%eax

 8048bdc:        85d2                test   %edx,%edx   //递归终止,返回%edx=0

 8048bde:        7435                je     8048c15 <fun7+0x4b>

 8048be0:        8b1a                mov    (%edx),%ebx

 8048be2:        39cb                 cmp    %ecx,%ebx

 8048be4:        7e13                jle    8048bf9 <fun7+0x2f>

//若*A>b,将(A+4)作为地址进入递归

 8048be6:        894c 24 04            mov    %ecx,0x4(%esp)

 8048bea:        8b42 04               mov    0x4(%edx),%eax

 8048bed:        8904 24             mov    %eax,(%esp)

 8048bf0:         e8d5 ff ff ff                call   8048bca<fun7>

 8048bf5:         01c0                 add    %eax,%eax //在此处将递归返回值加倍

 8048bf7:         eb1c                 jmp    8048c15 <fun7+0x4b>

 8048bf9:         b800 00 00 00          mov    $0x0,%eax

 8048bfe:         39cb                 cmp    %ecx,%ebx

 8048c00:        7413                je     8048c15 <fun7+0x4b>

//若*A<B,将(A+8)作为地址进入递归

 8048c02:        894c 24 04            mov    %ecx,0x4(%esp)

 8048c06:        8b42 08               mov    0x8(%edx),%eax

 8048c09:        8904 24             mov    %eax,(%esp)

 8048c0c:         e8b9 ff ff ff                call   8048bca<fun7>

//在此处将递归返回值加倍后在加1

 8048c11:        8d44 00 01            lea    0x1(%eax,%eax,1),%eax

 8048c15:        83c4 14             add    $0x14,%esp

 8048c18:        5b                   pop    %ebx

 8048c19:        5d                   pop    %ebp

 8048c1a:        c3                   ret

在调用完<fun7>之后,紧跟着cmp   $0x5,%eax,即返回值必须为5。<fun7>分析如上,为递归函数,与第四题十分相似。递归最深处的返回值肯定为0,最外层返回值为5,可得出如下反递归过程:

A*2+1=5 - ->A=2    即有*A<B

A*2=2  -->A=1    有*A>B

A*2+1=1 - ->A=0    即有*A<B

也就是说在这三次递归中两次执行了“若*A<B将(A+8)作为地址进入递归”系列代码,一次执行了“若*A>b,将(A+4)作为地址进入递归”系列代码。使用gdb查询储存值:


最后得到0x2f,即使我们要输入的十进制值47。

9、 测试(其中3题4题不止一个答案)


【实验总结】

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

1 0
原创粉丝点击