【CSAPP】Bomblab 详解

Lab要求:

使用gdb获取程序内部信息,理解由反汇编器生成的汇编代码

实验机器:

VMware的虚拟机——Ubuntu-20.04

实验材料:

CSAPP的官网selfstudy-handout:
点击以进入CMU提供的下载渠道
一定记得看Writeup!
在这里插入图片描述

Phase_1

在这里插入图片描述
注意到phase_1函数带了一个参数进入,保存在%rdi 中,然后给了%esi 赋了一个值,随后进入了一个叫string_not_equal的函数,合理猜测这函数就是字面意思。
先在函数进入前,阻拦一下,感觉0x402400应该就是要比较的字符串的首地址
在这里插入图片描述
去看一眼:
请添加图片描述
看到结果基本确定正确了,验证一下也确实正确。
在这里插入图片描述

Phase_2

在这里插入图片描述
同样地,phase_2也带了一个参数进入,又使用了一个%rsi 作为参数二,进入了一个 read_six_number的函数,同样怀疑是否就是字面意思。

请添加图片描述
值得注意的是,无论在命令行中输入了什么,都会以字符串的形式先保存,而这也是为什么又调用了read_six_numbers,在read_six_lines 中调用了scanf将字符串转换为int,再往图片的下部看发现调用的格式控制字符串是"%d %d %d %d %d %d"。

再想,读成数字保存在哪里?由于输入了两个参数,参数二是用%rsp 赋值的,sp是stack pointer的简写,显然是一处内存地址。
在这里插入图片描述
先输入一个"1 2 3 4 5 6"作为测试
在这里插入图片描述在这里插入图片描述
在函数的出口设一个断点
在这里插入图片描述
查看进入函数时用于赋值参数二的%rsp,发现我的输入果然是保存在栈中。

请添加图片描述

首先发现,输入的第一个数必须为 1,不然就 call exlplode_bomb.
随后进入了一个以rbx为搜索指针,以rbp为结尾信号的循环,
循环体是,将%rbx的上一个数乘以二,与%rbx指向的数作比较,即:

if(input[i] != input[i-1] * 2)  explode_bomb();

鉴于第一个数为1,每个数是上一个的2倍,且有6个数,
所以答案是"1 2 4 8 16 32"。
在这里插入图片描述

Phase_3

在这里插入图片描述

在这里插入图片描述
和之前的方法类似直接查看scanf的格式控制字符串,又在%rdi中地址所在内存找到了自己的输入。

圈1:scanf的返回值要大于1,不然BOOM!
圈2:input[0]要<=7,不然BOOM!
圈3:这是一个 地址跳转 指令!

到这里应该不难看出,答案不止一个,我们可以不断的修改%rip,在跳转指令执行前修改%rax值,再执行以查看跳转目的地。

  • Input[0] = 1,则:
    input[1]必须等于 0x137 = 311(dec)
    在这里插入图片描述
    在这里插入图片描述
  • Input[0] = 2,则:
    input[1]必须等于 0x2c3 = 707(dec)
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    太无聊了,就不继续了,但反正 input[0]=0、1…7 都是可以有答案的。
    以1为例:
    在这里插入图片描述

但值得一提的是,负数不行,编译器使用了一个很聪明的ja指令。

Phase_4

在这里插入图片描述
总之和 Phase_3 很像,也是输入两个数字,就不展开了,仍然是0x8(%rsp)里存input[0],0xc(%rsp)里存input[1]。

圈1:input[0] <= 14,和上一个Phase一样,负数是禁止的,编译器使用了一个jbe对一个signed int作unsigned int比较,这是一种比较隐蔽的技巧以剔除负数。
圈2:进入 func4 时,带了三个参数,参数一:input[0]、参数二:0、参数三:14。
圈3:离开func4之后才检查input[1],说明func4确实只与input[0]有关,正如参数表示的那样。
圈4:func4的返回值必须是0,不然就jump to BOOM!

