CS APP ex2 bomb

CS APP的实验2,拆解bomb

这里是在linux环境下通过gdb来进行的调试

进入gdb调试模式

终端中输入$ gdb bomb

CS APP ex2 bomb

查看主函数

输入(gdb) list main

发现其中初始化函数initialize_bomb()后,有两个print输出提示,然后紧接着一个input = read_line(),将我们的输入作为参数调用了phase_1函数。

CS APP ex2 bomb

所以这个phase_1应该就是对输入进行检查的函数,查看该函数的汇编。

phase_1函数

  • 输入(gdb) disassemble phase_1

输出如下

    Dump of assembler code for function phase_1:
        0x08048b80 <+0>:    push   %ebp
        0x08048b81 <+1>:    mov    %esp,%ebp
        0x08048b83 <+3>:    sub    $0x8,%esp
        0x08048b86 <+6>:    movl   $0x8049928,0x4(%esp)
        0x08048b8e <+14>:   mov    0x8(%ebp),%eax
        0x08048b91 <+17>:   mov    %eax,(%esp)
        0x08048b94 <+20>:   call   0x8049067 <strings_not_equal>
        0x08048b99 <+25>:   test   %eax,%eax
        0x08048b9b <+27>:   je     0x8048ba2 <phase_1+34>
        0x08048b9d <+29>:   call   0x804962e <explode_bomb>
        0x08048ba2 <+34>:   leave  
        0x08048ba3 <+35>:   ret    
    End of assembler dump.
  • 前两句其中对当前的ebp寄存器内容进行压栈,并将栈指针寄存器esp和基地址寄存器ebp修改为当前栈顶地址。

当前状态

    栈底方向^
    ...
    main
    ...
    参数
    旧ebp  <- ebp, esp寄存器
  • 第三行将栈顶指针移动了8个字节
    栈底方向^
    ...
    旧ebp <- ebp
    空
    空  <- esp寄存器
  • 然后两条mov指令,分别将两个值移入了空出来的栈位置,其中一个是0x08049928,另一个是基于ebp向栈底方向偏移8个字节的栈中内容,也就是我们的参数input
    栈底方向^
    ...
    旧ebp <- ebp
    0x8049928
    0x8(%ebp)  <- esp寄存器
  • 接着调用了函数<strings_not_equal>,所以我们对里的内存0x08049928内容进行查看,就是我们的拆炸弹需要的字符串。
(gdb) x/s 0x8049928

CS APP ex2 bomb

答案是:Why make trillions when we could make... billions?

