Bomblab



啃完了CSAPP的前四章,但是怎么能停在只是坑完了呢,当然要做配套的经典Lab,这次做实验把学到的都用起来了
因为实验会涉及到GDB的调试,可以参照

phase_1

首先调用objdump -d bomp > bomb.txt语句对其进行反汇编,产生bomb.txt

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的值为$Ox402400
400ee9: e8 4a 04 00 00 callq 401338 <strings_not_equal>//调用strings_not_equal函数
400eee: 85 c0 test %eax,%eax //比较%eax是否为0,%eax存储的是函数的返回值,即strings_not_equal的返回值
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

从对汇编代码的分析看,$0x402400地址的值是破解的关键

  • 我们先GDB该程序,并在phase_1函数开头设置一个断点,如下图所示
  • 之后run该程序,就会在断点处停下,并随机输入一个字符串xzy
    (当时在这里还碰到了个小问题,我是把直接把程序从Windows下直接复制到虚拟机中,导致该文件的权限为-rw-------,因此我是没有权限去执行它的,具体修改权限可以参照)

  • 我们的目的是要读取$0x402400地址的值,所以我们单步调试到mov语句之后,并读取该地址的值,我们可以看到写入的字符串xzy存在%eax,而我们要的信息存在%esi

  • 至此我们就找到破解第一关的密码了

phase_2

为了之后的做题带来方便,我们将答案存储在ans.txt,并在每次运行时作为参数输入,如下图所示这一关用到了一点栈操作的知识,前两行分别是将%rbp%rbx压入栈中,%rsp始终是指向栈顶,栈的结构类似下图
我们看到400f05: e8 52 05 00 00 callq 40145c <read_six_numbers>语句的时候,应该猜到第二关的密码是六个数字,并且读入的六个数字保存在%rsp%rsp+28

下一条语句比较1和%rsp指向的内存的值(也就是读入的第一个数字),如果相等则跳转到400f30 <phase_2+0x34>,该语句是设置寄存器值的操作,也就是将%rsp+4的值赋给%rbx,同理将%rsp+18的值赋给%rbp,继续跳转到400f17 <phase_2+0x1b>,该语句表示将(%rbx-4)地址所指向内存的值(即%rsp指向内存的值,也是第一个输入的数)赋给%eax,然后%eax自加后和%rbx(即第二个输入的值)比较,所以第二个数字应该是2,接下来就是一个循环,所以最后的结果应该是1 2 4 8 16 32

phase_3

经过两关的练习,做第三关的时候明显感到容易起来了

  • 前几行依旧是栈的预处理,直到我们看到这一行400f51: be cf 25 40 00 mov $0x4025cf,%esi,又在将某个东西传进来了,所以本着好奇的原则我们取查看一下%esi存了点什么东西,所有当调用(gdb) x/s $esi发现了点线索,提示0x4025cf: "%d %d",我们猜测这一关的密码应该是两个数字,继续往下看
  • 400f6a: 83 7c 24 08 07 cmpl $0x7,0x8(%rsp)这句话是将7和%rsp+8进行比较,但我们暂时还不知道他的作用是什么,但如果超过7就会跳转到炸弹爆炸函数
  • 继续往下看看,400f75: ff 24 c5 70 24 40 00 jmpq *0x402470(,%rax,8)这句话是这一关的关键,他会间接跳转,并且以%rax作为偏移量,跳转到0x402470+8*%rax,所以后面很工整的语句类似于switch语句
  • 回到第二点,应该就是表示第一个数字不能大于7,否则爆炸,而第二个数字的值是由第一个数字决定的

其他一些语句的意思

1
2
3
4
5
//判读sccanf函数的返回值是否大于1,如果大于1则继续,否则爆炸,这一点也肯定了我们要输入的是两个数
400f56: b8 00 00 00 00 mov $0x0,%eax
400f5b: e8 90 fc ff ff callq 400bf0 <__isoc99_sscanf@plt>
400f60: 83 f8 01 cmp $0x1,%eax
400f63: 7f 05 jg 400f6a <phase_3+0x27>

  • 接下来就是找出第二个数的值,可以通过看代码计算出来,可以切断点去查看1-7分别对应的数字,最终结果是0 207 || 1 311 || 2 707 || 3 256 || 4 389 || 5 206 || 6 682 || 7 327

phase_4

首先依旧是通过前几行去找出密码的形式是什么如图所以这一关的密码就是两个数字,之后通过调用scanf函数,并且检查返回值是否为2(即输入值的个数是否为2),之后连着的三条mov语句应该是设置func4函数的参数,我们设参数为a,b,输入值x,返回值为result,我们先跳过callq语句,接着往下看,检查返回值是否为0,比较第二个输入值是否为零,因此我们判断第二个数字就是0,接下来着重分析func4函数,其实该函数是递归函数,并将汇编代码大致翻译成如下的c语言版本

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
a in %edx,b in %esi,x in %edi,result in %eax,t in %ecx
func4(int a ,int b,int x)
{
int t,result; sub $0x8,%rsp
result = a; mov %edx,%eax
result -= b; sub %esi,%eax
t = result; mov %eax,%ecx
t = t >> 31; shr $0x1f,%ecx
result = result + t; add %ecx,%eax
result /= 2; sar %eax
t = result + b; lea (%rax,%rsi,1),%ecx
if(x <= t) cmp %edi,%ecx
{ jle 400ff2 <func4+0x24>
result = 0; mov $0x0,%eax
if(x >= t) cmp %edi,%ecx
return result; jge 401007 <func4+0x39>
else
b = t + 1; lea 0x1(%rcx),%esi
result = func4(a,b,x); callq 400fce <func4>
result = 2*result + 1; lea 0x1(%rax,%rax,1),%eax
}
else
{
a = t - 1; lea -0x1(%rcx),%edx
result = func4(a,b,x); callq 400fce <func4>
result = result*2; add %eax,%eax
}
return result;
}

