# 准备工作

首先我们打开 bomb.c :

/***************************************************************************
 * Dr. Evil's Insidious Bomb, Version 1.1
 * Copyright 2011, Dr. Evil Incorporated. All rights reserved.
 *
 * LICENSE:
 *
 * Dr. Evil Incorporated (the PERPETRATOR) hereby grants you (the
 * VICTIM) explicit permission to use this bomb (the BOMB).  This is a
 * time limited license, which expires on the death of the VICTIM.
 * The PERPETRATOR takes no responsibility for damage, frustration,
 * insanity, bug-eyes, carpal-tunnel syndrome, loss of sleep, or other
 * harm to the VICTIM.  Unless the PERPETRATOR wants to take credit,
 * that is.  The VICTIM may not distribute this bomb source code to
 * any enemies of the PERPETRATOR.  No VICTIM may debug,
 * reverse-engineer, run "strings" on, decompile, decrypt, or use any
 * other technique to gain knowledge of and defuse the BOMB.  BOMB
 * proof clothing may not be worn when handling this program.  The
 * PERPETRATOR will not apologize for the PERPETRATOR's poor sense of
 * humor.  This license is null and void where the BOMB is prohibited
 * by law.
 ***************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include "support.h"
#include "phases.h"
/* 
 * Note to self: Remember to erase this file so my victims will have no
 * idea what is going on, and so they will all blow up in a
 * spectaculary fiendish explosion. -- Dr. Evil 
 */
FILE *infile;
int main(int argc, char *argv[])
{
    char *input;
    /* Note to self: remember to port this bomb to Windows and put a 
     * fantastic GUI on it. */
    /* When run with no arguments, the bomb reads its input lines 
     * from standard input. */
    if (argc == 1) {  
    infile = stdin;
    } 
    /* When run with one argument <file>, the bomb reads from <file> 
     * until EOF, and then switches to standard input. Thus, as you 
     * defuse each phase, you can add its defusing string to <file> and
     * avoid having to retype it. */
    else if (argc == 2) {
    if (!(infile = fopen(argv[1], "r"))) {
        printf("%s: Error: Couldn't open %s\n", argv[0], argv[1]);
        exit(8);
    }
    }
    /* You can't call the bomb with more than 1 command line argument. */
    else {
    printf("Usage: %s [<input_file>]\n", argv[0]);
    exit(8);
    }
    /* Do all sorts of secret stuff that makes the bomb harder to defuse. */
    initialize_bomb();
    printf("Welcome to my fiendish little bomb. You have 6 phases with\n");
    printf("which to blow yourself up. Have a nice day!\n");
    /* Hmm...  Six phases must be more secure than one phase! */
    input = read_line();             /* Get input                   */
    phase_1(input);                  /* Run the phase               */
    phase_defused();                 /* Drat!  They figured it out!
                      * Let me know how they did it. */
    printf("Phase 1 defused. How about the next one?\n");
    /* The second phase is harder.  No one will ever figure out
     * how to defuse this... */
    input = read_line();
    phase_2(input);
    phase_defused();
    printf("That's number 2.  Keep going!\n");
    /* I guess this is too easy so far.  Some more complex code will
     * confuse people. */
    input = read_line();
    phase_3(input);
    phase_defused();
    printf("Halfway there!\n");
    /* Oh yeah?  Well, how good is your math?  Try on this saucy problem! */
    input = read_line();
    phase_4(input);
    phase_defused();
    printf("So you got that one.  Try this one.\n");
    
    /* Round and 'round in memory we go, where we stop, the bomb blows! */
    input = read_line();
    phase_5(input);
    phase_defused();
    printf("Good work!  On to the next...\n");
    /* This phase will never be used, since no one will get past the
     * earlier ones.  But just in case, make this one extra hard. */
    input = read_line();
    phase_6(input);
    phase_defused();
    /* Wow, they got it!  But isn't something... missing?  Perhaps
     * something they overlooked?  Mua ha ha ha ha! */
    
    return 0;
}

可以看到有 6 个 phase,每个 phase 基本都是要我们输入一行字符,然后它调用了判断我们的输入是否正确的函数。
知道要干什么之后把 bomb 文件反汇编一下:

objdump -d bomb > bomb.asm

得到了 bomb 的反汇编文件 bomb.asm ,然后就可以着手分析了。
在反汇编文件中搜索 phase,找到了各个 phase 所在地,就可以用 GDB 来进行调试了。

gdb bomb

然后在 gdb 中输入 disas phase_1 ,就可以看到 phase_1 的反汇编代码了,如下所示:

(gdb) disas phase_1
Dump of assembler code for function phase_1:
   0x08048bce <+0>:     push   %ebp
   0x08048bcf <+1>:     mov    %esp,%ebp
   0x08048bd1 <+3>:     sub    $0x18,%esp
   0x08048bd4 <+6>:     movl   $0x804a354,0x4(%esp)
   0x08048bdc <+14>:    mov    0x8(%ebp),%eax
   0x08048bdf <+17>:    mov    %eax,(%esp)
   0x08048be2 <+20>:    call   0x804915a <strings_not_equal>
   0x08048be7 <+25>:    test   %eax,%eax
   0x08048be9 <+27>:    je     0x8048bf0 <phase_1+34>
   0x08048beb <+29>:    call   0x80493bd <explode_bomb>
   0x08048bf0 <+34>:    leave
   0x08048bf1 <+35>:    ret
End of assembler dump.

给爆炸和 phase_1 打上断点

(gdb) b explode_bomb
Breakpoint 1 at 0x80493c3
(gdb) b phase_1
Breakpoint 2 at 0x8048bd4

# phase_1

08048bce <phase_1>:
 8048bce: 55                    push   %ebp
 8048bcf: 89 e5                 mov    %esp,%ebp
 8048bd1: 83 ec 18              sub    $0x18,%esp
 8048bd4: c7 44 24 04 54 a3 04  movl   $0x804a354,0x4(%esp)
 8048bdb: 08 
 8048bdc: 8b 45 08              mov    0x8(%ebp),%eax
 8048bdf: 89 04 24              mov    %eax,(%esp)
 8048be2: e8 73 05 00 00        call   804915a <strings_not_equal>
 8048be7: 85 c0                 test   %eax,%eax
 8048be9: 74 05                 je     8048bf0 <phase_1+0x22>
 8048beb: e8 cd 07 00 00        call   80493bd <explode_bomb>
 8048bf0: c9                    leave
 8048bf1: c3                    ret

调用 string_not_equal 函数判断输入的字符串与存储在 0x804a354 处的字符串是否相等,若相等(所调用函数为 0)炸弹就不会炸。

(gdb) x /s 0x804a354
0x804a354:      "I am the mayor. I can do anything I want."

所以本关的答案就是 0x804a354 处的字符串,只需通过 gdb 得到该地址的字符串即可。

# phase_2

