CSAPP Bomb Lab V.2016.01. 要求如下

/**
A "binary bomb" is a Linux executable C program that consists of six
"phases." Each phase expects the student to enter a particular string
on stdin.  If the student enters the expected string, then that phase
is "defused."  Otherwise the bomb "explodes" by printing "BOOM!!!".
The goal for the students is to defuse as many phases as possible.
*/

phase_1

0000000000400ee0 <phase_1>:
  400ee0:   48 83 ec 08             sub    $0x8,%rsp
  400ee4:   be 00 24 40 00          mov    $0x402400,%esi
  400ee9:   e8 4a 04 00 00          callq  401338 <strings_not_equal>
  400eee:   85 c0                   test   %eax,%eax
  400ef0:   74 05                   je     400ef7 <phase_1+0x17>
  400ef2:   e8 43 05 00 00          callq  40143a <explode_bomb>
  400ef7:   48 83 c4 08             add    $0x8,%rsp
  400efb:   c3                      retq   

本题很简单,输入一个字符串,并将其与地址为0x402400的字符串进行比较,若两字符串不相同,则引爆炸弹。使用GDB查看地址为0x402400的字符串为Border relations with Canada have never been better.,即为要输入的字符串。

(gdb) x /s 0x402400
0x402400:   "Border relations with Canada have never been better."

phase_2

0000000000400efc <phase_2>:
  400efc:   55                      push   %rbp
  400efd:   53                      push   %rbx
  400efe:   48 83 ec 28             sub    $0x28,%rsp
  400f02:   48 89 e6                mov    %rsp,%rsi
  400f05:   e8 52 05 00 00          callq  40145c <read_six_numbers>
  400f0a:   83 3c 24 01             cmpl   $0x1,(%rsp)
  400f0e:   74 20                   je     400f30 <phase_2+0x34>
  400f10:   e8 25 05 00 00          callq  40143a <explode_bomb>
  400f15:   eb 19                   jmp    400f30 <phase_2+0x34>
  400f17:   8b 43 fc                mov    -0x4(%rbx),%eax
  400f1a:   01 c0                   add    %eax,%eax
  400f1c:   39 03                   cmp    %eax,(%rbx)
  400f1e:   74 05                   je     400f25 <phase_2+0x29>
  400f20:   e8 15 05 00 00          callq  40143a <explode_bomb>
  400f25:   48 83 c3 04             add    $0x4,%rbx
  400f29:   48 39 eb                cmp    %rbp,%rbx
  400f2c:   75 e9                   jne    400f17 <phase_2+0x1b>
  400f2e:   eb 0c                   jmp    400f3c <phase_2+0x40>
  400f30:   48 8d 5c 24 04          lea    0x4(%rsp),%rbx
  400f35:   48 8d 6c 24 18          lea    0x18(%rsp),%rbp
  400f3a:   eb db                   jmp    400f17 <phase_2+0x1b>
  400f3c:   48 83 c4 28             add    $0x28,%rsp
  400f40:   5b                      pop    %rbx
  400f41:   5d                      pop    %rbp
  400f42:   c3                      retq   

本题需要先理解read_six_numbers的含义,翻译如下

000000000040145c <read_six_numbers>:
  40145c:   48 83 ec 18             sub    $0x18,%rsp
  401460:   48 89 f2                mov    %rsi,%rdx
  401463:   48 8d 4e 04             lea    0x4(%rsi),%rcx
  401467:   48 8d 46 14             lea    0x14(%rsi),%rax
  40146b:   48 89 44 24 08          mov    %rax,0x8(%rsp)
  401470:   48 8d 46 10             lea    0x10(%rsi),%rax
  401474:   48 89 04 24             mov    %rax,(%rsp)
  401478:   4c 8d 4e 0c             lea    0xc(%rsi),%r9
  40147c:   4c 8d 46 08             lea    0x8(%rsi),%r8
  401480:   be c3 25 40 00          mov    $0x4025c3,%esi
  401485:   b8 00 00 00 00          mov    $0x0,%eax
  40148a:   e8 61 f7 ff ff          callq  400bf0 <__isoc99_sscanf@plt>
  40148f:   83 f8 05                cmp    $0x5,%eax
  401492:   7f 05                   jg     401499 <read_six_numbers+0x3d>
  401494:   e8 a1 ff ff ff          callq  40143a <explode_bomb>
  401499:   48 83 c4 18             add    $0x18,%rsp
  40149d:   c3                      retq   

/*
 * read six numbers from input,
 * and store them in an arr
 * */
void read_six_numbers(input, arr) {
    int result =
        sscanf(input,         // rdi 
           0x4025c3,      // rsi // "%d %d %d %d %d %d"
           arr,           // rdx
           arr + 0x4,     // rcx
           arr + 0x8,     // r8
           arr + 0xc,     // r9
           arr + 0x10,    // rsp
           arr + 0x14);   // rsp + 8  
}

也就是接受一个字符串输入,使用sscanf函数从该字符串中读入6个数字,把结果存放在地址为arr的数组中。

phase_2的翻译如下

