实验概览
BombLab
提供给我们两个文件,一个是编译不了的C文件bomb.c
,一个是可执行文件bomb
。当运行bomb
文件时,它会要求输内容,如果其中的任何一句是错的,炸弹就会“爆炸”。我们必须利用反汇编工具逆向分析这个文件,并找到这6个字符串,从而“拆除”炸弹。
使用objdump -d bomb > bomb.asm
命令反汇编bomb
汇编基础
-
eax
寄存器常用来保存函数的返回值
-
test
指令在两个操作数的对应位之间进行 AND
操作,并根据运算结果设置符号标志位、零标志位和奇偶标志位,但test
指令不修改目标操作数,test
指令可以同时检查多个位
假设想要知道 AL 寄存器的位 0 和位 3 是否置 1,可以使用如下指令:
1
| test al, 00001001b ;测试位 0 和位 3
|
只有当所有测试位都清 0 时,零标志位ZF
才置 1
1 2 3 4 5 6 7
| 0 0 1 0 0 1 0 1 <- 输入值 0 0 0 0 1 0 0 1 <- 测试值 0 0 0 0 0 0 0 1 <- 结果:ZF=0
0 0 1 0 0 1 0 0 <- 输入值 0 0 0 0 1 0 0 1 <- 测试值 0 0 0 0 0 0 0 0 <- 结果:ZF=1
|
test %eax,%eax
指令可以用来检查eax
的值是否为零
-
在x86
架构中,eax
是32位的寄存器,其中包含了 ax
、ah
和 al
三个子寄存器。其中,al
是 eax
的低8位,ah
是中间的8位,ax
是 eax
的低16位
phase_1
对汇编代码进行分析:
1 2 3 4 5 6 7 8
| 400ee0: 48 83 ec 08 sub $0x8,%rsp // 为函数分配栈帧 400ee4: be 00 24 40 00 mov $0x402400,%esi // 设置函数strings_not_equal的传入参数,0x402400 400ee9: e8 4a 04 00 00 callq 401338 <strings_not_equal> 400eee: 85 c0 test %eax,%eax 400ef0: 74 05 je 400ef7 <phase_1+0x17> // 函数返回值若为0,拆除成功;不为0,则bomb! 400ef2: e8 43 05 00 00 callq 40143a <explode_bomb> 400ef7: 48 83 c4 08 add $0x8,%rsp 400efb: c3 retq
|
<strings_not_equal>
函数的汇编代码如下:(需要返回0)
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| 401338: 41 54 push %r12 40133a: 55 push %rbp 40133b: 53 push %rbx 40133c: 48 89 fb mov %rdi,%rbx 40133f: 48 89 f5 mov %rsi,%rbp 401342: e8 d4 ff ff ff callq 40131b <string_length> // 求出第一个字符串的长度并传给$r12d 401347: 41 89 c4 mov %eax,%r12d 40134a: 48 89 ef mov %rbp,%rdi 40134d: e8 c9 ff ff ff callq 40131b <string_length> // 求出第一个字符串的长度并与上一个字符串比较 401352: ba 01 00 00 00 mov $0x1,%edx 401357: 41 39 c4 cmp %eax,%r12d 40135a: 75 3f jne 40139b <strings_not_equal+0x63> // 将1作为返回值返回 40135c: 0f b6 03 movzbl (%rbx),%eax //rbx的低一字节传给eax 40135f: 84 c0 test %al,%al // 相当于要一个字节一个字节地比较 401361: 74 25 je 401388 <strings_not_equal+0x50> //将0作为返回值返回 401363: 3a 45 00 cmp 0x0(%rbp),%al 401366: 74 0a je 401372 <strings_not_equal+0x3a> //跳转到0x401372,去比较下一个字节 401368: eb 25 jmp 40138f <strings_not_equal+0x57> //将1作为返回值返回
/* 循环开始 */ 40136a: 3a 45 00 cmp 0x0(%rbp),%al 40136d: 0f 1f 00 nopl (%rax) 401370: 75 24 jne 401396 <strings_not_equal+0x5e> //如果不相等将1作为返回值返回 401372: 48 83 c3 01 add $0x1,%rbx //每一次地址加1,相当于比较下一个字节 401376: 48 83 c5 01 add $0x1,%rbp 40137a: 0f b6 03 movzbl (%rbx),%eax 40137d: 84 c0 test %al,%al 40137f: 75 e9 jne 40136a <strings_not_equal+0x32> //如果$al不等于0,即还没有比较完
401381: ba 00 00 00 00 mov $0x0,%edx 401386: eb 13 jmp 40139b <strings_not_equal+0x63> //将0作为返回值返回 401388: ba 00 00 00 00 mov $0x0,%edx 40138d: eb 0c jmp 40139b <strings_not_equal+0x63> //将0作为返回值返回 40138f: ba 01 00 00 00 mov $0x1,%edx 401394: eb 05 jmp 40139b <strings_not_equal+0x63> //将1作为返回值返回 401396: ba 01 00 00 00 mov $0x1,%edx 40139b: 89 d0 mov %edx,%eax 40139d: 5b pop %rbx 40139e: 5d pop %rbp 40139f: 41 5c pop %r12 4013a1: c3 retq
|
要判断的是输入字符串是否与0x402400
处存储的字符串相同,可以使用print (char*)0x402400
或x/s 0x402400
来查看,得到key
1
| Border relations with Canada have never been better.
|
phase_2
对汇编代码进行分析
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 26 27 28 29 30 31 32 33
| 400efc: 55 push %rbp // 将被调用者保存寄存器的值入栈 400efd: 53 push %rbx 400efe: 48 83 ec 28 sub $0x28,%rsp
400f02: 48 89 e6 mov %rsp,%rsi // 分配栈帧并调用<read_six_numbers>读入整数 400f05: e8 52 05 00 00 callq 40145c <read_six_numbers>
400f0a: 83 3c 24 01 cmpl $0x1,(%rsp) // 比较%rsp与1是否相等,不相等则引爆。可知第一个数为 1 400f0e: 74 20 je 400f30 <phase_2+0x34> 400f10: e8 25 05 00 00 callq 40143a <explode_bomb> 400f15: eb 19 jmp 400f30 <phase_2+0x34>
/* 循环开始 */ 400f17: 8b 43 fc mov -0x4(%rbx),%eax 400f1a: 01 c0 add %eax,%eax 400f1c: 39 03 cmp %eax,(%rbx) 400f1e: 74 05 je 400f25 <phase_2+0x29> //如果相等,进行下一步比较 400f20: e8 15 05 00 00 callq 40143a <explode_bomb> // 如果不等就bomb
400f25: 48 83 c3 04 add $0x4,%rbx 400f29: 48 39 eb cmp %rbp,%rbx , 400f2c: 75 e9 jne 400f17 <phase_2+0x1b> // 如果不等,回到循环开始 400f2e: eb 0c jmp 400f3c <phase_2+0x40> //如果相等,函数成功返回
400f30: 48 8d 5c 24 04 lea 0x4(%rsp),%rbx 400f35: 48 8d 6c 24 18 lea 0x18(%rsp),%rbp 400f3a: eb db jmp 400f17 <phase_2+0x1b>
/* 回收内存空间 */ 400f3c: 48 83 c4 28 add $0x28,%rsp 400f40: 5b pop %rbx 400f41: 5d pop %rbp 400f42: c3 retq
|
对<read_six_numbers>
的汇编分析如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 40145c: 48 83 ec 18 sub $0x18,%rsp 401460: 48 89 f2 mov %rsi,%rdx 401463: 48 8d 4e 04 lea 0x4(%rsi),%rcx 401467: 48 8d 46 14 lea 0x14(%rsi),%rax 40146b: 48 89 44 24 08 mov %rax,0x8(%rsp) 401470: 48 8d 46 10 lea 0x10(%rsi),%rax 401474: 48 89 04 24 mov %rax,(%rsp) 401478: 4c 8d 4e 0c lea 0xc(%rsi),%r9 40147c: 4c 8d 46 08 lea 0x8(%rsi),%r8
401480: be c3 25 40 00 mov $0x4025c3,%esi // 调用scanf()函数读取6个整数 401485: b8 00 00 00 00 mov $0x0,%eax 40148a: e8 61 f7 ff ff callq 400bf0 <__isoc99_sscanf@plt> 40148f: 83 f8 05 cmp $0x5,%eax 401492: 7f 05 jg 401499 <read_six_numbers+0x3d> 401494: e8 a1 ff ff ff callq 40143a <explode_bomb> 401499: 48 83 c4 18 add $0x18,%rsp 40149d: c3 retq
|
为了验证是读取6个整数,使用print (char*)0x4025c3
,输出%d %d %d %d %d %d
,猜想正确
<phase_2>
剩余代码的循环规律是,每个数都要与上一个数的2倍相等,从而可以得到key
phase_3
phase_4
phase_5
phase_6
本着互联网开源的性质,欢迎分享这篇文章,以帮助到更多的人,谢谢!