二进制炸弹攻略

X86汇编基础二进制炸弹

实验内容

这个实验是一个过关游戏,共有六关。在一台Linux服务器上有一个可执行的文件炸弹,运行它游戏就开始了。每一关都要输入正确的密码,才可以通过,否则炸弹爆炸。通过对炸弹进行反汇编得到相应的反汇编文件。阅读反汇编代码,根据代码之间的内部逻辑,找到相应的通关密钥,即可过关。

实验过程

进行破解关卡之前,先使用PUTTY登录远程Linux主机进行实验操作。并反汇编利用ftp下载反汇编代码。
具体语句对应的含义及注释请见报告末尾代码部分,此处仅贴出关键部位的截图和推理过程。

第一关
心路历程

第一关是演示关,教程上面有全部的破解过程,所以刚开始拆第一关更多的是新奇,看着一条条汇编语句也不是很熟悉但是充满了好奇。初步学会了查看内存内容与判读那函数的返回值,对后续的拆弹过程起到引路的作用。

代码分析

首先阅读第一关的反汇编代码,在反汇编文件中找到dump的位置,前三行我们可以看成是一个套路,对栈指针进行操作,对真正的程序阅读没有大的影响。在第四到六行出,我们看见我们输入的参数和一个地址一同传入了<strings_not_equal>函数,根据函数名我们大胆猜测这是判断两个字符串是否相等的函数。之后第八行判断eax寄存器是否为零,如果不为零就跳转到8048b76处引爆炸弹。
所以,为了炸弹不被引爆,只需要输入的字符串与同样作为参数传入<string_not_equal>函数的地址值的内容相同即可。
我们知道ebp+8这个位置是当前函数的第一个参数,由bomb.c知就是我们从屏幕上输入的那个字符串,函数把第一个参数和一个地址0x804a0ff同时传入了比价函数。
我们先在phase_1处设置一个断点,然后运行程序。由于我们此时还不知道答案,所以先随意输入一个字符串。
通过gdb反汇编工具中的x/s指令可以查看地址0x804a0ff中的内容。
在这里插入图片描述
所以“The future will be better tomorrow.”就是第一关的答案。
我们输入这个字符串,果然~hhhhh
在这里插入图片描述

伪代码

在这里插入图片描述

本关密钥

The future will be better tomorrow.

第二关
心路历程

第一关相对简单,代码很短,而且教程上的讲解很详细,到了第二关就没有那么简单了。记得做第二关的时候刚刚上手,上面那好多东西都不是很懂,反汇编工具也都不太会用,对着百度百科一条一条的查各个指令的意思,到最后也看不懂到底是说了些什么,后来没有办法就用gdb的ni指令一条一条的走,看怎么执行,硬生生把逻辑推理题做成了找规律≡(▔﹏▔)≡搞了整整一个下午才把第二关搞出来真的是深深地怀疑自己的智商╯︿╰ 不过不过呢,做过了第二关之后就越来越上手了,做后面的关明显简单清楚了很多,等破解完后面的关卡再回过头来看第二关,结构就清晰了很多。

破解过程

首先在phase_2处设置一个断点,运行程序,在输入第一关的答案之后,再随意输入一个值,由于有断点,程序运行到如图所示处停了下来。
在这里插入图片描述
继续运行到8048b96处,程序调用了<read_six_numbers>函数。我们看一下<read_siz_numbers>的函数体,里面同样是将一个地址值传入了函数
在这里插入图片描述
我们同样用gdb查看该地址处的内容如下所示
在这里插入图片描述
所以我们可以知道第二关需要按照如上格式输入六个数字。如果格式不正确则会引爆炸弹。之后程序运行到如图所示处。
在这里插入图片描述
将我们输入的第一个数字和5进行了比较,如果第一个数字比5大则跳转至箭头所示处,直接引爆炸弹,所以我们得出第一个输入的数字一定要小于等于五。
如果第一个数字小于等于5,则不跳转继续执行。在将ebx寄存器的值赋为1之后,运行了如下程序

在这里插入图片描述
可以知道ebx作为计数变量,每次循环ebx自增,并和6比较,所以一共循环了六次。其中语句
在这里插入图片描述
是指将地址ebp+4ebx-0x28中的值赋值给eax寄存器,是偏移寻址和比例寻址的结合。之后的代码段进行了一系列的赋值和移位操作,转换为C语言代码如下图所示
在这里插入图片描述
移位即为将原数字扩大相应的倍数,如果移1位,即为
2,两位即为4,所以本关的意思是:所输入的第一个数是一个小于等于五的数,接下来的第二个数是第一个数2,第三个数是第二个数4,第四个数是第三个数8……以此类推。

