实验二 拆炸弹

准备工作

首先反汇编

objdump -s -d bomb > bomb.txt

phase_1

开启gdb调试,并打断点到phase_1

>> gbd bomb
(gdb) break phase_1
(gdb) run
(gdb) disas

用disas 命令查看此函数汇编代码,对应在bomb.s中这一段:

08048b80 <phase_1>:8048b80:    55                      push   %ebp  8048b81:   89 e5                   mov    %esp,%ebp 8048b83:   83 ec 08                sub    $0x8,%esp 8048b86:   c7 44 24 04 d8 99 04    movl   $0x80499d8,0x4(%esp) 8048b8d:    08 8048b8e: 8b 45 08                mov    0x8(%ebp),%eax8048b91:   89 04 24                mov    %eax,(%esp)8048b94:  e8 7a 05 00 00          call   8049113 <strings_not_equal> 8048b99:   85 c0                   test   %eax,%eax8048b9b:    74 05                   je     8048ba2 <phase_1+0x22> 8048b9d:   e8 38 0b 00 00          call   80496da <explode_bomb> 8048ba2:    c9                      leave  8048ba3: c3                      ret
  • 首先压栈ebp栈底指针,返回地址
  • 然后将esp栈顶指针赋值给栈底指针,新的栈底
  • 将esp栈顶指针向下移动8个字节
  • 立即数$0x80499d8赋值给 %esp+4位置的对应的内存
  • 将%ebp+8位置存的值赋给%eax,这个值就是input参数
  • 将%eax赋值给%esp栈指向的这个位置
  • 接下来调用比较string是否相同,则刚才%esp位置和%esp+4位置分别存的是input的字符串和需要比较的字符串地址,作为参数传入这个比较函数
  • 当函数返回后,判断%eax这个值是否为0,为0说明就是不同,为1就说明是相同的,到8048ba2离开,ret返回,否则到8048b9d 触发炸弹爆炸。

所以查看$0x80499d8地址处的字符串,答案就是这个字符串

(gdb) x/s 0x80499d8
0x80499d8:      "Why make trillions when we could make... billions?"

phase_2

开启gdb调试,并打断点到phase_2,记得输入前面阶段的答案

>> gbd bomb
(gdb) break phase_2
(gdb) run
(gdb) disas

用disas 命令查看此函数汇编代码,对应在bomb.s中这一段:

08048ba4 <phase_2>:8048ba4:    55                      push   %ebp8048ba5: 89 e5                   mov    %esp,%ebp  8048ba7:  83 ec 28                sub    $0x28,%esp8048baa:   c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%ebp)8048bb1:  8d 45 e0                lea    -0x20(%ebp),%eax8048bb4: 89 44 24 04             mov    %eax,0x4(%esp) 8048bb8:  8b 45 08                mov    0x8(%ebp),%eax 8048bbb:  89 04 24                mov    %eax,(%esp)8048bbe:  e8 bd 04 00 00          call   8049080 <read_six_numbers>8048bc3: c7 45 f8 00 00 00 00    movl   $0x0,-0x8(%ebp)8048bca:  eb 27                   jmp    8048bf3 <phase_2+0x4f>8048bcc:    8b 45 f8                mov    -0x8(%ebp),%eax8048bcf:  8b 54 85 e0             mov    -0x20(%ebp,%eax,4),%edx8048bd3:  8b 45 f8                mov    -0x8(%ebp),%eax8048bd6:  83 c0 03                add    $0x3,%eax8048bd9:    8b 44 85 e0             mov    -0x20(%ebp,%eax,4),%eax8048bdd:  39 c2                   cmp    %eax,%edx8048bdf:    74 05                   je     8048be6 <phase_2+0x42>8048be1:    e8 f4 0a 00 00          call   80496da <explode_bomb>8048be6: 8b 45 f8                mov    -0x8(%ebp),%eax8048be9:  8b 44 85 e0             mov    -0x20(%ebp,%eax,4),%eax8048bed:  01 45 fc                add    %eax,-0x4(%ebp)8048bf0:  ff 45 f8                incl   -0x8(%ebp)8048bf3:   83 7d f8 02             cmpl   $0x2,-0x8(%ebp)8048bf7:  7e d3                   jle    8048bcc <phase_2+0x28>8048bf9:    83 7d fc 00             cmpl   $0x0,-0x4(%ebp)8048bfd:  75 05                   jne    8048c04 <phase_2+0x60>8048bff:    e8 d6 0a 00 00          call   80496da <explode_bomb>8048c04: c9                      leave  8048c05: c3                      ret
  • 首先还是压栈栈底地址%ebp ,返回地址
  • 赋值新的栈底
  • esp 向下移动0x28=40个字节,即空间分配40个字节
  • %ebp-4的位置存入0
  • %eax = ebp-32
  • 将地址ebp-32存入esp+4的内存位置
  • 将内存位置ebp+8位置存的值写入内存中%esp指向的位置
  • 调用函数read_six_numbers 读六个number
  • 然后将ebp-8位置内存写入0
  • 接下来跳转到8048bf3执行,注意这一开时候ebp-8位置内存是0,
  • 然后ebp-8位置内存中中内容和0比较,当小于等于0时跳转到8048bcc执行,否则继续
  • 然后内存中Mem(%ebp-8)的值赋值给%eax,计算%edx = Mem(4%eax+ebp-32):就是将ebp-32这个位置向上移4*%eax长度的位置中的值,%eax是Mem(%ebp-8)的值
  • 同样内存中Mem(%ebp-8)的值赋值给%eax,计算%eax = Mem(4(%eax+3)+ebp-32),注意现在是eax+3的值在做计算:同理就是将ebp-32这个位置向上移4*(%eax+3)长度的位置中的值,%eax是Mem(%ebp-8)的值
  • 比较edx与eax中的值,相同则跳过,否则就爆炸
  • 跳过后,内存中Mem(%ebp-8)的值赋值给%eax,计算%eax = Mem(4%eax+ebp-32),然后Mem(%ebp-4)+=%eax
  • 然后将Mem(%ebp-8)位置自增1
  • 接下来又进入判断Mem(%ebp-8)与0x2大小比较,然后跳转执行,可以理解当循环3次后,继续就不跳转
  • 不跳转继续执行是比较Mem(%ebp-4)的值是否为0,是0则爆炸,否则通过

