csapp lab2

PHASE_1

使用到的命令:

objdump -t bomb | less

我们得到的bomb文件是一个二进制文件,使用 objdump 可以得到反汇编的代码;

-t 表示生成符号表,不必关注 . 开头的内容,可以看到phase_1/phase_2/.../phase_6,显然应该是对应不同的关卡;

| less 表示使用一种方便浏览的分页方式,可以使用对应的快捷键辅助浏览。

objdump -d bomb > bomb.txt

-d将需要执行的内容生成反汇编代码。

chmod 777 bomb

赋予文件的读写及可执行权限,避免出现permission denied。

gdb bomb

用gab打开文件

break explode_bomb

可以在函数名、行号等地方设置断点

run

执行被gdb打开的文件

disas

显示当前函数的反汇编代码

info registers

查看当前寄存器使用情况

print $eax

输出一个寄存器的内容

x/s $eax(或是一个内存地址如0x12314)

x 命令表示访问相应地址下的内容,/s是可选项,表示这个地址的内容是string

stepi x

单步执行,可以跟一个可选参数表示单步执行x次,默认是一次。执行后再使用disas可以看到箭头指示的当前指令有所变化。

对于这关:

    0000000000400ef0 <phase_1>:  
      400ef0:   48 83 ec 08             sub    $0x8,%rsp  
      400ef4:   be 18 1b 40 00          mov    $0x401b18,%esi  
      400ef9:   e8 10 04 00 00          callq  40130e <strings_not_equal>  
      400efe:   85 c0                   test   %eax,%eax  
      400f00:   74 05                   je     400f07 <phase_1+0x17>  
      400f02:   e8 dc 07 00 00          callq  4016e3 <explode_bomb>  
      400f07:   48 83 c4 08             add    $0x8,%rsp  
      400f0b:   c3                      retq    

使用strings_not_equal这个函数接受两个参数,判断输入命令和答案是否一致,而ESI/EDI 分别叫做"源/目标索引寄存器"(source/destination index),出现在很多字符串操作指令中,所以使用到它的语句应该是有猫腻的。

对mov $0x401b18,%esi 所在的地址打断点后直接使用x/s就可得到答案,使用shift+ctrl+c/v在终端下复制黏贴答案:Science isn't about why, it's about why not?

PHASE_2

> 0x0000000000400f0c <+0>:    push   %rbp
   0x0000000000400f0d <+1>:    push   %rbx
   0x0000000000400f0e <+2>:    sub    $0x28,%rsp
   0x0000000000400f12 <+6>:    mov    %rsp,%rsi
   0x0000000000400f15 <+9>:    callq  0x401705 <read_six_numbers>
   0x0000000000400f1a <+14>:    cmpl   $0x1,(%rsp) //rsp=1,否则爆炸。这里用栈指针%rsp,说明这个函数的传参大于六个
   0x0000000000400f1e <+18>:    je     0x400f45 <phase_2+57>
   0x0000000000400f20 <+20>:    callq  0x4016e3 <explode_bomb>
   0x0000000000400f25 <+25>:    jmp    0x400f45 <phase_2+57>
   0x0000000000400f27 <+27>:    add    $0x1,%ebx //ebx=2
   0x0000000000400f2a <+30>:    mov    %ebx,%eax //eax=2
   0x0000000000400f2c <+32>:    imul   -0x4(%rbp),%eax //eax = 2*rsp = 2
   0x0000000000400f30 <+36>:    cmp    %eax,0x0(%rbp) //rbp == 2?
   0x0000000000400f33 <+39>:    je     0x400f3a <phase_2+46>
   0x0000000000400f35 <+41>:    callq  0x4016e3 <explode_bomb>
   0x0000000000400f3a <+46>:    add    $0x4,%rbp
   0x0000000000400f3e <+50>:    cmp    $0x6,%ebx
   0x0000000000400f41 <+53>:    jne    0x400f27 <phase_2+27>
   0x0000000000400f43 <+55>:    jmp    0x400f51 <phase_2+69>
   0x0000000000400f45 <+57>:    lea    0x4(%rsp),%rbp //rbp = rsp+4
   0x0000000000400f4a <+62>:    mov    $0x1,%ebx //ebx = 1
   0x0000000000400f4f <+67>:    jmp    0x400f27 <phase_2+27>
   0x0000000000400f51 <+69>:    add    $0x28,%rsp
   0x0000000000400f55 <+73>:    pop    %rbx
   0x0000000000400f56 <+74>:    pop    %rbp
   0x0000000000400f57 <+75>:    retq