本关密钥
1 2 8 64 1024 32768
2 4 16 128 2048 65536
3 6 24 192 3072 98304
4 8 32 256 4096 131072
5 10 40 320 5120 163840

如上五组数都为本关的答案,随便输入一组即可过关

第三关
心路历程

第三关好长鸭ヽ(*。>Д<)o゜,但是上手了之后就明显好做了很多嘛(ง •_•)ง

破解过程

同样是在phase_3设置了一个断点,程序运行至如图所示的地方停了下来
在这里插入图片描述
我们同样看到红色框框圈起了一个可以的地址,同样用gdb查看该处的内容,如图所示
在这里插入图片描述
可以知道本关需要输入两个数字,中间用一个空格分开。
其中绿色框圈起来的部分是对输入格式进行检查,如果输入符合格式,寄存器eax会被置为2,不会跳转。否则会跳转到引爆炸弹。之后程序运行至如下所示处
在这里插入图片描述
程序将我们输入的第一个值和7进行了比较,如果我们输入的第一个值比7大,则跳转引爆炸弹,所以可知我们所输入的第一个数字一定比7小。
之后程序将我们输入的第一个值赋给了eax寄存器,并根据eax的值进行跳转,我们可以将这个程序理解成一个switch case语句。我们先随便输入一个比7小的数字,如 4,第二个数字随便输入,如2.然后重新设置断点,运行程序,我们发现当程序运行至0x08048c23处时,单步执行后程序运行到了
在这里插入图片描述
此处,随后跳转至如下图箭头所指处
在这里插入图片描述
该处进行计算伪代码如下所示
在这里插入图片描述
所以可知我们输入的第二个数字应为0。
根据这个小输入的测试,我们可以根据运行的过程推理出第三关的伪代码
在这里插入图片描述
运行结果如下
在这里插入图片描述
但我们同时注意到
在这里插入图片描述

本关密钥
0 -355
1 -971
2 -171
3 -972
4 0
5 -972
如上六组数都为本关的答案,随便输入一组即可过关
第四关
心路历程

第四关真的好难好难啊啊ヽ(≧□≦)ノ
破解的时候其实已经看出来是0x13,但是没有注意到是16进制,一直在输入13,然后一直爆炸,直到最后才发现十六进制的13是十进制的19啊!!!!!!

破解过程

在phase_4调用了函数func4,而在函数func4中又调用了func4,所以可以知道func4是一个递归函数。
我们先来分析phase_4,首先还是对输入格式进行检查,通过查看内存可知第四关的输入仍为两个数字。
程序首先将我们输入的第一个数字和14进行比较,如果输入的第一个数字小于15则跳转否则爆炸。所以第一个数字要小于14。接着phase_4将14,0和我们输入的第一个数字传入了func4。接下来我们分析func4的代码。
首先最开始处还是套路操作,之后在8048cc0处开始分别把第一个参数第二个参数和第三个参数传入给了eax,edx,和ecx寄存器。再接下来是一系列的赋值移位等操作,之后到了8048cee和8048d05处就又一次调用了func4函数,由此我们可以推断出func4是一个递归函数。整体的调用过程如下图所示
在这里插入图片描述
其中如果经过运算后ebx寄存器中的值大于eax寄存器,则按照红色路线执行
如果经过运算后ebx寄存器中的值小于eax寄存器,则按照绿色路线执行。
Func4的C++伪代码如下所示
在这里插入图片描述
其中运算部分的逻辑如下所示
在这里插入图片描述
分析完func4后,我们回过头来继续看phase_4的代码。我们知道func4的返回值保存在eax中,在调用完func4的函数后,在phase_4中,程序将eax中的值和0x13(即19)进行了比较,如果不相等则引爆炸弹,也就是说,我们要求func4执行完后,返回值为0x13(即19)。之后程序又将我们输入的第二个数字和0x13比较,如果不相等则引爆炸弹,相等则通过第四关。所以可以立即确定第二个数字答案为19。至于第一个数字可以通过gdb一个数一个数测试,也可以写一个C语言源程序找出正确的答案,如下所示。
在这里插入图片描述
输出如下所示
在这里插入图片描述