由此可以看出Mem(%ebp-8)处存的是控制循环次数的变量i,初始i=0,i<=2。

Mem(%ebp-4)的值存的是初始为0,后面会连续累加每次计算Mem(4%eax+ebp-32)的值,这里eax就是i值。

在循环中,其实通过Mem(4%eax+ebp-32)和Mem(4(%eax+3)+ebp-32)就是比较每隔3个位置的值是否相同。

所以就是读入了6个值,然后六个值中间隔三个是相同的,如 1 2 3 1 2 3.

phase_3

开启gdb调试,并打断点到phase_3,记得输入前面阶段的答案

>> gbd bomb
(gdb) break phase_3
(gdb) run
(gdb) disas

用disas 命令查看此函数汇编代码,对应在bomb.s中这一段:

08048c06 <phase_3>:8048c06:    55                      push   %ebp8048c07: 89 e5                   mov    %esp,%ebp8048c09:    83 ec 38                sub    $0x38,%esp // 分配56字节空间,esp-568048c0c: c7 45 f8 00 00 00 00    movl   $0x0,-0x8(%ebp) // 将0存入Mem(%ebp-8)8048c13:   8d 45 f0                lea    -0x10(%ebp),%eax // eax = %ebp-16的地址值8048c16:   89 44 24 10             mov    %eax,0x10(%esp) // 将%ebp-16的地址值存入 Mem(%esp+16)8048c1a:  8d 45 ef                lea    -0x11(%ebp),%eax // eax = %ebp-17的地址值8048c1d:   89 44 24 0c             mov    %eax,0xc(%esp) // %ebp-17的地址值存入 Mem(%esp+12)8048c21:    8d 45 f4                lea    -0xc(%ebp),%eax // eax = %ebp-12的地址值8048c24:    89 44 24 08             mov    %eax,0x8(%esp) // %ebp-12的地址值 存入 Mem(%esp+8)8048c28:    c7 44 24 04 0b 9a 04    movl   $0x8049a0b,0x4(%esp) // 立即数0x8049a0b存入 Mem(%esp+4)8048c2f:  08 8048c30: 8b 45 08                mov    0x8(%ebp),%eax // eax = Mem(ebp+8),应该是取参数8048c33:  89 04 24                mov    %eax,(%esp) // Mem(ebp+8) -> 存入 Mem(esp)8048c36: e8 2d fc ff ff          call   8048868 <sscanf@plt> // 访问函数sscanf8048c3b:    89 45 f8                mov    %eax,-0x8(%ebp) // 将函数sscanf结果写入Mem(ebp-8)8048c3e:   83 7d f8 02             cmpl   $0x2,-0x8(%ebp) // 将函数sscanf(在Mem(ebp-8))与0x2比较8048c42:    7f 05                   jg     8048c49 <phase_3+0x43> // 大于2则跳过bomb,否则失败8048c44:  e8 91 0a 00 00          call   80496da <explode_bomb>8048c49: 8b 45 f4                mov    -0xc(%ebp),%eax // eax = Mem(ebp-12)8048c4c:    89 45 dc                mov    %eax,-0x24(%ebp) // 将Mem(ebp-12) -> 存入 Mem(ebp-36)8048c4f:    83 7d dc 07             cmpl   $0x7,-0x24(%ebp) // 比较Mem(ebp-36)内容与 0x78048c53: 0f 87 c0 00 00 00       ja     8048d19 <phase_3+0x113> 大于则跳到 8048d19 去爆炸8048c59: 8b 55 dc                mov    -0x24(%ebp),%edx // edx = Mem(ebp-36),即刚才要小于等7的判断值8048c5c:  8b 04 95 14 9a 04 08    mov    0x8049a14(,%edx,4),%eax // eax = Mem(0x8049a14+edx*4)8048c63:  ff e0                   jmp    *%eax // 跳转到%eax中存的地方,猜测这里就是在switch选择跳转位置了8048c65:    c6 45 ff 79             movb   $0x79,-0x1(%ebp) // Mem(ebp-1) = 0x798048c69:   8b 45 f0                mov    -0x10(%ebp),%eax // eax = Mem(ebp-16)8048c6c:   3d 46 03 00 00          cmp    $0x346,%eax // 比较 Mem(ebp-16) == 0x3468048c71: 0f 84 ab 00 00 00       je     8048d22 <phase_3+0x11c> // 相同则跳转8048d22,否则bomb,所以需要跳转8048c77:   e8 5e 0a 00 00          call   80496da <explode_bomb>8048c7c: e9 a1 00 00 00          jmp    8048d22 <phase_3+0x11c> // 跳转到8048d228048c81: c6 45 ff 69             movb   $0x69,-0x1(%ebp) // Mem(ebp-1) = 0x698048c85:   8b 45 f0                mov    -0x10(%ebp),%eax // eax = Mem(ebp-16)8048c88:   3d 6f 03 00 00          cmp    $0x36f,%eax // 比较Mem(ebp-16) 与 0x36f8048c8d: 0f 84 8f 00 00 00       je     8048d22 <phase_3+0x11c> // 相同则跳转8048d22,不同bomb8048c93: e8 42 0a 00 00          call   80496da <explode_bomb>8048c98: e9 85 00 00 00          jmp    8048d22 <phase_3+0x11c> // 跳转到8048d228048c9d: c6 45 ff 68             movb   $0x68,-0x1(%ebp) // Mem(ebp-1) = 0x688048ca1:   8b 45 f0                mov    -0x10(%ebp),%eax // eax = Mem(ebp-16)8048ca4:   3d 74 02 00 00          cmp    $0x274,%eax // 比较Mem(ebp-16) 与 0x2748048ca9: 74 77                   je     8048d22 <phase_3+0x11c> // 相同则跳转8048d22,不同bomb8048cab: e8 2a 0a 00 00          call   80496da <explode_bomb> // 8048cb0: eb 70                   jmp    8048d22 <phase_3+0x11c> // 跳转到8048d228048cb2: c6 45 ff 6e             movb   $0x6e,-0x1(%ebp) // Mem(ebp-1) = 0x6e8048cb6:   8b 45 f0                mov    -0x10(%ebp),%eax // eax = Mem(ebp-16)8048cb9:   83 f8 46                cmp    $0x46,%eax // 比较Mem(ebp-16) 与 0x468048cbc:   74 64                   je     8048d22 <phase_3+0x11c> // 相同则跳转8048d22,不同bomb8048cbe: e8 17 0a 00 00          call   80496da <explode_bomb> // 8048cc3: eb 5d                   jmp    8048d22 <phase_3+0x11c> // 跳转到8048d228048cc5: c6 45 ff 64             movb   $0x64,-0x1(%ebp) // Mem(ebp-1) = 0x648048cc9:   8b 45 f0                mov    -0x10(%ebp),%eax // eax = Mem(ebp-16)8048ccc:   3d 5b 01 00 00          cmp    $0x15b,%eax // 比较Mem(ebp-16) 与 0x15b8048cd1: 74 4f                   je     8048d22 <phase_3+0x11c> // 相同则跳转8048d22,不同bomb8048cd3: e8 02 0a 00 00          call   80496da <explode_bomb> // 8048cd8: eb 48                   jmp    8048d22 <phase_3+0x11c> // 跳转到8048d228048cda: c6 45 ff 6b             movb   $0x6b,-0x1(%ebp) // Mem(ebp-1) = 0x6b8048cde:   8b 45 f0                mov    -0x10(%ebp),%eax // eax = Mem(ebp-16)8048ce1:   3d 5c 03 00 00          cmp    $0x35c,%eax // 比较Mem(ebp-16) 与 0x35c8048ce6: 74 3a                   je     8048d22 <phase_3+0x11c> // 相同则跳转8048d22,不同bomb8048ce8: e8 ed 09 00 00          call   80496da <explode_bomb> // 8048ced: eb 33                   jmp    8048d22 <phase_3+0x11c> // 跳转到8048d228048cef: c6 45 ff 65             movb   $0x65,-0x1(%ebp) // Mem(ebp-1) = 0x658048cf3:   8b 45 f0                mov    -0x10(%ebp),%eax //  eax = Mem(ebp-16)8048cf6:  3d 9c 02 00 00          cmp    $0x29c,%eax // 比较Mem(ebp-16) 与 0x29c8048cfb: 74 25                   je     8048d22 <phase_3+0x11c> // 相同则跳转8048d22,不同bomb8048cfd: e8 d8 09 00 00          call   80496da <explode_bomb> // 8048d02: eb 1e                   jmp    8048d22 <phase_3+0x11c> // 跳转到8048d228048d04: c6 45 ff 63             movb   $0x63,-0x1(%ebp) // Mem(ebp-1) = 0x638048d08:   8b 45 f0                mov    -0x10(%ebp),%eax //  eax = Mem(ebp-16)8048d0b:  3d eb 00 00 00          cmp    $0xeb,%eax // 比较Mem(ebp-16) 与 0xeb8048d10:   74 10                   je     8048d22 <phase_3+0x11c> // 相同则跳转8048d22,不同bomb8048d12: e8 c3 09 00 00          call   80496da <explode_bomb> // 8048d17: eb 09                   jmp    8048d22 <phase_3+0x11c> // 跳转到8048d228048d19: c6 45 ff 63             movb   $0x63,-0x1(%ebp) // Mem(ebp-1) = 0x638048d1d:   e8 b8 09 00 00          call   80496da <explode_bomb> // 爆炸8048d22:   0f b6 45 ef             movzbl -0x11(%ebp),%eax // eax = Mem(ebp-17)8048d26:   38 45 ff                cmp    %al,-0x1(%ebp) // 比较al 1字节与 ebp-1位置的值,8048d29:    74 05                   je     8048d30 <phase_3+0x12a> 相同则推出,否则bomb8048d2b:   e8 aa 09 00 00          call   80496da <explode_bomb>8048d30: c9                      leave  8048d31: c3                      ret

