csapp lab2:Bomb Lab

Phase1
1
2
3
4
5
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");

首先根据代码,先输入一个字符串,然后传入phase_1()这个函数,所以要分析在phase_1()这个函数里都做了什么。

1
2
3
4
5
6
7
8
9
0000000000400ee0 <phase_1>:
400ee0: 48 83 ec 08 sub $0x8,%rsp ;入栈,栈指针减8
400ee4: be 00 24 40 00 mov $0x402400,%esi ;%esi=402400
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> ;如果%eax为0,跳转
400ef2: e8 43 05 00 00 callq 40143a <explode_bomb>
400ef7: 48 83 c4 08 add $0x8,%rsp
400efb: c3 retq
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
0000000000401338 <strings_not_equal>:
401338: 41 54 push %r12 ;原始值压入栈中
40133a: 55 push %rbp ;原始值压入栈中
40133b: 53 push %rbx ;原始值压入栈中
40133c: 48 89 fb mov %rdi,%rbx ;%rbx=%rdi
40133f: 48 89 f5 mov %rsi,%rbp ;%rbp=%rsi
401342: e8 d4 ff ff ff callq 40131b <string_length> ;获得字符串长度
401347: 41 89 c4 mov %eax,%r12d ;%r12d=%eax
40134a: 48 89 ef mov %rbp,%rdi ;%rdi=%rbp
40134d: e8 c9 ff ff ff callq 40131b <string_length> ;获得字符串长度
401352: ba 01 00 00 00 mov $0x1,%edx ;%edx=1
401357: 41 39 c4 cmp %eax,%r12d
40135a: 75 3f jne 40139b <strings_not_equal+0x63> ;比较两字符串长度,如果不同则跳转到40139b
40135c: 0f b6 03 movzbl (%rbx),%eax ;往上看,%rbx其实就是%rdi,也就是第一个字符串,因此%eax=(%rbx)即为第一个字符串的第一个字符
40135f: 84 c0 test %al,%al
401361: 74 25 je 401388 <strings_not_equal+0x50> ;如果%eax=0,则跳转到401388
401363: 3a 45 00 cmp 0x0(%rbp),%al ;往上看,%rbp其实就是%rsi,也就是第二个字符串,因此这里是判断s1的第一个字符和s2的第一个字符是否相等,如果相等跳转到401372,否则跳转到40138f
401366: 74 0a je 401372 <strings_not_equal+0x3a>
401368: eb 25 jmp 40138f <strings_not_equal+0x57>
40136a: 3a 45 00 cmp 0x0(%rbp),%al
40136d: 0f 1f 00 nopl (%rax)
401370: 75 24 jne 401396 <strings_not_equal+0x5e>
401372: 48 83 c3 01 add $0x1,%rbx ;%rbx=%rbx+1
401376: 48 83 c5 01 add $0x1,%rbp ;%rbp=%rbp+1
40137a: 0f b6 03 movzbl (%rbx),%eax
40137d: 84 c0 test %al,%al ;如果字符串1的当前字符不为0,则跳转到40136a
40137f: 75 e9 jne 40136a <strings_not_equal+0x32>
401381: ba 00 00 00 00 mov $0x0,%edx
401386: eb 13 jmp 40139b <strings_not_equal+0x63>
401388: ba 00 00 00 00 mov $0x0,%edx
40138d: eb 0c jmp 40139b <strings_not_equal+0x63>
40138f: ba 01 00 00 00 mov $0x1,%edx
401394: eb 05 jmp 40139b <strings_not_equal+0x63>
401396: ba 01 00 00 00 mov $0x1,%edx ;这几行的%edx其实都是标志位,当%edx为1时,两个字符串不相等,当%edx为0时,两个字符串相等
40139b: 89 d0 mov %edx,%eax
40139d: 5b pop %rbx
40139e: 5d pop %rbp
40139f: 41 5c pop %r12
4013a1: c3 retq
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
000000000040131b <string_length>:
40131b: 80 3f 00 cmpb $0x0,(%rdi) ;判断(%rdi)是否为0
40131e: 74 12 je 401332 <string_length+0x17> ;如果是0,则跳到401332
401320: 48 89 fa mov %rdi,%rdx ;%rdx=%rdi
401323: 48 83 c2 01 add $0x1,%rdx ;%rdx=%rdx+1
401327: 89 d0 mov %edx,%eax ;%eax=%edx
401329: 29 f8 sub %edi,%eax ;%eax=%eax-%edi
;这里的上下两步其实可以简写为%eax=%edx-%edi 其实按理来说应该是%eax=%rdx-%rdi 但他这里用的低位四个字节,我在这认为是编译器的优化
40132b: 80 3a 00 cmpb $0x0,(%rdx) ;判断(%rdx)是否为0
40132e: 75 f3 jne 401323 <string_length+0x8> ;如果不相等,跳到401323
401330: f3 c3 repz retq
401332: b8 00 00 00 00 mov $0x0,%eax ;%eax=0
401337: c3 retq

由此看来<string_length>这个函数,首先是判断一下传入的字符串是否为空,为空直接返回0;否则计算一下字符串长度然后返回。

根据C语言代码和phase_1的反汇编代码,可以看出当的返回值为0,也就是传入的两个字符串相等时,炸弹才能拆除。这两个字符串,第一个是我们从命令行输入的,第二个则是以0x402400地址开始的字符串。因此,只需要找到这个字符串是什么,然后输入即可。

1
2
(gdb) print (char*) 0x402400
$1 = 0x402400 "Border relations with Canada have never been better."
Phase2