phase_2函数

  • 输入(gdb) disassemble phase_1
   0x08048ba4 <+0>:     push    %ebp
   0x08048ba5 <+1>:     mov     %esp,%ebp
   0x08048ba7 <+3>:     sub     $0x28,%esp
   0x08048baa <+6>:     movl    $0x0,-0x4(%ebp)
   0x08048bb1 <+13>:    lea     -0x20(%ebp),%eax
   0x08048bb4 <+16>:    mov     %eax,0x4(%esp)
   0x08048bb8 <+20>:    mov     0x8(%ebp),%eax
   0x08048bbb <+23>:    mov     %eax,(%esp)
   0x08048bbe <+26>:    call    0x8048fd4 <read_six_numbers>
   0x08048bc3 <+31>:    movl    $0x0,-0x8(%ebp)
   0x08048bca <+38>:    jmp     0x8048bf3 <phase_2+79>
   0x08048bcc <+40>:    mov     -0x8(%ebp),%eax
   0x08048bcf <+43>:    mov     -0x20(%ebp,%eax,4),%edx
   0x08048bd3 <+47>:    mov     -0x8(%ebp),%eax
   0x08048bd6 <+50>:    add     $0x3,%eax
   0x08048bd9 <+53>:    mov     -0x20(%ebp,%eax,4),%eax
   0x08048bdd <+57>:    cmp     %eax,%edx
   0x08048bdf <+59>:    je      0x8048be6 <phase_2+66>
   0x08048be1 <+61>:    call    0x804962e <explode_bomb>
   0x08048be6 <+66>:    mov     -0x8(%ebp),%eax
   0x08048be9 <+69>:    mov     -0x20(%ebp,%eax,4),%eax
   0x08048bed <+73>:    add     %eax,-0x4(%ebp)
   0x08048bf0 <+76>:    incl    -0x8(%ebp)
   0x08048bf3 <+79>:    cmpl    $0x2,-0x8(%ebp)
   0x08048bf7 <+83>:    jle     0x8048bcc <phase_2+40>
   0x08048bf9 <+85>:    cmpl    $0x0,-0x4(%ebp)
   0x08048bfd <+89>:    jne     0x8048c04 <phase_2+96>
   0x08048bff <+91>:    call    0x804962e <explode_bomb>
   0x08048c04 <+96>:    leave  
   0x08048c05 <+97>:    ret
  • sub $0x28,%esp

    首先sp指针向下空出了0x28也就是40B的空间,32位的系统下,共10个单元,为了方便称之为M0~M9

  • movl $0x0,-0x4(%ebp)

    然后给第0h号单元置零,也就是M0=0

  • lea -0x20(%ebp),%eax; mov %eax,0x4(%esp)

    将第7号单元的地址赋值给第8号单元。也就是M8=&M7可以推断,M8可能是个数组指针,M7是数组的首地址。

  • mov 0x8(%ebp),%eax;mov %eax,(%esp);call 0x8048fd4 <read_six_numbers>

    将参数放入最后一个单元M9,然后调用函数,读取了6个数字。正好10个单元剩下了6个,组成了一个6个元素的数组,应该是放在了M6~M2中,这里我们成为A0~A5。

  • $movl 0x0,-0x8(%ebp)

    将1号单元置零,M1=0

  • $cmpl 0x2,-0x8(%ebp);jle 0x8048bcc <phase_2+40>

    将M1和2比较,循环条件是小于等于

  • mov -0x8(%ebp),%eax; mov -0x20(%ebp,%eax,4),%edx

    取值ebp-20+4*M1放入edx,其实就是edx=A[M1]

  • -0x8(%ebp),%eax; add $0x3,%eax; mov -0x20(%ebp,%eax,4),%eax

    eax=A[M1+3]

  • cmp %eax,%edx; je 0x8048be6 <phase_2+66>; call 0x804962e <explode_bomb>

    比较eax和edx,如果不相等这调用exploed_bomb

  • mov -0x8(%ebp),%eax; mov -0x20(%ebp,%eax,4),%eax; add %eax,-0x4(%ebp); incl -0x8(%ebp)

    将A[M1]写入M0,M1++

  • 经推断,原函数大致如下

    void phase_2(char* input){
        int result = 0;
        int a[6];
        input >> a;
        for(int i=0;i<=2;i++){
            if (a[i]!=a[i+3])
                bomb();
            result += a[i];
        }
        if(reuslt == 0)
            bomb()
    }

所以答案是一个6个元素的数组,a[i]要和a[i+3]相同,且sum(a[0],a[1],a[2])!=0即可。如1 2 3 1 2 31 1 1 1 1 1

