二进制拆弹一共设有七个关卡:

  • phase_1:字符串比较
  • phase_2:循环
  • phase_3:switch
  • phase_4:递归
  • phase_5:指针
  • phase_6:链表/指针/结构
  • secret_phase:隐藏关

Phase1-6通关截图

准备工作

  • 我的电脑是windows的,所以需要在虚拟机上安装VMware,并且安装gdb(百度寻找教程)。
  • 使用objdump反汇编bomb可执行程序,得到bomb的汇编代码asm.txt文件:objdump -d bomb > asm.txt
  • 使用gdb调试bomb程序,进入调试状态,如图1所示:gdb bomb

图1:进入调试状态
  • 附一个汇编中的跳转指令说明网址:https://blog.csdn.net/do2jiang/article/details/5262327

phase_1

08048b90 <phase_1>:8048b90:    83 ec 1c                sub    $0x1c,%esp8048b93:   c7 44 24 04 e4 a1 04    movl   $0x804a1e4,0x4(%esp)   //取0x804a1e4的内容,到%esp+0x48048b9a: 08 8048b9b: 8b 44 24 20             mov    0x20(%esp),%eax 8048b9f: 89 04 24                mov    %eax,(%esp)            //把用户的输入放入%esp8048ba2:    e8 03 05 00 00          call   80490aa <strings_not_equal>8048ba7:    85 c0                   test   %eax,%eax              //判断eax是否为08048ba9:   74 05                   je     8048bb0 <phase_1+0x20> //为0跳转8048bab: e8 05 06 00 00          call   80491b5 <explode_bomb> //不为0爆炸8048bb0: 83 c4 1c                add    $0x1c,%esp8048bb3:   c3                      ret   

程序解读:

  • 取内存0x804a1e4处的内容
  • 取用户输入的内容
  • 比较两者的值,相同则进入下一关,不相同则爆炸

获取x804a1e4处的内容: p  (char *) 0x804a1e4

第一关运行结果

图2:phase_1​​​​​

可以看到,第一关的密码为:Houses will begat jobs, jobs will begat houses.

phase_2


08048bb4 <phase_2>://程序第一部分8048bb4:   53                      push   %ebx8048bb5: 83 ec 38                sub    $0x38,%esp8048bb8:   8d 44 24 18             lea    0x18(%esp),%eax8048bbc:  89 44 24 04             mov    %eax,0x4(%esp)8048bc0:   8b 44 24 40             mov    0x40(%esp),%eax8048bc4:  89 04 24                mov    %eax,(%esp)8048bc7:  e8 10 06 00 00          call   80491dc <read_six_numbers>   //读入6个数字8048bcc:  83 7c 24 18 00          cmpl   $0x0,0x18(%esp)              //判断第一个数字是否为08048bd1:   79 22                   jns    8048bf5 <phase_2+0x41>       //如果不为0,则跳转到8048bf5处8048bd3:  e8 dd 05 00 00          call   80491b5 <explode_bomb>       //如果为0,爆炸!!!8048bd8:  eb 1b                   jmp    8048bf5 <phase_2+0x41>//程序第二部分,此处是一个循环8048bda: 89 d8                   mov    %ebx,%eax                    //%ebx的值赋值给%eax8048bdc: 03 44 9c 14             add    0x14(%esp,%ebx,4),%eax       //取了0x14+%esp+4*%ebx的值加上%eax的值,存到%eax中8048be0: 39 44 9c 18             cmp    %eax,0x18(%esp,%ebx,4)8048be4:   74 05                   je     8048beb <phase_2+0x37>8048be6:    e8 ca 05 00 00          call   80491b5 <explode_bomb>8048beb: 83 c3 01                add    $0x1,%ebx8048bee:    83 fb 06                cmp    $0x6,%ebx8048bf1:    75 e7                   jne    8048bda <phase_2+0x26>//程序第三部分8048bf3:    eb 07                   jmp    8048bfc <phase_2+0x48>8048bf5:    bb 01 00 00 00          mov    $0x1,%ebx                   //第一个数字不为0时跳转到这里来啦!!! %ebx里面存的是一个循环变量8048bfa:   eb de                   jmp    8048bda <phase_2+0x26>8048bfc:    83 c4 38                add    $0x38,%esp8048bff:   5b                      pop    %ebx8048c00: c3                      ret 

程序分析:

  • 通过对程序第一部分的分析,可以了解到是要输入6个数字,且第一个数字不能为0;
  • 第一个数字不为0时,跳转到8048bf5处。此处设置了一个新的变量,并且初始化为1。然后跳转到8048bda
  •  0x14(%esp,%ebx,4)是比例变址寻址方式,地址是 0x14+%esp+4*%ebx
  • 然后比较%eax0x18(%esp,%ebx,4)的操作数的值,如果相等的话,%eax的值++(循环变量++),如果不为6,则继续循环,如果循环体中没有explode_bomb的话,则会循环5次。
  • 循环体如果没有explode_bomb,则第二关破解。所以,关键是循环体8048be0处比较的是何处的值。实际比较的是后一个数字(0x18(%esp,%ebx,4))是否等于 当前数字+循环变量(%eax)。
  • 如果我们第一个输入的数字是1,则第二关的密码应该为 1  2(1+1)   4(2+2)   7(4+3)    1(7+4)   16(11+5) 即 1 2 4 7 11 16,答案不唯一,满足这个规律即可,但是第一个数字不能为0.

phase_3

