简单的前言
最近的作业有个二进制炸弹问题,具体点说就是使用汇编语言完成一些小问题,形式比较有趣,一起看看吧。
题目描述
-
实验目的
通过对一个二进制可执行程序(称为“二进制炸弹”)的理解和逆向工程,加深对对程序的机器级表示、汇编语言、调试器和逆向工程等方面知识的理解和掌握。 -
实验内容
作为实验目标的二进制炸弹“binary bombs” Linux可执行程序包含了多个阶段(或关卡),在每个阶段程序要求你输入一个特定字符串,如果输入满足程序代码所定义的要求,该阶段的炸弹就被拆除了,否则程序输出“炸弹爆炸BOOM!!! ”的提示并转到下一阶段再次等待对应的输入 ——实验的目标是设法得出解除尽可能多阶段的字符串。
为完成二进制炸弹拆除任务,需要通过反汇编和理解可执行炸弹文件程序或使用gdb调试器跟踪每一阶段的机器代码,从中理解关键机器指令的行为和作用,进而设法推断拆除炸弹所需的目标字符串。 -
实验环境:Linux 32-bit,C/汇编语言
一般来说,正在看本文的你肯定装好了虚拟机,和能运行32位程序的Linux系统,好了话不多说,系好安全带,准备出发。
环境准备
- 将炸弹数据包bomblab.tar(或解压缩后的文件夹)拷贝到虚拟机目录中,并使用图形化界面或者cmd进入炸弹压缩包所在目录(不会真有人不用图形界面直接肝黑白屏的代码吧,不会吧不会吧),
学生实验数据包:bomblab.tar(随不同学号有所不同)
解压命令:tar xvf bomblab.tar
数据包中包含下面文件:
bomb:二进制可执行炸弹程序
bomb.c:bomb程序的main函数
一些工具
objdump:反汇编二进制炸弹程序,获得其中汇编指令供分析
objdump -d bomb 输出bomb程序的反汇编结果
objdump -t bomb 打印bomb程序的符号表,其中包含bomb中所有函数、全局变量的名称和存储地址
而你需要的是,按以下步骤进行
1、先解压
2、objdump -d bomb > bomb.s 获得bomb程序的反汇编结果并保存于文本文件bomb.s中供分析
好了,现在多了一个小朋友,叫bomb.s 可以直接双击打开
打开以后就是代码,看不懂没关系,接下来的日子你不得不看的懂这些优美的代码(hhh)。
打开以后先来看看,按ctrl+f进行搜索,找到第一个炸弹phase_0
·
~~~好了,本期教程到此结束,剩下的内容自己看看代码就会了~
·
·
不闹了严肃点,我们继续前进。
二进制炸弹目标程序
包含了7个阶段以及1个隐藏阶段,分别集中考察对以下二进制程序表示各方面的理解和掌握:
- 阶段0:字符串比较
阶段1:浮点表示
阶段2:循环
阶段3:条件/分支
阶段4:递归调用和栈
阶段5:指针
阶段6:链表/指针/结构
隐藏阶段:只有给本文点赞以后才会出现(作为最后一个阶段)
接下来我们采用葫芦娃救爷爷模式(一个一个解决),马上开始,没上厕所的赶快去,喝水的喝水,犯困的抽自己两下,活动活动脖子,精神点【doge】.
实验阶段0 - 字符串比较
08049465 <phase_0>:
8049465: 55 push %ebp
8049466: 89 e5 mov %esp,%ebp
8049468: 83 ec 08 sub $0x8,%esp
804946b: 83 ec 08 sub $0x8,%esp
804946e: 68 d8 b1 04 08 push $0x804b1d8
8049473: ff 75 08 pushl 0x8(%ebp)
8049476: e8 05 08 00 00 call 8049c80 <strings_not_equal>
804947b: 83 c4 10 add $0x10,%esp
804947e: 85 c0 test %eax,%eax
8049480: 74 0c je 804948e <phase_0+0x29>
8049482: e8 61 0a 00 00 call 8049ee8 <explode_bomb>
8049487: b8 00 00 00 00 mov $0x0,%eax
804948c: eb 05 jmp 8049493 <phase_0+0x2e>
804948e: b8 01 00 00 00 mov $0x1,%eax
8049493: c9 leave
8049494: c3 ret
这一题比较简单,因为课程文档里有详细讲解。简单说下吧。
- 右键打开终端,输入 gdb bomb
- 然后打断点 输入 b phase_0
- 之后执行 输入 r
如图所示
看到了前两句欢迎语句,是不是很开心呢。have a nice day 哦
以看到,第11行代码会调用 <explode_bomb> ,也就是引爆炸弹,这不是我们所想要的,因此我们要避免程序运行到这一行。观察程序的结构,程序在第8行调用 strings_not_equal ,比较了 %eax和 %eax中的内容,而在这之前,程序push了 $0x804b1d8处的数据移动寄存器中,并可以猜到,地址$0x804b1d8处的数据是程序内置的正确答案,正是我们输入的字符串。
所以现在的问题是:如何获取地址$0x804b1d8处的数据内容?为此,我们需要运行程序,并且在合适的位置停下,这样你就可以查看答案了。
所以这么操作:
先随便输入几个字符,然后回车,
程序停在了断点处,然后用x/s 地址
(温馨小贴士,亲这边建议您右键单击复制粘贴呢哦,终端里不能ctrl+CV呢哦)
回车,出现了一串字符,为答案,复制保存即可。
先创建个文件保存答案
- 方法:输入
gedit 文件名字
,就可以创建成功啦。
继续。
重新打开终端,验证一下。
这次断点打在phase_1 别忘了。
还有那个运行改成r code 就可以直接运行读入文本里的答案了。
仔细点慢慢来。
欢迎语句下多了一行文字,大概意思就是答案正确,你好棒棒。
go on
实验阶段1 - 浮点表示
好了,经过第0个题,我们已经熟悉了很多,接着看看。
因为时间问题。不写太详细了,只写一下最快速的解题方法。而且对于汇编语言这一块,笔者水平确实有限,刚好能做题,具体知识原理还在摸爬滚打,如感觉光解题不讲知识点没营养,点个赞就可以关闭本文章了【坏笑】。
第1题代码
08049495 <phase_1>:
8049495: 55 push %ebp
8049496: 89 e5 mov %esp,%ebp
8049498: 83 ec 18 sub $0x18,%esp
804949b: c7 45 f4 9b 3a c7 17 movl $0x17c73a9b,-0xc(%ebp)
80494a2: db 45 f4 fildl -0xc(%ebp)
80494a5: d9 5d f0 fstps -0x10(%ebp)
80494a8: 8d 45 e8 lea -0x18(%ebp),%eax
80494ab: 50 push %eax
80494ac: 8d 45 ec lea -0x14(%ebp),%eax
80494af: 50 push %eax
80494b0: 68 ff b1 04 08 push $0x804b1ff
80494b5: ff 75 08 pushl 0x8(%ebp)
80494b8: e8 13 fc ff ff call 80490d0 <__isoc99_sscanf@plt>
80494bd: 83 c4 10 add $0x10,%esp
80494c0: 83 f8 02 cmp $0x2,%eax
80494c3: 74 0c je 80494d1 <phase_1+0x3c>
80494c5: e8 1e 0a 00 00 call 8049ee8 <explode_bomb>
80494ca: b8 00 00 00 00 mov $0x0,%eax
80494cf: eb 34 jmp 8049505 <phase_1+0x70>
80494d1: 8d 45 f0 lea -0x10(%ebp),%eax
80494d4: 83 c0 02 add $0x2,%eax
80494d7: 0f b7 00 movzwl (%eax),%eax
80494da: 0f bf d0 movswl %ax,%edx
80494dd: 8b 45 ec mov -0x14(%ebp),%eax
80494e0: 39 c2 cmp %eax,%edx
80494e2: 75 10 jne 80494f4 <phase_1+0x5f>
80494e4: 8d 45 f0 lea -0x10(%ebp),%eax
80494e7: 0f b7 00 movzwl (%eax),%eax
80494ea: 0f bf d0 movswl %ax,%edx
80494ed: 8b 45 e8 mov -0x18(%ebp),%eax
80494f0: 39 c2 cmp %eax,%edx
80494f2: 74 0c je 8049500 <phase_1+0x6b>
80494f4: e8 ef 09 00 00 call 8049ee8 <explode_bomb>
80494f9: b8 00 00 00 00 mov $0x0,%eax
80494fe: eb 05 jmp 8049505 <phase_1+0x70>
8049500: b8 01 00 00 00 mov $0x1,%eax
8049505: c9 leave
8049506: c3 ret
啊呀,这么多字啊,好烦啊,喝口奶茶压压惊。
好了,只讲快速解题步骤,如图。
随便输入几个字符,回车
输入 ni
进行逐步调试。一直回车。
发现到0x080494c5 然后boom了,所以回看代码看看有什么问题
发现前两行
80494c0: 83 f8 02 cmp $0x2,%eax
80494c3: 74 0c je 80494d1 <phase_1+0x3c>
80494c5: e8 1e 0a 00 00 call 8049ee8 <explode_bomb>
有个cmp是比较的意思,比较了0x2和%eax,就是十六进制数和寄存器的值比较,
je是相等,行灯就跳转到80494d1 <phase_1+0x3c>,否则继续下一行,
也就是call 8049ee8 <explode_bomb>。
要想不爆炸,得相等。往前看,有个明码地址,非常明目张胆。先看看它是什么东西。
C语言表示的两个整数。
哦,现在大概知道了,需要输入2个整数,第一步是判断是不是2个数,不是爆炸。所以重头开始
继续输入 r 即可,不必关终端。
仔细点,对照操作到上一步,然后单步调试。
输入 ni 之前,先看看思路,到什么地方会boom。
往下继续看,发现有一个cmp,也就是比较操作。然后他的下一行是jne,也就是说,不相等就跳转到后面的80494f4然后可以根据程序进行查找对照,发现这个4f4这一行,是炸弹boom的操作,
所以肯定需要两个数相等才可以。
80494e0: 39 c2 cmp %eax,%edx
80494e2: 75 10 jne 80494f4 <phase_1+0x5f>
以及
80494f0: 39 c2 cmp %eax,%edx
80494f2: 74 0c je 8049500 <phase_1+0x6b>
80494f4: e8 ef 09 00 00 call 8049ee8 <explode_bomb>
这一行同理跟前面是一个意思,也是要两数相等。所以现在单步调试的目的就是比较这两次的两个数是否相等。直接ni一直到80494e0。
我们发现之前前面的部分那一步没有爆炸,说明第一步确实是比较是不是输入了两个数。
然后输入 i r
查看寄存器内容。
发现cmp 的是%eax,%edx,而我们输入的是1 ,就是eax,edx是19902,用脚指头想一想,发现我们应该第一个说输入的和edx一样才可以。
于是得到第一个答案19902
继续单步调试哦
修改第一个为正确答案,下一次ni到这一行
80494f0: 39 c2 cmp %eax,%edx
看图
所以第二个答案是14805.
不解释。。
把这两个数保存在答案的文本里。然后可以验证一下。验证之前,在打断点的时候把断点打到第二题,然后然后运行答案文本就可以了。
最后一行看到了吧。就是说通过了。hhh我好棒【doge】
实验阶段2 - 循环
先看代码
08049507 <phase_2>:
8049507: 55 push %ebp
8049508: 89 e5 mov %esp,%ebp
804950a: 83 ec 28 sub $0x28,%esp
804950d: 83 ec 04 sub $0x4,%esp
8049510: 6a 06 push $0x6
8049512: 8d 45 dc lea -0x24(%ebp),%eax
8049515: 50 push %eax
8049516: ff 75 08 pushl 0x8(%ebp)
8049519: e8 a8 06 00 00 call 8049bc6 <read_n_numbers>
804951e: 83 c4 10 add $0x10,%esp
8049521: 85 c0 test %eax,%eax
8049523: 75 07 jne 804952c <phase_2+0x25>
8049525: b8 00 00 00 00 mov $0x0,%eax
804952a: eb 59 jmp 8049585 <phase_2+0x7e>
804952c: 8b 45 dc mov -0x24(%ebp),%eax
804952f: 3d a3 00 00 00 cmp $0xa3,%eax
8049534: 74 0c je 8049542 <phase_2+0x3b>
8049536: e8 ad 09 00 00 call 8049ee8 <explode_bomb>
804953b: b8 00 00 00 00 mov $0x0,%eax
8049540: eb 43 jmp 8049585 <phase_2+0x7e>
8049542: c7 45 f4 01 00 00 00 movl $0x1,-0xc(%ebp)
8049549: eb 2f jmp 804957a <phase_2+0x73>
804954b: 8b 45 f4 mov -0xc(%ebp),%eax
804954e: 8b 44 85 dc mov -0x24(%ebp,%eax,4),%eax
8049552: 8b 55 f4 mov -0xc(%ebp),%edx
8049555: 83 ea 01 sub $0x1,%edx
8049558: 8b 54 95 dc mov -0x24(%ebp,%edx,4),%edx
804955c: 8b 4d f4 mov -0xc(%ebp),%ecx
804955f: 01 c9 add %ecx,%ecx
8049561: 29 ca sub %ecx,%edx
8049563: 83 c2 01 add $0x1,%edx
8049566: 39 d0 cmp %edx,%eax
8049568: 74 0c je 8049576 <phase_2+0x6f>
804956a: e8 79 09 00 00 call 8049ee8 <explode_bomb>
804956f: b8 00 00 00 00 mov $0x0,%eax
8049574: eb 0f jmp 8049585 <phase_2+0x7e>
8049576: 83 45 f4 01 addl $0x1,-0xc(%ebp)
804957a: 83 7d f4 05 cmpl $0x5,-0xc(%ebp)
804957e: 7e cb jle 804954b <phase_2+0x44>
8049580: b8 01 00 00 00 mov $0x1,%eax
8049585: c9 leave
8049586: c3 ret
先看到<read_n_numbers>,我英语好,擅长翻译,花几分钟看了一下,意思是读取n个数。这里都直接说吧,n就是6。也就是说这一题需要输入六个数字。
解题思路与上一题完全一样。。ni调试。
等等,先看看ni到哪里。我输入的六个数字是123456。有顺序,而且比较小,便于观察题的规律。
看第一个boom前几行,于是锁定 804952f。开始调试!
804952f: 3d a3 00 00 00 cmp $0xa3,%eax
8049534: 74 0c je 8049542 <phase_2+0x3b>
8049536: e8 ad 09 00 00 call 8049ee8 <explode_bomb>
发现这里是1,也就是我们输入的第一个数,但是cmp的是0xa3,换成十进制就是163,对!聪明如你,已经想到第一个数应该是163.
然后相等就跳转,从而避开了炸弹。je 8049542 <phase_2+0x3b>
所以!!!!!!自己找一下,应该可以找到那剩余的5个数吧。。
好吧,不可以。那就一起来看一下吧,简单提示一下。
首先第一个数应该对才行。重新运行,输入r.
然后单步调试。
8049566: 39 d0 cmp %edx,%eax
8049568: 74 0c je 8049576 <phase_2+0x6f>
804956a: e8 79 09 00 00 call 8049ee8 <explode_bomb>
到这一行。快去调试!【doge】
cmp的是2和162,聪明如你,是不是已经想到第二个数是什么了。。。。。。。对,就是162,太聪明了叭【破音】。
好了,剩下的不写了,步骤都一样,最后结果6个数。
保存进答案文件夹里。
下一个。
实验阶段3 - 条件/分支
08049587 <phase_3>:
8049587: 55 push %ebp
8049588: 89 e5 mov %esp,%ebp
804958a: 83 ec 18 sub $0x18,%esp
804958d: c7 45 f4 00 00 00 00 movl $0x0,-0xc(%ebp)
8049594: c7 45 f0 00 00 00 00 movl $0x0,-0x10(%ebp)
804959b: 8d 45 e8 lea -0x18(%ebp),%eax
804959e: 50 push %eax
804959f: 8d 45 ec lea -0x14(%ebp),%eax
80495a2: 50 push %eax
80495a3: 68 ff b1 04 08 push $0x804b1ff
80495a8: ff 75 08 pushl 0x8(%ebp)
80495ab: e8 20 fb ff ff call 80490d0 <__isoc99_sscanf@plt>
80495b0: 83 c4 10 add $0x10,%esp
80495b3: 89 45 f0 mov %eax,-0x10(%ebp)
80495b6: 83 7d f0 01 cmpl $0x1,-0x10(%ebp)
80495ba: 7f 0f jg 80495cb <phase_3+0x44>
80495bc: e8 27 09 00 00 call 8049ee8 <explode_bomb>
80495c1: b8 00 00 00 00 mov $0x0,%eax
80495c6: e9 93 00 00 00 jmp 804965e <phase_3+0xd7>
80495cb: 8b 45 ec mov -0x14(%ebp),%eax
80495ce: 83 e8 35 sub $0x35,%eax
80495d1: 83 f8 09 cmp $0x9,%eax
80495d4: 77 63 ja 8049639 <phase_3+0xb2>
80495d6: 8b 04 85 08 b2 04 08 mov 0x804b208(,%eax,4),%eax
80495dd: ff e0 jmp *%eax
80495df: c7 45 f4 d5 00 00 00 movl $0xd5,-0xc(%ebp)
80495e6: eb 5d jmp 8049645 <phase_3+0xbe>
80495e8: c7 45 f4 d5 00 00 00 movl $0xd5,-0xc(%ebp)
80495ef: eb 54 jmp 8049645 <phase_3+0xbe>
80495f1: c7 45 f4 cb 03 00 00 movl $0x3cb,-0xc(%ebp)
80495f8: eb 4b jmp 8049645 <phase_3+0xbe>
80495fa: c7 45 f4 d5 00 00 00 movl $0xd5,-0xc(%ebp)
8049601: eb 42 jmp 8049645 <phase_3+0xbe>
8049603: c7 45 f4 cb 03 00 00 movl $0x3cb,-0xc(%ebp)
804960a: eb 39 jmp 8049645 <phase_3+0xbe>
804960c: c7 45 f4 d5 00 00 00 movl $0xd5,-0xc(%ebp)
8049613: eb 30 jmp 8049645 <phase_3+0xbe>
8049615: c7 45 f4 cb 03 00 00 movl $0x3cb,-0xc(%ebp)
804961c: eb 27 jmp 8049645 <phase_3+0xbe>
804961e: c7 45 f4 cb 03 00 00 movl $0x3cb,-0xc(%ebp)
8049625: eb 1e jmp 8049645 <phase_3+0xbe>
8049627: c7 45 f4 d5 00 00 00 movl $0xd5,-0xc(%ebp)
804962e: eb 15 jmp 8049645 <phase_3+0xbe>
8049630: c7 45 f4 cb 03 00 00 movl $0x3cb,-0xc(%ebp)
8049637: eb 0c jmp 8049645 <phase_3+0xbe>
8049639: e8 aa 08 00 00 call 8049ee8 <explode_bomb>
804963e: b8 00 00 00 00 mov $0x0,%eax
8049643: eb 19 jmp 804965e <phase_3+0xd7>
8049645: 8b 45 e8 mov -0x18(%ebp),%eax
8049648: 39 45 f4 cmp %eax,-0xc(%ebp)
804964b: 74 0c je 8049659 <phase_3+0xd2>
804964d: e8 96 08 00 00 call 8049ee8 <explode_bomb>
8049652: b8 00 00 00 00 mov $0x0,%eax
8049657: eb 05 jmp 804965e <phase_3+0xd7>
8049659: b8 01 00 00 00 mov $0x1,%eax
804965e: c9 leave
804965f: c3 ret
这一行很熟悉。应该知道什么意思叭。
读取2个数,所以这一关也是要读取两个数。
80495a3: 68 ff b1 04 08 push $0x804b1ff
很容易能够看出第一个炸弹爆炸的地方是要判断是不是读取了两个数。
然后看代码,调试。ni到哪一行呢????
80495d6: 8b 04 85 08 b2 04 08 mov 0x804b208(,%eax,4),%eax
发现有个明码地址,太猖狂了,先看看它。
啥也不是。。
看代码吧。
80495ce: 83 e8 35 sub $0x35,%eax
80495d1: 83 f8 09 cmp $0x9,%eax
80495d4: 77 63 ja 8049639 <phase_3+0xb2>
8049639是boom
ja是大于
eax先减0x35也就是53,然后cmp了0x9,也就是9,小学加减法,所以eax是53+9=62.最大是62,先试试62看看
更改答案的输入,重新来。
继续吧,,看代码到哪一行比较好呢
8049648: 39 45 f4 cmp %eax,-0xc(%ebp)
804964b: 74 0c je 8049659 <phase_3+0xd2>
804964d: e8 96 08 00 00 call 8049ee8 <explode_bomb>
对8049648这一行,来吧,调试
好像不大对。em…
细心点!慢慢看,与随后一个boom相邻,在它上面不远处,也有个boom,那么什么时候会用到它呢。调试康康。
发现异常!
单步调试的时候这里跳转的很多,所以看看它有什么猫腻。
8049630: c7 45 f4 cb 03 00 00 movl $0x3cb,-0xc(%ebp)
8049637: eb 0c jmp 8049645 <phase_3+0xbe>
8049639: e8 aa 08 00 00 call 8049ee8 <explode_bomb>
这里有比较?按捺住你的小激动,显然这里是结果。
嗯,没错就是971。
验证一下。第一个是62党的时候,第二个是971。
没毛病搞定。
当然这题是条件语句,根据第一个数不同,结果不唯一。
休息一下,累了累了。读文章的你,再接再厉哦!
等点赞有好多的时候,再继续更hhh
转载:https://blog.csdn.net/cena1001/article/details/109570188