问题描述
炸弹实验
实验目的
本次实验为熟悉汇编程序及其调试方法的实验。
实验内容包含2个文件bomb(可执行文件)和bomb.c(c源文件)。
实验主题内容为:程序运行在linux环境中。程序运行中有6个关卡(6个phase),每个phase需要用户在终端上输入特定的字符或者数字才能通关,否则会引爆炸弹!那么如何才能知道输入什么内容呢?这需要你使用gdb工具反汇编出汇编代码,结合c语言文件找到每个关卡的入口函数。然后分析汇编代码,找到在每个phase程序段中,引导程序跳转到“explode_bomb”程序段的地方,并分析其成功跳转的条件,以此为突破口寻找应该在命令行输入何种字符通关。
实验需要用到gdb工具,可到网上查找gdb使用方法和参数。
实验环境
Ubuntu 16.04 32位
实验内容及步骤
一条来自老学姐的忠告:
说实话这个炸弹实验还是蛮有趣的,建议大家自己多尝试,实在没有思路了再来看网上的解决方案,而且做完以后分析汇编的能力真的大幅度提升呐!!!还有还有,做这个实验一定要一鼓作气,最好是找一天专门来拆,还有隐藏关,别被它吓到,其实不难,主要是看如何进去。
好啦,学姐不唠叨啦
下面咱们继续学习之旅
我爱学习!!!!
【实验原理】
二进制炸弹是作为一个目标代码文件提供给学生们的程序,运行时,它提示用户输入6个不 同的字符串。如果其中任何一个不正确,炸弹就会“爆炸”:打印出一条错误信息。学生通过反汇 编和逆向工程来确定是哪六个字符串,从而解除他们各自炸弹的雷管
【实验过程】
一、准备阶段
1、下载好32位的实验代码后,将文件解压缩并且通过共享文件夹操作将文件添加到虚拟机中,双击查看bomb.c代码,将c代码完整看了一遍,发现看这里的c代码是无从下手的,代码中只含有主函数,触发炸弹的过程被隐藏了起来。阅读老师给出的readme文件后,得知本题需要在Linux终端中对汇编代码进行分析并且调试才能得出答案。
2、输入objdump -d bomb,将可执行文件反汇编成汇编文件,为方便查看,可以使用objdump -d bomb >1.txt将汇编代码输出到txt文档里
3、试探一下炸弹爆炸的效果,gdb调试该可执行文件,随便输入一个字符串,果然爆炸了
二、开始拆炸弹
首先找到main函数,发现它调用了从phase1到phase6这六个函数。这几个函数就是每一关需要看懂的函数。
第一关:首先找到phase1,其代表的应该是第一关,代码如下:
首先进行汇编代码的分析,首先是esp指针向下移,创造了一个更大的空间,然后代码$0x804a1e4,0x4(%esp)有立即数,是将该地址的值传递到%esp+0x4的位置,输入gdb bomb 进入调试状态,使用x/s 0x804a1e4查看该地址的内容,回车显示处字符串“I am not part of the problem. I am a Republican.”,下面的代码语句mov 0x20(%esp),%eax是将输入的字符串放进%eax中,然后下面一句是将其放进%esp中,再调用函数<string_not_equal>,容易推测得出要输入的应该是地址0x804a1e4的内容。
je是如果等于则跳转,test %eax %eax是将两者进行逻辑与运算。分析可知,如果%eax!=0时,会调用<explode_bomb>。
验证:设置断点b phase_1,输入run运行,进入程序,在输入提示的下一行输入“I am not part of the problem. I am a Republican.”,终端显示“phase 1 defused. How about the next one?”,第一关顺利通过。
第二关:找到phase2,其代表第二关,汇编代码如下:
下面进行汇编代码的分析:首先很容易观察到在phase_2中存在对于函数<read_six_number>的调用,通过分析可知第二关需要输入六个正确的数字,数字怎么推算出来就需要看下面的汇编代码了。
jns 8048bf5 <phase_2+0x41>是比较%esp+0x18与0,如果%esp+0x18不为负则跳转,如果为负则会引发炸弹。%esp+0x18是我们需要输入的第一个数,推测得知这里需要输入一个非负数,这里我按照了自己的喜好选择了喜欢的1,然后跳转到一个循环,第二个数到第六个数都由循环推测出。首先将eax与ebx置1,随后的关键指令是add 0x14(%esp,%ebx,4),%eax与cmp %eax,0x18(%esp,%ebx,4),判断当前输入的数与上一个数+当前%eax的值是否相等,不等就会触发炸弹。随后eax与ebx加1,继续循环,直到不满足循环条件.
设几个数为num[1]-num[6],其中num[1]为非负数,num[2]至num[6]的规律可以用以下等式来概括:num[n]=num[n-1]+n-1
验证:终端输入“1 2 4 7 11 16”显示“That’s number 2. Keep going!”第二关成功通过。
第三关:找到phase_3,其汇编代码如下:
下面进行汇编代码的分析:
首先能很明显的发现函数scanf的调用,在调用函数之前,注意到存在立即数寻址:movl $0x804a23e,0x4(%esp)。输入指令x/s 0x804a23e,查看到该地址存放的内容为:%d %c %d:
即输入三个内容,两个数字加一个字符。cmp $0x2,%eax表示必须输入至少两个参数。往下到达cmpl $0x7,0x28(%esp)与ja 8048d41 <phase_3+0x140>,输入的第一个参数必须小于7,否则会爆炸。再往下可以看到jmp 0x804a260(,%eax,4),这里是典型的switch语句,即跳转到以地址0x804a260为基址的跳转表中。输入p/x *0x804a260,可以查看到对应的地址为0x8048c50。
为简单起见,我的第一个参数选择0,则这里计算出来的地址是0x8048c50,找到该地址,执行指令mov $0x78,%eax以及cmpl $0x39f,0x2c(%esp),推测%esp+2c的值必须等于0x39f,接下来跳转到0x08048d4b,是cmp 0x27(%esp),%al,推测%esp+0x27的值应该等于%al的值,%al即%eax的低8位,也就是跳转之前赋值的0x78。于是得出结论,在输入的第一个数为0的情况下,答案分别为0 x 927。
下面使用Linux终端验证:
输入0 x 927 ,回车显示“Halfway there”,第三关成功通过。
第四关:找到phase_4,其汇编代码如下:
下面进行汇编代码的分析:
首先找到movl $0x804a3cf,0x4(%esp),这里又是一个立即数,使用gdb调试查看该地址的值,显示出“%d %d”,可以推测出这一关是需要输入两个数。接着判断scanf函数的返回值,必须要输入两个参数。接下来:cmpl $0xe,0x18(%esp)要求第一个数必须首先满足小于0xe的条件,接下来又调用了函数func4(),此函数的汇编代码如下:
调用函数func4()后的返回值必须要小于等于3,所以这里有对于第一个数有了更进一步的限制,分析func4()的功能,发现其实际上是一个递归,将其转换为c语言,由于计算递归比较麻烦,所以我使用了代码来计算出所有满足要求的数。
运行发现结果有:12,13,所以第一个数的取值情况只有12或者13两种。对于第二个要输入的数,cmpl $0x3,0x1c(%esp)限制了第二个输入的数只能为3。
验证:取其中一种答案组合:13,3,回车显示“So you got that one. Try this one”,第四关成功通过。
第五关:找到phase_5,其汇编代码如下:
下面进行汇编代码的分析:
由string_length和之后的cmp $0x6,%eax可以推测要输入一个长度为6的字符串。接下来是一个循环,首先将edx与eax置0,然后每次循环eax加1,直到6时退出循环,每次循环中movzbl (%ebx,%eax,1),%ecx以及and $0xf,%ecx即依次取出字符串中字符的最后一个字节,然后将这个值与0x0804a280相加后的地址里的值累加到最后的值与0x45比较。现在分析到这样的程度凑答案是很难的,使用gdb查看0x0804a280开始的16个地址分别存储的值(因为是十六进制)
查看ASCII码表的a到z的低四位,与上面的相对应。这时候拼凑就相对更容易,凑出来0x45=0xe+5*0xb。也就是对应的jlllll。
验证:Linux终端输入jlllll,回车显示“Good work! On to the next…”,第五关成功通过。
第六关:找到phase_6,其汇编代码如下:
下面进行汇编代码的分析:
首先依然是快速浏览一遍先搞清楚本关的目的,call 80491dc <read_six_numbers>表明本关又是需要输入六个数。紧随其后是一个略复杂的循环,循环中第一个关键部分是sub $0x1,%eax, cmp $0x5,%eax以及jbe 8048e9c <phase_6+0x2f>,也就是每个数都必须小于等于6,第二个关键部分的语句是一个嵌套的循环,mov 0x10(%esp,%ebx,4),%eax, cmp %eax,0xc(%esp,%esi,4),此部分的意思是对于每一次循环当前数的后面的数都不能等于它,一句话总结就是六个数互不相等且小于等于6.
判断每个数互不相等后,接着跳转到了地址0x8048ee3,此部分的代码过于复杂,我解题的时候是靠猜测的。分析到mov $0x804c13c,%edx时,发现又有立即数,本着在炸弹实验中不能放过任何一个立即数的原则,打开终端尝试输入“x/s 0x804c13c”以字符串的格式查看内容,但是输出的内容非常奇怪,又尝试“x/50w 0x804c13c”将从地址0x804c13c开始的50句打印出来,输出的内容依然看不懂,于是参考了资料,发现这里的地址原来是一个链表的指针,于是尝试输入“p/x *0x0804c13c@32”,查看列表内容,结果如下图所示:
三个数一组,第一个数是一个权值,第二个数按照123456排序下来,第三个数是下一个节点的首地址。中间那段代码我只看懂了一部分,cmp %eax,(%ebx),jge 8048f32 <phase_6+0xc5>,此部分的代码存在很多比较与移动数据,推测可能是在排序,具体按照什么排序,又猜测可能是按照权值在排序,在此列表中按照权值排序得出的答案是:6 2 5 3 4 1
验证:在终端中输入6 2 5 3 4 1,回车显示“Congratulations! You’ve defused the bomb!”第六关成功通过。炸弹成功拆除咯!
隐藏关:
由于phase_defused在之前的关卡中都没有出现过,因此推测其可能与隐藏关有关,下面对其进行分析:
以下是汇编代码:
根据上面的经验,对所有立即数都进行值的查看,可以得到:
这个说明了参数要有两个整数,一个字符串
有一个判断字符串相等的函数,查看地址内容可知第四关后面要输入DrEvil才开始隐藏关。
Secret_phase汇编分析:
下面对函数fun7进行分析,fun7的返回值要为3,查看fun7汇编代码;
将其转换为c++代码,
此函数会用于对一颗二叉树进行查询操作,最初传入函数的那个地址就是根结点的地址,具体的返回值来说就像代码中写的那样,当当前节点的值等于查询操作时,返回0,否则根据值的大小判断是继续查询左子树还是右子树。
二叉树的存储如下:
要思考得到3,方法有2*(0*2+1)+1,即二叉树遍历顺序为右右,据此,应该输入0x6b,即十进制下的107。
验证:输入107,显示“Wow, you ‘ve defused the secret stage!”文章来源:https://www.toymoban.com/news/detail-425444.html
至此,全部炸弹成功解除!
文章来源地址https://www.toymoban.com/news/detail-425444.html
到了这里,关于计算机系统实验2:炸弹实验bomb的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!