本关密钥
4 19
第五关
心路历程

可能是第四关破解了太久也没破解开,所以一解开第四关心情好到爆炸,一路直接过关斩将第五关也嗖嗖嗖地就解开了o( ̄▽ ̄)ブ

破解过程

二进制炸弹第一关考察字符串,第二关考察数组,第三关考察switch case语句,第四关考察递归函数,而第五关,考察的就是……指针!
首先程序调用了一个函数统计我们输入的字符串的长度,并将返回值和6进行比较,如果不相等就引爆炸弹,所以我们可以确定本关的密钥为一个长度为6的字符串。
在这里插入图片描述
之后我们继续阅读代码,阅读到了这一条语句
在这里插入图片描述
这个是我们之前从未见过的指令,初次见到他我们可能会有些发懵,但是其实它很符合指令的规整性。我们知道这条指令
在这里插入图片描述
就是偏移寻址和比例寻址的结合,含义是eax = eax + 1 + eax * 1.那么其实movzbl指令参数的格式和它是一样当,我们只需要照葫芦画瓢,即edx = ebx + eax * 1 。而我们看到之前eax寄存器被初始化为0,之后又和6进行了比较来确定下一条指令的执行,所以我们可以判断,此处为一个循环,eax保存循环变量的值,循环一共进行6次.同时我们通过查看寄存器内容,发现ebx所保存的地址的内容即为我们所输入的字符串,所以,本条指令即为依此取我们所输入的字符串的各个字母并对其进行操作。注意到指令
在这里插入图片描述
这一操作的真正含义是取我们所输入的字符的ASCII码的低四位,并把它存在edx中。
之后我们发现又出现了一个可疑的地址 0x804a290,我们查看这个地址的内容
在这里插入图片描述
后面那句话我们忽略(我也不知道那是个啥,不过如果考虑它好像搞不了)。也就是说我们的到了一个字符串“maduiersnfotvbyl”我们以edx的内容为下标索引到这个字符串的对应字符并把它存到edx中去,之后又依此把edx中保存过的字符存到了一个地址中。由此就成了以一个新的长度为六的字符串,我们姑且将之称为字符串s。
继续向下阅读代码,有一次出现了可疑的地址
在这里插入图片描述
我们查看这个地址所保存的内容
在这里插入图片描述
这里存储的是一个字符串,之后程序把这个字符串和之前的字符串s一起传入了函数<strings_not_equal>,如果两个字符串不相等,则引爆炸弹。
所以,阅读代码到此处,第五关的含义已经很清晰的,我们要以输入的六个字符的ASCII码的低四位作为字符串“maduiersnfotvbyl”的索引去除对应的六个字符,使这六个字符按顺序组成的新的字符串和"flyers"相同。
f 在“maduiersnfotvbyl”中为第十个,对应的下标为9,ASCII中第四位二进制为9的字符有i,y
l 在“maduiersnfotvbyl”中为第十六个,对应的下标为15,ASCII中第四位二进制为15的字符有o
y 在“maduiersnfotvbyl”中为第十五个,对应的下标为14,ASCII中第四位二进制为14的字符有n
e在“maduiersnfotvbyl”中为第六个,对应的下标为5,ASCII中第四位二进制为5的字符有e, u
r在“maduiersnfotvbyl”中为第七个,对应的下标为6,ASCII中第四位二进制为6的字符有f, v
s在“maduiersnfotvbyl”中为第八个,对应的下标为7,ASCII中第四位二进制为7的字符有g, w

所以本关的密钥我们也可以得到啦~

伪代码

在这里插入图片描述

本关密钥

ionefg ionefw ionevg ionevw
ionufg ionufw ionuvg ionuvw
yonefg yonefw yonevg yonevw
yonufg yonufw yonuvg yonuvw
上述任何一个字符串均为本关答案

第六关
心路历程

不愧是第六关,代码量果然不是前几关能比的╰( ̄ω ̄o)

破解过程

