bomblab

开始愉快的拆除炸弹吧,(bushi)

通过这次实验可以基本掌握gdb的使用,这次使用了pwndgb插件

可以使用objdump查看反汇编程序,也可以在gdb里使用disassemble反汇编

超详细的wpCSAPP | Lab2-Bomb Lab 深入解析 - 知乎 (zhihu.com)

pwndgb的基本指令

指令 效果
s 单步步入
n 单步步过
r 重新运行
c 继续运行
i b, i r 查看断点、寄存器
return 退出当前函数
search 114514 查找114514
b *0x114514 , b fun_name 在0x114514处下断点、在函数fun_name处下断点
delete ,delete 114514 删除所有断点,删除114514号断点
disassemble main 反汇编main函数
vmmap 显示进程的内存映射
cyclic 50 生成50个用来溢出的字符
cyclic -l 字符串 定位字符串再溢出字符的位置
p 0xffffceec-0xffffcf58 计算0xffffceec-0xffffcf58的值

phase_1

在phase_1处下断点b phase_1,然后使用s单步

发现传入四个参数,其中rdi寄存器存储我们输入的值,rsi寄存器存储着字符串。然后通过函数strings_not_equal可以判断出函数是将这两个字符串进行比较,然后通过test eax, eax对返回值进行判断

如果 eax 的值为零,则零标志位 (ZF) 将被设置为 1。

如果 eax 的值非零,则零标志位 (ZF) 将被设置为 0.

ZF标志位为1时,跳转到phase_1+23,否则调用explode——bomb

可以通过x/s查看内存

显然,phase_1的答案为Border relations with Canada have never been better.

phase_2

删除所有断点,进入phase2,下断点,s单步

进入read_six_numbers函数

cmp eax, 5应该是每成功处理一个输入,eax++。scanf函数返回已成功赋值的数据项数,返回值存储在eax寄存器。

eax==5即全部参数处理完毕,由此我们可以确定我们要输入六个参数。

然后继续分析phase_2,直接disassemble phase_2

1
cmp    DWORD PTR [rsp],0x1

判断第一个字符是否为1,否则bomb!!

1
add    eax,eax

每次输入的数就是前面数的两倍

从bomb.c 文件中可以看到,当输入两个命令行参数时,我们可以以一个文件作为输入,因此可以创建ans.txt文件作为输入

第一个参数为 /home/randolfluo/Desktop/csapp/bomb/bomb

第二个参数为 ans.txt

因此执行 argc为2的代码

phase_3

调用scanf函数,若已成功赋值的数据项数eax>1成立,则jg有符号大于则跳转,避开爆炸,因此必须要有三个输入

而我们从scanf函数的format中可以看到,输入了两个整型。但!回车就是第三个输入。

1
2
0x400f6a <phase_3+39>     cmp    dword ptr [rsp + 8], 7
0x400f6f <phase_3+44> ja phase_3+106 <phase_3+106>

判断第一个输入小于7,然后进行后续跳转

1
0x400f75 <phase_3+50>     jmp    qword ptr [rax*8 + 0x402470]  <phase_3+118>

通过你输入的第一个值来跳转不同验证代码,rax*8 + 0x402470,查看0x402470处的内存,因为linux在x86-64是小端序。小端序意味着较低地址的字节排放在内存中的较低地址,而较高地址的字节排放在内存中的较高地址。

如果输入 1,则跳转到0x0000000000400fb9

则第二个数则0x137,化为十进制即311

phase_4

依然是输入两个整型,将phase_4反汇编一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Dump of assembler code for function phase_4:
0x000000000040100c <+0>: sub rsp,0x18
0x0000000000401010 <+4>: lea rcx,[rsp+0xc]
0x0000000000401015 <+9>: lea rdx,[rsp+0x8]
0x000000000040101a <+14>: mov esi,0x4025cf
0x000000000040101f <+19>: mov eax,0x0
0x0000000000401024 <+24>: call 0x400bf0 <__isoc99_sscanf@plt>
0x0000000000401029 <+29>: cmp eax,0x2
0x000000000040102c <+32>: jne 0x401035 <phase_4+41>
0x000000000040102e <+34>: cmp DWORD PTR [rsp+0x8],0xe
0x0000000000401033 <+39>: jbe 0x40103a <phase_4+46>
0x0000000000401035 <+41>: call 0x40143a <explode_bomb>
0x000000000040103a <+46>: mov edx,0xe
0x000000000040103f <+51>: mov esi,0x0
0x0000000000401044 <+56>: mov edi,DWORD PTR [rsp+0x8]
0x0000000000401048 <+60>: call 0x400fce <func4>
0x000000000040104d <+65>: test eax,eax ,测试%eax寄存器值是否为0
0x000000000040104f <+67>: jne 0x401058 <phase_4+76>
0x0000000000401051 <+69>: cmp DWORD PTR [rsp+0xc],0x0 ,取输入的第二个数
0x0000000000401056 <+74>: je 0x40105d <phase_4+81> ,相等则跳转,则第二个数为0
0x0000000000401058 <+76>: call 0x40143a <explode_bomb> ,bomb!!!!!
0x000000000040105d <+81>: add rsp,0x18
0x0000000000401061 <+85>: ret
End of assembler dump.

调用了fun函数,,同时将第一个输入存放进%rdi寄存器。
发现fun函数是个递归函数。我们需要找到最快方式跳出循环

jle 0x400ff2 ——>0x401007 ——>return

可以使用word进行分析,其中不同颜色表示不同的跳转地址。

我们可以得出两个条件输入<=7和输入>=7,那么我们输入的第一个数为7,所以我们的输入为7 0

phase5

再次使用word大法

通过输入的字符串每个元素的低四位索引字符串maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?的值,最后与flyers进行比较,相同则通关。

写个脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
array='maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?'
key = 'flyers'
list=[]
for i in key:
j = array.find(i)
j = bin(j)[2:]#将0b前缀去掉
j = j.rjust(4,'0')#向右填充
i = '0110'
j = i+j
j = int(j,2)
j = chr(j)
print(j,end='')
#输出ionefg

phase_6

下次一定~