void phase_2() {
    read_six_numbers(input, rsp); // a,b,c,d,e,f

    /**
     * a = &(rsp)
     * b = &(rsp + 0x4)
     * c = &(rsp + 0x8)
     * d = &(rsp + 0xc)
     * e = &(rsp + 0x10)
     * f = &(rsp + 0x14)
     * */

    if(a != 1) bomb(); // a == 1
    else {
        rbx = rsp + 4;
        rbp = rsp + 18;

        while(true) {
            // b = 2, c = 4, d = 8, e = 16, f = 32
            if (&rbx != (&(rbx - 4) * 2)) bomb();
            else {
                rbx += 4;
                if(rbp == rbx) break;
            }
        }
    }
}

很容易发现,输入的这6个数第一个是1,其后的数依次为前者的两倍,故而输入的字符串为1 2 4 8 16 32

phase_3

0000000000400f43 <phase_3>:
  400f43:   48 83 ec 18             sub    $0x18,%rsp
  400f47:   48 8d 4c 24 0c          lea    0xc(%rsp),%rcx
  400f4c:   48 8d 54 24 08          lea    0x8(%rsp),%rdx
  400f51:   be cf 25 40 00          mov    $0x4025cf,%esi
  400f56:   b8 00 00 00 00          mov    $0x0,%eax
  400f5b:   e8 90 fc ff ff          callq  400bf0 <__isoc99_sscanf@plt>
  400f60:   83 f8 01                cmp    $0x1,%eax
  400f63:   7f 05                   jg     400f6a <phase_3+0x27>
  400f65:   e8 d0 04 00 00          callq  40143a <explode_bomb>
  400f6a:   83 7c 24 08 07          cmpl   $0x7,0x8(%rsp)
  400f6f:   77 3c                   ja     400fad <phase_3+0x6a>
  400f71:   8b 44 24 08             mov    0x8(%rsp),%eax
  400f75:   ff 24 c5 70 24 40 00    jmpq   *0x402470(,%rax,8)
  400f7c:   b8 cf 00 00 00          mov    $0xcf,%eax
  400f81:   eb 3b                   jmp    400fbe <phase_3+0x7b>
  400f83:   b8 c3 02 00 00          mov    $0x2c3,%eax
  400f88:   eb 34                   jmp    400fbe <phase_3+0x7b>
  400f8a:   b8 00 01 00 00          mov    $0x100,%eax
  400f8f:   eb 2d                   jmp    400fbe <phase_3+0x7b>
  400f91:   b8 85 01 00 00          mov    $0x185,%eax
  400f96:   eb 26                   jmp    400fbe <phase_3+0x7b>
  400f98:   b8 ce 00 00 00          mov    $0xce,%eax
  400f9d:   eb 1f                   jmp    400fbe <phase_3+0x7b>
  400f9f:   b8 aa 02 00 00          mov    $0x2aa,%eax
  400fa4:   eb 18                   jmp    400fbe <phase_3+0x7b>
  400fa6:   b8 47 01 00 00          mov    $0x147,%eax
  400fab:   eb 11                   jmp    400fbe <phase_3+0x7b>
  400fad:   e8 88 04 00 00          callq  40143a <explode_bomb>
  400fb2:   b8 00 00 00 00          mov    $0x0,%eax
  400fb7:   eb 05                   jmp    400fbe <phase_3+0x7b>
  400fb9:   b8 37 01 00 00          mov    $0x137,%eax
  400fbe:   3b 44 24 0c             cmp    0xc(%rsp),%eax
  400fc2:   74 05                   je     400fc9 <phase_3+0x86>
  400fc4:   e8 71 04 00 00          callq  40143a <explode_bomb>
  400fc9:   48 83 c4 18             add    $0x18,%rsp
  400fcd:   c3                      retq  

这是一个关于switch跳转表的题,翻译如下

eax = sscanf(input, 0x4025cf, rsp + 0x8, rsp + 0xc); // 0x4025cf : "%d %d"
if (eax <= 1) bomb();
else {
    if( &(rsp + 0x8) > 0x7) {
        bomb();
    } else {
        eax = &(rsp + 0x8);

        /* jmp to &(0x402470 + 8 * %rax)
         * 
         * Jump table:
         *
         * Address       Value        &(rsp + 0x8)    => eax == &(rsp + 0xc)
         * 0x402470      0x400f7c          0           0xcf
         * 0x402478      0x400fb9          1           0x137
         * 0x402480      0x400f83          2           0x2c3
         * 0x402488      0x400f8a          3           0x100
         * 0x402490      0x400f91          4           0x185
         * 0x402498      0x400f98          5           0xce
         * 0x4024a0      0x400f9f          6           0x2aa
         * 0x4024a8      0x400fa6          7           0x147
         */

        // then jump to 0x400fbe

        if (eax != &(rsp + 0xc)) bomb()
    }
}

故共有8种输入。

[(0, 207), (1, 311), (2, 707), (3, 256), (4, 389), (5, 206), (6, 682), (7, 327)]

phase_4

