Back to Blog

CSAPP - Bomb Lab

This post is not yet available in English. Showing the original version.
Table of Contents
Table of Contents

来挑战邪恶博士了!每一个 CS 学生的必经之路……
正在做,持续更新中……

这依然是 HNU 的计算机系统的实验作业,老师给每个同学生成了不同的 lab 文件,我做的结果只是我的文件答案。
不过解题方法都是大同小异啦。


这是题目,当然是翻译后的版本:

/***************************************************************************
 * 邪恶博士的阴险炸弹,版本 1.1
 * 版权所有 2011,邪恶博士股份有限公司。保留所有权利。
 *
 * 许可协议:
 *
 * 邪恶博士股份有限公司(下称“施害者”)特此授予你(下称“受害者”)
 * 使用本炸弹(下称“炸弹”)的明确许可。这是一个时间受限的许可,
 * 在受害者死亡时失效。
 * “施害者”对“受害者”可能遭受的伤害、挫折感、精神失常、眼球突出、
 * 腕管综合征、睡眠不足或其他损害概不负责——除非“施害者”想以此邀功。
 * “受害者”不得将此炸弹源代码分发给“施害人”的任何敌人。
 * 严禁任何“受害者”进行调试、逆向工程、运行 "strings"、反编译、
 * 解密或使用任何其他技术来获取相关知识并拆除炸弹。
 * 处理本程序时不得穿着防弹衣。
 * “施害人”不会为其糟糕的幽默感道歉。
 * 在法律禁止使用“炸弹”的地区,此许可无效。
 ***************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include "support.h"
#include "phases.h"

/* 
 * 自留笔记:记得把这个文件删了,这样我的受害者们就完全不知道发生了什么,
 * 然后他们就会在一次华丽壮观、充满恶意的大爆炸中死翘翘。
 * —— 邪恶博士 
 */

FILE *infile;

int main(int argc, char *argv[])
{
    char *input;

    /* 自留笔记:记得把这个炸弹移植到 Windows 并加上炫酷的图形界面。 */

    /* 当不带参数运行时,炸弹从标准输入(键盘)读取输入。 */
    if (argc == 1) {  
        infile = stdin;
    } 

    /* 当带有一个参数 <file> 时,炸弹会从该文件中持续读取直到文件结束(EOF),
     * 然后再切换到键盘输入。因此,当你每解开一个阶段,你都可以把破解字符串
     * 添加到文件中,从而避免重复输入。 */
    else if (argc == 2) {
        if (!(infile = fopen(argv[1], "r"))) {
            printf("%s: 错误:无法打开文件 %s\n", argv[0], argv[1]);
            exit(8);
        }
    }

    /* 你不能带超过 1 个命令行参数来调用炸弹。 */
    else {
        printf("用法: %s [<输入文件>]\n", argv[0]);
        exit(8);
    }

    /* 执行各种秘密操作,增加炸弹的拆除难度。 */
    initialize_bomb();

    printf("欢迎来到我邪恶的小炸弹。你一共有 6 个阶段\n");
    printf("可以让自己被炸飞。祝你今天愉快!\n");

    /* 唔…… 6 个阶段肯定比 1 个阶段更安全! */
    input = read_line();             /* 获取输入 */
    phase_1(input);                  /* 运行第一阶段 */
    phase_defused();                 /* 讨厌!他们竟然解开了!
                                      * 让我知道他们是怎么做到的。 */
    printf("第一阶段已拆除。下一关怎么样?\n");

    /* 第二阶段更难。没人能搞清楚怎么拆除这一个…… */
    input = read_line();
    phase_2(input);
    phase_defused();
    printf("这是第 2 关。继续努力!\n");

    /* 我猜目前为止还是太简单了。一些更复杂的代码会把大家搞糊涂。 */
    input = read_line();
    phase_3(input);
    phase_defused();
    printf("已经完成一半了!\n");

    /* 哦,是吗?那你的数学怎么样?试试这个刁钻的问题! */
    input = read_line();
    phase_4(input);
    phase_defused();
    printf("看来你把那个也解开了。试试这个。\n");
    
    /* 在内存里转啊转,转到哪儿,炸弹就炸到哪儿! */
    input = read_line();
    phase_5(input);
    phase_defused();
    printf("做得好!还剩最后一关!\n");

    /* 这就是你要面对的最终挑战。在这里认输并不会丢人。 */
    input = read_line();
    phase_6(input);
    phase_defused();

    /* 呜呼!你居然真的拆除了炸弹! */
    
    return 0;
}
 

第一关

我们使用 objdump -d bomb > bomb.s,找到 <phase_1> 这一段函数:

080488c0 <phase_1>:
 80488c0: 83 ec 1c                     	subl	$0x1c, %esp
 80488c3: c7 44 24 04 e4 92 04 08      	movl	$0x80492e4, 0x4(%esp)   # imm = 0x80492E4
 80488cb: 8b 44 24 20                  	movl	0x20(%esp), %eax
 80488cf: 89 04 24                     	movl	%eax, (%esp)
 80488d2: e8 03 05 00 00               	calll	0x8048dda <strings_not_equal>
 80488d7: 85 c0                        	testl	%eax, %eax
 80488d9: 74 05                        	je	0x80488e0 <phase_1+0x20>
 80488db: e8 05 06 00 00               	calll	0x8048ee5 <explode_bomb>
 80488e0: 83 c4 1c                     	addl	$0x1c, %esp
 80488e3: c3                           	retl

注意到这一堆汇编里面有一个 <strings_not_equal> 的调用,看名字应该就是个比较字符串是否相等的函数。
还可以看到一个 # imm = 0x80492E4 的注释,imm 是立即数的 immediate value 的缩写。
那看看这一行的含义中,80488c3 这一行本来的意思就是:把内存地址 0x80492E4 放到栈上,作为参数传给接下来的函数。

说明内存地址 0x80492E4 肯定有东西!于是 gdb 做调试,看看里面写了什么:

gdb ./bomb
(gdb) x/s 0x80492e4
0x80492e4:       "I was trying to give Tina Fey more material."
(gdb) 

是一段字符串!那应该就是我们的题解了,我们的输入的字符串与它一致,即可解除这个 bomb 啦。

root@794cb0e6f7ce:/workspace/bomb# ./bomb      
        
Welcome to my fiendish little bomb. You have 6 phases with
which to blow yourself up. Have a nice day!
# 在这里输入这个字符串
I was trying to give Tina Fey more material.

Phase 1 defused. How about the next one?

当然也可以 IDA Pro 或者更加 Apple Native 的 Hopper Disassembler,直接去反汇编,看 <phase_1> 的函数,答案就这样浮出水面了……


第二关

先去翻一翻第二个函数的汇编:

080488e4 <phase_2>:
 80488e4: 56                           	pushl	%esi
 80488e5: 53                           	pushl	%ebx
 80488e6: 83 ec 34                     	subl	$0x34, %esp
 80488e9: 8d 44 24 18                  	leal	0x18(%esp), %eax
 80488ed: 89 44 24 04                  	movl	%eax, 0x4(%esp)
 80488f1: 8b 44 24 40                  	movl	0x40(%esp), %eax
 80488f5: 89 04 24                     	movl	%eax, (%esp)
 80488f8: e8 0f 06 00 00               	calll	0x8048f0c <read_six_numbers>
 80488fd: 83 7c 24 18 01               	cmpl	$0x1, 0x18(%esp)
 8048902: 74 1e                        	je	0x8048922 <phase_2+0x3e>
 8048904: e8 dc 05 00 00               	calll	0x8048ee5 <explode_bomb>
 8048909: eb 17                        	jmp	0x8048922 <phase_2+0x3e>
 804890b: 8b 43 fc                     	movl	-0x4(%ebx), %eax
 804890e: 01 c0                        	addl	%eax, %eax
 8048910: 39 03                        	cmpl	%eax, (%ebx)
 8048912: 74 05                        	je	0x8048919 <phase_2+0x35>
 8048914: e8 cc 05 00 00               	calll	0x8048ee5 <explode_bomb>
 8048919: 83 c3 04                     	addl	$0x4, %ebx
 804891c: 39 f3                        	cmpl	%esi, %ebx
 804891e: 75 eb                        	jne	0x804890b <phase_2+0x27>
 8048920: eb 0a                        	jmp	0x804892c <phase_2+0x48>
 8048922: 8d 5c 24 1c                  	leal	0x1c(%esp), %ebx
 8048926: 8d 74 24 30                  	leal	0x30(%esp), %esi
 804892a: eb df                        	jmp	0x804890b <phase_2+0x27>
 804892c: 83 c4 34                     	addl	$0x34, %esp
 804892f: 5b                           	popl	%ebx
 8048930: 5e                           	popl	%esi
 8048931: c3                           	retl

看上去就明显复杂了很多,一眼看过去就看明白了个 <read_six_numbers> 的调用,读取六个数字?还有两个 <explode_bomb> 的触发?
先分析汇编,注释一下:

 80488e6: 83 ec 34              subl    $0x34, %esp      # 在栈上开辟 0x34 就是 52 的空间
 80488e9: 8d 44 24 18           leal    0x18(%esp), %eax # 计算栈上偏移 24 (0x18) 的地址
 80488ed: 89 44 24 04           movl    %eax, 0x4(%esp)  # 将该地址存为后面 read_six_numbers 的函数参数
 80488f1: 8b 44 24 40           movl    0x40(%esp), %eax 
 80488f5: 89 04 24              movl    %eax, (%esp)
 80488f8: e8 d9 02 00 00        calll   0x8048f0c <read_six_numbers> # 读取 6 个数并存入 0x18(%esp) 开始的区域
 80488fd: 83 7c 24 18 01        cmpl    $0x1, 0x18(%esp) # 比较!

