一个类似于破解的初级实验。用到的gdb的指令并不多,只是基础的使用和内存查看的指令。考的大多是汇编代码的熟练程度和分析能力。不过有几个函数长的让人吐血。本着不轻易爆炸的原则,只好慢慢调
。
1. 反汇编bomb
用objdump直接反汇编出汇编代码。
objdump -d ./bomb > bomb.s
可以看到以phase开始的八个函数,其中有1-6 6个必过关卡,还有一个defused是检测是否触发了进入secret的函数,而secert就是隐藏的boss了。按照顺序来吧。
2. phase_1
第一个函数的代码很简单,看到strings_not_equal()函数的调用就能大概知道参数应该是一个字符串。此外可以看到两个push将参数入栈,是为strings_not_equal()准备的。根据函数名,可以知道这个函数是在比较两个字符串是否相等,所以push的很有可能就是一个答案字符串所在地址(如果是程序自带的变量包括字符串等都会在.rodata部分,所以压栈时会直接压入对应地址),另一个来自标准输入的字符串地址。

图1 phase_1部分语句
那么接下来就可以进入gdb,加载bomb,在phase_1处设置断点并运行,任意输入一个字符串:
图2 设置断点并进行测试
图3 检查入栈的参数
很明显可以看到eax内容为来自标准输入的参数的地址,而直接入栈的地址很有可能就是答案。直接kill掉并重新加载bomb,在0x8048b3a处设置断点,由图4所示代码可以看出如果eax为0则通过。运行并输入测试字符串:

图4 phase_1检测部分代码

图5 测试字符串运行结果
如图 $eax==0 , 所以第一个关卡的答案就是 ”Public speaking is very easy.” 了。
3. phase_2
观察phase_2,可以看到如下片段:

图6 phase_2部分代码
入栈eax和edx可以看出是两个地址,而read_six_numbers可以看出这个关卡的答案是6个数字。所以就用1 2 3 4 5 6 来进行测试,并read_six_number()函数运行前后对参数进行追踪和对比:

图7 进行phase_2测试
图8 read_six_number()函数运行前
图8 read_six_number()函数运行后
对比可以看到输入的6个数字参数被存在以0xffffd0e0开始的24个字节中。接下来有如下代码:
图9 phase_2的第一个炸弹点
可以看到-18($ebp)是第一个数字的地址,而判断语句就是判断第一个数字是不是1,不是则爆炸。幸运的是测试的数字第一个数字是1,所以可以继续运行:
图10 运行到0x8048b73
图11 对phase_2核心代码的初步解释
如图11所示,由于ebp的值为0xffffd0f8,把输入的6个数字看作数组num[6],则可以将所示代码总结为以下C代码:
图12 phase_2的等价形式
根据图10 和11推断出第一个数字一定是1,根据循环算出后面的数字分别是1*2,2*3,6*4,24*5,120*6,即1 2 6 24 120 720。按照推断出的答案进行测试,在bomb_explode函数处设置断点,如果错误就可以直接Kill重新运行而不会爆炸:
图13 测试phase_2
可以看到并未触发断点,则结果正确。
4. phase_3
首先当然还是先观察phase_3的汇编代码,看是不是调用了有关输入的参数的函数:
图14 phase_3参数的读取部分
首先,将三个地址参数入栈,其中的数据大小为32位,4位,32位,则中间一个参数为单个字符型。在sscanf函数调用后检查$eax,因为sscanf在参数匹配成功后会将匹配成功的参数的个数放入eax中返回,所以检查eax是否大于2,即至少应匹配三个参数才能过第一个爆炸点。