初次调用fun4函数时为func(14,0,x),其中x为输入值
最后得到结果为7 0

phase_5

这一关还是有点难度,思路很难去想,也是看了好久的解题思路
首先早早就发现了这关的密码是长度为6的字符串,但是一开始以为密码就是单纯的保存在寄存器或者那个浅显的地址当中,导致但是看到两个明显的地址,就去尝试,如下图所示,但是显然这不是正确的密码这条线索很快就找到了,但是看不懂和密码之间的关系,后来就看到题解和ASCII码有关,就沿着这个思路去寻找答案

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
40
41
A[] in %rbx
int phase_5(int* A)
{
push %rbx
sub $0x20,%rsp
mov %rdi,%rbx
mov %fs:0x28,%rax
mov %rax,0x18(%rsp)
xor %eax,%eax
callq 40131b <string_length>
cmp $0x6,%eax
je 4010d2 <phase_5+0x70> if(A.length != 6)
callq 40143a <explode_bomb> explode_bomb
jmp 4010d2 <phase_5+0x70> int i = 0;
movzbl (%rbx,%rax,1),%ecx char c = A[i];
mov %cl,(%rsp) 取字符c的低四位
mov (%rsp),%rdx 0000-1111 即0-15
and $0xf,%edx
movzbl 0x4024b0(%rdx),%edx 找出字符串0x4024b0对应数字的字符
mov %dl,0x10(%rsp,%rax,1)
add $0x1,%rax i++;
cmp $0x6,%rax while(i<=6)
jne 40108b <phase_5+0x29>
movb $0x0,0x16(%rsp)
mov $0x40245e,%esi 将字符串flyers作为函数调用的参数
lea 0x10(%rsp),%rdi 将输入的字符串作为另外一个参数
callq 401338 <strings_not_equal> 比较两个参数是否相等
test %eax,%eax
je 4010d9 <phase_5+0x77>
callq 40143a <explode_bomb>
nopl 0x0(%rax,%rax,1)
jmp 4010d9 <phase_5+0x77>
mov $0x0,%eax
jmp 40108b <phase_5+0x29>
mov 0x18(%rsp),%rax
xor %fs:0x28,%rax
callq 400b30 <__stack_chk_fail@plt>
add $0x20,%rsp
pop %rbx
retq
}

看懂了反汇编代码,我们就要去找0x40245e"flyers"每个字母对应ASCII码的低四位,并根据这个去找在字符串0x4024b0"maduiersnfotvbyl对应的字母,最后答案有好几种,其中一个是ionefg

phase_6

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
push   %r14                        
push %r13
push %r12
push %rbp
push %rbx
sub $0x50,%rsp
mov %rsp,%r13
mov %rsp,%rsi
callq 40145c <read_six_numbers>
mov %rsp,%r14
mov $0x0,%r12d
mov %r13,%rbp
mov 0x0(%r13),%eax
sub $0x1,%eax
cmp $0x5,%eax
jbe 401128 <phase_6+0x34>
callq 40143a <explode_bomb>
add $0x1,%r12d
cmp $0x6,%r12d
je 401153 <phase_6+0x5f>
mov %r12d,%ebx
movslq %ebx,%rax
mov (%rsp,%rax,4),%eax
cmp %eax,0x0(%rbp)
jne 401145 <phase_6+0x51>
callq 40143a <explode_bomb>
add $0x1,%ebx
cmp $0x5,%ebx
jle 401135 <phase_6+0x41>
add $0x4,%r13
jmp 401114 <phase_6+0x20>
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>
mov $0x0,%esi
jmp 401197 <phase_6+0xa3>
mov 0x8(%rdx),%rdx
add $0x1,%eax
cmp %ecx,%eax
jne 401176 <phase_6+0x82>
jmp 401188 <phase_6+0x94>
mov $0x6032d0,%edx
mov %rdx,0x20(%rsp,%rsi,2)
add $0x4,%rsi
cmp $0x18,%rsi
je 4011ab <phase_6+0xb7>
mov (%rsp,%rsi,1),%ecx
cmp $0x1,%ecx
jle 401183 <phase_6+0x8f>
mov $0x1,%eax
mov $0x6032d0,%edx
jmp 401176 <phase_6+0x82>
mov 0x20(%rsp),%rbx
lea 0x28(%rsp),%rax
lea 0x50(%rsp),%rsi
mov %rbx,%rcx
mov (%rax),%rdx
mov %rdx,0x8(%rcx)
add $0x8,%rax
cmp %rsi,%rax
je 4011d2 <phase_6+0xde>
mov %rdx,%rcx
jmp 4011bd <phase_6+0xc9>
movq $0x0,0x8(%rdx)
mov $0x5,%ebp
mov 0x8(%rbx),%rax
mov (%rax),%eax
cmp %eax,(%rbx)
jge 4011ee <phase_6+0xfa>
callq 40143a <explode_bomb>
mov 0x8(%rbx),%rbx
sub $0x1,%ebp
jne 4011df <phase_6+0xeb>
add $0x50,%rsp
pop %rbx
pop %rbp
pop %r12
pop %r13
pop %r14

输入的六个数字有以下条件

  • 每个数字都不能大于6并且都大于0
  • 六个数字各不相同
  • 前五个数字做7-x反转