重点关注

   0x0000000000400f27 <+27>:    add    $0x1,%ebx //ebx=2
   0x0000000000400f2a <+30>:    mov    %ebx,%eax //eax=2
   0x0000000000400f2c <+32>:    imul   -0x4(%rbp),%eax //eax = 2*rsp = 2
   0x0000000000400f30 <+36>:    cmp    %eax,0x0(%rbp) //rbp == 2?
   0x0000000000400f33 <+39>:    je     0x400f3a <phase_2+46>
   0x0000000000400f35 <+41>:    callq  0x4016e3 <explode_bomb>
   0x0000000000400f3a <+46>:    add    $0x4,%rbp
   0x0000000000400f3e <+50>:    cmp    $0x6,%ebx
   0x0000000000400f41 <+53>:    jne    0x400f27 <phase_2+27>

这段代码是一个循环,cmp $0x6, %ebx以及跳转后$0x1, %ebx表明这段代码对应类似for(i = 0; i < 6; i++)的循环代码。

这段代码有两个会触发explode_bomb的地方,第二处在这个循环内,第一处是在一开始检测第一个输入的数,可以看出,这个数是1。

在循环中,每次flag加一并且将flag与上一次得到的数相乘。

所以得出来的数是1 2 6 24 120 720。

PHASE_3

这一关是一个switch的应该,第一个参数决定跳转到哪一条指令,这里以输入0为例。输入[1,7]对应的答案不同