0000000000400fce <func4>:
  400fce:   48 83 ec 08             sub    $0x8,%rsp
  400fd2:   89 d0                   mov    %edx,%eax
  400fd4:   29 f0                   sub    %esi,%eax
  400fd6:   89 c1                   mov    %eax,%ecx
  400fd8:   c1 e9 1f                shr    $0x1f,%ecx
  400fdb:   01 c8                   add    %ecx,%eax
  400fdd:   d1 f8                   sar    %eax
  400fdf:   8d 0c 30                lea    (%rax,%rsi,1),%ecx
  400fe2:   39 f9                   cmp    %edi,%ecx
  400fe4:   7e 0c                   jle    400ff2 <func4+0x24>
  400fe6:   8d 51 ff                lea    -0x1(%rcx),%edx
  400fe9:   e8 e0 ff ff ff          callq  400fce <func4>
  400fee:   01 c0                   add    %eax,%eax
  400ff0:   eb 15                   jmp    401007 <func4+0x39>
  400ff2:   b8 00 00 00 00          mov    $0x0,%eax
  400ff7:   39 f9                   cmp    %edi,%ecx
  400ff9:   7d 0c                   jge    401007 <func4+0x39>
  400ffb:   8d 71 01                lea    0x1(%rcx),%esi
  400ffe:   e8 cb ff ff ff          callq  400fce <func4>
  401003:   8d 44 00 01             lea    0x1(%rax,%rax,1),%eax
  401007:   48 83 c4 08             add    $0x8,%rsp
  40100b:   c3                      retq   

000000000040100c <phase_4>:
  40100c:   48 83 ec 18             sub    $0x18,%rsp
  401010:   48 8d 4c 24 0c          lea    0xc(%rsp),%rcx
  401015:   48 8d 54 24 08          lea    0x8(%rsp),%rdx
  40101a:   be cf 25 40 00          mov    $0x4025cf,%esi
  40101f:   b8 00 00 00 00          mov    $0x0,%eax
  401024:   e8 c7 fb ff ff          callq  400bf0 <__isoc99_sscanf@plt>
  401029:   83 f8 02                cmp    $0x2,%eax
  40102c:   75 07                   jne    401035 <phase_4+0x29>
  40102e:   83 7c 24 08 0e          cmpl   $0xe,0x8(%rsp)
  401033:   76 05                   jbe    40103a <phase_4+0x2e>
  401035:   e8 00 04 00 00          callq  40143a <explode_bomb>
  40103a:   ba 0e 00 00 00          mov    $0xe,%edx
  40103f:   be 00 00 00 00          mov    $0x0,%esi
  401044:   8b 7c 24 08             mov    0x8(%rsp),%edi
  401048:   e8 81 ff ff ff          callq  400fce <func4>
  40104d:   85 c0                   test   %eax,%eax
  40104f:   75 07                   jne    401058 <phase_4+0x4c>
  401051:   83 7c 24 0c 00          cmpl   $0x0,0xc(%rsp)
  401056:   74 05                   je     40105d <phase_4+0x51>
  401058:   e8 dd 03 00 00          callq  40143a <explode_bomb>
  40105d:   48 83 c4 18             add    $0x18,%rsp
  401061:   c3                      retq   

翻译如下。

void func4(edi, esi, edx) {
    eax = edx - esi;
    ecx = eax;

    ecx >>>= 0x1f; // sign bit

    eax += ecx;
    eax /= 2;
    ecx = rax + rsi;

    /*
     * ecx = ((edx - esi) / 2) + rsi = (edx + esi) / 2 
     *  
     * edx    esi     ecx   edi (to stop)
     * 14      0       7    7
     * 6       0       3    3
     * 2       0       1    1
     * 1       0       0    0
     */

    if (ecx > edi) {
        edx = rcx - 1;
        return 2 * func4(edi, esi, edx);
    } else {
        if (ecx < edi) { // unreachable
            esi = rcx + 1;
            return 1 + 2 * func4(edi, esi, edx);
        } else {
            return 0; // final 
        }
    }
}

void phase_4(input) { // input two number a,b
    n = sscanf(input, 0x4025cf, rsp + 0x8, rsp + 0xc);
    if (n != 2) bomb();
    else {
        if(&(rsp + 0x8) > 0xe) bomb();  // a < 14
        else {
            eax = func4(&(rsp + 0x8), 0x0, 0xe); // must return 0

            if(eax != 0) bomb();
            else if (&(rsp + 0xc) != 0x0) bomb(); // b must be 0
        }
    }
}

输入两个数,第二个必须为0,第一个数作为递归函数func4的终止条件,有四种取值。故本题有四种输入:

[(0, 0), (1, 0), (3, 0), (7, 0)]

phase_5