第六关的最开始同样是对栈指针进行操作的套路,接下来我们看见程序又一次调用了函数<read_six_numbers>,所以可以知道这一关的密钥同样是六个数字。
接下来的两行语句
在这里插入图片描述
很明显可以看出这是一个循环控制,esi是一个循环控制变量,eax保存的是数组首地址。由下面的语句
在这里插入图片描述
可以知道循环进行了6次,每次都对数组里的对应元素进行比较即a[i]-1<=5才不会引爆炸弹,所以a[i]<=6,那么我们可以确定,本关密钥是六个1~6的数字。在每一轮循环中,程序不仅仅是对该数字的大小和6进行了比较,比较之后程序跳转
在这里插入图片描述
在内层循环中,程序将外层循环的数字和这个数之后的每一个数字比较,如果相等就会引爆炸弹,也就是说,我们输入的六个数字应该互不相等,所以输入的一定是1 2 3 4 5 6这六个数字,只是应该符合一定的顺序。
继续向下阅读代码,发现又有一个循环,以ebx的内容作为循环变量。
在这里插入图片描述
其中mov $0x804c154,%edx 语句中出现了一个地址,这是很可疑的,我们查看这个地址和其之后的内容,发现它包含六个节点,每个节点占三个字,分别为自身的数据,自身的编号,和其下一个节点的地址。
查看的内容如下所示,查看0x804c154的内容后,我们再一次查看指针的地址所保存的内容
在这里插入图片描述
所以我们可以知道这个是一个链表的结构,而这个循环内就是在遍历链表寻找和编号相对应的节点。在接下来的操作是一系列的赋值。
在这里插入图片描述
开始我也是单纯的推理逻辑,但是发现很难理清,所以后来我采用了另一种方法,就是假设。我先假设我输入的就是1 2 3 4 5 6,然后把这六个数字代入代码中去理解他的内部意义,发现其实这段代码就是把这几个节点按照我们所输入的数字顺序和其编号对应,并以这个顺序输出,就相当于链表顺序的重排。
继续向下阅读代码,这段程序表明,我们需要将链表的数据由大到小排列
在这里插入图片描述
即原本的链表存储是这样的
在这里插入图片描述
我们按照大小顺序将其重新连接
在这里插入图片描述
即可通关

伪代码

在这里插入图片描述

本关密钥

5 6 1 4 2 3

好啦~
到目前位置所有的关卡都已经破解啦~
如果文章有帮助的话欢迎留言点赞哦~
要是发现有什么错误也尽管提出来哦~
大家一起加油呀~

竟然被你发现了(o゚v゚)ノ

没错!我们还有隐藏关!

隐藏关

心路历程

其他关都过了之后开始了浪荡的假期生活,本来以为拆了六关就可以得满分,应该不会有什么人会拆隐藏关,没有想到一看排行榜有一半的人都拆了隐藏关炸弹,一下子觉得不行我也要拆!!!

拆弹过程

隐藏关不愧为真正的大boss,连寻找进入隐藏关的方式都花了我好久的时间。根据压缩包里提供的C语言代码,我们先来看一下phase_defused。首先我们发现此处

程序将地址0x804c7ec中的值和6进行了比较,我们并不知道它的含义,那么我们现在phase_defused处设置一个断点,然后运行程序来查看一下0x804c7ec中的值。结果如下所示
在这里插入图片描述
我们发现每通过一关,地址0x804c7ec 中的内容就会加一,所以可以判断0x804c7ec中存的内容为我们当前通过的关卡数,也就是说只有通过了第六关才能触发隐藏关,否则直接跳出函数。
我们继续向下阅读函数发现又一次出现了可疑的地址
在这里插入图片描述
按照我们的套路遇见不清楚的地址就先查看它的内容,我们发现
在这里插入图片描述
其中的一个地址是指我们的输入格式,而第二个地址输出结果为4 19.是不是觉得这两个数字很眼熟?没错啊!这不是我们第四关的答案嘛!?我重新在第四关设置断点,输入了4 19之后有随便输入了一个字符串,结果,炸弹没有爆炸。这一瞬间感觉自己好像打开了新世界的大门,这个字符串可能就是通往隐藏关的钥匙啊!!!!
再向后阅读代码,中间有一些内容我们现在不清楚他们是在干什么,先继续向下看,又发现了一个老朋友<strings_not_equal>
在这里插入图片描述
按照第一关的套路,我们把压栈参数地址查看内容
在这里插入图片描述
那么我们就大胆猜测,要在第四关输入过答案之后再输入这个字符串“SecretSYSU”,我们进行一下尝试
在这里插入图片描述
啊啊啊啊啊!!!!我们看见了secret_phase的庐山真面目啦(悄咪咪地说是不是应该是difficult而不是different啊( ̄▽ ̄)~*)。
所以接下来就可以分析secret_phase的函数啦~
Secret_phase和第六关相比不是那么长,里面调用了fun7,我们再看fun7里面也自己调用了自己,所以我们可以推断这一个同样是一个递归。在secret_phase中我们首先调用了<read_line>函数,也就是说先读取一行,也就是读取我们所输入的一行,所以目前我们无法确定输入格式。不过我们看见后面将eax(保存着我们的输入-1)和一个数进行比较,我们可以先初步猜测我们的输入也为一个数字。

