# 准备工作
首先我们打开 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 可以抽象为以下的式子:
因为 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 周围的数据,我们发现其基本结构单元是:
- 一个数值
- 一个地址
- 一个地址
(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 可以抽象为以下的式子:
我们需要的返回值是 7。
| 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。