08048c01 <phase_3>:8048c01:    83 ec 3c                sub    $0x3c,%esp//输入8048c04:   8d 44 24 2c             lea    0x2c(%esp),%eax8048c08:  89 44 24 10             mov    %eax,0x10(%esp)8048c0c:  8d 44 24 27             lea    0x27(%esp),%eax8048c10:  89 44 24 0c             mov    %eax,0xc(%esp)8048c14:   8d 44 24 28             lea    0x28(%esp),%eax8048c18:  89 44 24 08             mov    %eax,0x8(%esp)8048c1c:   c7 44 24 04 3a a2 04    movl   $0x804a23a,0x4(%esp)   //取出入内容,这里可以看出入的数值类型8048c23:   08 8048c24: 8b 44 24 40             mov    0x40(%esp),%eax8048c28:  89 04 24                mov    %eax,(%esp)8048c2b:  e8 30 fc ff ff          call   8048860 <__isoc99_sscanf@plt>//输入的第一个数字要大于2,小于等于7,否则爆炸8048c30:  83 f8 02                cmp    $0x2,%eax8048c33:    7f 05                   jg     8048c3a <phase_3+0x39>8048c35:    e8 7b 05 00 00          call   80491b5 <explode_bomb>8048c3a: 83 7c 24 28 07          cmpl   $0x7,0x28(%esp)    //后者的值是输入的第一个数字,假设输入的第一个数字是58048c3f:   0f 87 fc 00 00 00       ja     8048d41 <phase_3+0x140>8048c45:   8b 44 24 28             mov    0x28(%esp),%eax//根据输入的数值进行跳转,这里可以通过调试,把所有可能的跳转地址都输出8048c49:    ff 24 85 60 a2 04 08    jmp    *0x804a260(,%eax,4)8048c50:  b8 76 00 00 00          mov    $0x76,%eax8048c55:   81 7c 24 2c f1 00 00    cmpl   $0xf1,0x2c(%esp)8048c5c: 00 8048c5d: 0f 84 e8 00 00 00       je     8048d4b <phase_3+0x14a>8048c63:   e8 4d 05 00 00          call   80491b5 <explode_bomb>8048c68: b8 76 00 00 00          mov    $0x76,%eax8048c6d:   e9 d9 00 00 00          jmp    8048d4b <phase_3+0x14a>8048c72:   b8 69 00 00 00          mov    $0x69,%eax8048c77:   81 7c 24 2c aa 01 00    cmpl   $0x1aa,0x2c(%esp)8048c7e:    00 8048c7f: 0f 84 c6 00 00 00       je     8048d4b <phase_3+0x14a>8048c85:   e8 2b 05 00 00          call   80491b5 <explode_bomb>8048c8a: b8 69 00 00 00          mov    $0x69,%eax8048c8f:   e9 b7 00 00 00          jmp    8048d4b <phase_3+0x14a>8048c94:   b8 67 00 00 00          mov    $0x67,%eax8048c99:   81 7c 24 2c 15 03 00    cmpl   $0x315,0x2c(%esp)8048ca0:    00 8048ca1: 0f 84 a4 00 00 00       je     8048d4b <phase_3+0x14a>8048ca7:   e8 09 05 00 00          call   80491b5 <explode_bomb>8048cac: b8 67 00 00 00          mov    $0x67,%eax8048cb1:   e9 95 00 00 00          jmp    8048d4b <phase_3+0x14a>8048cb6:   b8 62 00 00 00          mov    $0x62,%eax8048cbb:   81 7c 24 2c fd 01 00    cmpl   $0x1fd,0x2c(%esp)8048cc2:    00 8048cc3: 0f 84 82 00 00 00       je     8048d4b <phase_3+0x14a>8048cc9:   e8 e7 04 00 00          call   80491b5 <explode_bomb>8048cce: b8 62 00 00 00          mov    $0x62,%eax8048cd3:   eb 76                   jmp    8048d4b <phase_3+0x14a>8048cd5:   b8 74 00 00 00          mov    $0x74,%eax8048cda:   81 7c 24 2c 7b 02 00    cmpl   $0x27b,0x2c(%esp)8048ce1:    00 8048ce2: 74 67                   je     8048d4b <phase_3+0x14a>8048ce4:   e8 cc 04 00 00          call   80491b5 <explode_bomb>8048ce9: b8 74 00 00 00          mov    $0x74,%eax8048cee:   eb 5b                   jmp    8048d4b <phase_3+0x14a>8048cf0:   b8 79 00 00 00          mov    $0x79,%eax                //第一数字是5的时候,跳转到这里来啦!!!!8048cf5: 81 7c 24 2c 3a 01 00    cmpl   $0x13a,0x2c(%esp)         //判断第二个数字的值是不是 0x13a,即十进制的3148048cfc:  00 8048cfd: 74 4c                   je     8048d4b <phase_3+0x14a>8048cff:   e8 b1 04 00 00          call   80491b5 <explode_bomb>8048d04: b8 79 00 00 00          mov    $0x79,%eax8048d09:   eb 40                   jmp    8048d4b <phase_3+0x14a>8048d0b:   b8 70 00 00 00          mov    $0x70,%eax8048d10:   81 7c 24 2c cf 02 00    cmpl   $0x2cf,0x2c(%esp)8048d17:    00 8048d18: 74 31                   je     8048d4b <phase_3+0x14a>8048d1a:   e8 96 04 00 00          call   80491b5 <explode_bomb>8048d1f: b8 70 00 00 00          mov    $0x70,%eax8048d24:   eb 25                   jmp    8048d4b <phase_3+0x14a>8048d26:   b8 71 00 00 00          mov    $0x71,%eax8048d2b:   81 7c 24 2c 7b 01 00    cmpl   $0x17b,0x2c(%esp)8048d32:    00 8048d33: 74 16                   je     8048d4b <phase_3+0x14a>8048d35:   e8 7b 04 00 00          call   80491b5 <explode_bomb>8048d3a: b8 71 00 00 00          mov    $0x71,%eax8048d3f:   eb 0a                   jmp    8048d4b <phase_3+0x14a>8048d41:   e8 6f 04 00 00          call   80491b5 <explode_bomb>//如果第二个数字是314,就会跳转到这里来啦, 判断输入的字符是不是 0x65,即字母y8048d46: b8 65 00 00 00          mov    $0x65,%eax8048d4b:   3a 44 24 27             cmp    0x27(%esp),%al         8048d4f:  74 05                   je     8048d56 <phase_3+0x155> 8048d51:  e8 5f 04 00 00          call   80491b5 <explode_bomb>8048d56: 83 c4 3c                add    $0x3c,%esp8048d59:   c3                      ret    

