这个问题还得用GDB调试来做。
截图做笔记吧,实在写不动了!
1.执行反汇编
obj-dump -D bomb > mysrc.S
得到可执行文件的机器级程序(汇编文件)。
2.搜索main(每一个应用程序都是从main函数开始)
找到如下内容:
0000000000400da0 <main>:
400da0: 53 push %rbx
400da1: 83 ff 01 cmp $0x1,%edi
400da4: 75 10 jne 400db6 <main+0x16>
400da6: 48 8b 05 9b 29 20 00 mov 0x20299b(%rip),%rax # 603748 <stdin@@GLIBC_2.2.5>
400dad: 48 89 05 b4 29 20 00 mov %rax,0x2029b4(%rip) # 603768 <infile>
400db4: eb 63 jmp 400e19 <main+0x79>
400db6: 48 89 f3 mov %rsi,%rbx
400db9: 83 ff 02 cmp $0x2,%edi
400dbc: 75 3a jne 400df8 <main+0x58>
400dbe: 48 8b 7e 08 mov 0x8(%rsi),%rdi
400dc2: be b4 22 40 00 mov $0x4022b4,%esi
400dc7: e8 44 fe ff ff callq 400c10 <fopen@plt>
400dcc: 48 89 05 95 29 20 00 mov %rax,0x202995(%rip) # 603768 <infile>
400dd3: 48 85 c0 test %rax,%rax
400dd6: 75 41 jne 400e19 <main+0x79>
400dd8: 48 8b 4b 08 mov 0x8(%rbx),%rcx
400ddc: 48 8b 13 mov (%rbx),%rdx
400ddf: be b6 22 40 00 mov $0x4022b6,%esi
400de4: bf 01 00 00 00 mov $0x1,%edi
400de9: e8 12 fe ff ff callq 400c00 <__printf_chk@plt>
400dee: bf 08 00 00 00 mov $0x8,%edi
400df3: e8 28 fe ff ff callq 400c20 <exit@plt>
400df8: 48 8b 16 mov (%rsi),%rdx
400dfb: be d3 22 40 00 mov $0x4022d3,%esi
400e00: bf 01 00 00 00 mov $0x1,%edi
400e05: b8 00 00 00 00 mov $0x0,%eax
400e0a: e8 f1 fd ff ff callq 400c00 <__printf_chk@plt>
400e0f: bf 08 00 00 00 mov $0x8,%edi
400e14: e8 07 fe ff ff callq 400c20 <exit@plt>
400e19: e8 84 05 00 00 callq 4013a2 <initialize_bomb>
400e1e: bf 38 23 40 00 mov $0x402338,%edi
400e23: e8 e8 fc ff ff callq 400b10 <puts@plt>
400e28: bf 78 23 40 00 mov $0x402378,%edi
400e2d: e8 de fc ff ff callq 400b10 <puts@plt>
400e32: e8 67 06 00 00 callq 40149e <read_line>
400e37: 48 89 c7 mov %rax,%rdi
400e3a: e8 a1 00 00 00 callq 400ee0 <phase_1>
400e3f: e8 80 07 00 00 callq 4015c4 <phase_defused>
400e44: bf a8 23 40 00 mov $0x4023a8,%edi
400e49: e8 c2 fc ff ff callq 400b10 <puts@plt>
400e4e: e8 4b 06 00 00 callq 40149e <read_line>
400e53: 48 89 c7 mov %rax,%rdi
400e56: e8 a1 00 00 00 callq 400efc <phase_2>
400e5b: e8 64 07 00 00 callq 4015c4 <phase_defused>
再从上述内容找到如下内容:
400e3a: e8 a1 00 00 00 callq 400ee0 <phase_1>
在这条语句之前的内容都是初始化相关的操作,从这里开始才是开始执行拆炸弹的任务,phase_1表明这是拆炸弹的第一关。可以看到一直到phase_6,说明有6个关卡,而且最后还有一个隐藏的关口!于是我们可以利用GDB一个关口一个关口的设置断点,看看每一个关口如何判断我们输入的数据是否是正确的拆弹密码!
3.另外在反汇编文件里再找到phase_1的汇编代码如下:
0000000000400ee0 <phase_1>:
400ee0: 48 83 ec 08 sub $0x8,%rsp
400ee4: be 00 24 40 00 mov $0x402400,%esi
400ee9: e8 4a 04 00 00 callq 401338 <strings_not_equal>
400eee: 85 c0 test %eax,%eax
400ef0: 74 05 je 400ef7 <phase_1+0x17>
400ef2: e8 43 05 00 00 callq 40143a <explode_bomb>
400ef7: 48 83 c4 08 add $0x8,%rsp
400efb: c3 retq
仔细思考,炸弹就是通过explode_bomb函数引爆,所有后面就好办了!只要避免程序调用该函数就可以拆除炸弹。
4.利用GDB启动bomb程序,并设置断点:
因为程序会通过explode_bomb引爆炸弹,所以在phase_1和phase_2以及explode_bomb都设置断点,看看程序的具体执行流程,当开始run程序以后,会在phase_1停下来,之后利用disas反汇编可以查看phase_1的具体汇编代码,发现这个函数的流程就是:
(1)调整栈指针
(2)将0x402400赋值到%esi寄存器,根据X86-64的参数传递规则,我们知道%esi寄存器保存的是函数调用的第二个参数。第一个参数就是我们通过标准输入/指定的输入设备文件输入的数据,这里我们简单输入“1234”,存放在%rax指向的地址处
(3)根据strings_not_equal函数的返回结果进行判断密码是否正确,返回值存放在%rax中
(4)比较%rax指向的字符串和0x402400处存放的字符串是否相等,%rax不为0,表示不相等,就爆炸,相等就过了第一关!跳转到add $0x8,%rsp
处,结束pahse_1函数的调用!
所以我们只要知道0x402400处存放的字符串是什么就可以了!
输入:
x/sb 0x402400
就可以查看预设定的第一关密码了!可以得到第一关的密码是:
Border relations with Canada have never been better.
5.按照同样的方法,一个阶段一个阶段设置断点,反汇编具体的阶段函数,判断函数如何对比我们输入的数据就可以得到答案了!
6.第二阶段需要输入的数字是六个,后一个数字是前一个的两倍,第一个数字是1,所以密码是:
1 2 4 8 16 32
7.第三关反汇编以后,是一个含有switch语句的函数,要求输入两个数字,根据第一个数字决定分支语句,然后根据跳转表,就可以跳转到相应的语句块,判断第二个数字是否匹配,所以有多个密码组合。
好比
switch(n)
{
case n1:
if(m != m1)
explode_bomb();
break;
... ... ...
case n7:
if(m != m7)
explode_bomb();
break;
default:
explode_bomb();
break;
}
输入的第一个数就是n,第二个数就是m。从汇编代码可以看出,要求n小于等于7,所以又0-7八种可能,分别是:
n | m |
---|---|
0 | 0xcf |
1 | 0x137 |
2 | 0x2c3 |
3 | 0x100 |
4 | 0x185 |
5 | 0xce |
6 | 0x2aa |
7 | 0x147 |
这里我们选择1和311输入(0x137的十进制),注意要输入为十进制。
8.第四关主要是一个递归函数,主要是找到递归的终止条件,
这里也是要求输入两个数字,最后一个一定是0,第一个是0xe的因子,实质上只要是func4函数能够返回0的的输入值都可以。因为我们输入的第一个数回作为func4函数的第一个参数!他的第二个参数是0,第三个参数是0xe。然后反汇编func4函数就可以判断当输入的第一个数满足什么条件的时候函数才会返回0,如果不返回0,就会爆炸!
这里我们的密码是:
7 0
也许还有其他答案,但没有往下分析,因为输入第一个数是7的时候,明显满足func4返回0,能完成拆弹任务!
9.第五关的反汇编代码是:
大体意思是,要求输入六个字符,然后根据每一个字符的低四位(ASCII码是8位),作为索引/数组下标,这里的数组是0x4024b0开头一个字符串:
maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?
根据得到的索引从该字符串中取出六个字符,这些字符必须和0x40245e处存放的flyers
相等才可以通关。所以我们从0x4024b0处的字符串分别找到flyers
里面每个字符的下标,这些下标是我们输入的字符的低四位,然后对照ASCII表,既可找到我们应该输入的字符串!
比如f在0x4024b0处的字符串中是第9个,对应的二进制就是1001,也就是我们输入的第一个字符的低四位是1001,对照ASCII表可以得到对应的是i
或者’I’(也就是大小写字母都满足条件),
以此类推密码是:
ionefg
10.第六关的反汇编很长,截取部分如下:
从代码可以知道是要输入6个数字,看做一个数组a[6],然后对于每一个数组元素,在栈里面存放为7-a[i],其中i属于0–5,查看0x6032d0处的内容,可以下发现:
发现其实是一个结构体,类似于
struct {
int value;
int order;
node* next;
} node;
我们要做的,就是得到正确的 order(从大到小),保证栈里面的order对应的数据项依次从大到小即可。
比如说数据项为924的这个节点是这个结构体数组中最大的,他的order就应该是栈里面的第一个,他的order是3,所以我们栈里面存放的第一个数字就是3,就可以发现栈里面直观的顺序是:
3 4 5 6 1 2
对应的输入就应该是
4 3 2 1 6 5
拆弹任务完成!!!!
至于隐藏任务以后有时间再说,现在感觉找不到工作了,好忧桑。。。。。。
另外附上两个帖子有助于帮助:不能照抄答案,每年的实验内容都会变化!!!!
http://wdxtub.com/2016/04/16/thick-csapp-lab-2/
http://www.cnblogs.com/chkkch/archive/2011/05/21/2052708.html