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]。
后续是复杂的链表排序…这个有时间再看。答案是:
|  |  |