本文主要内容:1.简单介绍实验网站和实验背景。2.详细解析我在Bomb Lab里最后一个“炸弹”中所做的过程和解法
前段时间,同学推荐说CMU的这一套实验不错。于是,我就抽空开始做了。网址:http://csapp.cs.cmu.edu/3e/students.html
点开网站:
进入到实验页面:http://csapp.cs.cmu.edu/3e/labs.html
“README”文件详细叙述了各个文件的作用与实验流程。不过本实验只要重点看“Writeup”文件就可以了,里面详细解释了实验的目的,要求,步骤和建议。点击“Self-Study Handout”获得本实验要“拆的炸弹”,即一个二进可执行文件,该文件只能在x86_64的linux下运行,i386不可以。
任务:运行这个可执行程序,程序会要求我们依次输入6行短语,每输对一行,则对应“炸弹”被拆除。一旦输错一行,则对应的炸弹爆炸。短语没有任何格式的限定,可以是句子,字母,数字,一个或多个等等。
讲义里建议我们使用gdb 和 objdump这两个工具。本实验的目的主要是让我们熟悉gdb的使用,加深对程序运行的内存结构的理解。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
在gdb中我们只能获得程序框架的源代码,而真正执行校验的phase_1、phase_2、、、phase_6函数是分开编译的,我们取不到他们的源代码。只能通过objdump获得符号表,查看到一些全局符号的名字和地址。不过还好,程序框架帮助我们可以轻松定位程序。我们直接使用gdb的disas命令反汇编相应函数的二进制机器码,在汇编代码段中设置断点,通过分析内存和cpu寄存器数值来分析程序状态,从而获知程序的功能。
-
这个小游戏断断续续,零零散散解了一个星期,今天下午最后一个终于做出来了。前面的炸弹怎么解我已经忘记大半了。第一个非常简单,里面直接调用了字符串比较的库函数,所以,断点设在那个库函数的汇编代码开头,然后查看寄存器的值,得到两个参数的值,即两个字符串地址(这个库函数参数是指针),然后根据地址去内存查就可以了。可以看到一个是你输入的短语,另外一个就是与之比较的正确短语了。前五个炸弹的答案:
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 炸弹的难度是递增的,”the last phase will challenge even the best students“。我们这里直接讲最后一个,最后一个炸弹基本包含了前面所有炸弹所用到的东西。
利用解决第一个炸弹的方法,我们可以看到我们需要输入6个整数。
只要输入6个整数,我们就可以安全通过read_six_numbers函数。
可以看到read_six_numbers函数已经把我们输入的字符串转换成数值放在程序栈上了。
接下来phase_6 <35>到<93>是一个大循环,包含了<65>到<87>的小循环,这一段好像没什么技巧可用。只能硬撸汇编。大循环是轮询每一个数,检验其不能大于6。小循环嵌套在大循环里面,检验这个数是否与排在它后面的数相等。所以:我们保证输入6个小于6的不同整数,我们就可以安全通过。(里面将数都当成无符号处理了,输入负数是”自取其辱“,?)
接下来<108>到<121>是一个简单循环,作用是把我们输入的数和7作差,例如如果我们输入的是1 5 3 2 6 4,则会得到6 2 4 5 1 3。
从<130>到<181>这个循环可能是最难看出来的。这个循环的作用是把首地址为0x6032d0的链表的各个节点地址以一定的顺序放在rsp+0x20的所指内存后面。循环扫描rsp所指内存后面24字节的6个整数,小于等于1的,则往rsp+0x20到rsp+0x50空间对应位置写入首地址为0x6032d0(即第一节点地址)。大于1的,则从首节点寻溯到对应的节点,将其节点地址写入rsp+0x20到rsp+0x50空间的对应位置。(tips:有时候用16进制打印内容会非常清晰明了,开始的时候我用十进制,完全看不出来链表,浪费了时间精力。。。)
接下来的代码:
<+201>到<+220>的循环是重新整理链表,新链表按照rsp+0x20到rsp+0x50空间存储的6个节点指针地址依次链接起来。
接下来是最后一个循环!
从<+235>到<+257>的循环是对链表进行校验,前面节点的值要大于等于后面节点的值。里面的rbx+0x8就是在取next 指针的操作了。
由于我们输入的是输入的是1 5 3 2 6 4,和7做差得到6 2 4 5 1 3。得到链表的值是000001bb、000000a8、000002b3、000001dd.....................(注意它只取4字节) 。没有满足降序,所以按c(continue)如下图:BOOM!
6个节点的降序排法应该是这样0000039c 、000002b3、 000001dd、 000001bb 、 0000014c、000000a8 。对应老链表的第3、第4、第5、第6、第1、第2节点。也就是说栈指针rsp所指的那片内存的24字节的6个整数应该要是3 、4、5、6、1、2。也就是说我们应该输入没和7做差之前的数:4、3、2、1、6、5
haha!可以了!