一.承接上一篇文章,接下来是解决bomb的phase_2的问题
首先我们看看phase_2的汇编代码,上文已经提到了反汇编指令disas (dis assemble) phase_2(函数名)
其中有个函数名是否值得注意,<read_six_numbers>,显然,这个函数需要我们读入6个数字作为密码。
(作者一开始在0x400f0e处设置过断点,但是在run时,炸弹仍然爆炸了,处于好奇,我们可以打开read_six_numbers函数看看,里面的内容)
果不起然,里面有个 explode_bomb,表明在考量数字数量的环节也存在引爆此炸弹的风险,由于此函数名称<read_six_numbers>过于露骨,稍微尝试一下,就会发现只要你输入6个数字,炸弹在这个函数体内就不会被引爆。
当然,秉承学习的态度,作者决定观察一下这个函数体,当然还是从<explode_bomb>开始看,我们会发现关键在于上两行的两条指令
cmp $0x5,%eax
jg <read_six_numbers+61>
很显然,只要%eax的contents大于5即可,我们不妨输入7个数字观察一下。
我们在phase_2中,read_six_numbers后面的0x400f0e处设置一个断点,然后运行程序,看看炸弹会不会因为函数体<read_six_numbers>爆炸。
果然和我们猜想一样,程序并没有爆炸,所以第一个“引线”其实很简单,就是考虑你的输入是否有大于5而已。
二.我们正式的探讨一下phase_2的函数体,为避免无谓的浪费时间,我们当然知道真正的数字数目只有6个。不妨先设置1,2,3,4,5,6代入程序逐步分析。
显然 <read_six_numbers>及以上的汇编代码可以不用看了,其目的无非就是检查输入的数字是否大于5个而已,我们从紧接着下面的汇编代码开始看
cmpl $0x1,(%rsp)
je xxxx
call <explode_bomb>
着三行代码就大有深意了。首先是非常敏感的爆炸函数,显然如果这次compare错误,程序就直接终结了,我们观察一下compare的内容,%rsp是栈顶指针,(%rsp)显然是在引用内存的数据了,而另一个$0x1我们知道这是一个立即数,稍加思考,我们就能明白,我们输入的数据肯定不可能是以立即数的姿态出现,(否则bomb maker真的是未卜先知了),我们输入的数据势必是存放在memory里面的,而这里避免炸弹爆炸的条件就是 equal,所以第一个数字密码无疑就是立即数1了。
很幸运,我们猜对了,接下来我们继续逐步执行程序。
现在执行指令跳离了引爆函数,进入了一个有效地址加载指令,其中(%rsp)是显然的栈顶指针,这两条lea指令很有可能是要去检索我们一开始放在堆栈里面的数据,而且int型变量又本身是4个字节,这里的地址跳跃又是4个字节,相当之可疑,我们可以看看内存里0x4(%rsp)的内容。
果然不出我们所料,存放的正好就是我们输入的1,2,3,4,5,6中的2。
而第二条lea 0x18(%rsp) ,%rbp 这条指令又是什么用意呢?
(一开始我把这个18当成10进制了,结果就很疑惑18并不是4-bytes的倍数呀?这里是0x18,换算成十进制其实是24,也就正好是6个int 型变量的大小)。
经过上面分析,基本可以确定这是一个结束判别指令了。
与此同时我们心中对输入数据的存放大概也可以有一个比较抽象的认识了。
我们心中有了这样一个抽象的认识之后,问题瞬间变得明朗起来。我们继续逐步执行指令,
经过junp指令回到了上面,我们发现首先是一条mov指令,其内容在我们有了这样一个相对抽象的认识之后,就非常直观,即把栈顶的数据赋给%eax,接下来立刻就是double it 的操作,我们已经知道栈顶的正确元素就是1,那么显然第二个数字密码就是立即数2.其实我们看到这里,基本已经能直接阅读汇编代码了,甚至都不用调试了。
作者来带观众读一读,紧接着上一段,
cmp %eax,(%rbx),我们已经知道,(%rbx)存放的就是我们输入的第二个数据,显然就是把我们输入的第二个数据和数字
‘2’比较了。到这里为止,我们的运气仍然非常好,也是对的,我们沿着代码读下去。
到这里位置我们尚未引爆炸弹,接下来就是%rbx+4,我们知道栈的结构,这里是想去读我们输入的第三个数据,接下来的
cmp %rbp,%rbx,我们不用思考知道这是在检查%rbx有没有越过%rbp,当两者相等时,即检查完这6个数字密码了,可以终结函数了。
这里作者先告诉大家正确的密码是1 2 4 8 16 32,但比较有意思的是,由于bomb maker设计的检查函数只检查6个数字,也就意味着我们就算输入7个数字,只要前6位正确,bomb 依旧不能爆炸。不信?实验一下吧
服了吧~
接下来作者解释一下,为什么是1,2,4,8,16,32是正确的数字密码。
其实很简单,我们继续之前的分析,在cmp %rbp,%rbx 之后not equal的情况下,我们目光随着jump回到之前的函数,由于之前%rbx已经加4bytes了,显然此时指向的是memory里面的第三个数据,然后重复之前的步骤,取出第三个数据比较是不是第二个数据的两倍。
我们说简单点,这个phase想干啥,其实就是这样的
第一个数据必须是1,第二个数据得是第一个数据的两倍,第三个数据得是第二个的两倍,以此类推到第六个数据,第七个以及以上的数据无所谓,随便写的ok,之后的数学推导就是小学级别的了。
作者明天会更新phase_3的解法。
希望此文对后来学习csapp的童鞋有帮助~