08048bf2 <phase_2>:
 8048bf2: 55                    push   %ebp
 8048bf3: 89 e5                 mov    %esp,%ebp
 8048bf5: 83 ec 38              sub    $0x38,%esp
 8048bf8: 8d 45 dc              lea    -0x24(%ebp),%eax
 8048bfb: 89 44 24 04           mov    %eax,0x4(%esp)
 8048bff: 8b 45 08              mov    0x8(%ebp),%eax
 8048c02: 89 04 24              mov    %eax,(%esp)
 8048c05: e8 bb 04 00 00        call   80490c5 <read_six_numbers>
 8048c0a: 8b 45 dc              mov    -0x24(%ebp),%eax
 8048c0d: 85 c0                 test   %eax,%eax
 8048c0f: 75 08                 jne    8048c19 <phase_2+0x27>
 8048c11: 8b 45 e0              mov    -0x20(%ebp),%eax
 8048c14: 83 f8 01              cmp    $0x1,%eax
 8048c17: 74 05                 je     8048c1e <phase_2+0x2c>
 8048c19: e8 9f 07 00 00        call   80493bd <explode_bomb>
 8048c1e: c7 45 f4 02 00 00 00  movl   $0x2,-0xc(%ebp)
 8048c25: eb 2a                 jmp    8048c51 <phase_2+0x5f>
 8048c27: 8b 45 f4              mov    -0xc(%ebp),%eax
 8048c2a: 8b 44 85 dc           mov    -0x24(%ebp,%eax,4),%eax
 8048c2e: 8b 55 f4              mov    -0xc(%ebp),%edx
 8048c31: 83 ea 02              sub    $0x2,%edx
 8048c34: 8b 4c 95 dc           mov    -0x24(%ebp,%edx,4),%ecx
 8048c38: 8b 55 f4              mov    -0xc(%ebp),%edx
 8048c3b: 83 ea 01              sub    $0x1,%edx
 8048c3e: 8b 54 95 dc           mov    -0x24(%ebp,%edx,4),%edx
 8048c42: 01 ca                 add    %ecx,%edx
 8048c44: 39 d0                 cmp    %edx,%eax
 8048c46: 74 05                 je     8048c4d <phase_2+0x5b>
 8048c48: e8 70 07 00 00        call   80493bd <explode_bomb>
 8048c4d: 83 45 f4 01           addl   $0x1,-0xc(%ebp)
 8048c51: 83 7d f4 05           cmpl   $0x5,-0xc(%ebp)
 8048c55: 7e d0                 jle    8048c27 <phase_2+0x35>
 8048c57: c9                    leave
 8048c58: c3                    ret

首先调用 read_six_numbers ,获取六个整数,然后检查第一个整数是否为 0,再检查第二个整数是否为 1,然后进入一个循环,每次迭代时检查下一个整数是否等于前两个整数之和,如果以上条件有一个不满足,则会调用 explode_bomb 函数,炸弹直接爆炸,反之所有整数都满足条件,则函数正常返回。

所以我们需要做的便是构造一个 0、1 开始的斐波那契数列,因而最后的答案为 0 1 1 2 3 5

# phase_3

08048c59 <phase_3>:
 8048c59: 55                    push   %ebp
 8048c5a: 89 e5                 mov    %esp,%ebp
 8048c5c: 83 ec 28              sub    $0x28,%esp
 8048c5f: c7 45 f4 00 00 00 00  movl   $0x0,-0xc(%ebp)
 8048c66: c7 45 f0 00 00 00 00  movl   $0x0,-0x10(%ebp)
 8048c6d: 8d 45 e8              lea    -0x18(%ebp),%eax
 8048c70: 89 44 24 0c           mov    %eax,0xc(%esp)
 8048c74: 8d 45 ec              lea    -0x14(%ebp),%eax
 8048c77: 89 44 24 08           mov    %eax,0x8(%esp)
 8048c7b: c7 44 24 04 7e a3 04  movl   $0x804a37e,0x4(%esp)
 8048c82: 08 
 8048c83: 8b 45 08              mov    0x8(%ebp),%eax
 8048c86: 89 04 24              mov    %eax,(%esp)
 8048c89: e8 02 fc ff ff        call   8048890 <__isoc99_sscanf@plt>
 8048c8e: 89 45 f0              mov    %eax,-0x10(%ebp)
 8048c91: 83 7d f0 01           cmpl   $0x1,-0x10(%ebp)
 8048c95: 7f 05                 jg     8048c9c <phase_3+0x43>
 8048c97: e8 21 07 00 00        call   80493bd <explode_bomb>
 8048c9c: 8b 45 ec              mov    -0x14(%ebp),%eax
 8048c9f: 83 f8 07              cmp    $0x7,%eax
 8048ca2: 77 51                 ja     8048cf5 <phase_3+0x9c>
 8048ca4: 8b 04 85 84 a3 04 08  mov    0x804a384(,%eax,4),%eax
 8048cab: ff e0                 jmp    *%eax
 8048cad: c7 45 f4 90 03 00 00  movl   $0x390,-0xc(%ebp)
 8048cb4: eb 44                 jmp    8048cfa <phase_3+0xa1>
 8048cb6: c7 45 f4 7e 02 00 00  movl   $0x27e,-0xc(%ebp)
 8048cbd: eb 3b                 jmp    8048cfa <phase_3+0xa1>
 8048cbf: c7 45 f4 eb 01 00 00  movl   $0x1eb,-0xc(%ebp)
 8048cc6: eb 32                 jmp    8048cfa <phase_3+0xa1>
 8048cc8: c7 45 f4 9f 01 00 00  movl   $0x19f,-0xc(%ebp)
 8048ccf: eb 29                 jmp    8048cfa <phase_3+0xa1>
 8048cd1: c7 45 f4 dc 02 00 00  movl   $0x2dc,-0xc(%ebp)
 8048cd8: eb 20                 jmp    8048cfa <phase_3+0xa1>
 8048cda: c7 45 f4 d3 03 00 00  movl   $0x3d3,-0xc(%ebp)
 8048ce1: eb 17                 jmp    8048cfa <phase_3+0xa1>
 8048ce3: c7 45 f4 78 03 00 00  movl   $0x378,-0xc(%ebp)
 8048cea: eb 0e                 jmp    8048cfa <phase_3+0xa1>
 8048cec: c7 45 f4 51 00 00 00  movl   $0x51,-0xc(%ebp)
 8048cf3: eb 05                 jmp    8048cfa <phase_3+0xa1>
 8048cf5: e8 c3 06 00 00        call   80493bd <explode_bomb>
 8048cfa: 8b 45 e8              mov    -0x18(%ebp),%eax
 8048cfd: 39 45 f4              cmp    %eax,-0xc(%ebp)
 8048d00: 74 05                 je     8048d07 <phase_3+0xae>
 8048d02: e8 b6 06 00 00        call   80493bd <explode_bomb>
 8048d07: c9                    leave
 8048d08: c3                    ret
(gdb) x /s 0x804a37e
0x804a37e:      "%d %d"