0000000000401062 <phase_5>:
  401062:   53                      push   %rbx
  401063:   48 83 ec 20             sub    $0x20,%rsp
  401067:   48 89 fb                mov    %rdi,%rbx
  40106a:   64 48 8b 04 25 28 00    mov    %fs:0x28,%rax
  401071:   00 00 
  401073:   48 89 44 24 18          mov    %rax,0x18(%rsp)
  401078:   31 c0                   xor    %eax,%eax
  40107a:   e8 9c 02 00 00          callq  40131b <string_length>
  40107f:   83 f8 06                cmp    $0x6,%eax
  401082:   74 4e                   je     4010d2 <phase_5+0x70>
  401084:   e8 b1 03 00 00          callq  40143a <explode_bomb>
  401089:   eb 47                   jmp    4010d2 <phase_5+0x70>
  40108b:   0f b6 0c 03             movzbl (%rbx,%rax,1),%ecx
  40108f:   88 0c 24                mov    %cl,(%rsp)
  401092:   48 8b 14 24             mov    (%rsp),%rdx
  401096:   83 e2 0f                and    $0xf,%edx
  401099:   0f b6 92 b0 24 40 00    movzbl 0x4024b0(%rdx),%edx
  4010a0:   88 54 04 10             mov    %dl,0x10(%rsp,%rax,1)
  4010a4:   48 83 c0 01             add    $0x1,%rax
  4010a8:   48 83 f8 06             cmp    $0x6,%rax
  4010ac:   75 dd                   jne    40108b <phase_5+0x29>
  4010ae:   c6 44 24 16 00          movb   $0x0,0x16(%rsp)
  4010b3:   be 5e 24 40 00          mov    $0x40245e,%esi
  4010b8:   48 8d 7c 24 10          lea    0x10(%rsp),%rdi
  4010bd:   e8 76 02 00 00          callq  401338 <strings_not_equal>
  4010c2:   85 c0                   test   %eax,%eax
  4010c4:   74 13                   je     4010d9 <phase_5+0x77>
  4010c6:   e8 6f 03 00 00          callq  40143a <explode_bomb>
  4010cb:   0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)
  4010d0:   eb 07                   jmp    4010d9 <phase_5+0x77>
  4010d2:   b8 00 00 00 00          mov    $0x0,%eax
  4010d7:   eb b2                   jmp    40108b <phase_5+0x29>
  4010d9:   48 8b 44 24 18          mov    0x18(%rsp),%rax
  4010de:   64 48 33 04 25 28 00    xor    %fs:0x28,%rax
  4010e5:   00 00 
  4010e7:   74 05                   je     4010ee <phase_5+0x8c>
  4010e9:   e8 42 fa ff ff          callq  400b30 <__stack_chk_fail@plt>
  4010ee:   48 83 c4 20             add    $0x20,%rsp
  4010f2:   5b                      pop    %rbx
  4010f3:   c3                      retq   

本题是一个码表转换问题,翻译如下

rbx = rdi;

/*
 * input is a string containing 6 characters [a, b, c, d, e, f]. 
 * */
if(string_length(input) != 6 ) bomb();
else {
    eax = 0;
    while(rax != 6) { // 40108b
        ecx = &(rbx + rax);
        rdx = %cl; // last byte of each char

        edx &= 0xf;
        edx = &(rdx + 0x4024b0);

        /* offset = last four bits of each char 
         * 
         * base = 0x4024b0 =>
         * 
         * use these offsets to choose (0x40245e:  "flyers") from
         * "maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?" 
         *
         * So:
         * 
         * offset       char   
         *   9           f        a & 0xf = 9
         *   F           l        b & 0xf = F
         *   E           y        c & 0xf = E
         *   5           e        d & 0xf = 5
         *   6           r        e & 0xf = 6
         *   7           s        f & 0xf = 7
         * 
         * */



        &(rsp + 10 + rax) = dl;
        rax += 1;
    }



    &(rsp + 16) = 0; // '\0'
    if(strings_not_equal(rsp + 10, 0x40245e)) bomb(); // 0x40245e:  "flyers"
}

输入的六个字符对应的ASCII值取后四位,将其作为在字符串0x4024b0中的位移,取出对应的6个字符必须为flyers。因此,输入的六个字符对应的ASCII值的后四位依次为9 F E 5 6 7。下面的python代码求出所有可以打印出来的情况(剔除空白字符)

import itertools, string

map(lambda p: ''.join(p),
  itertools.product(
    *map(lambda i:
      filter(lambda c: c in string.printable and c not in string.whitespace,
        map(lambda x: chr(((x << 4) + i)), range(0, 0xf + 1))
      ),
      [0x9, 0xf, 0xe, 0x5, 0x6, 0x7]
    )
  )
)

phase_6