程序分析:

  • 查看输入的数据类型,可以看到输入为 整型数值、字符、整型数值
(gdb) p (char*)0x804a23a
$16 = 0x804a23a "%d %c %d"
  • 查看所有可能的跳转地址,假设输入的第一个数是5,则应该跳转到的地址是 p/x *(0x804a274)的结果,即跳转到 0x8048cf0

跳转入口地址查看
  • 然后顺着地址跳转就行了,我通过的密码是:5 y 314 解不唯一

phase_4

08048d5a <func4>:8048d5a:  56                      push   %esi8048d5b: 53                      push   %ebx8048d5c: 83 ec 14                sub    $0x14,%esp8048d5f:   8b 54 24 20             mov    0x20(%esp),%edx8048d63:  8b 44 24 24             mov    0x24(%esp),%eax8048d67:  8b 5c 24 28             mov    0x28(%esp),%ebx8048d6b:  89 d9                   mov    %ebx,%ecx8048d6d:    29 c1                   sub    %eax(0),%ecx(14)8048d6f: 89 ce                   mov    %ecx,%esi8048d71:    c1 ee 1f                shr    $0x1f,%esi8048d74:   01 f1                   add    %esi,%ecx8048d76:    d1 f9                   sar    %ecx8048d78: 01 c1                   add    %eax,%ecx8048d7a:    39 d1                   cmp    %edx,%ecx8048d7c:    7e 17                   jle    8048d95 <func4+0x3b> //不大于则转移8048d7e: 83 e9 01                sub    $0x1,%ecx8048d81:    89 4c 24 08             mov    %ecx,0x8(%esp)8048d85:   89 44 24 04             mov    %eax,0x4(%esp)8048d89:   89 14 24                mov    %edx,(%esp)8048d8c:  e8 c9 ff ff ff          call   8048d5a <func4>8048d91:    01 c0                   add    %eax,%eax8048d93:    eb 20                   jmp    8048db5 <func4+0x5b>8048d95:  b8 00 00 00 00          mov    $0x0,%eax8048d9a:    39 d1                   cmp    %edx,%ecx8048d9c:    7d 17                   jge    8048db5 <func4+0x5b>//大于或等于则转移8048d9e:    89 5c 24 08             mov    %ebx,0x8(%esp)8048da2:   83 c1 01                add    $0x1,%ecx8048da5:    89 4c 24 04             mov    %ecx,0x4(%esp)8048da9:   89 14 24                mov    %edx,(%esp)8048dac:  e8 a9 ff ff ff          call   8048d5a <func4>8048db1:    8d 44 00 01             lea    0x1(%eax,%eax,1),%eax8048db5:    83 c4 14                add    $0x14,%esp8048db8:   5b                      pop    %ebx8048db9: 5e                      pop    %esi8048dba: c3                      ret    08048dbb <phase_4>:8048dbb:    83 ec 2c                sub    $0x2c,%esp//输入两个值8048dbe:    8d 44 24 1c             lea    0x1c(%esp),%eax8048dc2:  89 44 24 0c             mov    %eax,0xc(%esp)8048dc6:   8d 44 24 18             lea    0x18(%esp),%eax8048dca:  89 44 24 08             mov    %eax,0x8(%esp)8048dce:   c7 44 24 04 cf a3 04    movl   $0x804a3cf,0x4(%esp)8048dd5: 08 8048dd6: 8b 44 24 30             mov    0x30(%esp),%eax8048dda:  89 04 24                mov    %eax,(%esp)//调用函数8048ddd:    e8 7e fa ff ff          call   8048860 <__isoc99_sscanf@plt>//比较返回值是否为2,不是2则爆炸8048de2:    83 f8 02                cmp    $0x2,%eax8048de5:    75 07                   jne    8048dee <phase_4+0x33>//比较输入的第一个数是否小于等于14,如果大于15则爆炸8048de7:    83 7c 24 18 0e          cmpl   $0xe,0x18(%esp)8048dec:  76 05                   jbe    8048df3 <phase_4+0x38>8048dee:    e8 c2 03 00 00          call   80491b5 <explode_bomb>//定义了两个变量,值分别为14 08048df3:    c7 44 24 08 0e 00 00    movl   $0xe,0x8(%esp)8048dfa:   00 8048dfb: c7 44 24 04 00 00 00    movl   $0x0,0x4(%esp)8048e02:   00 8048e03: 8b 44 24 18             mov    0x18(%esp),%eax8048e07:  89 04 24                mov    %eax,(%esp)//传入参数,调用函数fun4,传入的参数的顺序,从左到右为:输入的第一个数 0 148048e0a:   e8 4b ff ff ff          call   8048d5a <func4>//判断返回的值是否为7,不是7则爆炸8048e0f:  83 f8 07                cmp    $0x7,%eax8048e12:    75 07                   jne    8048e1b <phase_4+0x60>//判断输入的第二个参数是否为7,不是则爆炸,所以可以确定第二个参数值为78048e14: 83 7c 24 1c 07          cmpl   $0x7,0x1c(%esp)8048e19:  74 05                   je     8048e20 <phase_4+0x65>8048e1b:    e8 95 03 00 00          call   80491b5 <explode_bomb>8048e20: 83 c4 2c                add    $0x2c,%esp8048e23:   c3                      ret    