调用 __isoc99_sscanf@plt 函数,获得两个整数,若 sscanf 函数返回值 <=1,无法从输入中获取两个整数,则调用 explode_bomb 函数,炸弹直接爆炸。
接下来再检测第一个整数是否 <=7,若不是,炸弹也直接爆炸。
然后,再使用一个跳转表来根据第一个整数的值确定第二个整数所应该是的值。
再比较输入的第二个整数与期望的值是否相等,如果不是,则调用 explode_bomb 函数,炸弹爆炸,反之函数正常返回。
所以我们要做的就是查看 0x804a384 处的值,由于输入的第一个整数的范围在 0<=num1<=7 之间有 7 种可能。所以我们直接 x /7wx 0x804a384 即可获取相应跳转地址。

(gdb) x /7wx 0x804a384
0x804a384:      0x08048cad      0x08048cb6      0x08048cbf      0x08048cc8
0x804a394:      0x08048cd1      0x08048cda      0x08048ce3

得到跳转地址后,在根据反编译得到的汇编代码,获得每个第一个整数对应的第二个整数:

num1 num2_hex num2
0 0x390 912
1 0x27e 638
2 0x1eb 491
3 0x19f 415
4 0x2dc 732
5 0x3d3 979
6 0x378 888
7 0x51 81

我们选择一组作为输入即可。

# phase_4

08048d60 <phase_4>:
 8048d60: 55                    push   %ebp
 8048d61: 89 e5                 mov    %esp,%ebp
 8048d63: 83 ec 38              sub    $0x38,%esp
 8048d66: 8d 45 e8              lea    -0x18(%ebp),%eax
 8048d69: 89 44 24 0c           mov    %eax,0xc(%esp)
 8048d6d: 8d 45 e4              lea    -0x1c(%ebp),%eax
 8048d70: 89 44 24 08           mov    %eax,0x8(%esp)
 8048d74: c7 44 24 04 7e a3 04  movl   $0x804a37e,0x4(%esp)
 8048d7b: 08 
 8048d7c: 8b 45 08              mov    0x8(%ebp),%eax
 8048d7f: 89 04 24              mov    %eax,(%esp)
 8048d82: e8 09 fb ff ff        call   8048890 <__isoc99_sscanf@plt>
 8048d87: 89 45 f4              mov    %eax,-0xc(%ebp)
 8048d8a: 83 7d f4 02           cmpl   $0x2,-0xc(%ebp)
 8048d8e: 75 10                 jne    8048da0 <phase_4+0x40>
 8048d90: 8b 45 e8              mov    -0x18(%ebp),%eax
 8048d93: 83 f8 01              cmp    $0x1,%eax
 8048d96: 7e 08                 jle    8048da0 <phase_4+0x40>
 8048d98: 8b 45 e8              mov    -0x18(%ebp),%eax
 8048d9b: 83 f8 04              cmp    $0x4,%eax
 8048d9e: 7e 05                 jle    8048da5 <phase_4+0x45>
 8048da0: e8 18 06 00 00        call   80493bd <explode_bomb>
 8048da5: c7 45 f0 07 00 00 00  movl   $0x7,-0x10(%ebp)
 8048dac: 8b 45 e8              mov    -0x18(%ebp),%eax
 8048daf: 89 44 24 04           mov    %eax,0x4(%esp)
 8048db3: 8b 45 f0              mov    -0x10(%ebp),%eax
 8048db6: 89 04 24              mov    %eax,(%esp)
 8048db9: e8 4b ff ff ff        call   8048d09 <func4>
 8048dbe: 89 45 ec              mov    %eax,-0x14(%ebp)
 8048dc1: 8b 45 e4              mov    -0x1c(%ebp),%eax
 8048dc4: 39 45 ec              cmp    %eax,-0x14(%ebp)
 8048dc7: 74 05                 je     8048dce <phase_4+0x6e>
 8048dc9: e8 ef 05 00 00        call   80493bd <explode_bomb>
 8048dce: c9                    leave
 8048dcf: c3                    ret

调用 __isoc99_sscanf@plt 函数,获得两个整数,若 sscanf 函数返回值不等于 2,则调用 explode_bomb 函数,炸弹直接爆炸。
接下来再检测第一个整数是否满足 1<num1<=4 ,若不是,炸弹也直接爆炸。
然后,再调用 func4 函数,传入的参数分别是 7 和输入的第一个整数 num1 ,将函数的返回值与输入的第二个整数进行比较。如果不相等,炸弹爆炸,反之函数正常返回。

所以我们接下来要做的就是去刨析 func4 函数的构造:

08048d09 <func4>:
 8048d09: 55                    push   %ebp
 8048d0a: 89 e5                 mov    %esp,%ebp
 8048d0c: 53                    push   %ebx
 8048d0d: 83 ec 14              sub    $0x14,%esp
 8048d10: 83 7d 08 00           cmpl   $0x0,0x8(%ebp)
 8048d14: 7f 07                 jg     8048d1d <func4+0x14>
 8048d16: b8 00 00 00 00        mov    $0x0,%eax
 8048d1b: eb 3d                 jmp    8048d5a <func4+0x51>
 8048d1d: 83 7d 08 01           cmpl   $0x1,0x8(%ebp)
 8048d21: 75 05                 jne    8048d28 <func4+0x1f>
 8048d23: 8b 45 0c              mov    0xc(%ebp),%eax
 8048d26: eb 32                 jmp    8048d5a <func4+0x51>
 8048d28: 8b 45 08              mov    0x8(%ebp),%eax
 8048d2b: 8d 50 ff              lea    -0x1(%eax),%edx
 8048d2e: 8b 45 0c              mov    0xc(%ebp),%eax
 8048d31: 89 44 24 04           mov    %eax,0x4(%esp)
 8048d35: 89 14 24              mov    %edx,(%esp)
 8048d38: e8 cc ff ff ff        call   8048d09 <func4>
 8048d3d: 8b 55 0c              mov    0xc(%ebp),%edx
 8048d40: 8d 1c 10              lea    (%eax,%edx,1),%ebx
 8048d43: 8b 45 08              mov    0x8(%ebp),%eax
 8048d46: 8d 50 fe              lea    -0x2(%eax),%edx
 8048d49: 8b 45 0c              mov    0xc(%ebp),%eax
 8048d4c: 89 44 24 04           mov    %eax,0x4(%esp)
 8048d50: 89 14 24              mov    %edx,(%esp)
 8048d53: e8 b1 ff ff ff        call   8048d09 <func4>
 8048d58: 01 d8                 add    %ebx,%eax
 8048d5a: 83 c4 14              add    $0x14,%esp
 8048d5d: 5b                    pop    %ebx
 8048d5e: 5d                    pop    %ebp
 8048d5f: c3                    ret

func4 是一个递归函数,其接受两个整数参数同时返回一个整数值。
首先它会检查第一个参数是否为 0 或 1。如果是 0,则返回 0。如果是 1,则返回第二个参数的值。否则,它会将第一个参数减 1 并递归调用 func4 函数,将第一个参数减 2 并再次递归调用 func4 函数,再将两次递归调用的返回值与第二个参数 三个数相加作为当次函数的返回值返回。
因此 func4 可以抽象为以下的式子:

f(x,y)={0x0yx=1f(x1,y)+y+f(x2,y)x>1 f(x,y)=\begin{cases} 0 & x \leq 0 \\ y & x = 1 \\ f(x-1,y)+y+f(x-2,y) & x > 1 \end{cases}

因为 phase_4 获取的第一个数值, 1<num1<=4 ,有三种可能,所以我们相对应有三种答案,在这里我们得到答案最方便的方法是编写一个程序模拟 func4 的过程来计算结果。

#include "iostream"
int func4(int x, int y) {
    if (x <= 0) return 0;
    if (x == 1) return y;
    return y + func4(x - 1, y) + func4(x - 2, y);
}
int main() {
    std::cout << func4(7, 2) << "\n";
    std::cout << func4(7, 3) << "\n";
    std::cout << func4(7, 4) << "\n";
}

得到相应答案:

x y result
7 2 66
7 3 99
7 4 132

# phase_5

08048dd0 <phase_5>:
 8048dd0: 55                    push   %ebp
 8048dd1: 89 e5                 mov    %esp,%ebp
 8048dd3: 83 ec 28              sub    $0x28,%esp
 8048dd6: 8b 45 08              mov    0x8(%ebp),%eax
 8048dd9: 89 04 24              mov    %eax,(%esp)
 8048ddc: e8 4d 03 00 00        call   804912e <string_length>
 8048de1: 89 45 f0              mov    %eax,-0x10(%ebp)
 8048de4: 83 7d f0 06           cmpl   $0x6,-0x10(%ebp)
 8048de8: 74 05                 je     8048def <phase_5+0x1f>
 8048dea: e8 ce 05 00 00        call   80493bd <explode_bomb>
 8048def: c7 45 f4 00 00 00 00  movl   $0x0,-0xc(%ebp)
 8048df6: eb 26                 jmp    8048e1e <phase_5+0x4e>
 8048df8: 8b 55 f4              mov    -0xc(%ebp),%edx
 8048dfb: 8b 45 08              mov    0x8(%ebp),%eax
 8048dfe: 01 d0                 add    %edx,%eax
 8048e00: 0f b6 00              movzbl (%eax),%eax
 8048e03: 0f be c0              movsbl %al,%eax
 8048e06: 83 e0 0f              and    $0xf,%eax
 8048e09: 0f b6 80 a4 c1 04 08  movzbl 0x804c1a4(%eax),%eax
 8048e10: 8d 4d e9              lea    -0x17(%ebp),%ecx
 8048e13: 8b 55 f4              mov    -0xc(%ebp),%edx
 8048e16: 01 ca                 add    %ecx,%edx
 8048e18: 88 02                 mov    %al,(%edx)
 8048e1a: 83 45 f4 01           addl   $0x1,-0xc(%ebp)
 8048e1e: 83 7d f4 05           cmpl   $0x5,-0xc(%ebp)
 8048e22: 7e d4                 jle    8048df8 <phase_5+0x28>
 8048e24: c6 45 ef 00           movb   $0x0,-0x11(%ebp)
 8048e28: c7 44 24 04 a4 a3 04  movl   $0x804a3a4,0x4(%esp)
 8048e2f: 08 
 8048e30: 8d 45 e9              lea    -0x17(%ebp),%eax
 8048e33: 89 04 24              mov    %eax,(%esp)
 8048e36: e8 1f 03 00 00        call   804915a <strings_not_equal>
 8048e3b: 85 c0                 test   %eax,%eax
 8048e3d: 74 05                 je     8048e44 <phase_5+0x74>
 8048e3f: e8 79 05 00 00        call   80493bd <explode_bomb>
 8048e44: c9                    leave
 8048e45: c3                    ret

首先调用了 string_length 函数来获取输入字符串的长度。然后检查其长度是否等于 6,如果不是,则调用 explode_bomb 函数,炸弹爆炸。
接下来进入到一个循环结构中,在每次迭代中都会处理字符串中的一个字符。对于这个字符,它首先将该字符转换它他的 ascii 码,然后将其与 0xf 进行按位与,然后,使用这个按位与后的结果作为索引在 0x804c1a4 处的一个长度为 16 的字符数组取到相应的字符。

(gdb) x /s 0x804c1a4
0x804c1a4 <array.2906>: "maduiersnfotvbyl"

最后调用 strings_not_equal 函数,来判断根据索引所取得的字符串是否等于地址 0x804a3a4 处的字符串。如果不是,则调用 explode_bomb 函数,炸弹爆炸,反之函数正常返回。

(gdb) x /s 0x804a3a4
0x804a3a4:      "sabres"
m a d u i e r s n f o t v b y l
0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xa 0xb 0xc 0xd 0xe 0xf

所以我们便是要根据 0x804c1a4 处的索引数组,输入相应的 ascii 字符,是的索引后的结果字符串与 0x804a3a4 处字符串相等。

s a b r e s
0x7 0x1 0xd 0x6 0x5 0x7
7 1 -, =, M, m, ], } 6 5 7

所以我们的答案可以是 71m657

# phase_6