phase_3函数

    Dump of assembler code for function phase_3:
    0x08048c06 <+0>:    push   %ebp
    0x08048c07 <+1>:    mov    %esp,%ebp
    0x08048c09 <+3>:    sub    $0x28,%esp
    0x08048c0c <+6>:    movl   $0x0,-0x8(%ebp)
    0x08048c13 <+13>:   movl   $0x0,-0x4(%ebp)
    0x08048c1a <+20>:   lea    -0x10(%ebp),%eax
    0x08048c1d <+23>:   mov    %eax,0xc(%esp)
    0x08048c21 <+27>:   lea    -0xc(%ebp),%eax
    0x08048c24 <+30>:   mov    %eax,0x8(%esp)
    0x08048c28 <+34>:   movl   $0x804995b,0x4(%esp)
    0x08048c30 <+42>:   mov    0x8(%ebp),%eax
    0x08048c33 <+45>:   mov    %eax,(%esp)
    0x08048c36 <+48>:   call   0x8048868 <sscanf@plt>
    0x08048c3b <+53>:   mov    %eax,-0x4(%ebp)
    0x08048c3e <+56>:   cmpl   $0x1,-0x4(%ebp)
    0x08048c42 <+60>:   jg     0x8048c49 <phase_3+67>
    0x08048c44 <+62>:   call   0x804962e <explode_bomb>
    0x08048c49 <+67>:   mov    -0xc(%ebp),%eax
    0x08048c4c <+70>:   mov    %eax,-0x14(%ebp)
    0x08048c4f <+73>:   cmpl   $0x7,-0x14(%ebp)
    0x08048c53 <+77>:   ja     0x8048c98 <phase_3+146>
    0x08048c55 <+79>:   mov    -0x14(%ebp),%edx
    0x08048c58 <+82>:   mov    0x8049964(,%edx,4),%eax
    0x08048c5f <+89>:   jmp    *%eax
    0x08048c61 <+91>:   addl   $0x2cc,-0x8(%ebp)
    0x08048c68 <+98>:   subl   $0x3a9,-0x8(%ebp)
    0x08048c6f <+105>:  addl   $0x12f,-0x8(%ebp)
    0x08048c76 <+112>:  subl   $0xcb,-0x8(%ebp)
    0x08048c7d <+119>:  addl   $0x5d,-0x8(%ebp)
    0x08048c81 <+123>:  subl   $0x2f4,-0x8(%ebp)
    0x08048c88 <+130>:  addl   $0x2f4,-0x8(%ebp)
    0x08048c8f <+137>:  subl   $0x207,-0x8(%ebp)
    0x08048c96 <+144>:  jmp    0x8048c9d <phase_3+151>
    0x08048c98 <+146>:  call   0x804962e <explode_bomb>
    0x08048c9d <+151>:  mov    -0xc(%ebp),%eax
    0x08048ca0 <+154>:  cmp    $0x5,%eax
    0x08048ca3 <+157>:  jg     0x8048cad <phase_3+167>
    0x08048ca5 <+159>:  mov    -0x10(%ebp),%eax
    0x08048ca8 <+162>:  cmp    %eax,-0x8(%ebp)
    0x08048cab <+165>:  je     0x8048cb2 <phase_3+172>
    0x08048cad <+167>:  call   0x804962e <explode_bomb>
    0x08048cb2 <+172>:  leave  
    0x08048cb3 <+173>:  ret    
    End of assembler dump.
  • 0x08048c06 ~ 0x08048c36

    进行了如下操作,申请10个字(4B/字)的空间,M0=0, M1=0, M6=&M3, M7=&M2,M8=0x804995b, M9=%ebp+8(依然是从ebp-0x4到ebp-0x28,每4B作为一个单位),然后调用函数sscanf

    惯例其中M9是我们的input,然后M8未知,M7,M2作为指针参数来传递值,放入M2,M3中,因为参数的逆序入栈的。

  • 我们检查M8(0x804995b)的内容

    CS APP ex2 bomb

    可以发现,里面存放的是字符串"%d %d"

    也就是告诉我们输入两个数字,两个数字正是通过两个指针来实现传递的。

  • 0x08048c3b~0x08048c44

    sscanf的结果进行检查,查看是否大于1,否则调用bomb()

  • 0x08048c49~0x08048c53

    对num1进行检查,要求不大于7。

  • 0x08048c55~0x08048c5f

    一条无条件跳转语句,跳转到(num1*4+0x8049964),注意外面的*,跳转的目标不是num1*4+0x8049964,而是该内存中该地址里的值,因为目前num1的范围是不大于7,还有点大

  • 0x08048c61~0x08048c96

    这部分应该是上一条跳转语句将要跳转的位置,分别是M1加上或减去某个数。

  • 0x08048c9d~0x08048ca3

    对num1再进行检查,要求不大于5,这里再次缩小了num1的范围

  • 0x08048ca5~0x08048cab

    对M1和num2进行比较,要求相等

所以num2就应该是M1计算的结果,而num1则是小于等于5的值,我们可以取5,计算得num2位-519