第四关是递归,核心是要写出递归函数。

我得到的递归函数是:

int func4(int a1, int a2, int a3)
{int v3; // ecxint result; // eaxv3 = a2 + (a3 - a2) / 2;if ( v3 > a1 )return 2 * func4(a1, a2, v3 - 1);result = 0;if ( v3 < a1 )result = 2 * func4(a1, v3 + 1, a3) + 1;return result;
}
  • 第一次调用fun4的时候,传入的第二个参数值是0,第三个参数值是14
  • 分析phase_4,可以知道只有fun4的返回值是7的时候,才能不爆炸
  • 我们输入的第一个数不大于14,所以尝试求解,可以看到,当输入14的时候,返回值为7,所以第四关的密码是 14 7

代码和运行过程

phase_5

08048e24 <phase_5>:8048e24:    53                      push   %ebx8048e25: 83 ec 18                sub    $0x18,%esp8048e28:   8b 5c 24 20             mov    0x20(%esp),%ebx8048e2c:  89 1c 24                mov    %ebx,(%esp)8048e2f:  e8 57 02 00 00          call   804908b <string_length>//输入字符串的长度不等于6,则爆炸//所以输入是长度为6的字符串8048e34:    83 f8 06                cmp    $0x6,%eax8048e37:    74 05                   je     8048e3e <phase_5+0x1a>8048e39:    e8 77 03 00 00          call   80491b5 <explode_bomb>8048e3e: ba 00 00 00 00          mov    $0x0,%edx8048e43:    b8 00 00 00 00          mov    $0x0,%eax//这里是一个循环8048e48:   0f b6 0c 03             movzbl (%ebx,%eax,1),%ecx//取低四位8048e4c: 83 e1 0f                and    $0xf,%ecx                //这里是关键!!!//相加8048e4f: 03 14 8d 80 a2 04 08    add    0x804a280(,%ecx,4),%edx  //这里是关键!!!8048e56: 83 c0 01                add    $0x1,%eax8048e59:    83 f8 06                cmp    $0x6,%eax8048e5c:    75 ea                   jne    8048e48 <phase_5+0x24>//判断%edx中的操作数是不是0x2b,即十进制的438048e5e: 83 fa 2b                cmp    $0x2b,%edx8048e61:   74 05                   je     8048e68 <phase_5+0x44>8048e63:    e8 4d 03 00 00          call   80491b5 <explode_bomb>8048e68: 83 c4 18                add    $0x18,%esp8048e6b:   5b                      pop    %ebx8048e6c: c3                      ret    

程序分析:

  • 输入一个长度为6的字符串
  • 经过一个循环操作
  • 判断某个变量的值是不是43,如果是43则通关
  • 查看一下0x804a280(,%ecx,4)的内容,%ecx依次取0 1 2 3 4 ....

依次尝试查看地址中的值
  • 43 = 10+10+10+10+1+2 = 0xa + 0xa + 0xa + 0xa + 0x1 + 0x2
  • 0xa对应的%ecx中的值为1(0x804a280+4*1=0x804a284),所以只要 输入的字符的二进制表示&0x0f == 1 即可,例如a的ASCII码的二进制表示是01100001,01100001&0f =1
  • 0x1对应的%ecx中的值为3(0x804a280+4*3=0x804a28c),  01100011 & 0x0f 对应的十进制是3,01100011对应的字符是c
  • 0x2对应的%ecx中的值为0(0x804a280+4*0=0x804a280),  01110000 & 0x0f 对应的十进制是0,01110000对应的字符是p
  • 所以密码之一是:aaaacp(答案不唯一)

phase_6