=> 0x0000000000400f58 <+0>:    sub    $0x18,%rsp //参考中文教材P196-197,x86通过固定栈指针加偏移量的方式访问变量,这里一开始移动栈指针%rsp分配了18个字节
   0x0000000000400f5c <+4>:    lea    0x8(%rsp),%r8
   0x0000000000400f61 <+9>:    lea    0x7(%rsp),%rcx
   0x0000000000400f66 <+14>:    lea    0xc(%rsp),%rdx //x86通过六个寄存器及栈来传参(64位下为rdi/rsi/rdx/rcx/r8/r9),传参顺序从右到左,所以这是第一个参数
   0x0000000000400f6b <+19>:    mov    $0x401b6e,%esi //有了PHASE_1的经验,这里esi应该保存了一个字符串,我们通过x/s 0x401b6e打印之,发现是下面sscanf的格式化输入
   0x0000000000400f70 <+24>:    mov    $0x0,%eax
   0x0000000000400f75 <+29>:    callq  0x400c00 <__isoc99_sscanf@plt>
   0x0000000000400f7a <+34>:    cmp    $0x2,%eax
   0x0000000000400f7d <+37>:    jg     0x400f84 <phase_3+44> //这里调用sscanf会返回输入参数数量,要求大于2,否则爆炸
   0x0000000000400f7f <+39>:    callq  0x4016e3 <explode_bomb>
   0x0000000000400f84 <+44>:    cmpl   $0x7,0xc(%rsp) //第一个参数和7比较,说明第一个参数应该在属于[0,7],否则爆炸
   0x0000000000400f89 <+49>:    ja     0x40108b <phase_3+307> 
   0x0000000000400f8f <+55>:    mov    0xc(%rsp),%eax //将第一个参数存入eax
   0x0000000000400f93 <+59>:    jmpq   *0x401b80(,%rax,8) //参考中文书P146,jmp的操作数前缀为*,表明这是一个间接跳转,操作数指定一个存储器位置,偏移量由eax给出
   0x0000000000400f9a <+66>:    mov    $0x6a,%eax //对之前的0x401b80间接跳转(或者说对地址0x401b80解引用)得到的就是这一条指令的地址-- 400f9a
   0x0000000000400f9f <+71>:    cmpl   $0x240,0x8(%rsp) //第三个参数和0x240(即576)比较,不相等就爆炸
   0x0000000000400fa7 <+79>:    je     0x401095 <phase_3+317>
   0x0000000000400fad <+85>:    callq  0x4016e3 <explode_bomb>
   0x0000000000400fb2 <+90>:    mov    $0x6a,%eax
   0x0000000000400fb7 <+95>:    jmpq   0x401095 <phase_3+317>
   0x0000000000400fbc <+100>:    mov    $0x66,%eax
   0x0000000000400fc1 <+105>:    cmpl   $0x3bc,0x8(%rsp)
   0x0000000000400fc9 <+113>:    je     0x401095 <phase_3+317>
   0x0000000000400fcf <+119>:    callq  0x4016e3 <explode_bomb>
   0x0000000000400fd4 <+124>:    mov    $0x66,%eax
   0x0000000000400fd9 <+129>:    jmpq   0x401095 <phase_3+317>
   0x0000000000400fde <+134>:    mov    $0x6a,%eax
   0x0000000000400fe3 <+139>:    cmpl   $0x22a,0x8(%rsp)
   0x0000000000400feb <+147>:    je     0x401095 <phase_3+317>
   0x0000000000400ff1 <+153>:    callq  0x4016e3 <explode_bomb>
   0x0000000000400ff6 <+158>:    mov    $0x6a,%eax
   0x0000000000400ffb <+163>:    jmpq   0x401095 <phase_3+317>
   0x0000000000401000 <+168>:    mov    $0x76,%eax
   0x0000000000401005 <+173>:    cmpl   $0xc9,0x8(%rsp)
   0x000000000040100d <+181>:    je     0x401095 <phase_3+317>
   0x0000000000401013 <+187>:    callq  0x4016e3 <explode_bomb>
   0x0000000000401018 <+192>:    mov    $0x76,%eax
  0x000000000040101d <+197>:    jmp    0x401095 <phase_3+317>
   0x000000000040101f <+199>:    mov    $0x62,%eax
   0x0000000000401024 <+204>:    cmpl   $0x107,0x8(%rsp)
   0x000000000040102c <+212>:    je     0x401095 <phase_3+317>
   0x000000000040102e <+214>:    callq  0x4016e3 <explode_bomb>
   0x0000000000401033 <+219>:    mov    $0x62,%eax
   0x0000000000401038 <+224>:    jmp    0x401095 <phase_3+317>
   0x000000000040103a <+226>:    mov    $0x69,%eax
   0x000000000040103f <+231>:    cmpl   $0x33b,0x8(%rsp)
   0x0000000000401047 <+239>:    je     0x401095 <phase_3+317>
   0x0000000000401049 <+241>:    callq  0x4016e3 <explode_bomb>
   0x000000000040104e <+246>:    mov    $0x69,%eax
   0x0000000000401053 <+251>:    jmp    0x401095 <phase_3+317>
   0x0000000000401055 <+253>:    mov    $0x71,%eax
   0x000000000040105a <+258>:    cmpl   $0xc6,0x8(%rsp)
   0x0000000000401062 <+266>:    je     0x401095 <phase_3+317>
   0x0000000000401064 <+268>:    callq  0x4016e3 <explode_bomb>
   0x0000000000401069 <+273>:    mov    $0x71,%eax
   0x000000000040106e <+278>:    jmp    0x401095 <phase_3+317>
   0x0000000000401070 <+280>:    mov    $0x77,%eax
   0x0000000000401075 <+285>:    cmpl   $0x174,0x8(%rsp)
   0x000000000040107d <+293>:    je     0x401095 <phase_3+317>
   0x000000000040107f <+295>:    callq  0x4016e3 <explode_bomb>
   0x0000000000401084 <+300>:    mov    $0x77,%eax
   0x0000000000401089 <+305>:    jmp    0x401095 <phase_3+317>
   0x000000000040108b <+307>:    callq  0x4016e3 <explode_bomb>
   0x0000000000401090 <+312>:    mov    $0x68,%eax
   0x0000000000401095 <+317>:    cmp    0x7(%rsp),%al //第二个参数和eax:0x0000000000400f9a <+66>: mov $0x6a,%eax,比较,这里没有使用立即数标志$,所以是ASCII字符的j
   0x0000000000401099 <+321>:    je     0x4010a0 <phase_3+328>
   0x000000000040109b <+323>:    callq  0x4016e3 <explode_bomb>
   0x00000000004010a0 <+328>:    add    $0x18,%rsp
   0x00000000004010a4 <+332>:    retq   