具体分析见注释,然后可以得到如下分析:

  • 首先分配了56字节的地址空间,初始化Mem(%ebp-8)为0

  • 然后将三个地址%ebp-16、%ebp-17、%ebp-12和立即数0x8049a0b存入靠近esp的24个字节位置中,将Mem(ebp+8)内存中值(应该是phase_3的输入参数)存入Mem(esp),这一部分应该是存参数,作为sscanf函数的入参,所以参数有3个指针引用,一个立即数,一个普通参数,然后调用sscanf函数。

  • 然后将sscanf函数的返回结果存入Mem(ebp-8)位置,将此返回结果与0x2比较,若大于2则跳到8048c49(即跳过了爆炸),否则爆炸,所以这个返回结果一定是要大于2的

  • 大于2跳过爆炸后,将Mem(ebp-12)内容存入Mem(ebp-36),猜测Mem(ebp-12)内存中存的值应该是被sscanf函数改变过的,将此值,即Mem(ebp-36) 复制的Mem(ebp-12)的值与0x7比较,大于0x7则跳去8048d19,修改Mem(ebp-1) = 0x63,然后爆炸

  • 否则小于等于7的话不跳过的话继续执行,将这个值,即Mem(ebp-36) 复制的Mem(ebp-12)的值存入%edx,然后作计算 Mem(0x8049a14+edx*4),相当于edx作索引,从0x8049a14地址后面取第i个int类型值。

  • 然后将此int类型的值作为一个地址跳转过去,猜测这里应该就是switch操作,选择 跳转表 中的地址用于跳转,那么就查看一下地址0x8049a14后面的值,是否是跳转表内容,用命令 x/40x 0x8049a14 查看地址0x8049a14后40个字节的十六进制内容

  • (gdb) x/40x 0x8049a14
    0x8049a14:      0x65    0x8c    0x04    0x08    0x81    0x8c    0x040x08
    0x8049a1c:      0x9d    0x8c    0x04    0x08    0xb2    0x8c    0x040x08
    0x8049a24:      0xc5    0x8c    0x04    0x08    0xda    0x8c    0x040x08
    0x8049a2c:      0xef    0x8c    0x04    0x08    0x04    0x8d    0x040x08
    0x8049a34:      0x25    0x64    0x00    0x73    0x61    0x69    0x6e0x74
    
  • 则由查看结果,可以看到0x8049a14有8个地址和代码中地址能对应,依次是

  • 0x08048c65
    0x08048c81
    0x08048c9d
    0x08048cb2
    0x08048cc5
    0x08048cda
    0x08048cef
    0x08048d04
    
  • 那么这些就对应switch选择后跳转的位置标签,选择值通过Mem(0x8049a14+edx*4)计算,关键就是%edx的索引,而%edx是Mem(ebp-36) 复制的Mem(ebp-12)的值,Mem(ebp-12)的值应该是被sscanf函数修改过的。

  • 接下来就重复了8组代码,都是每个switch case中的处理,逻辑都相同,将Mem(ebp-1) 赋值,然后比较Mem(ebp-16)与一个数是否相同,不同bomb,相同跳转到8048d22最后处理,Mem(ebp-16)的值应该是被sscanf函数修改过的。

  • 在8048d22处代码,比较Mem(ebp-17)与Mem(ebp-1) 值,相同则推出,Mem(ebp-1) 是在switch case处理中赋值的,Mem(ebp-17)是sscanf有修改的值。

  • 最后串起来最初调用sscanf之前做的处理,有三个指针,即三个地址%ebp-16、%ebp-17、%ebp-12,后面也用到了,然后有一个立即数0x8049a0b,看看这个立即数地址存的什么

  • (gdb) x/s 0x8049a0b
    0x8049a0b:      "%d %c %d"
    
  • 所以这个代表sscanf读入三个数, int char int 三个类型

  • 所以推测整个函数功能就是,在sscanf函数中输入一个数(%ebp-12位置)作为选择switch分支,这个数在范围是0~7;第二个参数char(%ebp-17位置)与Mem(ebp-1)比较是否相同,Mem(ebp-1)是在每个case分支赋值的,故选择一个分支,对应相同的填入即可,第三个参数(%ebp-16位置)是在case分支内必须和其一个数相同。

  • 这里选择第一个数来尝试,即输入0 ,跳转到case0x08048c65,Mem(ebp-1) = 0x79,那么输入的char也应该是0x79(代表y),比较 Mem(ebp-16) == 0x346,则输入的int应该是838