08048e6d <phase_6>:8048e6d:    56                      push   %esi8048e6e: 53                      push   %ebx8048e6f: 83 ec 44                sub    $0x44,%esp8048e72:   8d 44 24 10             lea    0x10(%esp),%eax8048e76:  89 44 24 04             mov    %eax,0x4(%esp)8048e7a:   8b 44 24 50             mov    0x50(%esp),%eax8048e7e:  89 04 24                mov    %eax,(%esp)8048e81:  e8 56 03 00 00          call   80491dc <read_six_numbers>8048e86: be 00 00 00 00          mov    $0x0,%esi//这是第一个循环,第一个循环里面还嵌入着一个小循环  这里的条件是数组中所有的元素都小于等于6,且不能存在两个数相同。所以输入的数字是1-6的排列组合之一8048e8b:    8b 44 b4 10             mov    0x10(%esp,%esi,4),%eax8048e8f:   83 e8 01                sub    $0x1,%eax8048e92:    83 f8 05                cmp    $0x5,%eax8048e95:    76 05                   jbe    8048e9c <phase_6+0x2f>   //%eax-1 <= 5则继续,否则爆炸8048e97: e8 19 03 00 00          call   80491b5 <explode_bomb>   //爆炸8048e9c:  83 c6 01                add    $0x1,%esi                //计数器加1,如果不等于6则循环8048e9f:    83 fe 06                cmp    $0x6,%esi8048ea2:    75 07                   jne    8048eab <phase_6+0x3e>8048ea4:    bb 00 00 00 00          mov    $0x0,%ebx8048ea9:    eb 38                   jmp    8048ee3 <phase_6+0x76>  //此处是第一个大循环的出口8048eab:    89 f3                   mov    %esi,%ebx8048ead:    8b 44 9c 10             mov    0x10(%esp,%ebx,4),%eax  //取数组中的下一个值  小循环开始8048eb1:   39 44 b4 0c             cmp    %eax,0xc(%esp,%esi,4)   //进行了一次比较,打印看看值是多少8048eb5:    75 05                   jne    8048ebc <phase_6+0x4f>8048eb7:    e8 f9 02 00 00          call   80491b5 <explode_bomb>8048ebc: 83 c3 01                add    $0x1,%ebx8048ebf:    83 fb 05                cmp    $0x5,%ebx8048ec2:    7e e9                   jle    8048ead <phase_6+0x40>  //小于等于则跳转  小循环结束8048ec4:  eb c5                   jmp    8048e8b <phase_6+0x1e>//这是第二个循环8048ec6:   8b 52 08                mov    0x8(%edx),%edx8048ec9:   83 c0 01                add    $0x1,%eax8048ecc:    39 c8                   cmp    %ecx,%eax8048ece:    75 f6                   jne    8048ec6 <phase_6+0x59>8048ed0:    eb 05                   jmp    8048ed7 <phase_6+0x6a>8048ed2:    ba 3c c1 04 08          mov    $0x804c13c,%edx        //这里存的是一个地址值8048ed7:  89 54 b4 28             mov    %edx,0x28(%esp,%esi,4) //把地址传给了一个结点8048edb:  83 c3 01                add    $0x1,%ebx              //++8048ede:    83 fb 06                cmp    $0x6,%ebx8048ee1:    74 17                   je     8048efa <phase_6+0x8d>   //8048ee3:   89 de                   mov    %ebx,%esi                //从此处进入第二个循环,%ebx寄存器内的初始值是08048ee5:  8b 4c 9c 10             mov    0x10(%esp,%ebx,4),%ecx   //取数组中的第一个元素8048ee9:    83 f9 01                cmp    $0x1,%ecx8048eec:    7e e4                   jle    8048ed2 <phase_6+0x65>   //小于等于1则跳转8048eee:   b8 01 00 00 00          mov    $0x1,%eax8048ef3:    ba 3c c1 04 08          mov    $0x804c13c,%edx8048ef8:  eb cc                   jmp    8048ec6 <phase_6+0x59>8048efa:    8b 5c 24 28             mov    0x28(%esp),%ebx8048efe:  8d 44 24 2c             lea    0x2c(%esp),%eax8048f02:  8d 74 24 40             lea    0x40(%esp),%esi8048f06:  89 d9                   mov    %ebx,%ecx8048f08:    8b 10                   mov    (%eax),%edx   //间接寻址8048f0a: 89 51 08                mov    %edx,0x8(%ecx)8048f0d:   83 c0 04                add    $0x4,%eax8048f10:    39 f0                   cmp    %esi,%eax8048f12:    74 04                   je     8048f18 <phase_6+0xab>8048f14:    89 d1                   mov    %edx,%ecx8048f16:    eb f0                   jmp    8048f08 <phase_6+0x9b>8048f18:    c7 42 08 00 00 00 00    movl   $0x0,0x8(%edx)//这是第四个循环,前一个节点中的值要比后一个结点中的值大,从大到小排序8048f1f: be 05 00 00 00          mov    $0x5,%esi8048f24:    8b 43 08                mov    0x8(%ebx),%eax8048f27:   8b 00                   mov    (%eax),%eax8048f29:  39 03                   cmp    %eax,(%ebx)8048f2b:  7d 05                   jge    8048f32 <phase_6+0xc5>8048f2d:    e8 83 02 00 00          call   80491b5 <explode_bomb>8048f32: 8b 5b 08                mov    0x8(%ebx),%ebx8048f35:   83 ee 01                sub    $0x1,%esi8048f38:    75 ea                   jne    8048f24 <phase_6+0xb7>8048f3a:    83 c4 44                add    $0x44,%esp8048f3d:   5b                      pop    %ebx8048f3e: 5e                      pop    %esi8048f3f: c3                      ret    

程序分析:如下如,各节点存储的值分别为:0x4d<1>  0x11a<2>  0x223<3>   0x3d9<4>   0x106<5>   0x202<6> ,然后由大到小排序:4 3 6 2 5 1

结点中的值

secret_phase

这一关是隐藏关卡,我在asm.txt(反汇编得到的文件)中搜索 secret_phase,发现只有phase_defused函数中使用到了secret_phase函数,所以推测phase_defused是隐藏关卡的入口。

问:phase_defused函数什么时候会被调用???

答:每次密码输对的时候会调用phase_defused函数。所以说,如果调用了phase_defused函数,就意味着通过了一个普通关卡(不包括隐藏关卡)

0x1 分析phase_defused函数