因为是读取六个数,且一个数会占4个字节,所以 0x18(%esp)0x1c(%esp)0x20(%esp)... 分别存6个数。
那么 cmpl $0x1, 0x18(%esp) 就是用存的第一个数做比较了。

注意到第一个 explode 前面的 cmp 比较指令:

 80488fd: 83 7c 24 18 01               	cmpl	$0x1, 0x18(%esp)
 8048902: 74 1e                        	je	0x8048922 <phase_2+0x3e>
 8048904: e8 dc 05 00 00               	calll	0x8048ee5 <explode_bomb>

这一段汇编的含义大概是:比较第一个数字是否为1,如果是就跳过,否则就 boom

然后看第二个 bomb 之前的事情,这后面的看的好头疼……

 804890b: 8b 43 fc                     	movl	-0x4(%ebx), %eax
 804890e: 01 c0                        	addl	%eax, %eax
 8048910: 39 03                        	cmpl	%eax, (%ebx)
 8048912: 74 05                        	je	0x8048919 <phase_2+0x35>
 8048914: e8 cc 05 00 00               	calll	0x8048ee5 <explode_bomb>

这一段比较有意思,把往前偏移4位的值存起来,其实也就是上一位数字,然后再自己相加,再和原数比较。
如果翻译成 c,大概的意思就是:

int a = arr[x-1];
a+=a;
if (a==arr[x]){
	continue;
}
else explode_bomb();
//...

那这个就很明了了,我们的下一位数字,必须是上一位数字的2倍!
后面的汇编实在是看的头疼,于是就直接写6个数字符合这个条件吧~也就是 1 2 4 8 16 32

root@794cb0e6f7ce:/workspace/bomb# ./bomb 
Welcome to my fiendish little bomb. You have 6 phases with
which to blow yourself up. Have a nice day!

I was trying to give Tina Fey more material.
Phase 1 defused. How about the next one?

1 2 4 8 16 32
That's number 2.  Keep going!

总算过了。


第三关

再看看题目:

08048932 <phase_3>:
 8048932: 83 ec 3c                     	subl	$0x3c, %esp
 8048935: 8d 44 24 2c                  	leal	0x2c(%esp), %eax
 8048939: 89 44 24 10                  	movl	%eax, 0x10(%esp)
 804893d: 8d 44 24 27                  	leal	0x27(%esp), %eax
 8048941: 89 44 24 0c                  	movl	%eax, 0xc(%esp)
 8048945: 8d 44 24 28                  	leal	0x28(%esp), %eax
 8048949: 89 44 24 08                  	movl	%eax, 0x8(%esp)
 804894d: c7 44 24 04 3a 93 04 08      	movl	$0x804933a, 0x4(%esp)   # imm = 0x804933A
 8048955: 8b 44 24 40                  	movl	0x40(%esp), %eax
 8048959: 89 04 24                     	movl	%eax, (%esp)
 804895c: e8 9f fc ff ff               	calll	0x8048600 <__isoc99_sscanf@plt>
 8048961: 83 f8 02                     	cmpl	$0x2, %eax
 8048964: 7f 05                        	jg	0x804896b <phase_3+0x39>
 8048966: e8 7a 05 00 00               	calll	0x8048ee5 <explode_bomb>
 804896b: 83 7c 24 28 07               	cmpl	$0x7, 0x28(%esp)
 8048970: 0f 87 fc 00 00 00            	ja	0x8048a72 <phase_3+0x140>
 8048976: 8b 44 24 28                  	movl	0x28(%esp), %eax
 804897a: ff 24 85 60 93 04 08         	jmpl	*0x8049360(,%eax,4)
 8048981: b8 73 00 00 00               	movl	$0x73, %eax
 8048986: 81 7c 24 2c 19 03 00 00      	cmpl	$0x319, 0x2c(%esp)      # imm = 0x319
 804898e: 0f 84 e8 00 00 00            	je	0x8048a7c <phase_3+0x14a>
 8048994: e8 4c 05 00 00               	calll	0x8048ee5 <explode_bomb>
 8048999: b8 73 00 00 00               	movl	$0x73, %eax
 804899e: e9 d9 00 00 00               	jmp	0x8048a7c <phase_3+0x14a>
 80489a3: b8 62 00 00 00               	movl	$0x62, %eax
 80489a8: 81 7c 24 2c a3 03 00 00      	cmpl	$0x3a3, 0x2c(%esp)      # imm = 0x3A3
 80489b0: 0f 84 c6 00 00 00            	je	0x8048a7c <phase_3+0x14a>
 80489b6: e8 2a 05 00 00               	calll	0x8048ee5 <explode_bomb>
 80489bb: b8 62 00 00 00               	movl	$0x62, %eax
 80489c0: e9 b7 00 00 00               	jmp	0x8048a7c <phase_3+0x14a>
 80489c5: b8 66 00 00 00               	movl	$0x66, %eax
 80489ca: 81 7c 24 2c c4 03 00 00      	cmpl	$0x3c4, 0x2c(%esp)      # imm = 0x3C4
 80489d2: 0f 84 a4 00 00 00            	je	0x8048a7c <phase_3+0x14a>
 80489d8: e8 08 05 00 00               	calll	0x8048ee5 <explode_bomb>
 80489dd: b8 66 00 00 00               	movl	$0x66, %eax
 80489e2: e9 95 00 00 00               	jmp	0x8048a7c <phase_3+0x14a>
 80489e7: b8 6f 00 00 00               	movl	$0x6f, %eax
 80489ec: 81 7c 24 2c 61 01 00 00      	cmpl	$0x161, 0x2c(%esp)      # imm = 0x161
 80489f4: 0f 84 82 00 00 00            	je	0x8048a7c <phase_3+0x14a>
 80489fa: e8 e6 04 00 00               	calll	0x8048ee5 <explode_bomb>
 80489ff: b8 6f 00 00 00               	movl	$0x6f, %eax
 8048a04: eb 76                        	jmp	0x8048a7c <phase_3+0x14a>
 8048a06: b8 73 00 00 00               	movl	$0x73, %eax
 8048a0b: 81 7c 24 2c 95 03 00 00      	cmpl	$0x395, 0x2c(%esp)      # imm = 0x395
 8048a13: 74 67                        	je	0x8048a7c <phase_3+0x14a>
 8048a15: e8 cb 04 00 00               	calll	0x8048ee5 <explode_bomb>
 8048a1a: b8 73 00 00 00               	movl	$0x73, %eax
 8048a1f: eb 5b                        	jmp	0x8048a7c <phase_3+0x14a>
 8048a21: b8 68 00 00 00               	movl	$0x68, %eax
 8048a26: 81 7c 24 2c da 02 00 00      	cmpl	$0x2da, 0x2c(%esp)      # imm = 0x2DA
 8048a2e: 74 4c                        	je	0x8048a7c <phase_3+0x14a>
 8048a30: e8 b0 04 00 00               	calll	0x8048ee5 <explode_bomb>
 8048a35: b8 68 00 00 00               	movl	$0x68, %eax
 8048a3a: eb 40                        	jmp	0x8048a7c <phase_3+0x14a>
 8048a3c: b8 6c 00 00 00               	movl	$0x6c, %eax
 8048a41: 81 7c 24 2c a8 02 00 00      	cmpl	$0x2a8, 0x2c(%esp)      # imm = 0x2A8
 8048a49: 74 31                        	je	0x8048a7c <phase_3+0x14a>
 8048a4b: e8 95 04 00 00               	calll	0x8048ee5 <explode_bomb>
 8048a50: b8 6c 00 00 00               	movl	$0x6c, %eax
 8048a55: eb 25                        	jmp	0x8048a7c <phase_3+0x14a>
 8048a57: b8 62 00 00 00               	movl	$0x62, %eax
 8048a5c: 81 7c 24 2c c9 03 00 00      	cmpl	$0x3c9, 0x2c(%esp)      # imm = 0x3C9
 8048a64: 74 16                        	je	0x8048a7c <phase_3+0x14a>
 8048a66: e8 7a 04 00 00               	calll	0x8048ee5 <explode_bomb>
 8048a6b: b8 62 00 00 00               	movl	$0x62, %eax
 8048a70: eb 0a                        	jmp	0x8048a7c <phase_3+0x14a>
 8048a72: e8 6e 04 00 00               	calll	0x8048ee5 <explode_bomb>
 8048a77: b8 6a 00 00 00               	movl	$0x6a, %eax
 8048a7c: 3a 44 24 27                  	cmpb	0x27(%esp), %al
 8048a80: 74 05                        	je	0x8048a87 <phase_3+0x155>
 8048a82: e8 5e 04 00 00               	calll	0x8048ee5 <explode_bomb>
 8048a87: 83 c4 3c                     	addl	$0x3c, %esp
 8048a8a: c3                           	retl

立马变得超级复杂!有好多 explode 指令……



0 / 2000
Loading comments...