phase_4函数

    Dump of assembler code for function phase_4:
        0x08048ce3 <+0>:    push   %ebp
        0x08048ce4 <+1>:    mov    %esp,%ebp
        0x08048ce6 <+3>:    sub    $0x28,%esp
        0x08048ce9 <+6>:    lea    -0xc(%ebp),%eax
        0x08048cec <+9>:    mov    %eax,0x8(%esp)
        0x08048cf0 <+13>:   movl   $0x8049984,0x4(%esp)
        0x08048cf8 <+21>:   mov    0x8(%ebp),%eax
        0x08048cfb <+24>:   mov    %eax,(%esp)
        0x08048cfe <+27>:   call   0x8048868 <sscanf@plt>
        0x08048d03 <+32>:   mov    %eax,-0x4(%ebp)
        0x08048d06 <+35>:   cmpl   $0x1,-0x4(%ebp)
        0x08048d0a <+39>:   jne    0x8048d13 <phase_4+48>
        0x08048d0c <+41>:   mov    -0xc(%ebp),%eax
        0x08048d0f <+44>:   test   %eax,%eax
        0x08048d11 <+46>:   jg     0x8048d18 <phase_4+53>
        0x08048d13 <+48>:   call   0x804962e <explode_bomb>
        0x08048d18 <+53>:   mov    -0xc(%ebp),%eax
        0x08048d1b <+56>:   mov    %eax,(%esp)
        0x08048d1e <+59>:   call   0x8048cb4 <func4>
        0x08048d23 <+64>:   mov    %eax,-0x8(%ebp)
        0x08048d26 <+67>:   cmpl   $0x78,-0x8(%ebp)
        0x08048d2a <+71>:   je     0x8048d31 <phase_4+78>
        0x08048d2c <+73>:   call   0x804962e <explode_bomb>
        0x08048d31 <+78>:   leave  
        0x08048d32 <+79>:   ret    
    End of assembler dump.
  • 0x08048ce3~0x08048cfe

    进行了如下操作:申请了4B*10的栈空间,将M2(ebp-0xc)的地址写入M7(esp+0x8),将0x8049984写入M8,将上层传递进来的参数写入M9,然后调用<sscanf@plt>

    我们对0x8049984进行检查,里面是字符串%d,可见是读取一个整型同指针M7写入M2

    CS APP ex2 bomb

  • 0x08048d03~0x08048d06

    sscanf的返回值进行检查,要求只输入了一个数值

  • 0x08048d0c~0x08048d11

    对输入的数进行检查,要求大于0

  • 0x08048d18~0x08048d1e

    将输入作为参数,调用func4

  • 检查func4

    CS APP ex2 bomb

    可以发现,func4是一个求阶乘的递归函数。

  • 0x08048d23~0x08048d2a

    将func4的返回值放入M1,对M1和0x78进行比较,要求相等,也就是要求M1==120

我们只需找出阶乘结果位120的数,即答案:5

phase_5

    (gdb) disassemble phase_5
    Dump of assembler code for function phase_5:
        0x08048d33 <+0>:    push   %ebp
        0x08048d34 <+1>:    mov    %esp,%ebp
        0x08048d36 <+3>:    sub    $0x18,%esp
        0x08048d39 <+6>:    mov    0x8(%ebp),%eax
        0x08048d3c <+9>:    mov    %eax,(%esp)
        0x08048d3f <+12>:   call   0x804903d <string_length>
        0x08048d44 <+17>:   mov    %eax,-0x4(%ebp)
        0x08048d47 <+20>:   cmpl   $0x6,-0x4(%ebp)
        0x08048d4b <+24>:   je     0x8048d52 <phase_5+31>
        0x08048d4d <+26>:   call   0x804962e <explode_bomb>
        0x08048d52 <+31>:   movl   $0x0,-0x8(%ebp)
        0x08048d59 <+38>:   movl   $0x0,-0xc(%ebp)
        0x08048d60 <+45>:   jmp    0x8048d7e <phase_5+75>
        0x08048d62 <+47>:   mov    -0xc(%ebp),%eax
        0x08048d65 <+50>:   add    0x8(%ebp),%eax
        0x08048d68 <+53>:   movzbl (%eax),%eax
        0x08048d6b <+56>:   movsbl %al,%eax
        0x08048d6e <+59>:   and    $0xf,%eax
        0x08048d71 <+62>:   mov    0x804a5c0(,%eax,4),%eax
        0x08048d78 <+69>:   add    %eax,-0x8(%ebp)
        0x08048d7b <+72>:   incl   -0xc(%ebp)
        0x08048d7e <+75>:   cmpl   $0x5,-0xc(%ebp)
        0x08048d82 <+79>:   jle    0x8048d62 <phase_5+47>
        0x08048d84 <+81>:   cmpl   $0x49,-0x8(%ebp)
        0x08048d88 <+85>:   je     0x8048d8f <phase_5+92>
        0x08048d8a <+87>:   call   0x804962e <explode_bomb>
        0x08048d8f <+92>:   leave  
        0x08048d90 <+93>:   ret    
    End of assembler dump.
  • 0x08048d33~0x08048d44

    申请4B*6的栈空间,将输入作为参数调用函数<string_length>,并将结果写入M0,即M0=length(input)

  • 0x08048d47~0x08048d4d

    对输入长度进行检查,要求长度为6。

  • 0x08048d52~0x08048d60

    将M1,M2赋值为0,跳转

  • 0x08048d62~0x08048d6e

    将(input+M2)0扩展后放入eax,再取出eax对应地址里的值,因为input其实是个字符串,也就是相当于eax=input[M2],然后截取eax的后8位,符号位扩展写回,最后取后四位。这里因为输入是个ascii码,范围是0~127所以符号位扩展肯定是0扩展。最终这系列操作就相当于取输入每个字符的ascii码值的后四位。

  • 0x08048d71~0x08048d78

    取基于0x804a5c0偏移的内存中的内容,累加到M1中

  • 0x08048d7b~0x08048d82

    M2自增,判断是否小于等于5,继续循环

  • 0x08048d84

    要求累加结果位0x49

  • 检查0x804a5c0内容

    CS APP ex2 bomb

    对应的数值:2, 10, 6, 1, 12, 16, 9, 3, 4, 7, 14, 5, 11, 8, 15,

    我们取其中6个,让和等于0x49