所以第三阶段的一个答案是

0 y 838

phase_4

开启gdb调试,并打断点到phase_4, 记得输入前面阶段的答案

>> gbd bomb
(gdb) break phase_4
(gdb) run
(gdb) disas

用disas 命令查看此函数汇编代码,对应在bomb.s中这一段:

08048d72 <phase_4>:8048d72:    55                      push   %ebp  // 压栈返回地址8048d73:  89 e5                   mov    %esp,%ebp // 新的栈底8048d75:    83 ec 28                sub    $0x28,%esp // 分配40字节空间,esp8048d78:    8d 45 f4                lea    -0xc(%ebp),%eax // eax = ebp-128048d7b: 89 44 24 08             mov    %eax,0x8(%esp) // 将地址ebp-12存入Mem(esp+8)8048d7f: c7 44 24 04 34 9a 04    movl   $0x8049a34,0x4(%esp) // 将地址0x8049a34存入Mem(esp+4)8048d86:    08 8048d87: 8b 45 08                mov    0x8(%ebp),%eax // eax = Mem(ebp+8) 这个地址应该是提取phase_4的一个参数8048d8a: 89 04 24                mov    %eax,(%esp) // 将此参数存入Mem(esp)8048d8d:    e8 d6 fa ff ff          call   8048868 <sscanf@plt> // 调用sscanf函数8048d92:    89 45 fc                mov    %eax,-0x4(%ebp) // 将sscanf函数结果存入Mem(ebp-4)8048d95:   83 7d fc 01             cmpl   $0x1,-0x4(%ebp) // 比较这个结果和18048d99:  75 07                   jne    8048da2 <phase_4+0x30> // 不同则跳到8048da2爆炸,否则继续8048d9b:  8b 45 f4                mov    -0xc(%ebp),%eax // eax = Mem(ebp-12) 也就是之前传入sscanf的指针指向的区域中的值8048d9e:   85 c0                   test   %eax,%eax // 判断eax中的值是否为08048da0:    7f 05                   jg     8048da7 <phase_4+0x35> // >0则跳过,否则爆炸8048da2:   e8 33 09 00 00          call   80496da <explode_bomb> // 爆炸8048da7:   8b 45 f4                mov    -0xc(%ebp),%eax // eax = Mem(ebp-12) 也就是之前传入sscanf的指针指向的区域中的值8048daa:   89 04 24                mov    %eax,(%esp) // 将此结果Mem(ebp-12)存入Mem(esp)8048dad: e8 80 ff ff ff          call   8048d32 <func4> // 调用函数func48048db2:   89 45 f8                mov    %eax,-0x8(%ebp) // 将结果存入 Mem(ebp-8)8048db5:  81 7d f8 62 02 00 00    cmpl   $0x262,-0x8(%ebp) // 比较结果Mem(ebp-8)这个值和 0x2628048dbc:    74 05                   je     8048dc3 <phase_4+0x51> // 相等则离开,否则爆炸8048dbe:   e8 17 09 00 00          call   80496da <explode_bomb> // 8048dc3: c9                      leave  8048dc4: c3                      ret