08048e46 <phase_6>:
 8048e46: 55                    push   %ebp
 8048e47: 89 e5                 mov    %esp,%ebp
 8048e49: 83 ec 58              sub    $0x58,%esp
 8048e4c: c7 45 e8 e4 c0 04 08  movl   $0x804c0e4,-0x18(%ebp)
 8048e53: 8d 45 d0              lea    -0x30(%ebp),%eax
 8048e56: 89 44 24 04           mov    %eax,0x4(%esp)
 8048e5a: 8b 45 08              mov    0x8(%ebp),%eax
 8048e5d: 89 04 24              mov    %eax,(%esp)
 8048e60: e8 60 02 00 00        call   80490c5 <read_six_numbers>
 8048e65: c7 45 f0 00 00 00 00  movl   $0x0,-0x10(%ebp)
 8048e6c: eb 4c                 jmp    8048eba <phase_6+0x74>
 8048e6e: 8b 45 f0              mov    -0x10(%ebp),%eax
 8048e71: 8b 44 85 d0           mov    -0x30(%ebp,%eax,4),%eax
 8048e75: 85 c0                 test   %eax,%eax
 8048e77: 7e 0c                 jle    8048e85 <phase_6+0x3f>
 8048e79: 8b 45 f0              mov    -0x10(%ebp),%eax
 8048e7c: 8b 44 85 d0           mov    -0x30(%ebp,%eax,4),%eax
 8048e80: 83 f8 06              cmp    $0x6,%eax
 8048e83: 7e 05                 jle    8048e8a <phase_6+0x44>
 8048e85: e8 33 05 00 00        call   80493bd <explode_bomb>
 8048e8a: 8b 45 f0              mov    -0x10(%ebp),%eax
 8048e8d: 83 c0 01              add    $0x1,%eax
 8048e90: 89 45 ec              mov    %eax,-0x14(%ebp)
 8048e93: eb 1b                 jmp    8048eb0 <phase_6+0x6a>
 8048e95: 8b 45 f0              mov    -0x10(%ebp),%eax
 8048e98: 8b 54 85 d0           mov    -0x30(%ebp,%eax,4),%edx
 8048e9c: 8b 45 ec              mov    -0x14(%ebp),%eax
 8048e9f: 8b 44 85 d0           mov    -0x30(%ebp,%eax,4),%eax
 8048ea3: 39 c2                 cmp    %eax,%edx
 8048ea5: 75 05                 jne    8048eac <phase_6+0x66>
 8048ea7: e8 11 05 00 00        call   80493bd <explode_bomb>
 8048eac: 83 45 ec 01           addl   $0x1,-0x14(%ebp)
 8048eb0: 83 7d ec 05           cmpl   $0x5,-0x14(%ebp)
 8048eb4: 7e df                 jle    8048e95 <phase_6+0x4f>
 8048eb6: 83 45 f0 01           addl   $0x1,-0x10(%ebp)
 8048eba: 83 7d f0 05           cmpl   $0x5,-0x10(%ebp)
 8048ebe: 7e ae                 jle    8048e6e <phase_6+0x28>
 8048ec0: c7 45 f0 00 00 00 00  movl   $0x0,-0x10(%ebp)
 8048ec7: eb 36                 jmp    8048eff <phase_6+0xb9>
 8048ec9: 8b 45 e8              mov    -0x18(%ebp),%eax
 8048ecc: 89 45 f4              mov    %eax,-0xc(%ebp)
 8048ecf: c7 45 ec 01 00 00 00  movl   $0x1,-0x14(%ebp)
 8048ed6: eb 0d                 jmp    8048ee5 <phase_6+0x9f>
 8048ed8: 8b 45 f4              mov    -0xc(%ebp),%eax
 8048edb: 8b 40 08              mov    0x8(%eax),%eax
 8048ede: 89 45 f4              mov    %eax,-0xc(%ebp)
 8048ee1: 83 45 ec 01           addl   $0x1,-0x14(%ebp)
 8048ee5: 8b 45 f0              mov    -0x10(%ebp),%eax
 8048ee8: 8b 44 85 d0           mov    -0x30(%ebp,%eax,4),%eax
 8048eec: 3b 45 ec              cmp    -0x14(%ebp),%eax
 8048eef: 7f e7                 jg     8048ed8 <phase_6+0x92>
 8048ef1: 8b 45 f0              mov    -0x10(%ebp),%eax
 8048ef4: 8b 55 f4              mov    -0xc(%ebp),%edx
 8048ef7: 89 54 85 b8           mov    %edx,-0x48(%ebp,%eax,4)
 8048efb: 83 45 f0 01           addl   $0x1,-0x10(%ebp)
 8048eff: 83 7d f0 05           cmpl   $0x5,-0x10(%ebp)
 8048f03: 7e c4                 jle    8048ec9 <phase_6+0x83>
 8048f05: 8b 45 b8              mov    -0x48(%ebp),%eax
 8048f08: 89 45 e8              mov    %eax,-0x18(%ebp)
 8048f0b: 8b 45 e8              mov    -0x18(%ebp),%eax
 8048f0e: 89 45 f4              mov    %eax,-0xc(%ebp)
 8048f11: c7 45 f0 01 00 00 00  movl   $0x1,-0x10(%ebp)
 8048f18: eb 1a                 jmp    8048f34 <phase_6+0xee>
 8048f1a: 8b 45 f0              mov    -0x10(%ebp),%eax
 8048f1d: 8b 54 85 b8           mov    -0x48(%ebp,%eax,4),%edx
 8048f21: 8b 45 f4              mov    -0xc(%ebp),%eax
 8048f24: 89 50 08              mov    %edx,0x8(%eax)
 8048f27: 8b 45 f4              mov    -0xc(%ebp),%eax
 8048f2a: 8b 40 08              mov    0x8(%eax),%eax
 8048f2d: 89 45 f4              mov    %eax,-0xc(%ebp)
 8048f30: 83 45 f0 01           addl   $0x1,-0x10(%ebp)
 8048f34: 83 7d f0 05           cmpl   $0x5,-0x10(%ebp)
 8048f38: 7e e0                 jle    8048f1a <phase_6+0xd4>
 8048f3a: 8b 45 f4              mov    -0xc(%ebp),%eax
 8048f3d: c7 40 08 00 00 00 00  movl   $0x0,0x8(%eax)
 8048f44: 8b 45 e8              mov    -0x18(%ebp),%eax
 8048f47: 89 45 f4              mov    %eax,-0xc(%ebp)
 8048f4a: c7 45 f0 00 00 00 00  movl   $0x0,-0x10(%ebp)
 8048f51: eb 23                 jmp    8048f76 <phase_6+0x130>
 8048f53: 8b 45 f4              mov    -0xc(%ebp),%eax
 8048f56: 8b 10                 mov    (%eax),%edx
 8048f58: 8b 45 f4              mov    -0xc(%ebp),%eax
 8048f5b: 8b 40 08              mov    0x8(%eax),%eax
 8048f5e: 8b 00                 mov    (%eax),%eax
 8048f60: 39 c2                 cmp    %eax,%edx
 8048f62: 7d 05                 jge    8048f69 <phase_6+0x123>
 8048f64: e8 54 04 00 00        call   80493bd <explode_bomb>
 8048f69: 8b 45 f4              mov    -0xc(%ebp),%eax
 8048f6c: 8b 40 08              mov    0x8(%eax),%eax
 8048f6f: 89 45 f4              mov    %eax,-0xc(%ebp)
 8048f72: 83 45 f0 01           addl   $0x1,-0x10(%ebp)
 8048f76: 83 7d f0 04           cmpl   $0x4,-0x10(%ebp)
 8048f7a: 7e d7                 jle    8048f53 <phase_6+0x10d>
 8048f7c: c9                    leave
 8048f7d: c3                    ret

先调用 read_six_numbers ,获取六个整数,判断输入的这六个数是否有相等的,如果有,则调用 explode_bomb 函数,炸弹爆炸。
获取完 6 个数后,从 ebp-18 (0x804c0e4) 中取一个偏移为 4 的值我们可以发现,取得值也是一个地址,再依次将其取出,结果如下:

(gdb) x /3wx 0x0804c0e4
0x804c0e4 <node1>:      0x0000017f      0x00000001      0x0804c0d8
(gdb) x /3wx 0x0804c0d8
0x804c0d8 <node2>:      0x00000202      0x00000002      0x0804c0cc
(gdb) x /3wx 0x0804c0cc
0x804c0cc <node3>:      0x000000b3      0x00000003      0x0804c0c0
(gdb) x /3wx 0x0804c0c0
0x804c0c0 <node4>:      0x0000012e      0x00000004      0x0804c0b4
(gdb) x /3wx 0x0804c0b4
0x804c0b4 <node5>:      0x0000035d      0x00000005      0x0804c0a8
(gdb) x /3wx 0x0804c0a8
0x804c0a8 <node6>:      0x00000171      0x00000006      0x00000000

