Bomblab
首先通过以下指令得到可执行文件的汇编:
|
|
启动gdb进行debug,为了方便,我们将所有的字符串写在一个文件中,具体操作流程如下(按esc进入代码界面,按i进入调试界面):
PA 1
首先看一下main函数中调用phase_1的过程:
|
|
回忆$rax
存储函数返回值,$rdi
存储函数的第一个参数。根据上下文可知这里传递的是字符串,我们可以通过实时查看汇编确认这一点:
此后我们再查看phase_1的汇编:
|
|
这里将输入的字符串$rsp
作为第一个参数,$0x402400
中的值传给$rsi($esi)
作为第二个参数,后送入strings_not_equal进行比较,返回值存储在$rax($eax)
中。如果不相等,函数不会跳转,从而调用explode_bomb函数,炸弹破解失败。关键是要找出$0x402400
中的值:
据此,第一题的答案为:
|
|
PA 2
查看phase_2的汇编:
|
|
首先函数会调用read_six_numbers:
|
|
首先观察到sscanf这个函数,之后看到$0x4025c3
这个特殊的地址值,打印其值:
这提示了我们的输入要求。那输入的6个值又被存放在哪里?
在调用完毕read_six_numbers之后查看栈空间,可见最开始输入的数位于栈顶、其他依次向后排。输入的六个数是逆序入栈,第一个数最后入栈,为栈顶。
再看比较部分的代码:
|
|
首先比较第一个值与1是否相等,如果不相等就会直接引爆炸弹,如果相等,执行以下语句:
|
|
第一句将栈指针偏移,指向下一个数;第二句复制栈底值(用于判断何时结束循环);跳转过后执行以下语句:
|
|
首先提取出$rbx
的前一个值,复制给$eax
,再将其乘2,再与当前的$rbx
比较。可见比较的要求是后一个数是前一个数的2倍,直到6个数循环完毕。据此可以得到答案:
|
|
PA 3
|
|
PA3同样调用了scanf,打印$0x4025cf
的值发现应该需要输入两个数:
运行到cmpl $0x7,0x8(%rsp)
这步时,查看栈上的值:
这时栈顶的两个值恰好是我们的两个输入值。根据cmpl $0x7,0x8(%rsp)
可知,第一个值不能大于7;再根据之后的jmpq *0x402470(,%rax,8)
可知,这里以$rax*8 + 0x402470
为读地址,从内存中读出跳转目标。
第一个值被赋给$rax
,跳转的位置与$rax
本身的值密切相关,有必要查看0x402470
附近的值分布:
这样就可以建立起$rax
值和跳转地址之间的映射关系,而不同的跳转值又对应着不同的比较值,据此可以确定第二个输入值:
$rax |
jump address | compare var |
---|---|---|
0 | 0x00400f7c | 0xcf |
1 | 0x00400fb9 | 0x137 |
2 | 0x00400f83 | 0x2c3 |
3 | 0x00400f8a | 0x100 |
4 | 0x00400f91 | 0x185 |
5 | 0x00400f98 | 0xce |
6 | 0x00400f9f | 0x2aa |
7 | 0x00400fa6 | 0x147 |
8个答案中任选一个即可。
|
|
PA 4
PA 4的核心代码设计到两个部分:
|
|
|
|
首先我们仍然需要输入两个值:
在func4
中,我们看到代码再一次调用了函数本身,说明这是一个递归函数。首先,根据上一题的经验,输入的两个值分别储存在0x8($rsp)
中和0xc($rsp)
中,据此可以定位判断输入是否合法的代码:
|
|
据此可以直接判断出第一个数应该小于等于14,第二个数为0,而且func4
的返回值一定为0。
调用func_4
时,寄存器使用情况如下:
然后看func4
,$rdi,$rsi,$rdx,$rcx,$r8,$r9
这几个寄存器中出现的只有前4个,而只有前3个作为原数据,剩下1个是中间变量,而且整个函数没有出现$rax
的更新,据此可以推断该函数的类型是void (int, int, int)
。
转写后的c代码如下:
|
|
phase_4
中考察的是$eax
的值,我们只需要关心t的变化。注意到x
和y
分别是输入的两个值(y已经确定为0),而z
是固定值14。显然递归的终止条件是k=x
,此时t=0
。我们不妨让func4
只执行一次,简单计算可得x=7
。于是得到最终答案:
|
|
PA 5
|
|
首先注意到__stack_chk_fail
这个函数,说明栈中含有“金丝雀值”(当然,这一点和题目本身没有太大关系)。
这一段要求字符串的长度必须是6:
|
|
随后在movzbl (%rbx,%rax,1),%ecx
这一句中,
|
|
这段汇编实际上是一段循环,将其翻译为C代码:
|
|
array
数组如下:
循环结束之后,查看$0x4024b0
的值与所获得的字符串是否相同。目标字符串是:
据此我们可以列出一个表来查找所有符合比较条件的值:
target val | shift in array (input[i] & 0b1111) | ascii of input[i] |
---|---|---|
f | 9 | 41, 57, 73, 89 |
l | 15 | 47, 63, 79, 95 |
y | 14 | 46, 62, 78, 94 |
e | 5 | 37, 53, 69, 85 |
r | 6 | 38, 54, 70, 86 |
s | 7 | 39, 55, 71, 87 |
之后从表格里排列组合即可,一个结果是:
|
|
PA 6
PA 6涉及到链表这一数据结构,略麻烦,直接给出汇编的解析吧:
|
|
我们逐个逐个循环分析这段代码。读入6个整数之后:
|
|
第一个炸弹的触发条件是数组中有数字小于6,第二个是每个数字都不相等。
|
|
这段是将输入的六个值a[i]
转换为7 - a[i]
。
后续是复杂的链表排序…这个有时间再看。答案是:
|
|