00000000004010f4 <phase_6>:
  4010f4:   41 56                   push   %r14
  4010f6:   41 55                   push   %r13
  4010f8:   41 54                   push   %r12
  4010fa:   55                      push   %rbp
  4010fb:   53                      push   %rbx
  4010fc:   48 83 ec 50             sub    $0x50,%rsp
  401100:   49 89 e5                mov    %rsp,%r13
  401103:   48 89 e6                mov    %rsp,%rsi
  401106:   e8 51 03 00 00          callq  40145c <read_six_numbers>
  40110b:   49 89 e6                mov    %rsp,%r14
  40110e:   41 bc 00 00 00 00       mov    $0x0,%r12d
  401114:   4c 89 ed                mov    %r13,%rbp
  401117:   41 8b 45 00             mov    0x0(%r13),%eax
  40111b:   83 e8 01                sub    $0x1,%eax
  40111e:   83 f8 05                cmp    $0x5,%eax
  401121:   76 05                   jbe    401128 <phase_6+0x34>
  401123:   e8 12 03 00 00          callq  40143a <explode_bomb>
  401128:   41 83 c4 01             add    $0x1,%r12d
  40112c:   41 83 fc 06             cmp    $0x6,%r12d
  401130:   74 21                   je     401153 <phase_6+0x5f>
  401132:   44 89 e3                mov    %r12d,%ebx
  401135:   48 63 c3                movslq %ebx,%rax
  401138:   8b 04 84                mov    (%rsp,%rax,4),%eax
  40113b:   39 45 00                cmp    %eax,0x0(%rbp)
  40113e:   75 05                   jne    401145 <phase_6+0x51>
  401140:   e8 f5 02 00 00          callq  40143a <explode_bomb>
  401145:   83 c3 01                add    $0x1,%ebx
  401148:   83 fb 05                cmp    $0x5,%ebx
  40114b:   7e e8                   jle    401135 <phase_6+0x41>
  40114d:   49 83 c5 04             add    $0x4,%r13
  401151:   eb c1                   jmp    401114 <phase_6+0x20>
  401153:   48 8d 74 24 18          lea    0x18(%rsp),%rsi
  401158:   4c 89 f0                mov    %r14,%rax
  40115b:   b9 07 00 00 00          mov    $0x7,%ecx
  401160:   89 ca                   mov    %ecx,%edx
  401162:   2b 10                   sub    (%rax),%edx
  401164:   89 10                   mov    %edx,(%rax)
  401166:   48 83 c0 04             add    $0x4,%rax
  40116a:   48 39 f0                cmp    %rsi,%rax
  40116d:   75 f1                   jne    401160 <phase_6+0x6c>
  40116f:   be 00 00 00 00          mov    $0x0,%esi
  401174:   eb 21                   jmp    401197 <phase_6+0xa3>
  401176:   48 8b 52 08             mov    0x8(%rdx),%rdx
  40117a:   83 c0 01                add    $0x1,%eax
  40117d:   39 c8                   cmp    %ecx,%eax
  40117f:   75 f5                   jne    401176 <phase_6+0x82>
  401181:   eb 05                   jmp    401188 <phase_6+0x94>
  401183:   ba d0 32 60 00          mov    $0x6032d0,%edx
  401188:   48 89 54 74 20          mov    %rdx,0x20(%rsp,%rsi,2)
  40118d:   48 83 c6 04             add    $0x4,%rsi
  401191:   48 83 fe 18             cmp    $0x18,%rsi
  401195:   74 14                   je     4011ab <phase_6+0xb7>
  401197:   8b 0c 34                mov    (%rsp,%rsi,1),%ecx
  40119a:   83 f9 01                cmp    $0x1,%ecx
  40119d:   7e e4                   jle    401183 <phase_6+0x8f>
  40119f:   b8 01 00 00 00          mov    $0x1,%eax
  4011a4:   ba d0 32 60 00          mov    $0x6032d0,%edx
  4011a9:   eb cb                   jmp    401176 <phase_6+0x82>
  4011ab:   48 8b 5c 24 20          mov    0x20(%rsp),%rbx
  4011b0:   48 8d 44 24 28          lea    0x28(%rsp),%rax
  4011b5:   48 8d 74 24 50          lea    0x50(%rsp),%rsi
  4011ba:   48 89 d9                mov    %rbx,%rcx
  4011bd:   48 8b 10                mov    (%rax),%rdx
  4011c0:   48 89 51 08             mov    %rdx,0x8(%rcx)
  4011c4:   48 83 c0 08             add    $0x8,%rax
  4011c8:   48 39 f0                cmp    %rsi,%rax
  4011cb:   74 05                   je     4011d2 <phase_6+0xde>
  4011cd:   48 89 d1                mov    %rdx,%rcx
  4011d0:   eb eb                   jmp    4011bd <phase_6+0xc9>
  4011d2:   48 c7 42 08 00 00 00    movq   $0x0,0x8(%rdx)
  4011d9:   00 
  4011da:   bd 05 00 00 00          mov    $0x5,%ebp
  4011df:   48 8b 43 08             mov    0x8(%rbx),%rax
  4011e3:   8b 00                   mov    (%rax),%eax
  4011e5:   39 03                   cmp    %eax,(%rbx)
  4011e7:   7d 05                   jge    4011ee <phase_6+0xfa>
  4011e9:   e8 4c 02 00 00          callq  40143a <explode_bomb>
  4011ee:   48 8b 5b 08             mov    0x8(%rbx),%rbx
  4011f2:   83 ed 01                sub    $0x1,%ebp
  4011f5:   75 e8                   jne    4011df <phase_6+0xeb>
  4011f7:   48 83 c4 50             add    $0x50,%rsp
  4011fb:   5b                      pop    %rbx
  4011fc:   5d                      pop    %rbp
  4011fd:   41 5c                   pop    %r12
  4011ff:   41 5d                   pop    %r13
  401201:   41 5e                   pop    %r14
  401203:   c3                      retq   

本题汇编比较长,而且跳转指令比较多。先初步翻译如下

r13 = rsp
rsi = rsp

read_six_numbers // start at rsp

r14 = rsp
r12d = 0

rbp = r13 // 0x401114

eax = &r13
eax = eax - 1

if(eax > 5) bomb
else { // 0x401128
    r12d = r12d + 1
    if (r12d != 6) {
        ebx = r12d
        eax = ebx /*0x401135*/
        eax = &(rsp + 4 * eax)

        if (eax == &rbp) bomb
        else { // 0x401145
          ebx = ebx + 1
          if (ebx > 5) {
             r13 = r13 + 4
             //jmp to 0x401114
          } else  // jmp to 0x401135
        }
    } else { // 0x401153
      rsi = rsp + 0x18
      rax = r14
      ecx = 0x7
      edx = ecx /* 0x401160 */
      edx = edx - &rax
      &rax = edx
      rax = rax + 4
      if(rax == rsi) {
        esi = 0
        // jmp to 0x401197
      } else // jump to 0x401160
    }
}

/* 0x401197 */
ecx = &(rsp + rsi)
ecx = exc + 1

