返回博客

CSAPP - Bomb Lab

目录
目录

来挑战邪恶博士了!每一个 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
正在加载评论...