08049326 <phase_defused>:8049326:  81 ec 8c 00 00 00       sub    $0x8c,%esp804932c:   65 a1 14 00 00 00       mov    %gs:0x14,%eax8049332:    89 44 24 7c             mov    %eax,0x7c(%esp)8049336:  31 c0                   xor    %eax,%eax//判断 0x804c3c8 中的值是否为6,不是6则跳转,跳转后,phase_defused函数会顺序执行完毕,无法进入隐藏关卡8049338:   83 3d c8 c3 04 08 06    cmpl   $0x6,0x804c3c8804933f:   75 72                   jne    80493b3 <phase_defused+0x8d>//调用 <__isoc99_sscanf@plt>8049341: 8d 44 24 2c             lea    0x2c(%esp),%eax8049345:  89 44 24 10             mov    %eax,0x10(%esp)8049349:  8d 44 24 28             lea    0x28(%esp),%eax804934d:  89 44 24 0c             mov    %eax,0xc(%esp)8049351:   8d 44 24 24             lea    0x24(%esp),%eax8049355:  89 44 24 08             mov    %eax,0x8(%esp)8049359:   c7 44 24 04 29 a4 04    movl   $0x804a429,0x4(%esp) //"%d %d %s"8049360:  08 8049361: c7 04 24 d0 c4 04 08    movl   $0x804c4d0,(%esp)8049368:    e8 f3 f4 ff ff          call   8048860 <__isoc99_sscanf@plt>//判断<__isoc99_sscanf@plt>的返回值是否为3,不是3则跳转804936d:   83 f8 03                cmp    $0x3,%eax   //sscanf返回参数,表示输入的参数个数8049370:    75 35                   jne    80493a7 <phase_defused+0x81>8049372:  c7 44 24 04 32 a4 04    movl   $0x804a432,0x4(%esp)  //DrEvil8049379:   08 804937a: 8d 44 24 2c             lea    0x2c(%esp),%eax804937e:  89 04 24                mov    %eax,(%esp)8049381:  e8 24 fd ff ff          call   80490aa <strings_not_equal>8049386:    85 c0                   test   %eax,%eax8049388:    75 1d                   jne    80493a7 <phase_defused+0x81>804938a:  c7 04 24 f8 a2 04 08    movl   $0x804a2f8,(%esp)   //0x804a2f8中的值:Curses, you've found the secret phase!"8049391:  e8 5a f4 ff ff          call   80487f0 <puts@plt>8049396:    c7 04 24 20 a3 04 08    movl   $0x804a320,(%esp)   //But finding it and solving it are quite different...804939d:   e8 4e f4 ff ff          call   80487f0 <puts@plt>80493a2:    e8 ea fb ff ff          call   8048f91 <secret_phase> //调用secret_phase函数80493a7:  c7 04 24 58 a3 04 08    movl   $0x804a358,(%esp)  //Congratulations! You've defused the bomb!80493ae:  e8 3d f4 ff ff          call   80487f0 <puts@plt>80493b3:    8b 44 24 7c             mov    0x7c(%esp),%eax80493b7:  65 33 05 14 00 00 00    xor    %gs:0x14,%eax80493be:    74 05                   je     80493c5 <phase_defused+0x9f>80493c0:  e8 fb f3 ff ff          call   80487c0 <__stack_chk_fail@plt>80493c5:    81 c4 8c 00 00 00       add    $0x8c,%esp80493cb:   c3                      ret    80493cc: 66 90                   xchg   %ax,%ax80493ce:  66 90                   xchg   %ax,%ax

程序分析:

  • 8049338:    83 3d c8 c3 04 08 06     cmpl   $0x6,0x804c3c8 函数首先判断0x804c3c8中的值是不是6,如果不是6的话则跳转,不会进入隐藏关卡。我们可以在0x8049338处设置断点(b *0x8049338),每次查看0x804c3c8中的值(p *(int *)0x804c3c8)。查看值的时候发现,每通过一个普通关卡,0x804c3c8中的值加1,例如第三关的密码正确,就会调用phase_defused函数,此时0x804c3c8中的值为3。所以当0x804c3c8中的值为6的时候,才有可能进入隐藏关卡。
  • 当通过了前6关以后,0x804c3c8中的值为6,此时函数不会跳转,会继续执行。
  • 8049359:    c7 44 24 04 29 a4 04     movl   $0x804a429,0x4(%esp) ,输出0x804a429中的内容,发现是%d %d %s
  • 8049361:    c7 04 24 d0 c4 04 08     movl   $0x804c4d0,(%esp),输出0x804c4d0中的内容,发现是14 7,是第四关输入的内容。但是上一步我们分析了输入是%d %d %s,这样的话还缺少一个%s,也就是说,隐藏关卡的调用可能需要在第四关多输入一个字符串。
  • 继续分析,我们发现sscanf函数调用完后,对其返回值进行了判断:804936d:    83 f8 03   cmp    $0x3,%eax,即判断 输入的参数是不是有3个,如果不是的话,就会跳转,无法继续执行。这也进一步验证了我们上一步的猜测。
  • 那,输入的字符串是啥呢?类似第一关,我们在phase_defused中发现了<strings_not_equal>,所以前面应该有个程序直接设定好的字符串,找一找,果然8049372:    c7 44 24 04 32 a4 04     movl   $0x804a432,0x4(%esp) ,输出0x804a432的内容,发现是DrEvil,这不就是一个字符串么!!
  • 如果第六关通过、第四关的输入是14 7 DrEvil,那么我们就能进入隐藏关卡了!!
  • 继续分析secret_phase函数吧!!

0x2 分析secret_phase函数