if(ecx > 1) {
  eax = 0x1
  edx = 0x6032d0

  /* 0x401176 */
  rdx = &(rdx + 8)
  eax = eax + 1 
  if (ecx != eax) // jmp to 0x401176
  else // jmp to 0x401188

} else { // 0x401183
  edx = 0x6032d0
  &(rsp + 2 * rsi + 0x20) = rdx // 0x401188
  rsi = rsi + 4
  if (rsi != 0x18) {
      // jmp to 0x401197
  } else // jmp to 0x4011ab
}

/* 0x4011ab */

rbx = &(rsp + 0x20)
rax = rsp + 0x28
rsi = rsp + 0x50
rcx = rbx
rdx = &rax // 0x4011bd
&(rcx + 8) = rdx
rax = rax + 8
if (rax == rsi) // jmp to 0x4011d2
else {
  rcx = rdx
  // jmp to 0x4011bd
}

/* 0x4011d2 */
(rdx + 0x8) = 0
ebp = 0x5
rax = &(rbx + 0x8) // 0x4011df
eax = &rax

if(&rbx < eax) // bomb
else { 
  rbx = &(rbx + 0x8)
  ebp = ebp - 0x1
  if (ebp != 0) // jmp to 0x4011df
  else // over
}

然后用循环简化其中的跳转指令

r13 = rsp;
rsi = rsp;

read_six_numbers(input,rsp); // array [a,b,c,d,e,f] start at rsp
r14 = rsp;

r12d = 0;
while(true){
    rbp = r13;
    eax = &r13 - 1;

    if(eax > 5) bomb(); // each number is not more than 6
    else {
        r12d++;
        if(r12d < 6) {
            ebx = r12d;
            do {
                if (&(rsp + 4 * ebx) == &rbp) // each number differs from others
                    bomb();
                else 
                    ebx++;
            } while(ebx <= 5)
            r13 = r13 + 4;
        } else{
            break;
        }
    }    
}

rsi = rsp + 0x18;
rax = r14;
ecx = 7;
do {
    &rax = 7 - &rax; // map with (7 - _)
    rax += 4;
} while (rax < rsi)

rsi = 0;
do () { // At 0x401197
    ecx = &(rsp + rsi);
    if(ecx > 1) {
        eax = 1;
        edx = 0x6032d0;
        do {
            rdx = &(rdx + 8); // At 0x401176
            eax++;         
        } while (eax < ecx)       
    } else { // At 0x401183
        edx = 0x6032d0;
    }

    /* address of the value of the linked list
     * +8 is the address of next node
     *
     * rsp + 0x20 + 0x00 = 0x6032d0 + ((7 - a) - 1) * 16
     * rsp + 0x20 + 0x08 = 0x6032d0 + ((7 - b) - 1) * 16
     * rsp + 0x20 + 0x10 = 0x6032d0 + ((7 - c) - 1) * 16
     * rsp + 0x20 + 0x18 = 0x6032d0 + ((7 - d) - 1) * 16
     * rsp + 0x20 + 0x20 = 0x6032d0 + ((7 - e) - 1) * 16
     * rsp + 0x20 + 0x28 = 0x6032d0 + ((7 - f) - 1) * 16
     * */
    &(rsp + 2 * rsi + 0x20) = rdx; // At 0x401188
    rsi += 4;

} while (rsi < 0x18)

/* The original linked list
 *
 * Address      Value          Next
 * 0x6032d0     0x10000014c    0x6032e0
 * 0x6032e0     0x2000000a8    0x6032f0
 * 0x6032f0     0x30000039c    0x603300
 * 0x603300     0x4000002b3    0x603310
 * 0x603310     0x5000001dd    0x603320
 * 0x603320     0x6000001bb    0x000000
 */

rbx = &(rsp + 0x20);
rax = rsp + 0x28;
rsi = rsp + 0x50; // limit
rcx = rbx;

// relink the linked list from (rsp + 0x20) to (rsp + 0x48) in sequence
while(true) {
    rdx = &rax;
    &(rcx + 8) = rdx;
    rax += 8;

    if(rax < rsi) rcx = rdx;
    else break;
}
(rdx + 0x8) = 0; // set Nil to the end of list


ebp = 5;
do {
    rax = &(rbx + 0x8);
    eax = &rax; // next value, truncate the higher 32 bits

    // &rbx is the current value 
    if(&rbx < eax) bomb(); // so the relinked list is descending sorted 
    else {
        rbx = &(rbx + 0x8);
        ebp -= 1;       
    }   
} while(ebp > 0)

/* let's sort the original list:
 *
 * Value     Index          Calculation 
 * 0x39c      2      7 - a - 1 = 2  => a = 4
 * 0x2b3      3      7 - b - 1 = 3  => b = 3
 * 0x1dd      4      7 - c - 1 = 4  => c = 2
 * 0x1bb      5      7 - d - 1 = 5  => d = 1
 * 0x14c      0      7 - e - 1 = 0  => e = 6
 * 0x0a8      1      7 - f - 1 = 1  => f = 5
 * */

可以发现这其实是一个简单的链表问题。输入六个互不相同且不大于6的正整数。记为Xi,则按(7 - Xi - 1)作为在链表中的位置,依次将对应的元素取出并按照取出顺序重新链接。变换后的链表必须满足降序排列。