不难发现,从 0x804c0e4 开始的正是一个链表,该链表结构中主要有三个元素:
1、 当前节点的值;2、当前节点再列表中的顺序;3、当前节点的下一个节点。

node_add value_hex value order next_node_add
0x804c0e4 0x0000017f 383 0x00000001 0x0804c0d8
0x804c0d8 0x00000202 514 0x00000002 0x0804c0cc
0x0804c0cc 0x000000b3 179 0x00000003 0x0804c0c0
0x0804c0c0 0x0000012e 302 0x00000004 0x0804c0b4
0x0804c0b4 0x0000035d 861 0x00000005 0x0804c0a8
0x0804c0a8 0x00000171 369 0x00000006 0x00000000

再回到汇编中,我们可以发现,它将输入的值作为顺序 order ,将原本的链表进行了非原地排序。
接着再分析之后的一个循环结构,可以发先它是在校验我们排序后的链表是否满足非递增的顺序。

因此我们要做的就是针对取出来的数,进行递减的排序,然后对于排序后的元素,取其中的 order 作为输入即可满足要求。

383,514,179,302,861,369 --> 861,514,383,369,302,179 --> 5 2 1 6 4 3
得到答案 5 2 1 6 4 3

# phase_defused

080493e7 <phase_defused>:
 80493e7: 55                    push   %ebp
 80493e8: 89 e5                 mov    %esp,%ebp
 80493ea: 81 ec 88 00 00 00     sub    $0x88,%esp
 80493f0: a1 e8 c3 04 08        mov    0x804c3e8,%eax
 80493f5: 83 f8 06              cmp    $0x6,%eax
 80493f8: 75 72                 jne    804946c <phase_defused+0x85>
 80493fa: 8d 45 a4              lea    -0x5c(%ebp),%eax
 80493fd: 89 44 24 10           mov    %eax,0x10(%esp)
 8049401: 8d 45 9c              lea    -0x64(%ebp),%eax
 8049404: 89 44 24 0c           mov    %eax,0xc(%esp)
 8049408: 8d 45 a0              lea    -0x60(%ebp),%eax
 804940b: 89 44 24 08           mov    %eax,0x8(%esp)
 804940f: c7 44 24 04 f3 a4 04  movl   $0x804a4f3,0x4(%esp)
 8049416: 08 
 8049417: c7 04 24 f0 c4 04 08  movl   $0x804c4f0,(%esp)
 804941e: e8 6d f4 ff ff        call   8048890 <__isoc99_sscanf@plt>
 8049423: 89 45 f4              mov    %eax,-0xc(%ebp)
 8049426: 83 7d f4 03           cmpl   $0x3,-0xc(%ebp)
 804942a: 75 34                 jne    8049460 <phase_defused+0x79>
 804942c: c7 44 24 04 fc a4 04  movl   $0x804a4fc,0x4(%esp)
 8049433: 08 
 8049434: 8d 45 a4              lea    -0x5c(%ebp),%eax
 8049437: 89 04 24              mov    %eax,(%esp)
 804943a: e8 1b fd ff ff        call   804915a <strings_not_equal>
 804943f: 85 c0                 test   %eax,%eax
 8049441: 75 1d                 jne    8049460 <phase_defused+0x79>
 8049443: c7 04 24 04 a5 04 08  movl   $0x804a504,(%esp)
 804944a: e8 e1 f3 ff ff        call   8048830 <puts@plt>
 804944f: c7 04 24 2c a5 04 08  movl   $0x804a52c,(%esp)
 8049456: e8 d5 f3 ff ff        call   8048830 <puts@plt>
 804945b: e8 81 fb ff ff        call   8048fe1 <secret_phase>
 8049460: c7 04 24 64 a5 04 08  movl   $0x804a564,(%esp)
 8049467: e8 c4 f3 ff ff        call   8048830 <puts@plt>
 804946c: c9                    leave
 804946d: c3                    ret

最后的最后就是隐藏关卡 secret_phase ,但我们首先要找到进入 secret_phase 的方法。
不难发现,汇编代码中,只有在 phase_defused 调用了 secret_phase
分析汇编代码,可以发现主要是在 sscanf 时,从 0x804c4f0 的字符串中按照 0x804a4f3 的格式读取数值,如果返回值为 3,同时输入的字符串与 0x804a4fc 的字符串相等,则进入 secret_phase

(gdb) x /s 0x804c4f0
0x804c4f0 <input_strings+240>:  ""
(gdb) x /s 0x804a4f3
0x804a4f3:      "%d %d %s"
(gdb) x /s 0x804a4fc
0x804a4fc:      "DrEvil"

而形如 "%d %d %s" 的输入我们只在关卡 3 与关卡 4 中输入过,所以我们主要要找到 0x804c4f0 处对应的输入,我尝试在 3、4 中皆输入 DrEvil

(gdb) x /256s 0x804c400
0x804c400 <input_strings>:      "I am the mayor. I can do anything I want."
...
0x804c450 <input_strings+80>:   "0 1 1 2 3 5"
...
0x804c4a0 <input_strings+160>:  "6 888 DrEvil"
...
0x804c4f0 <input_strings+240>:  "66 2 DrEvil"

可以发现, 0x804c4f0 对应的是第四关的输入。
因此我们只需要在第四关的答案中添加 DrEvil 即可进入 secret_phase

# secret_phase

08048fe1 <secret_phase>:
 8048fe1: 55                    push   %ebp
 8048fe2: 89 e5                 mov    %esp,%ebp
 8048fe4: 83 ec 28              sub    $0x28,%esp
 8048fe7: e8 9a 02 00 00        call   8049286 <read_line>
 8048fec: 89 45 f4              mov    %eax,-0xc(%ebp)
 8048fef: 8b 45 f4              mov    -0xc(%ebp),%eax
 8048ff2: 89 04 24              mov    %eax,(%esp)
 8048ff5: e8 d6 f8 ff ff        call   80488d0 <atoi@plt>
 8048ffa: 89 45 f0              mov    %eax,-0x10(%ebp)
 8048ffd: 83 7d f0 00           cmpl   $0x0,-0x10(%ebp)
 8049001: 7e 09                 jle    804900c <secret_phase+0x2b>
 8049003: 81 7d f0 e9 03 00 00  cmpl   $0x3e9,-0x10(%ebp)
 804900a: 7e 05                 jle    8049011 <secret_phase+0x30>
 804900c: e8 ac 03 00 00        call   80493bd <explode_bomb>
 8049011: 8b 45 f0              mov    -0x10(%ebp),%eax
 8049014: 89 44 24 04           mov    %eax,0x4(%esp)
 8049018: c7 04 24 98 c1 04 08  movl   $0x804c198,(%esp)
 804901f: e8 5a ff ff ff        call   8048f7e <fun7>
 8049024: 89 45 ec              mov    %eax,-0x14(%ebp)
 8049027: 83 7d ec 07           cmpl   $0x7,-0x14(%ebp)
 804902b: 74 05                 je     8049032 <secret_phase+0x51>
 804902d: e8 8b 03 00 00        call   80493bd <explode_bomb>
 8049032: c7 04 24 ac a3 04 08  movl   $0x804a3ac,(%esp)
 8049039: e8 f2 f7 ff ff        call   8048830 <puts@plt>
 804903e: e8 a4 03 00 00        call   80493e7 <phase_defused>
 8049043: c9                    leave
 8049044: c3                    ret