见注释分析后,总结其功能如下:

  • 在调用sscanf函数之前,做了一些准备工作,即其传入的参数有三个,地址ebp-12即一个指针,猜测是要将输入的值存在这,地址立即数0x8049a34即输入的格式, Mem(ebp+8)中的内容作为参数3。查看0x8049a34地址中存的内容

  • (gdb) x/s 0x8049a34
    0x8049a34:      "%d"
    
  • 说明输入的是一个int值,然后存在地址ebp-12处的内存。

  • 调用sscanf函数后,将返回值存在Mem(ebp-4),返回值不为1则继续执行

  • 判断Mem(ebp-12)这个传入sscanf指针指向的内存中存的值是否大于0,大于继续执行

  • 然后将Mem(ebp-12)这个值存入Mem(esp)作为func4函数的参数,然后调用func4,注意Mem(ebp-12)中的值其实就是sscanf中我们输入的值

  • func4函数执行完成后比对结果是否等于0x262 = 610,相等则离开

所以关键在于func4利用sscanf输入的值做了什么处理,最后一定要返回0x262才行,接下来查看func4函数中的代码,同样设置断点并查看

(gdb) break func4
(gdb) continue
Breakpoint 3, 0x08048d36 in func4 ()
(gdb) disas

展现的代码就是下面这一段:

08048d32 <func4>:8048d32:  55                      push   %ebp \\ 保存返回地址8048d33:   89 e5                   mov    %esp,%ebp \\ 新的栈底8048d35:    53                      push   %ebx \\ 被调用者保存寄存器8048d36:    83 ec 08                sub    $0x8,%esp \\ 分配空间8字节8048d39: 83 7d 08 01             cmpl   $0x1,0x8(%ebp) \\ 比较输入的值Mem(ebp+8) 与 18048d3d:  7f 09                   jg     8048d48 <func4+0x16> \\ 大于的话跳转到8048d48,否则继续执行8048d3f:  c7 45 f8 01 00 00 00    movl   $0x1,-0x8(%ebp) \\ 将Mem(ebp-8)处的值赋值为 18048d46:   eb 21                   jmp    8048d69 <func4+0x37> \\ 跳转到 8048d69继续执行8048d48:   8b 45 08                mov    0x8(%ebp),%eax \\ eax = 输入的参数Mem(ebp+8)8048d4b:    48                      dec    %eax \\ 输入的值-18048d4c:   89 04 24                mov    %eax,(%esp) \\ 将此值放入Mem(%esp)作为下面调用func4的递归参数8048d4f:    e8 de ff ff ff          call   8048d32 <func4>8048d54:    89 c3                   mov    %eax,%ebx \\ ebx = 将func4返回值 8048d56:   8b 45 08                mov    0x8(%ebp),%eax \\ eax = 输入参数Mem(ebp+8)8048d59: 83 e8 02                sub    $0x2,%eax \\ eax = 输入参数Mem(ebp+8)-28048d5c:    89 04 24                mov    %eax,(%esp) \\ 将此值放入Mem(%esp)作为下面调用func4的递归参数8048d5f:    e8 ce ff ff ff          call   8048d32 <func4> 8048d64:   01 c3                   add    %eax,%ebx \\ 将func4的返回结果累加到ebx中8048d66:  89 5d f8                mov    %ebx,-0x8(%ebp) \\ 将ebx的值存入 Mem(ebp-8)8048d69:   8b 45 f8                mov    -0x8(%ebp),%eax \\ 将Mem(ebp-8)中的值作为结果8048d6c:    83 c4 08                add    $0x8,%esp \\ 将esp+=8 释放空间8048d6f:  5b                      pop    %ebx \\ 弹出ebp8048d70:    5d                      pop    %ebp \\ 弹栈出函数地址8048d71:  c3                      ret

见代码注释,可以得到如下分析:

  • 整个代码将输入参数x,做了两次func4递归, func4(x-1)和func4(x-2),然后将两次递归的结果累加返回,如果x==1的时候返回1.
  • 所以这就是一个递归的裴波那契数列计算函数
  • 则F(x) = F(x-1)+F(x-2), F(1)=1,F(2)=1,F(3)=2…F(14)=610=0x262

所以由上分析,需要输入一个参数使得裴波那契函数计算结果为610,即输入14就可以得到。

phase_5

开启gdb调试,并打断点到phase_5,记得输入前面阶段的答案

>> gbd bomb
(gdb) break phase_5
(gdb) run
(gdb) disas

用disas 命令查看此函数汇编代码,对应在bomb.s中这一段:

08048dc5 <phase_5>:8048dc5:    55                      push   %ebp \\ 保存返回地址8048dc6:   89 e5                   mov    %esp,%ebp \\ 新的栈底8048dc8:    83 ec 18                sub    $0x18,%esp \\ 分配24字节空间,esp8048dcb:    8b 45 08                mov    0x8(%ebp),%eax \\ 将函数输入参数Mem(ebp+8)放入eax8048dce:    89 04 24                mov    %eax,(%esp) \\ 将此参数放入 Mem(ebp) ,即调用string_length的参数8048dd1:   e8 13 03 00 00          call   80490e9 <string_length>8048dd6:    89 45 fc                mov    %eax,-0x4(%ebp) \\ 将string_length返回值存入Mem(ebp-4)位置8048dd9:   83 7d fc 06             cmpl   $0x6,-0x4(%ebp) \\ 比较string_length返回值 与 68048ddd:    74 05                   je     8048de4 <phase_5+0x1f> \\ 相同则跳8048de4,否则爆炸,实际就是跳过爆炸代码8048ddf:   e8 f6 08 00 00          call   80496da <explode_bomb>8048de4: c7 45 f8 00 00 00 00    movl   $0x0,-0x8(%ebp) \\ 初始化Mem(ebp-8) 为 08048deb: eb 20                   jmp    8048e0d <phase_5+0x48> \\ 跳转到 8048e0d8048ded: 8b 55 f8                mov    -0x8(%ebp),%edx \\ edx = Mem(ebp-8)8048df0: 8b 45 f8                mov    -0x8(%ebp),%eax\\ eax = Mem(ebp-8)8048df3:  03 45 08                add    0x8(%ebp),%eax \\ eax += (ebp+8),即循环的次数+输入参数指针 ptr+i8048df6:    0f b6 00                movzbl (%eax),%eax \\ 将Mem(eax)的一个字节写入 eax,0补充高位8048df9: 0f be c0                movsbl %al,%eax \\ 将al填入eax,符号位填充8048dfc:    83 e0 0f                and    $0xf,%eax \\ 将eax &= 0xf(即0b1111) and运算 8048dff:    0f b6 80 c0 a5 04 08    movzbl 0x804a5c0(%eax),%eax \\ 将Mem(0x804a5c0+%eax) 值写入eax8048e06: 88 44 15 f1             mov    %al,-0xf(%ebp,%edx,1) \\ 将eax的值存入 Mem(ebp+edx-16) exd中存的是循环此数,即要存ebp-16到ebp-11的6个字节值8048e0a: ff 45 f8                incl   -0x8(%ebp) \\ 自增Mem(ebp-8),其实就是循环完成次数+18048e0d:  83 7d f8 05             cmpl   $0x5,-0x8(%ebp) \\ 将 Mem(ebp-8) 与 5 比较,猜测这是循环次数的判断8048e11:    7e da                   jle    8048ded <phase_5+0x28> \\ 小于等于5则跳到 8048ded继续执行8048e13:    c6 45 f7 00             movb   $0x0,-0x9(%ebp) \\ 大于5后, 将Mem(ebp-9) = 08048e17: c7 44 24 04 37 9a 04    movl   $0x8049a37,0x4(%esp) \\ 然后将立即数0x8049a37赋值到 Mem(esp+4)8048e1e:   08 8048e1f: 8d 45 f1                lea    -0xf(%ebp),%eax \\ eax = ebp-158048e22: 89 04 24                mov    %eax,(%esp) \\ Mem(esp) = ebp-15的地址,其实相当于传了一个指针8048e25:  e8 e9 02 00 00          call   8049113 <strings_not_equal> 调用判断字符串不相等8048e2a: 85 c0                   test   %eax,%eax \\ 判断结果8048e2c:    74 05                   je     8048e33 <phase_5+0x6e> \\ 若和0相同说明相等,就正确推出,若不等就爆炸8048e2e:    e8 a7 08 00 00          call   80496da <explode_bomb>8048e33: c9                      leave  8048e34: c3                      ret

可见注释分析,总结如下:

  • 将输入的字符串在string_length函数中得到长度,返回值即字符串长度一定是6

  • 初始化Mem(ebp-8) 为 0,跳到8048e0d出判断此值是否小于等于5,是则跳入8048ded继续执行,所以这里其实是一个循环6次的循环,应该是把每个输入的字符作处理判断

  • 循环内部:将循环次数i存入 %edx %eax。然后eax+Mem(ebp+8),即循环的次数+输入参数,将Mem(eax+Mem(ebp+8))的一个字节写入 eax,0补充高位,然后将al填入eax,符号位填充;将eax &= 0xf(即0b1111) ,其实这一段运算就是将Mem(eax+Mem(ebp+8))存的一个字节内容放入eax中,只留下了低4位bit的内容。

  • 然后将刚刚一堆计算得到的eax值继续运算,Mem(0x804a5c0+%eax) 存的内容写一个字节到Mem(ebp+edx-16)位置。那么6次循环,exd中存的是循环此数,即要存ebp-16到ebp-11的6个字节值。所以查看一下0x804a5c0位置存了些什么

  • (gdb) x/s 0x804a5c0
    0x804a5c0 <array.2511>: "isrveawhobpnutf", <incomplete sequence \371>
    
  • 可以看到0x804a5c0处是一个数组,存了0x76727369 0x68776165 0x6e70626f 0x67667475 四个数。

  • 然后就是循环次数自增i++,

  • 跳出循环后会判断ebp-15的地址的字符串是否和地址0x8049a37处的字符串相同,不同则爆炸,相同则成功,所以查看0x8049a37处的字符串

  • (gdb) x/s 0x8049a37
    0x8049a37:      "saints"
    
  • 所以理解其实就是从0x804a5c0处的字符串“isrveawhobpnutf”中通过下标取值,一次得到一个字符串,最后一共6个字符,和“saints”字符串要相等。那么输入6次的下标可以选择为1,5,0,11,13,1,然后回看循环内部如何处理输入得到索引下标呢, 索引字符串“isrveawhobpnutf”下标eax是 eax += (ebp+8),将输入参数地址ebp+8与循环次数eax累加,依次访问6个字符,然后每个字符的低4位作为索引。所以这里我直接选择0x31,0x35,0x30,0x3b,0x3d,0x31作为结果,即他们的低4位是1,5,0,11,13,1,对应ASCII为

150;=1

phase_6

开启gdb调试,并打断点到phase_6,记得输入前面阶段的答案

>> gbd bomb
(gdb) break phase_6
(gdb) run
(gdb) disas

用disas 命令查看此函数汇编代码,对应在bomb.s中这一段:

08048ec9 <phase_6>:8048ec9:  push   %ebp  \\ 保存返回地址8048eca:  mov    %esp,%ebp \\ 新的栈底8048ecc:  sub    $0x18,%esp \\ 分配24字节空间8048ecf:  movl   $0x804a63c,-0x8(%ebp) \\ 将立即数0x804a63c 存入 Mem(ebp-8)8048ed6:  mov    0x8(%ebp),%eax \\ 将输入参数Mem(ebp+8) 赋值给 eax8048ed9:  mov    %eax,(%esp) \\ 将Mem(ebp+8)参数放入 Mem(esp) 作为atoi的输入参数8048edc:  call   8048858 <atoi@plt> \\ 将字符串转换为int类型数据8048ee1:  mov    %eax,%edx \\ 将转换的int数结果存入edx8048ee3:  mov    -0x8(%ebp),%eax \\ eax = Mem(ebp-8) = 0x804a63c8048ee6:  mov    %edx,(%eax) \\ 将转换的int数结果存入 Mem(0x804a63c)8048ee8:  mov    -0x8(%ebp),%eax \\ eax = Mem(ebp-8) = 0x804a63c8048eeb:  mov    %eax,(%esp) \\ 将0x804a63c 作为参数放在Mem(esp)8048eee:  call   8048e35 <fun6> \\ 调用fun6函数8048ef3:  mov    %eax,-0x8(%ebp) \\ 将fun6返回结果放入 Mem(ebp-8) = 0x0x804a6308048ef6:  mov    -0x8(%ebp),%eax \\ eax = Mem(ebp-8)8048ef9:  mov    %eax,-0x4(%ebp) \\ 将fun6返回结果存入 Mem(ebp-4)8048efc:  movl   $0x1,-0xc(%ebp) \\ 将1存入 Mem(ebp-12)8048f03:  jmp    8048f11 <phase_6+0x48> \\ 跳转到 8048f118048f05:  mov    -0x4(%ebp),%eax \\ eax = Mem(ebp-4)8048f08:  mov    0x8(%eax),%eax \\ eax = Mem(8 + Mem(ebp-4))8048f0b:  mov    %eax,-0x4(%ebp) \\ 将更新的结果放回 Mem(ebp-4)8048f0e:  incl   -0xc(%ebp) \\ Mem[ebp-12]++,即循环次数i++8048f11:  cmpl   $0x7,-0xc(%ebp) \\ 比较 7 和 Mem(ebp-12)的值,Mem(ebp-12)的值初始化是18048f15:  jle    8048f05 <phase_6+0x3c> \\ 小于等于则跳 8048f05继续执行,所以这里其实是循环8次8048f17:  mov    -0x4(%ebp),%eax \\ eax = Mem(ebp-4)8048f1a:  mov    (%eax),%edx \\ edx = Mem(Mem(ebp-4))8048f1c:  mov    0x804a63c,%eax \\ eax = Mem(0x804a63c)地址中存的值8048f21:  cmp    %eax,%edx \\ 比较这两个值,相同就能通过8048f23:  je     8048f2a <phase_6+0x61> \\ 8048f25:  call   80496da <explode_bomb> \\ 8048f2a:  leave  8048f2b:  ret

分析见注释,总结如下:

  • 首先初始化栈空间,分配了24字节,将立即数0x804a63c 存入 Mem(ebp-8)

  • 将输入参数作为atoi的输入参数,字符串转化为int数据类型,结果存到Mem(0x804a63c)这个立即数指向的位置

  • 然后将0x804a63c 作为参数放在Mem(esp)调用fun6函数,fun6的调用结果放在了 Mem(ebp-8)和Mem(ebp-4)中

  • Mem(ebp-12)作为一个循环的计数器,初始化为1,<=7一直循环

  • 循环内部每次对Mem(ebp-12)++,内部的工作为:Mem(ebp-4) = Mem(Mem(ebp-4)+8),这个有点像链表的next操作 p = p->next,实际就是向后操作了7次

  • 然后edx= Mem(Mem(ebp-4))的结果和eax = Mem(0x804a63c)地址中存的值(即输入的值转化为int类型的结果)比较是否相同

  • 所以直接查看%edx中的结果是什么即可。

  • 0x08048f1c in phase_6 ()
    (gdb) p/x $edx
    $2 = 0xf8
    
  • 所以答案就是0xf8 = 248

  • 但其实解答过程并没有这么简单,之前0x804a63c这一片的地址查看,其实可以知道他们是10个节点串起来的指针,

  • (gdb) x/120b 0x804a5d0
    0x804a5d0 <node9>:      0xf9    0x00    0x00    0x00    0x09    0x00    0x00    0x00
    0x804a5d8 <node9+8>:    0x00    0x00    0x00    0x00    0xf8    0x00    0x00    0x00
    0x804a5e0 <node8+4>:    0x08    0x00    0x00    0x00    0xd0    0xa5    0x04    0x08
    0x804a5e8 <node7>:      0xb1    0x02    0x00    0x00    0x07    0x00    0x00    0x00
    0x804a5f0 <node7+8>:    0xdc    0xa5    0x04    0x08    0x03    0x01    0x00    0x00
    0x804a5f8 <node6+4>:    0x06    0x00    0x00    0x00    0xe8    0xa5    0x04    0x08
    0x804a600 <node5>:      0x69    0x03    0x00    0x00    0x05    0x00    0x00    0x00
    0x804a608 <node5+8>:    0xf4    0xa5    0x04    0x08    0xe7    0x01    0x00    0x00
    0x804a610 <node4+4>:    0x04    0x00    0x00    0x00    0x00    0xa6    0x04    0x08
    0x804a618 <node3>:      0x16    0x03    0x00    0x00    0x03    0x00    0x00    0x00
    0x804a620 <node3+8>:    0x0c    0xa6    0x04    0x08    0x6d    0x00    0x00    0x00
    0x804a628 <node2+4>:    0x02    0x00    0x00    0x00    0x18    0xa6    0x04    0x08
    0x804a630 <node1>:      0xa9    0x03    0x00    0x00    0x01    0x00    0x00    0x00
    0x804a638 <node1+8>:    0x24    0xa6    0x04    0x08    0x00    0x00    0x00    0x00
    0x804a640 <node0+4>:    0x00    0x00    0x00    0x00    0x30    0xa6    0x04    0x08
  • 每个节点12个字节,故猜测其结构是

  • struct node{int val; \\node+0node* next; \\ node+8
    }
    
  • 可以看到每个node有12个字节,高地址4字节存next指针,所以可以看到链表初始结构如下:

  • node0->next = 0x804a630 node1 , node0->val = 输入值
    node1->next = 0x804a624 node2 , node1->val = 0x3a9
    node2->next = 0x804a618 node3 , node2->val = 0x6d
    node3->next = 0x804a60c node4 , node3->val = 0x316
    node4->next = 0x804a600 node5 , node4->val = 0x1e7
    node5->next = 0x804a5f4 node6 , node5->val = 0x369
    node6->next = 0x804a5e8 node7 , node6->val = 0x103
    node7->next = 0x804a5dc node8 , node7->val = 0x2b1
    node8->next = 0x804a5d0 node9 , node8->val = 0xf8
    node9->next = 0x0000000 null  , node9->val = 0xf9
    
  • 而函数中循环7次,是从返回的结果开始循环,最后其实操作p = p->next 7次到一个节点,而链表结构是在fun4函数中有一堆更改操作,总之这里我们取巧直接去尝试每一个节点的值作为输入值比对,最后发现结果有两个:

  • 0xf8=248
    或者
    0xf9=249
    