08048f91 <secret_phase>:8048f91:   53                      push   %ebx8048f92: 83 ec 18                sub    $0x18,%esp8048f95:   e8 92 02 00 00          call   804922c <read_line>8048f9a:    c7 44 24 08 0a 00 00    movl   $0xa,0x8(%esp)8048fa1:   00 8048fa2: c7 44 24 04 00 00 00    movl   $0x0,0x4(%esp)8048fa9:   00 8048faa: 89 04 24                mov    %eax,(%esp)8048fad:  e8 1e f9 ff ff          call   80488d0 <strtol@plt>8048fb2:  89 c3                   mov    %eax,%ebx8048fb4:    8d 40 ff                lea    -0x1(%eax),%eax8048fb7:  3d e8 03 00 00          cmp    $0x3e8,%eax8048fbc:  76 05                   jbe    8048fc3 <secret_phase+0x32>8048fbe:   e8 f2 01 00 00          call   80491b5 <explode_bomb>8048fc3: 89 5c 24 04             mov    %ebx,0x4(%esp)   //我们输入的数字8048fc7:   c7 04 24 88 c0 04 08    movl   $0x804c088,(%esp) //程序设定的数组8048fce:  e8 6d ff ff ff          call   8048f40 <fun7>8048fd3: 83 f8 01                cmp    $0x1,%eax   //查看fun7返回值是否等于1,不等于则引爆8048fd6:   74 05                   je     8048fdd <secret_phase+0x4c>8048fd8:   e8 d8 01 00 00          call   80491b5 <explode_bomb>8048fdd: c7 04 24 14 a2 04 08    movl   $0x804a214,(%esp)8048fe4:    e8 07 f8 ff ff          call   80487f0 <puts@plt>8048fe9:    e8 38 03 00 00          call   8049326 <phase_defused>8048fee:    83 c4 18                add    $0x18,%esp8048ff1:   5b                      pop    %ebx8048ff2: c3                      ret    8048ff3: 66 90                   xchg   %ax,%ax8048ff5:  66 90                   xchg   %ax,%ax8048ff7:  66 90                   xchg   %ax,%ax8048ff9:  66 90                   xchg   %ax,%ax8048ffb:  66 90                   xchg   %ax,%ax8048ffd:  66 90                   xchg   %ax,%ax8048fff:  90                      nop

程序分析

  • 初步分析,应该是需要我们输入一个数字(假设是x),然后判断x是不是小于0x3e9,如果大于的话,就抱歉地爆炸了,如果小于的话,调用函数fun7,函数fun7的参数是一个指针(或者理解为数组的首地址,程序设定好的:0x804c088)和一个数字(输入的x),也就是说fun7的是这样的:fun7(int *a,int x)。
  • fun7调用完后,判断fun7的返回值,如果不是1,则爆炸,如果是1,恭喜,你过关了!!!
  • 最后,我们来愉快地分析一波fun7吧,开森!!

0x2 分析fun7函数

08048f40 <fun7>:8048f40:   53                      push   %ebx8048f41: 83 ec 18                sub    $0x18,%esp8048f44:   8b 54 24 20             mov    0x20(%esp),%edx8048f48:  8b 4c 24 24             mov    0x24(%esp),%ecx8048f4c:  85 d2                   test   %edx,%edx8048f4e:    74 37                   je     8048f87 <fun7+0x47>8048f50:   8b 1a                   mov    (%edx),%ebx8048f52:  39 cb                   cmp    %ecx,%ebx8048f54:    7e 13                   jle    8048f69 <fun7+0x29>8048f56:   89 4c 24 04             mov    %ecx,0x4(%esp)8048f5a:   8b 42 04                mov    0x4(%edx),%eax8048f5d:   89 04 24                mov    %eax,(%esp)8048f60:  e8 db ff ff ff          call   8048f40 <fun7>8048f65: 01 c0                   add    %eax,%eax   //,2*%eax ,eax中存储的是函数的返回值8048f67:  eb 23                   jmp    8048f8c <fun7+0x4c>8048f69:   b8 00 00 00 00          mov    $0x0,%eax8048f6e:    39 cb                   cmp    %ecx,%ebx8048f70:    74 1a                   je     8048f8c <fun7+0x4c>8048f72:   89 4c 24 04             mov    %ecx,0x4(%esp)8048f76:   8b 42 08                mov    0x8(%edx),%eax8048f79:   89 04 24                mov    %eax,(%esp)8048f7c:  e8 bf ff ff ff          call   8048f40 <fun7>8048f81: 8d 44 00 01             lea    0x1(%eax,%eax,1),%eax8048f85:    eb 05                   jmp    8048f8c <fun7+0x4c>8048f87:   b8 ff ff ff ff          mov    $0xffffffff,%eax8048f8c: 83 c4 18                add    $0x18,%esp8048f8f:   5b                      pop    %ebx8048f90: c3                      ret    

程序分析:

  • 初步分析,这是一个简单的递归函数
  • 根据递归函数,写出伪代码,然后进行程序分析

fun7的伪代码
  • 你看了可能会有个问题:指针a1里面是啥,里面是啥,里面是啥???a[1]、a[2]里面居然还是个地址???我们来倒推一波吧!
  • 分析secret_phase函数,我们知道只有fun7的返回值是1,才能过关。如果fun7的返回值是1,则意味着在第一次递归的时候,我们进入的分支是if(*a1 != a2)的分支,且2 * fun7(a1[2], a2) 的结果是0,即 fun7(a1[2], a2) 是0。同时我们还能知道,第一次递归的时候a2 > *a1.
  • fun7(a1[2], a2) 的返回值是0,意味着啥呢?意味着 *a[2] = a2,嘻嘻,所以现在的关键是研究一波第一次递归传入的指针。
  • 第一次递归,传入的指针,实际是个地址,的值是0x804c088,查看其附近的值。

  • 从上图,得到输入的值应该是32H,即十进制的50,至于为啥,仔细看前几步的分析呀!
  • 至此,二进制炸弹拆完了!!!