在这里插入图片描述
这里我们发现程序将我们输入的数字减一后和1000比较,如果大于就会引爆炸弹,所以我们输入的数字的范围应该是1~1001.寄存器ebx中保存的值为我们所输入的数字,程序接下来将我们输入的数字和一个可疑的地址0x804c0a0一起传入了fun7,按照套路,我们查看地址的内容
在这里插入图片描述
目前这个地址的内容并不能给我们提供什么,只能告诉我们fun7的参数应该是一个int* 和一个int型变量。接下来在调用fun7之后返回,我们发现程序判断了eax的值是否为0.如果不为0则爆炸,也就是说我们要求fun7返回的值必须为0.接下来我们先阅读fun7的代码来寻找蛛丝马迹。
可以看出这又是一个递归函数,具体语句含义请见程序代码部分,我们这里贴出这段程序的伪代码
在这里插入图片描述
如上图所示,我们想要返回值为0,则可以是进入了情况①,但任何一重递归都不能进入情况②,我们不妨考虑最简单的一种情况,就是一开始就满足a1==a2,也就是我们传递进来的参数a2 = 0x24 ,即为十进制的32,运行程序
在这里插入图片描述
答案正确!!!!撒花!!!
但是事实上本关不只有这一个答案,可以使它嵌套调用fun7,如第一轮调用时
a1>a2,进入情况一,而第二轮时有第二轮的参数a1==a2,相对于第一轮就是(a1+1)==a2,我们可以查看*(a1+1)的内容
在这里插入图片描述
所以8同样为隐藏关的答案,那么以此类推,本关的答案可以有很多。到此为止,二进制炸弹就全部破解结束啦!真的好辛苦哇o((>ω< ))o

本关密钥

32

心得体会

体会和建议。
1、 在最开始实验的时候,会认真的看每一条指令,认真的分析每一条的含义,会在一条指令的地方卡很久,后来发现这其实是没有必要的,很多语句是对栈指针进行操作,而非是真正的数据,这样的语句不用太过纠结。
2、 在刚开始的时候不清楚gdb的使用,不会查看寄存器的值,也不会查看内存的值,整个过程很痛苦。后来才发现gdb是很好用的,熟悉gdb的指令对整个学习过程很有帮助。
3、 我们反汇编得到的操作数和我们所学习的MIPS汇编指令操作数的顺序是反过来的,所以刚开始的时候会发现很多地方逻辑都是反的。
4、 汇编语言中$加操作数表示立即数
5、 线性地址格式套路section:displacement(base, index, scale) //线性地址=section + displacement + base + index*scale
6、 分析过程中可以根据跳转指令将程序分段,判断出哪一部分是循环,哪一部分是分支判断,这样分割程序的能力是程序员应该具备的
7、 破解炸弹的过程是对汇编指令的一次熟悉,本次实验中我对跳转指令,赋值指令,比较指令等等都有了更深刻的了解,并且初步具备了举一反三的能力,了解到指令的规整性可以使我们很容易地根据以前学习的内容推断出指令的含义,这也更强调了规整性的重要。
8、 分析函数时不要只看某几条语句,看到不懂的地方就先向下看,根据后面的逻辑反推前面的逻辑。
9、 大胆的猜测也是一个很重要的策略。
10、递归函数和循环往往很复杂,这种时候好的转换为C语言伪代码的能力就变得很重要,对于复杂难以推到的函数,可以利用C语言工具另计算机帮我们寻找答案。

好啦~这回事真的结束了~
总之就是,希望这篇文章对大家有帮助叭~
  • 63
    点赞
  • 242
    收藏
    觉得还不错? 一键收藏
  • 15
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值