bomblab
phase_1
反汇编后
0000000000001204 <phase_1>:
# 指令将栈指针寄存器减 8,作用是给栈空间分配 8 字节
1204: 48 83 ec 08 sub $0x8,%rsp
# lea 是 “load effective address” 的缩写,简单的说,lea 指令可以用来将一个内存地址直接赋给目的操作数
# 从键盘输入六个整数,用空格分开
1208: 48 8d 35 c1 15 00 00 lea 0x15c1(%rip),%rsi# 27d0 <_IO_stdin_used+0x150>
120f: e8 a9 04 00 00 callq 16bd <strings_not_equal>
1214: 85 c0 test %eax,%eax
1216: 75 05 jne 121d <phase_1+0x19>
1218: 48 83 c4 08 add $0x8,%rsp
121c: c3 retq
121d: e8 a7 05 00 00 callq 17c9 <explode_bomb>
1222: eb f4 jmp 1218 <phase_1+0x14>
解题过程:
- 打断点到 phase_1 这一函数体
- layout regs 更换界面
- run 启动程序
- 此时断点会到函数内第一行
- si 分步骤执行到 lea 指令,我们可以看到寄存器地址是 0x5555555567d0
- 下图有个 strings_not_equal 函数,推测是比较字符串,所以看看 0x5555555567d0 存放了什么字符串去和我输入的字符串进行比较,用 x /s 0x5555555567d0 取值
- 可以看到寄存器输出了一段英文,这就是需要比较的字符串。
I can see Russia from my house!
phase_2
0000000000001224 <phase_2>:
# 存放寄存器状态
1224: 55 push %rbp
1225: 53 push %rbx
# 分配局部变量空间
1226: 48 83 ec 28 sub $0x28,%rsp
122a: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
1231: 00 00
1233: 48 89 44 24 18 mov %rax,0x18(%rsp)
1238: 31 c0 xor %eax,%eax
# 把栈指针状态存到寄存器rsi
123a: 48 89 e6 mov %rsp,%rsi
# 进入函数 read_six_number-> 00000000000017ef
123d: e8 ad 05 00 00 callq 17ef <read_six_numbers>
# 判断第一个元素是否小于0
1242: 83 3c 24 00 cmpl $0x0,(%rsp)
1246: 78 0a js 1252 <phase_2+0x2e>
1248: bb 01 00 00 00 mov $0x1,%ebx
124d: 48 89 e5 mov %rsp,%rbp
1250: eb 11 jmp 1263 <phase_2+0x3f>
1252: e8 72 05 00 00 callq 17c9 <explode_bomb>
#for i=1 i!=6 ++1
1257: eb ef jmp 1248 <phase_2+0x24>
//当索引自增1
1259: 48 83 c3 01 add $0x1,%rbx
//当索引为6,跳出循环
125d: 48 83 fb 06 cmp $0x6,%rbx
1261: 74 13 je 1276 <phase_2+0x52>
//取当前数组位值
1263: 89 d8 mov %ebx,%eax
// 4rbx+rpb地址-0x4 + eax
1265: 03 44 9d fc add -0x4(%rbp,%rbx,4),%eax
// 4rbx+rpb地址-0x0 和eax比 就是数组当前位比上数组前一位的值加上数组索引
1269: 39 44 9d 00 cmp %eax,0x0(%rbp,%rbx,4)
//相等就回到循环
126d: 74 ea je 1259 <phase_2+0x35>
//不相等就爆炸
126f: e8 55 05 00 00 callq 17c9 <explode_bomb>
1274: eb e3 jmp 1259 <phase_2+0x35>
1276: 48 8b 44 24 18 mov 0x18(%rsp),%rax
127b: 64 48 33 04 25 28 00 xor %fs:0x28,%rax
1282: 00 00
1284: 75 07 jne 128d <phase_2+0x69>
1286: 48 83 c4 28 add $0x28,%rsp
128a: 5b pop %rbx
128b: 5d pop %rbp
128c: c3 retq
128d: e8 be fb ff ff callq e50 <__stack_chk_fail@plt>
解题过程:
- 进入 gdb 打断点打到 phase_2
- 执行完 read_six_number
- 继续往下走,发现到循环里了
- 不难看出这里是数组当前位与数组前一位的值加上数组索引的值相比较,不相等就炸。
- 输入 1 2 4 7 11 16
6.答案正确,跳回主函数
phase_3
0x0000000000001292 <+0>: sub $0x18,%rsp
0x0000000000001296 <+4>: mov %fs:0x28,%rax
0x000000000000129f <+13>: mov %rax,0x8(%rsp)
0x00000000000012a4 <+18>: xor %eax,%eax
0x00000000000012a6 <+20>: lea 0x4(%rsp),%rcx
0x00000000000012ab <+25>: mov %rsp,%rdx
0x00000000000012ae <+28>: lea 0x16da(%rip),%rsi # 0x298f
# 输入两个整数,以空格分开
0x00000000000012b5 <+35>: callq 0xef0 <__isoc99_sscanf@plt>
# eax = 2
# eax小于等于1就炸
0x00000000000012ba <+40>: cmp $0x1,%eax
0x00000000000012bd <+43>: jle 0x12d8 <phase_3+70>
# rsp第二个数 比上7 大于7就炸
0x00000000000012bf <+45>: cmpl $0x7,(%rsp)
0x00000000000012c3 <+49>: ja 0x1310 <phase_3+126>
0x00000000000012c5 <+51>: mov (%rsp),%eax
0x00000000000012c8 <+54>: lea 0x1551(%rip),%rdx # 0x2820
# 扩展从2位变4位地址
0x00000000000012cf <+61>: movslq (%rdx,%rax,4),%rax
0x00000000000012d3 <+65>: add %rdx,%rax
0x00000000000012d6 <+68>: jmpq *%rax
0x00000000000012d8 <+70>: callq 0x17c9 <explode_bomb>
0x00000000000012dd <+75>: jmp 0x12bf <phase_3+45>
0x00000000000012df <+77>: mov $0x3e0,%eax
0x00000000000012e4 <+82>: jmp 0x1321 <phase_3+143>
0x00000000000012e6 <+84>: mov $0x13a,%eax
0x00000000000012eb <+89>: jmp 0x1321 <phase_3+143>
0x00000000000012ed <+91>: mov $0x1ee,%eax
0x00000000000012f2 <+96>: jmp 0x1321 <phase_3+143>
0x00000000000012f4 <+98>: mov $0x4d,%eax
0x00000000000012f9 <+103>: jmp 0x1321 <phase_3+143>
0x00000000000012fb <+105>: mov $0x3a,%eax
0x0000000000001300 <+110>: jmp 0x1321 <phase_3+143>
0x0000000000001302 <+112>: mov $0x22c,%eax
0x0000000000001307 <+117>: jmp 0x1321 <phase_3+143>
0x0000000000001309 <+119>: mov $0x8d,%eax
0x000000000000130e <+124>: jmp 0x1321 <phase_3+143>
0x0000000000001310 <+126>: callq 0x17c9 <explode_bomb>
0x0000000000001315 <+131>: mov $0x0,%eax
0x000000000000131a <+136>: jmp 0x1321 <phase_3+143>
0x000000000000131c <+138>: mov $0x3c2,%eax
// 和扩容地址比较
0x0000000000001321 <+143>: cmp %eax,0x4(%rsp)
0x0000000000001325 <+147>: je 0x132c <phase_3+154>
0x0000000000001327 <+149>: callq 0x17c9 <explode_bomb>
0x000000000000132c <+154>: mov 0x8(%rsp),%rax
0x0000000000001331 <+159>: xor %fs:0x28,%rax
0x000000000000133a <+168>: jne 0x1341 <phase_3+175>
0x000000000000133c <+170>: add $0x18,%rsp
0x0000000000001340 <+174>: retq
0x0000000000001341 <+175>: callq 0xe50 <__stack_chk_fail@plt>
- 看 35 行代码不难发现,这里需要输入两个以空格间隔的整数。
其中,40-49 行:
- 判断输入的第一个数是否小于等于 1,符合则爆炸。
- 判断输入的第一个数是否大于 7,符合则爆炸
- 这里可以推测输入第一个数是 1
- 这里可以得知我们输入的第二个数可以是 992
输入 1 992,phase_3 通过
phase_4
0x0000000000001385 <+0>: sub $0x18,%rsp
// rax = 40
0x0000000000001389 <+4>: mov %fs:0x28,%rax
// rsp + 8 = 40
0x0000000000001392 <+13>: mov %rax,0x8(%rsp)
0x0000000000001397 <+18>: xor %eax,%eax
0x0000000000001399 <+20>: lea 0x4(%rsp),%rcx
0x000000000000139e <+25>: mov %rsp,%rdx
// 输入两个整数
0x00000000000013a1 <+28>: lea 0x15e7(%rip),%rsi # 0x298f
0x00000000000013a8 <+35>: callq 0xef0 <__isoc99_sscanf@plt>
//比较数组数值是否为2不是就炸
0x00000000000013ad <+40>: cmp $0x2,%eax
0x00000000000013b0 <+43>: jne 0x13b8 <phase_4+51>
0x00000000000013b2 <+45>: cmpl $0xe,(%rsp)
0x00000000000013b6 <+49>: jbe 0x13bd <phase_4+56>
0x00000000000013b8 <+51>: callq 0x17c9 <explode_bomb>
0x00000000000013bd <+56>: mov $0xe,%edx
0x00000000000013c2 <+61>: mov $0x0,%esi
0x00000000000013c7 <+66>: mov (%rsp),%edi
0x00000000000013ca <+69>: callq 0x1346 <func4>
//调用完func4,eax需要等于3
0x00000000000013cf <+74>: cmp $0x3,%eax
0x00000000000013d2 <+77>: jne 0x13db <phase_4+86>
//
0x00000000000013d4 <+79>: cmpl $0x3,0x4(%rsp)
0x00000000000013d9 <+84>: je 0x13e0 <phase_4+91>
0x00000000000013db <+86>: callq 0x17c9 <explode_bomb>
0x00000000000013e0 <+91>: mov 0x8(%rsp),%rax
0x00000000000013e5 <+96>: xor %fs:0x28,%rax
0x00000000000013ee <+105>: jne 0x13f5 <phase_4+112>
0x00000000000013f0 <+107>: add $0x18,%rsp
0x00000000000013f4 <+111>: retq
0x00000000000013f5 <+112>: callq 0xe50 <__stack_chk_fail@plt>
- 断点打到 phase_4,可以看到前面分配了两个内存点到 rcx,rsi,用来接收从键盘输入的两个整数。
- 验证数组里是否是两个数,第一个数是否小于 14,否则爆炸。
- 可以看到 69 行进入到 func4 方法里面了,反汇编一下,
0x0000000000001346 <+0>: sub $0x8,%rsp
0x000000000000134a <+4>: mov %edx,%eax
0x000000000000134c <+6>: sub %esi,%eax
<em>// cx = ax</em>
0x000000000000134e <+8>: mov %eax,%ecx
<em>//逻辑右移 cx 31位 正0负1</em>
0x0000000000001350 <+10>: shr $0x1f,%ecx
<em>// cx +=ax</em>
0x0000000000001353 <+13>: add %eax,%ecx
<em>// 算术右移一位cx</em>
0x0000000000001355 <+15>: sar %ecx
<em>//cx ++si</em>
0x0000000000001357 <+17>: add %esi,%ecx
<em>// ecx>edi?</em>
0x0000000000001359 <+19>: cmp %edi,%ecx
0x000000000000135b <+21>: jg 0x136b <func4+37>
0x000000000000135d <+23>: mov $0x0,%eax
0x0000000000001362 <+28>: cmp %edi,%ecx
0x0000000000001364 <+30>: jl 0x1377 <func4+49>
0x0000000000001366 <+32>: add $0x8,%rsp
0x000000000000136a <+36>: retq
0x000000000000136b <+37>: lea -0x1(%rcx),%edx
0x000000000000136e <+40>: callq 0x1346 <func4>
0x0000000000001373 <+45>: add %eax,%eax
0x0000000000001375 <+47>: jmp 0x1366 <func4+32>
0x0000000000001377 <+49>: lea 0x1(%rcx),%esi
0x000000000000137a <+52>: callq 0x1346 <func4>
观察代码可以发现这里做了个运算,cx = (ax-si)/2+ax
代入式子 cx = (ax-si)/2+ax
得(14-0)/2 +0 = 7
- 如果满足条件 1,就要进入递归
Retq 2*func4(edx,esi,ecx-1)
- 不然就到下一步骤 retq 2 * func4(edx, (ecx + 1), edx) + 1
- 已知最后 phase_4 需要递归函数结束后返回值为 3,可以写出代码模拟程序进行破解。
#include <iostream>
using namespace std;
int func4(int a1, int a2, int a3) {
int ecx;
<em>// rax</em>
<em> </em>int result;
<em> </em>ecx = a2 + (a3 - a2) / 2;
if (ecx > a1)
return 2 * func4(a1, a2, (ecx - 1));
result = 0;
if (ecx < a1)
return 2 * func4(a1, (ecx + 1), a3) + 1;
return result;
}
int main() {
int v2 = 1, v3 = 3;
while (v2 <= 14) {
if (func4(v2, 0, 14) == 3) {
//打印递归函数返回为3的v2
cout << v2;
break;
} else {
v2++;
}
}
}
枚举出第一个参数应该是 12。
- 我们回到 phase_4,可以看到,他要求我们第二个参数必须为 3。
- 所以输入 12 3。phase_4 通过
phase_5
13fa: 53 push %rbx
13fb: 48 89 fb mov %rdi,%rbx
13fe: e8 9d 02 00 00 callq 16a0 <string_length>
1403: 83 f8 06 cmp $0x6,%eax
1406: 75 31 jne 1439 <phase_5+0x3f>
1408: 48 89 d8 mov %rbx,%rax
140b: 48 8d 7b 06 lea 0x6(%rbx),%rdi
140f: b9 00 00 00 00 mov $0x0,%ecx
1414: 48 8d 35 25 14 00 00 lea 0x1425(%rip),%rsi # 2840 <array.3415>
141b: 0f b6 10 movzbl (%rax),%edx
//dx and 0xf保留后四位
141e: 83 e2 0f and $0xf,%edx
//ecx += 4rdx +rsi
1421: 03 0c 96 add (%rsi,%rdx,4),%ecx
// 取下标
1424: 48 83 c0 01 add $0x1,%rax
//i<6? 回到循环:跳出
1428: 48 39 f8 cmp %rdi,%rax
142b: 75 ee jne 141b <phase_5+0x21>
//比72
142d: 83 f9 48 cmp $0x48,%ecx
1430: 74 05 je 1437 <phase_5+0x3d>
1432: e8 92 03 00 00 callq 17c9 <explode_bomb>
1437: 5b pop %rbx
1438: c3 retq
1439: e8 8b 03 00 00 callq 17c9 <explode_bomb>
143e: eb c8 jmp 1408 <phase_5+0xe>
- 反汇编代码,显然是输入长度为 6 的字符串
- 在反汇编代码里面查看一下那个数组 array3415
数组应该是这样的
array_3415[16] = { 2, 10, 6, 1, 12, 16, 9, 3, 4, 7, 14, 5, 11, 8, 15, 13 }
- 这个题的关键是比较 ecx 是否等于 0x48(10 进制就是 72),不相等就爆炸
- and $0xf,%edx,这里是保留 edx 也就是每一位输入的值的下标值二进制的后四位。
- 可以读出这里的意思是:拿每一个输入元素的值对 15 做与后的值取到数组下标对应元素的值的和等于 72,这里我取下标 4
array_3415[4] = 12
12+12+12+12+12+12=72
所以输入 444444,phase_5 通过