将原链表按降序排列就知道该按什么顺序重新排列了,所以最后得到的输入为4 3 2 1 6 5

secret_phase

本实验还有一个隐藏关卡。但是观察main函数的汇编代码并没有发现有调用这个隐藏关卡secret_phase。查找发现,隐藏函数在0x401630处被调用,即在phase_defused函数中被调用。

$ objdump -d bomb | grep secret
0000000000401242 <secret_phase>:
  401265:   76 05                   jbe    40126c <secret_phase+0x2a>
  40127b:   74 05                   je     401282 <secret_phase+0x40>
  401630:   e8 0d fc ff ff          callq  401242 <secret_phase>

观察phase_defused可以发现,当输入的字符串个数大于6时,会进入隐藏关卡。并用sscanf函数接收格式为0x402619: "%d %d %s"的输入,输入的字符串地址为0x603870,并不是再次读取用户的输入,而是从一个固定地址读入。从这个地址读入的字符串必须是与在0x402622处的字符串DrEvil相同。

00000000004015c4 <phase_defused>:
  4015c4:   48 83 ec 78             sub    $0x78,%rsp
  4015c8:   64 48 8b 04 25 28 00    mov    %fs:0x28,%rax
  4015cf:   00 00 
  4015d1:   48 89 44 24 68          mov    %rax,0x68(%rsp)
  4015d6:   31 c0                   xor    %eax,%eax
  4015d8:   83 3d 81 21 20 00 06    cmpl   $0x6,0x202181(%rip)        # 603760 <num_input_strings>
  4015df:   75 5e                   jne    40163f <phase_defused+0x7b>
  4015e1:   4c 8d 44 24 10          lea    0x10(%rsp),%r8
  4015e6:   48 8d 4c 24 0c          lea    0xc(%rsp),%rcx
  4015eb:   48 8d 54 24 08          lea    0x8(%rsp),%rdx
  4015f0:   be 19 26 40 00          mov    $0x402619,%esi
  4015f5:   bf 70 38 60 00          mov    $0x603870,%edi
  4015fa:   e8 f1 f5 ff ff          callq  400bf0 <__isoc99_sscanf@plt>
  4015ff:   83 f8 03                cmp    $0x3,%eax
  401602:   75 31                   jne    401635 <phase_defused+0x71>
  401604:   be 22 26 40 00          mov    $0x402622,%esi
  401609:   48 8d 7c 24 10          lea    0x10(%rsp),%rdi
  40160e:   e8 25 fd ff ff          callq  401338 <strings_not_equal>
  401613:   85 c0                   test   %eax,%eax
  401615:   75 1e                   jne    401635 <phase_defused+0x71>
  401617:   bf f8 24 40 00          mov    $0x4024f8,%edi
  40161c:   e8 ef f4 ff ff          callq  400b10 <puts@plt>
  401621:   bf 20 25 40 00          mov    $0x402520,%edi
  401626:   e8 e5 f4 ff ff          callq  400b10 <puts@plt>
  40162b:   b8 00 00 00 00          mov    $0x0,%eax
  401630:   e8 0d fc ff ff          callq  401242 <secret_phase>
  401635:   bf 58 25 40 00          mov    $0x402558,%edi
  40163a:   e8 d1 f4 ff ff          callq  400b10 <puts@plt>
  40163f:   48 8b 44 24 68          mov    0x68(%rsp),%rax
  401644:   64 48 33 04 25 28 00    xor    %fs:0x28,%rax
  40164b:   00 00 
  40164d:   74 05                   je     401654 <phase_defused+0x90>
  40164f:   e8 dc f4 ff ff          callq  400b30 <__stack_chk_fail@plt>
  401654:   48 83 c4 78             add    $0x78,%rsp
  401658:   c3                      retq   

尝试在汇编代码中查找0x603870却无果。想到这个DrEvil一定是用户输入的,程序刚开始运行时利用GDB发现该处的字符串为空串,那么该字符串一定是在运行时改变的。于是用GDB监视地址为0x603870处的内存变化。

(gdb) watch *0x603870
Hardware watchpoint 1: *0x603870
(gdb) r inputs.txt 
Starting program: /home/yieldnull/CSAPP/lab2-bomb/src/bomb inputs.txt
Welcome to my fiendish little bomb. You have 6 phases with
which to blow yourself up. Have a nice day!
Phase 1 defused. How about the next one?
That's number 2.  Keep going!
Halfway there!

Hardware watchpoint 1: *0x603870

Old value = 0
New value = 49
__memcpy_sse2 () at ../sysdeps/x86_64/multiarch/../memcpy.S:74
74  ../sysdeps/x86_64/multiarch/../memcpy.S: No such file or directory.
(gdb) x /s 0x603870
0x603870 <input_strings+240>:   "1"
(gdb) next
75  in ../sysdeps/x86_64/multiarch/../memcpy.S
(gdb) next
80  in ../sysdeps/x86_64/multiarch/../memcpy.S
(gdb) next
81  in ../sysdeps/x86_64/multiarch/../memcpy.S
(gdb) next
83  in ../sysdeps/x86_64/multiarch/../memcpy.S
(gdb) next
84  in ../sysdeps/x86_64/multiarch/../memcpy.S
(gdb) next

Hardware watchpoint 1: *0x603870