在该函数中,它先调用 read_line 函数获取一行输入。调用 atoi@plt 函数,将读取的一行输入转换为整数。然后检查该整数是否满足 0<x<=1001 ,如果不是,则调用 explode_bomb 函数,炸弹爆炸。
接下来,调用 fun7 函数,将地址 0x804c198 作为第一个参数,输入的整数作为第二个参数,将 fun7 函数的返回值与 7 进行比较。如果不相等,则炸弹爆炸。反之,成功拆除炸弹。

所以我们要做的就是和关卡 4 类似,将重心转移至 fun7 中,刨析 fun7 的构造:

08048f7e <fun7>:
 8048f7e: 55                    push   %ebp
 8048f7f: 89 e5                 mov    %esp,%ebp
 8048f81: 83 ec 18              sub    $0x18,%esp
 8048f84: 83 7d 08 00           cmpl   $0x0,0x8(%ebp)
 8048f88: 75 07                 jne    8048f91 <fun7+0x13>
 8048f8a: b8 ff ff ff ff        mov    $0xffffffff,%eax
 8048f8f: eb 4e                 jmp    8048fdf <fun7+0x61>
 8048f91: 8b 45 08              mov    0x8(%ebp),%eax
 8048f94: 8b 00                 mov    (%eax),%eax
 8048f96: 3b 45 0c              cmp    0xc(%ebp),%eax
 8048f99: 7e 19                 jle    8048fb4 <fun7+0x36>
 8048f9b: 8b 45 08              mov    0x8(%ebp),%eax
 8048f9e: 8b 40 04              mov    0x4(%eax),%eax
 8048fa1: 8b 55 0c              mov    0xc(%ebp),%edx
 8048fa4: 89 54 24 04           mov    %edx,0x4(%esp)
 8048fa8: 89 04 24              mov    %eax,(%esp)
 8048fab: e8 ce ff ff ff        call   8048f7e <fun7>
 8048fb0: 01 c0                 add    %eax,%eax
 8048fb2: eb 2b                 jmp    8048fdf <fun7+0x61>
 8048fb4: 8b 45 08              mov    0x8(%ebp),%eax
 8048fb7: 8b 00                 mov    (%eax),%eax
 8048fb9: 3b 45 0c              cmp    0xc(%ebp),%eax
 8048fbc: 75 07                 jne    8048fc5 <fun7+0x47>
 8048fbe: b8 00 00 00 00        mov    $0x0,%eax
 8048fc3: eb 1a                 jmp    8048fdf <fun7+0x61>
 8048fc5: 8b 45 08              mov    0x8(%ebp),%eax
 8048fc8: 8b 40 08              mov    0x8(%eax),%eax
 8048fcb: 8b 55 0c              mov    0xc(%ebp),%edx
 8048fce: 89 54 24 04           mov    %edx,0x4(%esp)
 8048fd2: 89 04 24              mov    %eax,(%esp)
 8048fd5: e8 a4 ff ff ff        call   8048f7e <fun7>
 8048fda: 01 c0                 add    %eax,%eax
 8048fdc: 83 c0 01              add    $0x1,%eax
 8048fdf: c9                    leave
 8048fe0: c3                    ret

观察输入的第一个参数 0x804c198 周围的数据,我们发现其基本结构单元是:

  1. 一个数值
  2. 一个地址
  3. 一个地址
(gdb) x /45wx 0x804c0f0
0x804c0f0 <n48>:        0x000003e9      0x00000000      0x00000000      0x0000002f
0x804c100 <n46+4>:      0x00000000      0x00000000      0x00000014      0x00000000
0x804c110 <n43+8>:      0x00000000      0x00000007      0x00000000      0x00000000
0x804c120 <n44>:        0x00000023      0x00000000      0x00000000      0x00000063
0x804c130 <n47+4>:      0x00000000      0x00000000      0x00000001      0x00000000
0x804c140 <n41+8>:      0x00000000      0x00000028      0x00000000      0x00000000
0x804c150 <n34>:        0x0000006b      0x0804c12c      0x0804c0f0      0x00000006
0x804c160 <n31+4>:      0x0804c138      0x0804c114      0x0000002d      0x0804c144
0x804c170 <n33+8>:      0x0804c0fc      0x00000016      0x0804c108      0x0804c120
0x804c180 <n22>:        0x00000032      0x0804c168      0x0804c150      0x00000008
0x804c190 <n21+4>:      0x0804c15c      0x0804c174      0x00000024      0x0804c18c
0x804c1a0 <n1+8>:       0x0804c180

这种结构很像我们在数据结构课程中学过的二叉树,我们尝试把这颗树画出来:

在知道了第一个参数输入的是二叉树的头节点时,我们接下来对于 fun7 所做的事也就有了更加明确的认知。
fun7 同样是一个递归函数,该函数接受一个指向二叉树节点的指针和一个整数作为参数,并返回一个整数值。
如果节点指针为 NULL,则函数返回 - 1。
否则,它将节点的值与第二个参数进行比较。
如果节点的值大于第二个参数,则函数递归调用自身,将当前节点的左子节点和第二个参数作为新的参数,并将其返回值乘以 2 作为新的返回值返回。
如果节点的值等于第二个参数,则函数返回 0。
否则,函数递归调用自身,将该节点的右子节点和第二个参数作为新参数,并将返回值乘以 2 再加 1 作为新的返回值返回。

因此 fun7 可以抽象为以下的式子:

fun7(node,x)={1if node=NULL2×fun7(node.left,x)if node.value>x0if node.value=x2×fun7(node.right,x)+1if node.value<x fun7(node, x) = \begin{cases} -1 & \text{if } node = NULL \\ 2 \times fun7(node.left, x) & \text{if } node.value > x \\ 0 & \text{if } node.value = x \\ 2 \times fun7(node.right, x) + 1 & \text{if } node.value < x \end{cases}

我们需要的返回值是 7。

7=(((02+1)2+1)2+1)7=(((0 \ast 2+1) \ast 2+1) \ast 2+1)

1 *2+1 right
2 *2+1 right
3 *2+1 right

再根据我们之前画出的二叉树,可以得到我们所需要的之是 1001。

# read_six_number

080490c5 <read_six_numbers>:
 80490c5: 55                    push   %ebp
 80490c6: 89 e5                 mov    %esp,%ebp
 80490c8: 56                    push   %esi
 80490c9: 53                    push   %ebx
 80490ca: 83 ec 30              sub    $0x30,%esp
 80490cd: 8b 45 0c              mov    0xc(%ebp),%eax
 80490d0: 8d 70 14              lea    0x14(%eax),%esi
 80490d3: 8b 45 0c              mov    0xc(%ebp),%eax
 80490d6: 8d 58 10              lea    0x10(%eax),%ebx
 80490d9: 8b 45 0c              mov    0xc(%ebp),%eax
 80490dc: 8d 48 0c              lea    0xc(%eax),%ecx
 80490df: 8b 45 0c              mov    0xc(%ebp),%eax
 80490e2: 8d 50 08              lea    0x8(%eax),%edx
 80490e5: 8b 45 0c              mov    0xc(%ebp),%eax
 80490e8: 83 c0 04              add    $0x4,%eax
 80490eb: 89 74 24 1c           mov    %esi,0x1c(%esp)
 80490ef: 89 5c 24 18           mov    %ebx,0x18(%esp)
 80490f3: 89 4c 24 14           mov    %ecx,0x14(%esp)
 80490f7: 89 54 24 10           mov    %edx,0x10(%esp)
 80490fb: 89 44 24 0c           mov    %eax,0xc(%esp)
 80490ff: 8b 45 0c              mov    0xc(%ebp),%eax
 8049102: 89 44 24 08           mov    %eax,0x8(%esp)
 8049106: c7 44 24 04 7d a4 04  movl   $0x804a47d,0x4(%esp)
 804910d: 08 
 804910e: 8b 45 08              mov    0x8(%ebp),%eax
 8049111: 89 04 24              mov    %eax,(%esp)
 8049114: e8 77 f7 ff ff        call   8048890 <__isoc99_sscanf@plt>
 8049119: 89 45 f4              mov    %eax,-0xc(%ebp)
 804911c: 83 7d f4 05           cmpl   $0x5,-0xc(%ebp)
 8049120: 7f 05                 jg     8049127 <read_six_numbers+0x62>
 8049122: e8 96 02 00 00        call   80493bd <explode_bomb>
 8049127: 83 c4 30              add    $0x30,%esp
 804912a: 5b                    pop    %ebx
 804912b: 5e                    pop    %esi
 804912c: 5d                    pop    %ebp
 804912d: c3                    ret
(gdb) x /s 0x804a47d
0x804a47d:      "%d %d %d %d %d %d"

该函数的作用是从标准输入中读取六个整数,调整栈指针为这些变量分配所需的空间,然后调用了 __isoc99_sscanf@plt 函数,与我们平常使用的 sscanf 相似, 0x804a47d 地址所存放的就是格式字符串,最后,检查所调用函数返回值是否大于 5,若小于五则调用 explode_bomb ,炸弹直接爆炸。

# string_length

0804912e <string_length>:
 804912e: 55                    push   %ebp
 804912f: 89 e5                 mov    %esp,%ebp
 8049131: 83 ec 10              sub    $0x10,%esp
 8049134: 8b 45 08              mov    0x8(%ebp),%eax
 8049137: 89 45 f8              mov    %eax,-0x8(%ebp)
 804913a: c7 45 fc 00 00 00 00  movl   $0x0,-0x4(%ebp)
 8049141: eb 08                 jmp    804914b <string_length+0x1d>
 8049143: 83 45 f8 01           addl   $0x1,-0x8(%ebp)
 8049147: 83 45 fc 01           addl   $0x1,-0x4(%ebp)
 804914b: 8b 45 f8              mov    -0x8(%ebp),%eax
 804914e: 0f b6 00              movzbl (%eax),%eax
 8049151: 84 c0                 test   %al,%al
 8049153: 75 ee                 jne    8049143 <string_length+0x15>
 8049155: 8b 45 fc              mov    -0x4(%ebp),%eax
 8049158: c9                    leave
 8049159: c3                    ret

是一个简单的循环结构,每次迭代都会检查字符串中的下一个字符是否为 '\0' (字符串末尾)若不是,则递增字符串指针和长度,直到碰到 '\0' ,退出循环并返回长度。

# string_not_equal

0804915a <strings_not_equal>:
 804915a: 55                    push   %ebp
 804915b: 89 e5                 mov    %esp,%ebp
 804915d: 53                    push   %ebx
 804915e: 83 ec 14              sub    $0x14,%esp
 8049161: 8b 45 08              mov    0x8(%ebp),%eax
 8049164: 89 04 24              mov    %eax,(%esp)
 8049167: e8 c2 ff ff ff        call   804912e <string_length>
 804916c: 89 c3                 mov    %eax,%ebx
 804916e: 8b 45 0c              mov    0xc(%ebp),%eax
 8049171: 89 04 24              mov    %eax,(%esp)
 8049174: e8 b5 ff ff ff        call   804912e <string_length>
 8049179: 39 c3                 cmp    %eax,%ebx
 804917b: 74 07                 je     8049184 <strings_not_equal+0x2a>
 804917d: b8 01 00 00 00        mov    $0x1,%eax
 8049182: eb 3c                 jmp    80491c0 <strings_not_equal+0x66>
 8049184: 8b 45 08              mov    0x8(%ebp),%eax
 8049187: 89 45 f8              mov    %eax,-0x8(%ebp)
 804918a: 8b 45 0c              mov    0xc(%ebp),%eax
 804918d: 89 45 f4              mov    %eax,-0xc(%ebp)
 8049190: eb 1f                 jmp    80491b1 <strings_not_equal+0x57>
 8049192: 8b 45 f8              mov    -0x8(%ebp),%eax
 8049195: 0f b6 10              movzbl (%eax),%edx
 8049198: 8b 45 f4              mov    -0xc(%ebp),%eax
 804919b: 0f b6 00              movzbl (%eax),%eax
 804919e: 38 c2                 cmp    %al,%dl
 80491a0: 74 07                 je     80491a9 <strings_not_equal+0x4f>
 80491a2: b8 01 00 00 00        mov    $0x1,%eax
 80491a7: eb 17                 jmp    80491c0 <strings_not_equal+0x66>
 80491a9: 83 45 f8 01           addl   $0x1,-0x8(%ebp)
 80491ad: 83 45 f4 01           addl   $0x1,-0xc(%ebp)
 80491b1: 8b 45 f8              mov    -0x8(%ebp),%eax
 80491b4: 0f b6 00              movzbl (%eax),%eax
 80491b7: 84 c0                 test   %al,%al
 80491b9: 75 d7                 jne    8049192 <strings_not_equal+0x38>
 80491bb: b8 00 00 00 00        mov    $0x0,%eax
 80491c0: 83 c4 14              add    $0x14,%esp
 80491c3: 5b                    pop    %ebx
 80491c4: 5d                    pop    %ebp
 80491c5: c3                    ret

首先对于输入的两个字符串,调用 string_length 函数,返回两个字符串的长度,若长度不相等则直接返回 1(不相等),如果长度相等,则逐个比较两个字符串中的字符。如果发现不同的字符,则返回 1。如果所有字符都相同,则返回 0。