PHASE_4

00000000004010a5 <func4>:  
  4010a5:   53                      push   %rbx  
  4010a6:   89 fb                   mov    %edi,%ebx  
  4010a8:   b8 01 00 00 00          mov    $0x1,%eax  
  4010ad:   83 ff 01                cmp    $0x1,%edi  
  4010b0:   7e 0b                   jle    4010bd <func4+0x18>  
  4010b2:   8d 7f ff                lea    -0x1(%rdi),%edi  
  4010b5:   e8 eb ff ff ff          callq  4010a5 <func4>  
  4010ba:   0f af c3                imul   %ebx,%eax  
  4010bd:   5b                      pop    %rbx  
  4010be:   c3                      retq     
  
00000000004010bf <phase_4>:  
  4010bf:   48 83 ec 18             sub    $0x18,%rsp  
  4010c3:   48 8d 54 24 0c          lea    0xc(%rsp),%rdx  
  4010c8:   be 74 1b 40 00          mov    $0x401b74,%esi  
  4010cd:   b8 00 00 00 00          mov    $0x0,%eax  
  4010d2:   e8 29 fb ff ff          callq  400c00 <__isoc99_sscanf@plt>  
  4010d7:   83 f8 01                cmp    $0x1,%eax  
  4010da:   75 07                   jne    4010e3 <phase_4+0x24>  
  4010dc:   83 7c 24 0c 00          cmpl   $0x0,0xc(%rsp)  
  4010e1:   7f 05                   jg     4010e8 <phase_4+0x29>  
  4010e3:   e8 fb 05 00 00          callq  4016e3 <explode_bomb>  
  4010e8:   8b 7c 24 0c             mov    0xc(%rsp),%edi  
  4010ec:   e8 b4 ff ff ff          callq  4010a5 <func4>  
  4010f1:   3d 00 5f 37 00          cmp    $0x375f00,%eax  
  4010f6:   74 05                   je     4010fd <phase_4+0x3e>  
  4010f8:   e8 e6 05 00 00          callq  4016e3 <explode_bomb>  
  4010fd:   48 83 c4 18             add    $0x18,%rsp  
  401101:   c3                      retq    

在phase_4的代码中可以看到:

  4010ec:   e8 b4 ff ff ff          callq  4010a5 <func4>  
  4010f1:   3d 00 5f 37 00          cmp    $0x375f00,%eax  
  4010f6:   74 05                   je     4010fd <phase_4+0x3e>  
  4010f8:   e8 e6 05 00 00          callq  4016e3 <explode_bomb>

0x375f00等于3628800,接下来看func4的功能

参考书中p199的练习题3.52

递归阶乘函数
long int rfact(long int x)
{
    if (x <= 0)
        return 1;
    else {
        long int xm1 = x - 1;
        return x * rfact(xm1);
    }
}
生成汇编代码
rfact:
    pushq     %rbx
    movq      %rdi, %rbx
    movl      $1, %eax
    testq     %rdi, %rdi
    jle       .L11
    leaq      -1(%rdi), %rdi
    call      rfact
    imulq     %rbx, %rax
.L11:
    popq   %rbx                //restore %rbx
    ret

