CSAPP Bomb Lab
答案
- Border relations with Canada have never been better.
- 1 2 4 8 16 32
- 多个答案
- 0 207
- 1 311
- 2 707
- 3 256
- 4 389
- 5 206
- 6 682
- 7 327
- 应该有多个答案
- 7 0
- 一个6个字符的字符串,字符串的ascii值依次为
- $9+k\times16$
- $15+k\times16$
- $14+k\times16$
- $5+k\times16$
- $6+k\times16$
- $7+k\times16$
- 4 3 2 1 6 5
第一题
解答思路
- string_not_equal函数比对(0x402400)位置的string与输入的string
- 直接运行gdb,
print (char*)0x402400
即可 - 不需要读完string_not_equal函数,可以猜测0x402400就是要找的,然后一试就过了。不过也不可以绝对相信函数名,或许出题人骗我们呢 2333
第二题
解答思路
- 汇编代码显示,调用read_six_numbers读入6个数字,放在从栈底开始的24个字节里。测试第一个是否为1,用循环测试后一个是否是前一个的2倍。所以直接输入
1 2 4 8 16 32
即可AC
第三题
解题思路
1
2
3
4
5lea 0xc(%rsp),%rcx //rcx=12+rsp
lea 0x8(%rsp),%rdx //rdx=8+rsp
mov $0x4025cf,%esi //%d %d
mov $0x0,%eax
callq 400bf0 <__isoc99_sscanf@plt>从调用sscanf前的寄存器准备工作中看出,%esi放着格式字符串,用gdb
print (char*)0x4025cf
打印出"%d %d"
然后测试读入的第一个数字是否大于7,如果是,explode_bomb
然后就是一个switch,用gdb
x/14w 0x402470
打印出1
2
3
40x402470: 0x00400f7c 0x00000000 0x00400fb9 0x00000000
0x402480: 0x00400f83 0x00000000 0x00400f8a 0x00000000
0x402490: 0x00400f91 0x00000000 0x00400f98 0x00000000
0x4024a0: 0x00400f9f 0x00000000按照对应关系确定第二个读入的数字即可
第四题
解题思路
- 同样是用sscanf读入两个数字
- 由于逻辑比较复杂,但是反编译比较简单,可以直接反编译得到结果
第五题
解题思路
1
2callq 40131b <string_length>
cmp $0x6,%eax读入6个字符的字符串
1
2
3
4
5
6movzbl (%rbx,%rax,1),%ecx
mov %cl,(%rsp)
mov (%rsp),%rdx
and $0xf,%edx
movzbl 0x4024b0(%rdx),%edx
mov %dl,0x10(%rsp,%rax,1)提取每个字符的ascii的低4bits,放在
edx
里,然后从0x4024b0+edx
的位置读入数据放在栈上1
2
3mov $0x40245e,%esi
lea 0x10(%rsp),%rdi
callq 401338 <strings_not_equal>后面调用了strings_not_equal,比对0x40245e处的字符串及我们放在栈上的数据。
用gdb分别打印0x4024b0 0x40245e处的字符串,获得
maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?
flyers
此时可以知道,前面提取数据时,是以我们输入的字符的低4bit的数值为偏移量,从0x4024b0开始的字符串按照偏移量提取字符放在栈上,然后与
flyers
比对。偏移量依次为$9\ 15\ 14\ 5\ 6\ 7$但是这些字符不可打印出来,所以我们需要加上$16\times k$来获得可打印的字符
第六题
一些心得
不要尝试完整的人肉反汇编成C,而是大概知道哪段代码有哪些功能,然后用gdb调试,看看猜的对不对。比如本题以下代码
1
2
3
4
5
6
7
8
9
10
11#from 代码段的401153
lea 0x18(%rsp),%rsi
mov %r14,%rax
mov $0x7,%ecx
mov %ecx,%edx
sub (%rax),%edx
mov %edx,(%rax)
add $0x4,%rax
cmp %rsi,%rax
jne 401160 <phase_6+0x6c>大概知道是改变读入的数字的值,但是不确定确切功能,这时候可以在用gdb,在读入之后的地方设断点,打印这块内存区域的多个字节的值,然后再在这段代码执行后的地方设断点,打印值,观察变化,结合汇编代码,更加容易知道其功能
安利一个gdb插件peda,大大提高gdb的用户体验
解题思路
整段代码分为 个部分
0x4010fc 到 0x401106:读入6个数值
0x40110b 到 0x401151:一个大循环,对读入的数字的合法性进行检查,要求读入的数字的取值范围是$[1,6]$,并且相互之间不相等。(这时候或许就可以暴力了233333)
0x401153 到 0x40116d:另一个循环,对输入的数字顺序进行调整。(一开始想着直接看汇编,但是出错了,后来用gdb调试,很容易就看到了这个特性)
0x401176 到 0x4011a9:一个大循环把链表的node的地址按照一定顺序写到栈上(里面有多个小循环、跳转,比较复杂)。通过大概的反汇编、gdb打印该段代码执行前后内存的值、猜测、测试不同的输入的数字序列,获得该段的功能。
其中,该段中把关于0x6032d0的值写入栈中,所以用gdb命令
x/30w 0x6032d0
打印改地址附近的多个字节,结果如下1
2
3
4
5
60x6032d0 <node1>: 0x0000014c 0x00000001 0x006032e0 0x00000000
0x6032e0 <node2>: 0x000000a8 0x00000002 0x006032f0 0x00000000
0x6032f0 <node3>: 0x0000039c 0x00000003 0x00603300 0x00000000
0x603300 <node4>: 0x000002b3 0x00000004 0x00603310 0x00000000
0x603310 <node5>: 0x000001dd 0x00000005 0x00603320 0x00000000
0x603320 <node6>: 0x000001bb 0x00000006 0x00000000 0x00000000可以看到有6个node,每个node的第三个字节都对应另一个node的地址,很明显,这是一个链表。
由此可以知道该段代码的功能为把node的地址写在栈上,地址在栈上的排列顺序由读入的数字确定。当然,读入的数字的顺序在前面的一段代码被处理了,所以直接从汇编了解其逻辑比较难,还是用gdb打印该段代码执行后的值,并且尝试改变输入的数字的顺序来确定该段的逻辑。
node地址在栈上关于读入的数字的分布规则为
- node地址越大,对应的数字越小,1对应0x603320,2对应0x603310,3对应0x603300,4对应0x6032f0,5对应0x6032e0,6对应0x6032d0
- node地址按照读入数字的顺序,排列在栈上。
0x4011ab 到 0x4011d9:该段从
%rsp+0x20
开始,读入前面写到栈上的node地址,然后写到上一个节点的偏移量为8bit的位置,也就是node里存放next node的指针的位置。其实就是按照栈上node地址的排列顺序重新排列链表里node的顺序,其等价的C代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19struct node{
int num;
int index;
node *next;
};
node **next = %rsp+0x28;
node **endnode = %rsp+0x50;
node *currentnode = *(%rsp+0x20);
node *temp;
while(1){
temp = *next;
currentnode->next = temp;
next += 1; //in fact, it add 8 bytes;
if(endnode==next)
break;
currentnode = temp;
}
temp->next = null;0x4011df 到 0x4011f5:依次访问链表的节点,判断是否有后一个节点的数据大于前一个的情况,如果有,bomb。