Old value = 49
New value = 3153969
__memcpy_sse2 () at ../sysdeps/x86_64/multiarch/../memcpy.S:86
86  in ../sysdeps/x86_64/multiarch/../memcpy.S
(gdb) x /s 0x603870
0x603870 <input_strings+240>:   "1 0"

发现该地址处内存在第四次输入时改变,而且正好是第四次输入的字符串。于是在第四次输入字符串后加入DrEvil即可进入隐藏关卡。

秘密关卡的汇编代码如下:

0000000000401204 <fun7>:
  401204:   48 83 ec 08             sub    $0x8,%rsp
  401208:   48 85 ff                test   %rdi,%rdi
  40120b:   74 2b                   je     401238 <fun7+0x34>
  40120d:   8b 17                   mov    (%rdi),%edx
  40120f:   39 f2                   cmp    %esi,%edx
  401211:   7e 0d                   jle    401220 <fun7+0x1c>
  401213:   48 8b 7f 08             mov    0x8(%rdi),%rdi
  401217:   e8 e8 ff ff ff          callq  401204 <fun7>
  40121c:   01 c0                   add    %eax,%eax
  40121e:   eb 1d                   jmp    40123d <fun7+0x39>
  401220:   b8 00 00 00 00          mov    $0x0,%eax
  401225:   39 f2                   cmp    %esi,%edx
  401227:   74 14                   je     40123d <fun7+0x39>
  401229:   48 8b 7f 10             mov    0x10(%rdi),%rdi
  40122d:   e8 d2 ff ff ff          callq  401204 <fun7>
  401232:   8d 44 00 01             lea    0x1(%rax,%rax,1),%eax
  401236:   eb 05                   jmp    40123d <fun7+0x39>
  401238:   b8 ff ff ff ff          mov    $0xffffffff,%eax
  40123d:   48 83 c4 08             add    $0x8,%rsp
  401241:   c3                      retq   

0000000000401242 <secret_phase>:
  401242:   53                      push   %rbx
  401243:   e8 56 02 00 00          callq  40149e <read_line>
  401248:   ba 0a 00 00 00          mov    $0xa,%edx
  40124d:   be 00 00 00 00          mov    $0x0,%esi
  401252:   48 89 c7                mov    %rax,%rdi
  401255:   e8 76 f9 ff ff          callq  400bd0 <strtol@plt>
  40125a:   48 89 c3                mov    %rax,%rbx
  40125d:   8d 40 ff                lea    -0x1(%rax),%eax
  401260:   3d e8 03 00 00          cmp    $0x3e8,%eax
  401265:   76 05                   jbe    40126c <secret_phase+0x2a>
  401267:   e8 ce 01 00 00          callq  40143a <explode_bomb>
  40126c:   89 de                   mov    %ebx,%esi
  40126e:   bf f0 30 60 00          mov    $0x6030f0,%edi
  401273:   e8 8c ff ff ff          callq  401204 <fun7>
  401278:   83 f8 02                cmp    $0x2,%eax
  40127b:   74 05                   je     401282 <secret_phase+0x40>
  40127d:   e8 b8 01 00 00          callq  40143a <explode_bomb>
  401282:   bf 38 24 40 00          mov    $0x402438,%edi
  401287:   e8 84 f8 ff ff          callq  400b10 <puts@plt>
  40128c:   e8 33 03 00 00          callq  4015c4 <phase_defused>
  401291:   5b                      pop    %rbx
  401292:   c3                      retq   

程序比较简单,翻译如下

/* Address    Value
 * 0x6030f0   0x24
 * 0x6030f8   0x603110
 * 0x603110   0x8
 * 0x603120   0x603150
 * 0x603150   0x16
 *
 * rdi : 0x6030f0
 * esi : num - 1
 * */
void fun7(rdi, esi) { // must return 2
    if(rdi == NULL) {
        return -1;
    } else {
        edx = &rdi;
        if(edx > esi) {
            return 2 * fun7(&(rdi + 8), esi); // No.1 => esi <= 0x24
        } else {
            if (edx == esi) {
                return 0; // No.3  esi == 0x16 
            } else {
                return 1 + 2 * fun7(&(rdi + 0x10), esi); // No.2 => esi > 0x8
            }
        }
    }
}

void secret() {
    rdi = read_line();
    rax = strtol(rdi, NULL, 10);

    rbx = rax; 
    eax = rax - 1;

    if(eax > 0x3eb) bomb();
    else {
        eax = fun7(0x6030f0, ebx);
        if(eax != 0x2) bomb();
    }
}

func7的返回值必须为2,那么func7的调用返回流程必须是2 * (1 + 2 * 0),故很容易得出输入为22

sample_input

输入示例

$ cat inputs.txt 
Border relations with Canada have never been better.
1 2 4 8 16 32
1 311
1 0 DrEvil
iOnE&7
4 3 2 1 6 5
22
$ ./bomb inputs.txt 
Welcome to my fiendish little bomb. You have 6 phases with
which to blow yourself up. Have a nice day!
Phase 1 defused. How about the next one?
That's number 2.  Keep going!
Halfway there!
So you got that one.  Try this one.
Good work!  On to the next...
Curses, you've found the secret phase!
But finding it and solving it are quite different...
Wow! You've defused the secret stage!
Congratulations! You've defused the bomb!

(完)

翻译后的代码见[YieldNull/CSAPP]