在这里插入图片描述Phase_4 是肯定有其他答案,但func4的迭代我感觉挺难看出它的用意的,本着详解的原则我尝试把它翻译成了C语言:
在这里插入图片描述
所以只有3个答案,“1 0”、“3 0”、“7 0”
在这里插入图片描述
没有问题!

Phase_5

Phase_5汇编代码太长,我将程序分为了几个连续发生的阶段。

Stage 1

在这里插入图片描述
圈1:这段代码是canary,是一种栈保护机制,是编译器对你程序的附加部分。
圈2:因为很有可能string_length与phase_5的参数一致,就不需要额外加参数。而且,我的输入必须为6个字符。

Stage 2

在这里插入图片描述

代码的上一部分是对我的输入作出一些变化,但你会注意到变化遵循固定规定,他并不随机。
紧接着,程序就把 0x40245e作为参数二,我的输入作为参数一,进入了string_not_equal, 就考虑这是字面意思,那参数二应该就是目标对比物。
在这里插入图片描述
很快发现,目标对比物为"flyers",但注意,我的输入需要在经由上一步的变化后,变为"flyers"。
其实不需要知道程序的具体编码规则,只需要找出每个字母会变化成什么就OK了:
在这里插入图片描述
就以上述方式,5次就能确定编码规则:
% abcdef ghijkl mnopqr stuvwx yz
% aduier snfotv bylmad uiersn fo
上面是源,下面是密文
所以 “ionevw”->“flyers”
在这里插入图片描述
没得问题。

Phase_6

phase_6的解答也比较复杂,同样地,我把程序分了几个连续发生的阶段,在分别解释这几个阶段的内容:

Stage 1

在这里插入图片描述

和上几个阶段类似,read_six_numbers会从我的输入中,读取6个数字将我输入的数组按顺序向上储存,首地址是%rsp。

Stage 2

注意下 %r13里保存的是%rsp(in stage 1)
在这里插入图片描述
这是一个二层循环,长虚线是外层,短虚线是内层。
大概是:

for(int i=0;i!=6;i++){
	if(input[i]>6) explode_bomb();
	for(int j=i+1;j<=5;j++){
		if(input[i]==input[j]) explode_bomb();
	}
}

Stage 3

注意%r14中保存的是%rsp(in stage 1)

在这里插入图片描述
相当于:

for(int i=0;i<6;i++){
	input[i]=7-input[i];
}

Stage 4

我觉得bomblab中最难的部分就是这里,我无数次不解这个0x6032d0是在干什么。

在这里插入图片描述

进入前:
在这里插入图片描述
注意看这是一个应该是作者设置的类——node,它是按1,2…的顺序依次连接。

进入后:
在这里插入图片描述
然后我就看到了,1、2…6的序号不变,但指针的指向已经变了,这段数据,就变成了以 node4 为首的类链表。
在这里插入图片描述
可以看到,链表的首节点已经保存于 0x20(%rsp)

值得注意的是, 0x4011d2 处的指令是给尾结点赋NULL,虽然这个指令没执行,但效果还正常,这是因为我的尾结点就是node6,与原值一致,这是小意外。

Stage 5

可以发现,这个lab最烦人的点在于,对我的输入做了很多的变化,但只在最后做检查。
Stage 5 就是对 phase_6 的最终检查:

在这里插入图片描述

  • 注意 %rbx中保存了链表首节点:
    在这里插入图片描述
    有意思的是,这几个nodevalue都不是它的序号。

  • 圈1是说,对于每个 i,要有input[ i ]>input[ i+1 ]

所以,要使节点值递减,得调整一下:
注意到,即使链表变了,但序号对应的值不变:
在这里插入图片描述
0x39c > 0x2b3 > 0x1dd > 0x1bb > 0x14c > 0xa8
node3 > node4 > node5 > node6 > node1 > node2
考虑到数据会被倒转一次,所以最终的输入为 “4 3 2 1 6 5”

在这里插入图片描述
完结撒花!

  • 5
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值