二进制拆弹(20181023-20181026)相关推荐

  1. [逆向工程] 二进制拆弹Binary Bombs 快乐拆弹 详解

    二进制拆弹 binary bombs 教你最快速解题,成功拆弹 最近计算机基础课,的实验lab2,二进制拆弹,可以说是拆的我很快乐了(sub n, %hair) 此处头发减n 我刚开始做的时候很是懵逼 ...

  2. 二进制拆弹(炸弹炸掉了我的头发 T.T)

    文章目录 所需要使用的调试工具 开始进入拆弹过程 phase_1 phase_2 pahse_3 phase_4 phase_5 phase_6 secret_phase 所需要使用的调试工具 1. ...

  3. CSAPP lab2 二进制拆弹 binary bombs phase_6

    给出对应于7个阶段的7篇博客 phase_1  https://www.cnblogs.com/wkfvawl/p/10632044.html phase_2  https://www.cnblogs ...

  4. 二进制拆弹实验详解_Population Count算法-求二进制数中1的个数

    所谓Population Count算法,即是指计算一个二进制数中1的个数的算法.具体来说,就是任意给定一个无符号整数N,求N的二进制表示中1的个数,比如N = 5(0101)时,返回2:N = 15 ...

  5. 计算机系统基础实验:二进制拆弹实验

    一.实验目的: 学习并熟练使用 gdb 调试器和 objdump 理解汇编语言代码的行为或作用 提高阅读和理解汇编代码的能力 二.实验要求 实验共包括七个阶段,每个阶段考察机器级语言程序的不同方面,难 ...

  6. 二进制拆弹实验详解linux,拆解二进制炸弹

    拆解二进制炸弹 一.实验目的 1.理解C语言程序的机器级表示. 2.初步掌握GDB调试器的用法. 3.阅读C编译器生成的x86-64机器代码,理解不同控制结构生成的基本指令模式,过程的实现. 二. 实 ...

  7. 计算机系统二进制炸弹实验报告,二进制拿炸弹实验报告完整版.doc

    课程名称:计算机系统原理实验 实验课时:32课时 实验项目名称:BombLab二进制炸弹 实验材料:可执行文件bomb.源代码bomb.c.说明README 实验环境:Linux操作系统(安装虚拟机和 ...

  8. CSAPP实验二:二进制炸弹(Bomb Lab)

    本系列文章为中国科学技术大学计算机专业学科基础课<计算机系统>布置的实验,上课所用教材和内容为黑书CSAPP,当时花费很大精力和弯路,现来总结下各个实验,本文章为第二个实验--二进制炸弹( ...

  9. bomb二进制炸弹拆除实验(MIPS)

    上学期计算机系统基础课程的一个实验,在这里再简略整理一下: 实验要求: 仅给定一个MIPS二进制可执行文件bomb,要求运用GDB调试工具,通过分析反汇编代码来输入正确的参数以拆除炸弹. 辅助工具推荐 ...

  10. 哈工大 计算机系统 二进制炸弹实验报告

    实验报告 实 验(三) 题     目 Binary Bomb 二进制炸弹 专       业 计算机学院 学    号 班    级 学       生 指 导 教 师 实 验 地 点 实 验 日 ...

最新文章

  1. [MYSQL] 如何彻底卸载MYSQL5.x
  2. ASP权限管理系统源码下载
  3. 2019年四月计算机语言排名,2019编程语言排行榜_编程语言排行榜2019年4月 TIOBE编程语言排行榜2019年最...
  4. java 关联表做修改_java-图关联列表实现
  5. Swift - defer关键字(推迟执行)
  6. 写代码用什么笔记本_1—2千预算,编程、写代码、办公、PS修图笔记本推荐/选购指南...
  7. 特征工程(三):特征缩放,从词袋到 TF-IDF
  8. CStatic类的GDI双缓冲绘图
  9. 安卓rom制作教程_刹那 刷机教程20-1三星S10 N10等系列 韩版国行安卓9升级10 详细步骤...
  10. Python制作任意音频文件
  11. 霍尼韦尔门禁说明书_霍尼韦尔指纹锁说明书
  12. ESP8266-Arduino编程实例-HMC5883L磁场传感器
  13. 产品读书.心理学《梦的解析》
  14. C#设计一个Windows应用程序,在该程序中首先构造中学生、大学生等派生类,当输入相关数据,单击不同的按钮(“小学生”“中学生”“大学生”)将分别创建不同的学生对象,并输入当前的学生总人数,该学生的
  15. dig的现在分词_现在分词的变化规则(含双写加ing的动词归纳)
  16. 欢迎加入我们的前端技术交流群
  17. 有极性电容和无极性电容区别
  18. 阿拉伯数字改为中文大小写
  19. 长安大学计算机专业考研科目,长安大学(专业学位)计算机技术研究生考试科目和考研参考书目...
  20. 基于ssm+mysql+oracle宿舍管理

热门文章

  1. ios 简单的单元测试
  2. iPhone恢复模式、DFU模式的区别及操作方法
  3. FindBugs 汇总(持续修改)
  4. win10配置ffmpeg环境
  5. 剖析 Microsoft.AspNetCore.Identity 的精髓 —— 前言
  6. VB中的“msgbox”函数各参数详解
  7. CUE 文件格式说明
  8. 服务器怎么做无限耐久装备,饥荒物品无限耐久控制台指令 | 手游网游页游攻略大全...
  9. 自己动手编译最新Android源码及SDK
  10. 随机子空间(RSM)