这段代码一开始pushq %rbx是为了保存寄存器rbx里的值,因为寄存器rbx是指定为被调用者保存的寄存器,包括%rbx,%rbp,%r12~%r15,在函数写这些寄存器时,必须先在栈上保存它们。

PHASE_6

0000000000401102 <phase_5>:  
  401102:   53                      push   %rbx  
  401103:   48 89 fb                mov    %rdi,%rbx  
  401106:   e8 e6 01 00 00          callq  4012f1 <string_length>  
  40110b:   83 f8 06                cmp    $0x6,%eax     //输入字符串的长度必须是6,否则bomb  
  40110e:   74 05                   je     401115 <phase_5+0x13>  
  401110:   e8 ce 05 00 00          callq  4016e3 <explode_bomb>  
  401115:   b8 00 00 00 00          mov    $0x0,%eax // 把%eax %edx 寄存器赋值为0 ,%eax用作循环计数器, %edx用作累加器
  40111a:   ba 00 00 00 00          mov    $0x0,%edx  
  40111f:   0f b6 0c 03             movzbl (%rbx,%rax,1),%ecx //%rbx + %rax*1 赋值给%ecx,%rbx保存的是%rdi的值,即第一个参数的值  
  401123:   83 e1 0f                and    $0xf,%ecx    //取%ecx低4位赋值给%ecx  
  401126:   03 14 8d c0 1b 40 00    add    0x401bc0(,%rcx,4),%edx  //以%rcx为index取出数组中的数并累加    
  40112d:   48 83 c0 01             add    $0x1,%rax  
  401131:   48 83 f8 06             cmp    $0x6,%rax //判断循环次数是否满6次  
  401135:   75 e8                   jne    40111f <phase_5+0x1d>  
  401137:   83 fa 3e                cmp    $0x3e,%edx  //判断累加是否已达到0x3e,即62
  40113a:   74 05                   je     401141 <phase_5+0x3f>  
  40113c:   e8 a2 05 00 00          callq  4016e3 <explode_bomb>  
  401141:   5b                      pop    %rbx  
  401142:   c3                      retq      

一开始的输入检查要求我们输入六个数字a1,a2,a3,a4,a5,a6,随后通过一个循环把每个数字对应的A[ai]相加,使得到的总和为0x3e,即62

通过 x/6d 0x401bc0 可以得知数组中的六个数字是 2 10 6 1 12 16,下标从0开始。

这里可以选取1+1+12+16+16+16=62,对应下标3 3 4 5 5 5,每个下标都来自于一个寄存器的低4位,

所以可以取c c b e e e,ACSII值的低四位就是3 3 4 5 5 5。

PHASE_6

00000000004011b2 <phase_6>:  
  4011b2:   48 83 ec 08             sub    $0x8,%rsp  
  4011b6:   ba 0a 00 00 00          mov    $0xa,%edx  
  4011bb:   be 00 00 00 00          mov    $0x0,%esi  
  4011c0:   e8 1b fa ff ff          callq  400be0 <strtol@plt>  
  4011c5:   89 05 55 21 20 00       mov    %eax,0x202155(%rip)        # 603320 <node0>  
  4011cb:   bf 20 33 60 00          mov    $0x603320,%edi  
  4011d0:   e8 6e ff ff ff          callq  401143 <fun6>  
  4011d5:   48 8b 40 08             mov    0x8(%rax),%rax  
  4011d9:   8b 0d 41 21 20 00       mov    0x202141(%rip),%ecx        # 603320 <node0>  
  4011df:   39 08                   cmp    %ecx,(%rax)  
  4011e1:   74 05                   je     4011e8 <phase_6+0x36>  
  4011e3:   e8 fb 04 00 00          callq  4016e3 <explode_bomb>  
  4011e8:   48 83 c4 08             add    $0x8,%rsp  
  4011ec:   c3                      retq   

因为func6很长,所以可以用一个tricky的方法来解决问题,就是断点到最后判断爆炸与否的地方,直接x/d或者print *(int*)得到答案。

原文地址:https://www.cnblogs.com/autoria/p/5613663.html