这里我们选取249作为结果

总结结果

最终选取的结果是

Why make trillions when we could make... billions?
1 2 3 1 2 3
0 y 838
14
150;=1
249

然后运行命令测试

CSAPP 拆炸弹 中科大实验相关推荐

  1. 计算机系统实验拆炸弹,CSAPP 炸弹实验解析上

    CSAPP(Computer Systems A Programmer's Perspective),中译名为深入理解计算机系统,是一本优秀的计算机教材.该书配套了若干个课后实验,可供读者检验所学知识 ...

  2. CSAPP_实验二 拆炸弹 汇编知识应用

    CSAPP--实验二 拆炸弹 Phase1 disas phase_1, 反汇编 phase_1 函数 在 phase_1函数入口处 设置断点 break phase_1 run 开始运行,输入字符串 ...

  3. 虚数有物理意义:中科大潘建伟、南科大范靖云团队首次实验排除实数形式的标准量子力学...

    视学算法报道 编辑:泽南.小舟 量子力学的理论能否只用实数构造?从理论上和实践上,研究人员都得出了否定的答案. 虽然在高中数学里,大家都接触过虚数这个概念,但它看起来总是那么反直觉:虚数这个名词是 1 ...

  4. 计算机分子模拟聚乙烯,高分子物理实验思考题@中科大.pdf

    高分子物理实验思考题@中科大 1.为什么在计算机模拟实验1 (用"分子模拟"软件构建全同立构聚丙烯分子.聚乙烯分子 并计算它们末端的直线距离)中我们一再把第一个碳原子到最后一个碳原 ...

  5. 科大奥锐实验报告霍尔效应_中科大929半导体物理专业课高分学长考研经验

    推送会和你一起进步哒,记得右上角把我设为星标哦~ 考研经验 3 August 2020 中科大上岸学长 考研途中最重要的是选择 本人参加了2020中科大929半导体物理考研,分数137. 关于半导体物 ...

  6. 西北农林科技大学linux实验,2021双非科班调剂985(一志愿中科大,调剂上岸西北农林科技大学)初试复试经验帖...

    马上毕业啦,回想过去的一年想通过这一份经验帖给自己的大学画上一个圆满的句号,很多同学应该看过其他学长学姐各种各样的经验帖和各科的时间规划,本文就不过多谈各科的时间安排,主要是阐述自己这一年各科踩过的坑 ...

  7. 汇编 二进制拆炸弹 r -t 3 -x 124

    文章目录 注 实验环境: 实验内容 实验步骤 调试过程及实验 总结 附录 注 QUT大二汇编最后一个作业:拆炸弹 通过两天中间隙来做这个实验,不能交个实验报告就完事了,毕竟是第一次接触逆向工程,老师为 ...

  8. MIPS反汇编拆炸弹

    计算机系统原理的实验,参考了我的前舍友和不知道哪位同学的博客,不过写得也太简略了并且还有一些错误,更像是单纯的记录,对手头没代码的人大概没啥意义吧.所以就把我自己做时的带注释的代码贴住来吧,也不过是记 ...

  9. MIPS - 反汇编 - 拆炸弹 - bomb

    MIPS - 反汇编 - 拆炸弹 - bomb 前言 整理文档发现了之前的实验报告,鉴于从17级开始才开始使用MIPS实验环境,取实验报告精华,整理主要思路如下.该博客叙述风格参考了窦优秀学长的博客. ...

最新文章

  1. 人脑计划:大脑研究如何对超级计算提出新要求
  2. 服务器内存一般多大_各类网站服务器内存多大才合适?
  3. 关于jQuery获取Action返回的JSON数据 项目真实案例 记录(Struts2)
  4. 怎么获取codeforces的数据_手把手教你学会新媒体运营——如何通过数据分析来优化新媒体运营...
  5. 关于让bootstrap3兼容ie8
  6. 计算机打印机节支措施,“节支降耗,从我做起 ”倡导篇 ——节约纸张
  7. VMware与宿主机同一网段
  8. kafaka,activityMQ,rabbitMQ消息中间件对比
  9. 推荐个工作日志的软件nyfedit
  10. rfid考勤系统c语言,基于RFID的员工考勤系统设计
  11. CAN、CAN FD
  12. matlab 求obb,obb包围盒代码
  13. Windows动态链接库DLL和静态库的原理以及创建方法
  14. APP内跳转QQ和陌生人聊天实现客服功能
  15. CSS3实现360度循环旋转
  16. 【Idea】人工智能编程他来了,Idea集成一款和ChatGPT一样智能的编码辅助神器
  17. PS3视频媒体播放基本说明
  18. axure如何页面滑动时广告位上移_Axure实现滚动广告效果
  19. 高通SM4350平台指纹移植流程
  20. Spring MVC 学习总结(五)——校验与文件上传 转自 张果 博客;已经编程校验;正确无误;...

热门文章

  1. 菜鸟零基础建站入门指引(仅供参考)
  2. python研究股价_用python处理月度股价数据
  3. 小米机型root刷magisk面具教程(非联发科处理器机型)不用刷入第三方twrp(recovery)
  4. 爱普生L355打印机进纸故障排除
  5. 程序员CMD命令大全
  6. UDP打洞(UDP Hole Punching)原理
  7. switch写打折促销活动C语言,求C语言大神编一个程序(分别用switch和if-else结构)某商店推出打折活动,要求购物达到或超过2000元的...
  8. 自带显示大屏 富士通ScanSnap iX1500扫描仪初体验
  9. 记住下次看小电影前一定要检查域名是不是 HTTPS 的!
  10. Future 用法详解