这里我取下标2,5,5,5,5,7的6个数,只要后四位满足条件即可,所以字符串可以取255557

phase_6

    (gdb) disassemble phase_6
    Dump of assembler code for function phase_6:
        0x08048e25 <+0>:    push   %ebp
        0x08048e26 <+1>:    mov    %esp,%ebp
        0x08048e28 <+3>:    sub    $0x18,%esp
        0x08048e2b <+6>:    movl   $0x804a660,-0x8(%ebp)
        0x08048e32 <+13>:   mov    0x8(%ebp),%eax
        0x08048e35 <+16>:   mov    %eax,(%esp)
        0x08048e38 <+19>:   call   0x8048858 <atoi@plt>
        0x08048e3d <+24>:   mov    %eax,-0xc(%ebp)
        0x08048e40 <+27>:   mov    -0x8(%ebp),%eax
        0x08048e43 <+30>:   mov    %eax,(%esp)
        0x08048e46 <+33>:   call   0x8048d91 <fun6>
        0x08048e4b <+38>:   mov    %eax,-0x8(%ebp)
        0x08048e4e <+41>:   mov    -0x8(%ebp),%eax
        0x08048e51 <+44>:   mov    %eax,-0x4(%ebp)
        0x08048e54 <+47>:   movl   $0x1,-0x10(%ebp)
        0x08048e5b <+54>:   jmp    0x8048e69 <phase_6+68>
        0x08048e5d <+56>:   mov    -0x4(%ebp),%eax
        0x08048e60 <+59>:   mov    0x8(%eax),%eax
        0x08048e63 <+62>:   mov    %eax,-0x4(%ebp)
        0x08048e66 <+65>:   incl   -0x10(%ebp)
        0x08048e69 <+68>:   cmpl   $0x4,-0x10(%ebp)
        0x08048e6d <+72>:   jne    0x8048e5d <phase_6+56>
        0x08048e6f <+74>:   mov    -0x4(%ebp),%eax
        0x08048e72 <+77>:   mov    (%eax),%eax
        0x08048e74 <+79>:   cmp    -0xc(%ebp),%eax
        0x08048e77 <+82>:   je     0x8048e7e <phase_6+89>
        0x08048e79 <+84>:   call   0x804962e <explode_bomb>
        0x08048e7e <+89>:   leave  
        0x08048e7f <+90>:   ret    
    End of assembler dump.
  • 0x08048e25~0x08048e3d

    申请4B*6的栈空间,将0x804a660放入M1中,将input作为参数调用<atoi@plt>,返回结果写入M2

  • 0x08048e40~0x08048e4b

    将M1作为参数,调用fun6,将返回结果写回M1

  • emmm,fun6还没细看

未完待续,哈哈哈

CS APP ex2 bomb

上一篇:如何使用adb工具在电脑上使用程序的方式操控自己的android手机


下一篇:Android实现apk插件方式换肤