系统级编程实验——二进制拆弹
一, 前言
实验环境:window7
工具:objdump.exe,gdb.exe
材料:bomb.exe
准备:在 cmd.exe 中用 objdump.exe 反汇编 bomb.exe 并将反汇编的内容保存到一个 txt
文件里面,后面分析会用到。我的实验目录结构是 D:/test,所以在实验室只需 cd 到这个文
件夹里面即可对 objdump.exe,gdb.exe,bomb.exe 进行操作。
关键操作:调试时常用的操作有 设断点 break phase_1
r
运行
单步执行 s
查看寄存器 info reg
查看某个内存内容 x/2xh x/1sb x/1xb 等。
二, 拆弹分析
查看文件 boom.c, main 函数里面的内容用文字描述就是从屏幕读取一行字符串,将
字符串传入 phase_x 处理,说明是否爆炸由 phase_x 函数决定,phase_x 函数需要重点分析。
反汇编的 main 函数也是如此,代码如下
读取 phase_1
40175b:
401762:
401767:
40176c:
401770:
401774:
401777:
40177c:
读取 phase_2
401781:
401788:
40178d:
401792:
401796:
40179a:
40179d:
4017a2:
c7 04 24 dc 60 40 00
e8 89 28 00 00
e8 53 07 00 00
89 44 24 1c
8b 44 24 1c
89 04 24
e8 ca 00 00 00
e8 4d 08 00 00
c7 04 24 08 61 40 00
e8 63 28 00 00
e8 2d 07 00 00
89 44 24 1c
8b 44 24 1c
89 04 24
e8 c8 00 00 00
e8 27 08 00 00
读取 phase_3
4017a7:
4017ae:
4017b3:
4017b8:
4017bc:
c7 04 24 31 61 40 00
e8 3d 28 00 00
e8 07 07 00 00
89 44 24 1c
8b 44 24 1c
movl
call
call
mov
mov
mov
call
call
movl
call
call
mov
mov
mov
call
call
movl
call
call
mov
mov
$0x4060dc,(%esp)
403ff0 <_puts>
401ebf <_read_line>
%eax,0x1c(%esp)
0x1c(%esp),%eax
%eax,(%esp)
401846 <_phase_1>
401fce <_phase_defused>
$0x406108,(%esp)
403ff0 <_puts>
401ebf <_read_line>
%eax,0x1c(%esp)
0x1c(%esp),%eax
%eax,(%esp)
40186a <_phase_2>
401fce <_phase_defused>
$0x406131,(%esp)
403ff0 <_puts>
401ebf <_read_line>
%eax,0x1c(%esp)
0x1c(%esp),%eax
4017c0:
4017c3:
4017c8:
89 04 24
e8 fd 00 00 00
e8 01 08 00 00
读取 phase_4
4017cd:
4017d4:
4017d9:
4017de:
4017e2:
4017e6:
4017e9:
4017ee:
c7 04 24 4f 61 40 00
e8 17 28 00 00
e8 e1 06 00 00
89 44 24 1c
8b 44 24 1c
89 04 24
e8 37 02 00 00
e8 db 07 00 00
读取 phase_5
call
c7 04 24 60 61 40 00
e8 f1 27 00 00
4017f3:
4017fa:
4017ff:e8 bb 06 00 00
401804:
89 44 24 1c
8b 44 24 1c
401808:
89 04 24
40180c:
40180f:
e8 59 02 00 00
e8 b5 07 00 00
401814:
读取 pahse_6
401819:
401820:
401825:
40182a:
40182e:
401832:
401835:
40183a:
c7 04 24 84 61 40 00
e8 cb 27 00 00
e8 95 06 00 00
89 44 24 1c
8b 44 24 1c
89 04 24
e8 a5 02 00 00
e8 8f 07 00 00
mov
call
call
movl
call
call
mov
mov
mov
call
call
%eax,(%esp)
4018c5 <_phase_3>
401fce <_phase_defused>
$0x40614f,(%esp)
403ff0 <_puts>
401ebf <_read_line>
%eax,0x1c(%esp)
0x1c(%esp),%eax
%eax,(%esp)
401a25 <_phase_4>
401fce <_phase_defused>
movl
call
$0x406160,(%esp)
403ff0 <_puts>
401ebf <_read_line>
mov
mov
mov
call
call
movl
call
call
mov
mov
mov
call
call
%eax,0x1c(%esp)
0x1c(%esp),%eax
%eax,(%esp)
401a6d <_phase_5>
401fce <_phase_defused>
$0x406184,(%esp)
403ff0 <_puts>
401ebf <_read_line>
%eax,0x1c(%esp)
0x1c(%esp),%eax
%eax,(%esp)
401adf <_phase_6>
401fce <_phase_defused>
一,
第一颗:第一颗比较简单,就是将你输入的字符串与程序自带的字符串相比较,
如果相等则拆弹成功,不相等则调用炸弹函数。
00401846 <_phase_1>:
401846:
401847:
401849:
40184c:
401853:
401854:
401857:
40185a:
40185f:
401861:
401863:
55
89 e5
83 ec 18
c7 44 24 04 a2 61 40
00
8b 45 08
89 04 24
e8 53 05 00 00
85 c0
74 05
e8 3c 07 00 00
push
mov
sub
movl
mov
mov
call
test
je
call
%ebp
%esp,%ebp
$0x18,%esp
$0x4061a2,0x4(%esp)
0x8(%ebp),%eax
%eax,(%esp)
401db2 <_strings_not_equal>
%eax,%eax
401868 <_phase_1+0x22>
401fa4 <_explode_bomb>
在刚进入这个 phase_1 时,传给 phase_1 的参数放在 phase_1 的 0x(8%ebp)里面
c7 44 24 04 a2 61 40
的,
40184c:
401854: 8b 45 08
401857: 89 04 24
40185a:
e8 53 05 00 00
movl
mov
mov
call
$0x4061a2,0x4(%esp)
0x8(%ebp),%eax
%eax,(%esp)
401db2 <_strings_not_equal>
参数几经周转最终存放到(%esp),程序自带的字符串首地址$0x4061a2 放在
4(%esp),调用 call
401db2 <_strings_not_equal>字符串比较函数比较输入的字符串
与程序自带的字符串,如果不相等则调用炸弹函数。因此需要输入的字符串就是
0x4061a2 处的字符串
所以猜测第一个钥匙就是 Public speaking is very easy.
输入看看
第一个炸弹拆弹成功。
二,
第二颗,第二颗比较麻烦,花了我很多时间才把代码看懂,尽管在网上看了些
参考
0040186a <_phase_2>:
55
40186a:
89 e5
40186b:
83 ec 38
40186d:
8d 45 dc
401870:
89 44 24 04
401873:
8b 45 08
401877:
89 04 24
40187a:
e8 9d 04 00 00
40187d:
8b 45 dc
401882:
83 f8 01
401885:
74 05
401888:
40188a:
e8 15 07 00 00
40188f:c7 45 f4 01 00 00 00
401896:
401899:
40189c:
4018a0:
4018a3:
8b 45 f4
83 e8 01
8b 44 85 dc
8b 55 f4
83 c2 01
push
mov
sub
lea
mov
mov
mov
call
mov
cmp
je
call
movl
mov
sub
mov
mov
add
%ebp
%esp,%ebp
$0x38,%esp
-0x24(%ebp),%eax
%eax,0x4(%esp)
0x8(%ebp),%eax
%eax,(%esp)
401d1f <_read_six_numbers>
-0x24(%ebp),%eax
$0x1,%eax
40188f <_phase_2+0x25>
401fa4 <_explode_bomb>
$0x1,-0xc(%ebp)
-0xc(%ebp),%eax
$0x1,%eax
-0x24(%ebp,%eax,4),%eax
-0xc(%ebp),%edx
$0x1,%edx
4018a6:
4018a9:
4018ac:
4018b0:
4018b2:
4018b4:
4018b9:
4018bd:
4018c1:
4018c3:
4018c4:
0f af d0
8b 45 f4
8b 44 85 dc
39 c2
74 05
e8 eb 06 00 00
83 45 f4 01
83 7d f4 05
7e d3
c9
c3
%eax,%edx
-0xc(%ebp),%eax
-0x24(%ebp,%eax,4),%eax
%eax,%edx
4018b9 <_phase_2+0x4f>
401fa4 <_explode_bomb>
$0x1,-0xc(%ebp)
$0x5,-0xc(%ebp)
401896 <_phase_2+0x2c>
imul
mov
mov
cmp
je
call
addl
cmpl
jle
leave
ret
上面是 phase_2 的反汇编代码,下面代码的意思是将-0x24(%ebp)处的地址存放到
0x4(%esp)中,将 0x8(%ebp)处的内容放到(%esp)中,然后调用 read_six _numbers
401870:
401873:
401877:
40187a:
40187d:
8d 45 dc
89 44 24 04
8b 45 08
89 04 24
e8 9d 04 00 00
lea
mov
mov
mov
call
-0x24(%ebp),%eax
%eax,0x4(%esp)
0x8(%ebp),%eax
%eax,(%esp)
401d1f <_read_six_numbers>
因为看到 read_six_numbers 函数所以推测拆弹钥匙是 6 个数字,所以随便输入 6 个数字,调
试,phase_2 的 EBP 值是 0x28fec8, 那么 0x8(%ebp)的值是 0x28fed0,看看 0x28fed0 里面是什
么
0x8(%ebp)里面明显存放的是输入的字符串的首地址,这在调用 read_six_numbers 之前将其
放到(%esp)里面。
40187d:
401882:
401885:
e8 9d 04 00 00
8b 45 dc
83 f8 01
call
mov
cmp
401d1f <_read_six_numbers>
-0x24(%ebp),%eax
$0x1,%eax
这里调用 read_six_numbers 函数后就直接从-0x24(%ebp)里面取内容跟 1 作比较,根
据上面-0x24(%ebp)处的地址存放到 0x4(%esp)中,可以猜出将我输入的字符串中的
数 字 通 过 read_six_numers 函 数 提 取 出 来 放 到 了 -0x24(%ebp) 处 ,
-0x24(%ebp)=0x28fea4 验证一下,
果然,就是输入的那几个数字。
401885: 83 f8 01
401888: 74 05
40188a: e8 15 07 00 00
cmp
$0x1,%eax
je
40188f <_phase_2+0x25>
call
401fa4 <_explode_bomb>
这一段是判断第一个数字如果不等于 1 那么调用炸弹,
c7 45 f4 01 00 00 00
40188f:
401896: 8b 45 f4
401899: 83 e8 01
40189c: 8b 44 85 dc
4018a0: 8b 55 f4
4018a3: 83 c2 01
4018a6: 0f af d0
4018a9: 8b 45 f4
4018ac: 8b 44 85 dc
4018b0: 39 c2
4018b2: 74 05
movl
mov
sub
mov
mov
add
imul
mov
mov
$0x1,-0xc(%ebp)
-0xc(%ebp),%eax
$0x1,%eax
-0x24(%ebp,%eax,4),%eax
-0xc(%ebp),%edx
$0x1,%edx
%eax,%edx
-0xc(%ebp),%eax
-0x24(%ebp,%eax,4),%eax
cmp
je
%eax,%edx
4018b9 <_phase_2+0x4f>
4018b4: e8 eb 06 00 00
call
401fa4 <_explode_bomb>
4018b9: 83 45 f4 01
4018bd: 83 7d f4 05
4018c1: 7e d3
addl
cmpl
$0x1,-0xc(%ebp)
$0x5,-0xc(%ebp)
jle
401896 <_phase_2+0x2c>
上面这一段的意思用我自己的话描述就是假设输入的 6 个数为 a1,a2,a3,a4,a5,a6.
他们必须满足关系 a1*2=a2
a2*3=a3;
a3*4=a4
a4*5=a5
a5*6=a6 否则就会爆炸
而上面规定 a1 必须等于 1,所以一次算出 6 个数分别是 1,2,6,24,120,720
输入试试看,
成功。
三,
第三颗,第三颗是一个 switch 结构,看代码。汉字是我标注的。
004018c5 <_phase_3>:
4018c5:
4018c6:
4018c8:
55
89 e5
83 ec 38
push
mov
sub
%ebp
%esp,%ebp
$0x38,%esp
停 1
8d 45 f0
89 44 24 10
8d 45 ef
89 44 24 0c
8d 45 e8
89 44 24 08
c7 44 24 04 c0 61 40
00
8b 45 08
89 04 24
e8 05 27 00 00
4018cb:
4018ce:
4018d2:
4018d5:
4018d9:
4018dc:
4018e0:
4018e7:
4018e8:
4018eb:
4018ee:
4018f3:83 f8 02
4018f6:7f 05
lea
mov
lea
mov
lea
mov
movl
mov
mov
call
cmp
jg
-0x10(%ebp),%eax
%eax,0x10(%esp)
-0x11(%ebp),%eax
%eax,0xc(%esp)
-0x18(%ebp),%eax
%eax,0x8(%esp)
$0x4061c0,0x4(%esp)
0x8(%ebp),%eax
%eax,(%esp)
403ff8 <_sscanf>
$0x2,%eax
4018fd <_phase_3+0x38> 满 足 此
条件
4018f8:e8 a7 06 00 00
call
401fa4 <_explode_bomb>
停 2
4018fd:8b 45 e8
401900:
401903:
83 f8 07
0f 87 c9 00 00 00
条会爆炸
401909:
401910:
8b 04 85 cc 61 40 00
ff e0
mov
cmp
ja
mov
jmp
-0x18(%ebp),%eax
$0x7,%eax
4019d2 <_phase_3+0x10d>执行此
0x4061cc(,%eax,4),%eax
*%eax
case 1:
401912:
401916:
401919:
40191e:
401920:
401925:
40192a:
case 2:
c6 45 f7 71
8b 45 f0
3d 09 03 00 00
74 0a
e8 7f 06 00 00
e9 b1 00 00 00
e9 ac 00 00 00
40192f:c6 45 f7 62
401933:
401936:
40193b:
40193d:
401942:
401947:
8b 45 f0
3d d6 00 00 00
74 0a
e8 62 06 00 00
e9 94 00 00 00
e9 8f 00 00 00
case 3:
40194c:
401950:
401953:
401958:
c6 45 f7 62
8b 45 f0
3d f3 02 00 00
74 07
movb
mov
cmp
$0x71,-0x9(%ebp)
-0x10(%ebp),%eax
$0x309,%eax
je
40192a <_phase_3+0x65>
call
jmp
jmp
401fa4 <_explode_bomb>
4019db <_phase_3+0x116>
4019db <_phase_3+0x116>
movb
mov
cmp
$0x62,-0x9(%ebp)
-0x10(%ebp),%eax
$0xd6,%eax
je
401947 <_phase_3+0x82>
call
jmp
jmp
401fa4 <_explode_bomb>
4019db <_phase_3+0x116>
4019db <_phase_3+0x116>
movb
mov
cmp
$0x62,-0x9(%ebp)
-0x10(%ebp),%eax
$0x2f3,%eax
je
401961 <_phase_3+0x9c>
e8 45 06 00 00
40195a:
40195f:eb 7a
401961:
eb 78
case 4:
401963:
401967:
40196a:
40196f:74 07
401971:
401976:
401978:
c6 45 f7 6b
8b 45 f0
3d fb 00 00 00
e8 2e 06 00 00
eb 63
eb 61
case 5:
40197a:
40197e:
401981:
401986:
401988:
40198d:
40198f:eb 4a
c6 45 f7 6f
8b 45 f0
3d a0 00 00 00
74 07
e8 17 06 00 00
eb 4c
case 6:
c6 45 f7 74
8b 45 f0
3d ca 01 00 00
74 07
401991:
401995:
401998:
40199d:
40199f:e8 00 06 00 00
4019a4:
4019a6:
eb 35
eb 33
case 7:
4019a8:
c6 45 f7 76
4019ac:
8b 45 f0
4019af:3d 0c 03 00 00
4019b4:
4019b6:
74 05
e8 e9 05 00 00
case 8:
c6 45 f7 62
4019bb:
4019bf:8b 45 f0
4019c2:
4019c7:
4019c9:
4019ce:
4019d0:
4019d2:
4019d6:
3d 0c 02 00 00
74 07
e8 d6 05 00 00
eb 0b
eb 09
c6 45 f7 78
e8 c9 05 00 00
call
jmp
jmp
401fa4 <_explode_bomb>
4019db <_phase_3+0x116>
4019db <_phase_3+0x116>
movb
mov
cmp
je
call
jmp
jmp
movb
mov
cmp
je
call
jmp
jmp
movb
mov
cmp
je
call
jmp
jmp
movb
mov
cmp
je
call
movb
mov
cmp
je
call
jmp
jmp
movb
call
$0x6b,-0x9(%ebp)
-0x10(%ebp),%eax
$0xfb,%eax
401978 <_phase_3+0xb3>
401fa4 <_explode_bomb>
4019db <_phase_3+0x116>
4019db <_phase_3+0x116>
$0x6f,-0x9(%ebp)
-0x10(%ebp),%eax
$0xa0,%eax
40198f <_phase_3+0xca>
401fa4 <_explode_bomb>
4019db <_phase_3+0x116>
4019db <_phase_3+0x116>
$0x74,-0x9(%ebp)
-0x10(%ebp),%eax
$0x1ca,%eax
4019a6 <_phase_3+0xe1>
401fa4 <_explode_bomb>
4019db <_phase_3+0x116>
4019db <_phase_3+0x116>
$0x76,-0x9(%ebp)
-0x10(%ebp),%eax
$0x30c,%eax
4019bb <_phase_3+0xf6>
401fa4 <_explode_bomb>
$0x62,-0x9(%ebp)
-0x10(%ebp),%eax
$0x20c,%eax
4019d0 <_phase_3+0x10b>
401fa4 <_explode_bomb>
4019db <_phase_3+0x116>
4019db <_phase_3+0x116>
$0x78,-0x9(%ebp)
401fa4 <_explode_bomb>
0f b6 45 ef
4019db:
4019df:3a 45 f7
4019e2:
4019e4:
74 05
e8 bb 05 00 00
movzbl -0x11(%ebp),%eax
-0x9(%ebp),%al
cmp
4019e9 <_phase_3+0x124>
je
call
401fa4 <_explode_bomb>
4019e9:
4019ea:
c9
c3
leave
ret
Phase_3 的 开 头 跟 上 phase_1,phase_2 一 样 , 都 是 把 我 输 入 的 字 符 串 放 到
0x8(%ebp)里面,
4018cb:
4018ce:
4018d2:
4018d5:
4018d9:
4018dc:
4018e0:
4018e7:
4018e8:
4018eb:
4018ee:
8d 45 f0
89 44 24 10
8d 45 ef
89 44 24 0c
8d 45 e8
89 44 24 08
c7 44 24 04 c0 61 40
00
8b 45 08
89 04 24
e8 05 27 00 00
lea
mov
lea
mov
lea
mov
movl
mov
mov
call
-0x10(%ebp),%eax
%eax,0x10(%esp)
-0x11(%ebp),%eax
%eax,0xc(%esp)
-0x18(%ebp),%eax
%eax,0x8(%esp)
$0x4061c0,0x4(%esp)
0x8(%ebp),%eax
%eax,(%esp)
403ff8 <_sscanf>
上面这一段的意思就是将-0x10(%ebp)的地址存放到 0x10(%esp)中,-0x11(%ebp)
的地址放到 0xc(%esp)中,将-0x18(%ebp)的地址放到 0x8(%esp)中,上面黄色字体
0x4061c0 不知道是什么,查看一下,
这是 C 语言的输入格式控制,表示输入的内容是数字 字符 数字,所以根据前
面的经验,可以猜测读取的内容分别放到了-0x10(%ebp),-0x11(%ebp),-0x18(%ebp)
里面,查看这三个地址中的内容,
十六进制的 0xfb 就是 251,输入的第三个数字,0x28feb0 处是第一个数字 3,
0x28feb7 处的 0x6b,其实就 k 的数字表示 107,107 转换为十六进制就是 0x6b。接着
往后看,
83 f8 02
7f 05
e8 a7 06 00 00
4018f3:
4018f6:
4018f8:
这里的%eax 里面的值是 3 怎么放进去的我也没想清楚,说明就是用第一个数
字与 0x2 作比较,看样子如果不大于 2 就会爆炸所以得出结论第一个数字必须大于 2.再往后
就跳到了 0x4018fd 了,
$0x2,%eax
4018fd <_phase_3+0x38>
401fa4 <_explode_bomb>
cmp
jg
call