二进制拿炸弹实验报告完整版.docx
- 文档编号:11454812
- 上传时间:2023-03-01
- 格式:DOCX
- 页数:29
- 大小:5.44MB
二进制拿炸弹实验报告完整版.docx
《二进制拿炸弹实验报告完整版.docx》由会员分享,可在线阅读,更多相关《二进制拿炸弹实验报告完整版.docx(29页珍藏版)》请在冰豆网上搜索。
二进制拿炸弹实验报告完整版
课程名称:
计算机系统原理实验
实验课时:
32课时
实验项目名称:
BombLab二进制炸弹
实验材料:
可执行文件bomb、源代码bomb.c、说明README
实验环境:
Linux操作系统(安装虚拟机和Ubuntu)
实验工具:
调试工具gdb
实验内容:
程序运行中有6道关卡(phases),每道关卡需要用户在终端上输入特定的字符串。
输入正确则炸弹解除,错误则炸弹引爆。
实验方法:
1.将程序反汇编成汇编语言。
2.结合C语言文件找到每个关卡的入口函数。
3.然后分析汇编代码,找到每个phase中引导程序跳转到explode_bomb程序的地方。
4.分析其成功跳转的条件——以此为突破口寻找应该输入的字符串!
【实验分析】
预先准备
首先查看整个bomb.c的代码,发现整个炸弹组是由6个小炸弹(函数)组成的。
整个main函数比较简单,函数间变量几乎没有影响。
因此,只需要依次解除6个小炸弹即可。
实验材料只有一个可执行文件,先把他用objdump-d反汇编得到汇编文件。
打开文件发现对应6个phase,对应的汇编文件十分长,因此我们直接步入phase部分。
一、phase_1:
1.利用gdb调试,gdbbomb
2.disassemblephaes_1
经分析,答案极有可能存放在0x804a15c处
3.可看汇编代码如上图所示,以下为栈帧的计算过程
4.我们看到如上图所示,调用了一个
我们找到这个函数来验证我们的猜想是否正确:
5.在gdb下调试,设立断点,并检验
二、phase_2:
1.disassemblephase_2
可看到phase_2的汇编代码以及理解,利用一个循环,不断计算出这个六位密码。
2.栈帧的计算步骤
a[0]=0a[1]=1a[2]=a[0]+a[1]=1a[3]=a[2]+a[1]=2a[4]=3a[5]=5
即答案猜想为 011235,下面进行检验
3.gdb下进行调试检验
总结
通过这次『拆弹』的历练,我对数据在内存中以及汇编的表示方法有了更加深刻的认识,做得过程可能有时候会摸不着头脑,这个时候告诉自己冷静,相信自己。
三、phase_3:
6.利用gdb调试,gdbbomb
7.disassemblephaes_3
通过汇编代码画出栈的情况可看汇编代码如上图所示,以下为栈帧的计算过程
可以看到调用函数sscanf,那么sscanf函数是做什么用的的呢?
查阅资料如下:
function
sscanf
intsscanf(constchar*s,constchar*format,...);
Readformatteddatafromstring
Readsdatafromsandstoresthemaccordingtoparameterformatintothelocationsgivenbytheadditionalarguments,asifscanfwasused,butreadingfromsinsteadofthestandardinput(stdin).
Theadditionalargumentsshouldpointtoalreadyallocatedobjectsofthetypespecifiedbytheircorrespondingformatspecifierwithintheformatstring.
ReturnValue
Onsuccess,thefunctionreturnsthenumberofitemsintheargumentlistsuccessfullyfilled.Thiscountcanmatchtheexpectednumberofitemsorbeless(evenzero)inthecaseofamatchingfailure.
Inthecaseofaninputfailurebeforeanydatacouldbesuccessfullyinterpreted,EOFisreturned.
成功时,该函数返回已成功填充的参数列表中的个数。
在匹配的失败情况下,此计数可以与预期的参数个数匹配,或小于(甚至为零)。
在成功解释任何数据之前输入失败的情况下,将返回EOF
※即这个函数的返回值为函数接收到的有效参数的个数
结合有疑问的语句
0x08048eb5<+20>:
movl$0x804a23e,0x4(%esp)
%eax,(%esp)
查看该处将地址0x804a23e传给了0x4(%esp),0x8(%edp)传给了(%esp)
经过分析,是让我们输入两个十进制整数
加上对sscanf函数的理解并结合上面两句,将函数__isoc99_sscanf@plt的返回值与1比较,即输入的数据数目应该大于一个,那就应该是两个数据了,并且输入的第一个数还要比7小。
当看到汇编代码后半部分时,发现当输入的第一个数大于5时,炸弹依旧会爆炸,因此判断第一个数的取值范围为0-5.
接下来看汇编代码:
此句话有着至关重要的位置,因为它是典型的switch语句,调试*0x804a1a0处,我们看见了其调用的初步步骤,接下来开始实验检查。
设置断点,追踪验证
断点1:
0x08048ed2<+49>:
cmpl$0x7,-0xc(%ebp)
断点2:
0x08048f53<+178>:
cmp-0x10(%ebp),%eax
第三关的初始输入值我们先输入09999进行下一步操作
可以看到,输入第一个之后,系统会生成一个数与输入的第二个数作比较,即和eax作比较,不相同时炸弹就会bomb
由此可知,0147是本关卡的一个正确答案,剩下的结果均可以照此方法得出
本周收获:
一些指令的使用更加轻松一些,结合老师讲的栈的内容做了一些深入复习,同时在计算机系统课上又得到了巩固,效果很好。
四、phase_4:
8.利用gdb调试,gdbbomb
9.disassemblephaes_4
经过分析,是让我们输入两个十进制整数,和第三关一样,查看0x804a23e处的值,结合语句cmp$0x2,%eax,jne0x8048e66
这里将第一个参数传给eax,然后执行js指令则当eax为负数时会引爆炸弹,然后将eax与14作比较当它大于14时会引爆炸弹,压栈,并调用func4函数。
func4函数内部:
经过检验得到函数func4的C语言代码:
intfunc4(intb,inta,inta1)
{
intx,y;
y=b;
y=y-a;
x=y;
x=(unsigned)x>>31;
y=x+y;
y=y/2;
y+=a;
if((y-a1)>0)
{
y=y-1;
func4(y,a,a1);
return2*a;
}
else
{
a=0;
if((y-a1)<0)
{
y=y+1;
func4(b,y,a1);
return2*a+1;
}
}
return0;
}
phase_4最后将14.0,和第一个我们输入的参数传进func4函数,比较func4的返回值,其要求返回值只能为1,否则就会爆炸。
并且第二个参数也只能为1.
经过编译运行,第一个参数值为8、9、11.
即其答案为:
81;
91;
111。
五、phase_5:
1.利用gdb调试,gdbbomb
2.disassemblephaes_4
可以看到,栈进行初始化时,使用了被调用者保存寄存器,并且如前两关一样,查了寄存器的使用如下:
程序寄存器是唯一一个被所有过程共享的资源。
虽然在给定时刻只能有一个过程是活动的,我们必须保证当一个过程(调用者)调用另一个(被调用者)时,被调用者不会覆盖某个调用者稍后会使用的寄存器的值。
为此,IA32采用了一组统一的寄存器使用惯例,所有的过程都必须遵守,包括程序库中的过程。
根据惯例,寄存器eax,edx,ecx被划分为调用者保存(callersave)寄存器。
当过程P调用Q时,Q可以覆盖这些寄存器,而不会破坏P所需要的数据。
另外,寄存器ebx,esi和edi被划分为被调用者保存(calleesave)寄存器。
这意味着Q必须在覆盖它们之前,将这些寄存器的值保存到栈中,并在返回前恢复它们,因为P(或某个更高层次的过程)可能会在今后的计算中需要这些值。
此外,根据这里描述的惯例,必须保存寄存器ebp,esp。
过程P在调用Q之前计算y,但是它必须保证y的值在Q返回后是可用的。
有两种方式可以做到这一点:
1:
它可以在调用Q之前,将y的值存放在自己的栈帧中;当Q返回时,它可以从栈中取出y的值。
2:
它可以将y的值保存在被调用者保存寄存器中。
如果Q,或任何其他Q调用的程序,想使用这个寄存器,它必须将这个寄存器的值保存在栈帧中,并在返回前恢复该值。
因此,当Q返回到P时,y的值会在被调用者保存寄存器中,或者是因为寄存器根本就没有改变,或者是因为它被保存并恢复了。
这里还是要输入两个数,eax要大于1,不然炸弹就爆炸,那么返回值肯定是2。
如果eax此时等于1111,炸弹爆炸,即原输入参数1与0xf作位与不能为1111。
所以参数1的低4位不能都为1.经过分析,参数1的范围为0-14。
可以看到这个地址里存放着一串数字。
接着看,把ebx(也就是0x804a1c0)加4倍eax的地址处的值,传给eax,如果这次的
eax低4位不是1111,就回到
这里当eax不等15时就会一直循环并且每次循环edx会加1,ecx会不断加上eax的值。
而上面那一串数字中也有15这个数字。
将完成循环的ecx和参数2进行比较,相等就顺利结束,可知,参数2的值需要通过循环来计算。
1021478121511041133965
0123456789101112131415
故得到:
15-66-1414-22-11-1010-00-88-44-99-1313-1111-77-33-12
12-5,即有5 12 3 7 11 13 9 4 8 0 10 1 2 14 6 15
第二个输入的参数与ecx的值进行了比较,我们知道ecx是每次eax的值累加得到所以可以计算得出第二个参数是115
得到一组密码为5 115,其他解类推。
六、phase_6:
1.利用gdb调试,gdbbomb
2.disassemblephaes_4
接下来把eax-1与5比,如果小于等于就继续,大于就爆炸,所以eax的值小于等于6,又因为jbe是无符号操作,所以eax的值的范围确定为1、2、3、4、5、6
这段表示,将输入的第二个数的地址给ebx,将地址ebx中的内容与第一个输入作比较,相等时就会爆炸。
将ebp-76的值加1,即ebp-76为2;如果ebp-76的值小于等于5,跳到65行,大于5跳到35行。
ebp-48及其之前的5个数即为要输入的数,
并且它们的取值范围为1-6,相邻两个数不能相等,差值不能是4的倍数。
检查地址0x804c0c4中的内容。
可以看到,这里存放的是一个链表。
有关链表操作时,有一个跳转条件是0x08048d15 <+140>:
cmp$0x1,%ecx,而ecx就是某一个参数,这些参数的值是1~6中的。
在做这一步的分析时,就需要我们假设输入的这个数列是某个顺序才能继续计算下去了,假设原数列为2,3,5,6,1,4,则有参数1为节点2的值,参数2是节点3的值,循环的任务是把原来的数列中值为i的数,换成链表中对应节点i的值。
对于我假定的这个数列,完成这个循环之后的结果就是:
(Node2.data,Node3.data,Node5.data,Node6.data,Node1.data,Node4.data)
判断上一步生成的六个结点的值的大小应该是由大到小排列的,不然就爆炸。
链表中的六个值分别是:
1a7 6c 155 187 3bd 255 要按照从大到小排序,为:
3bd 255 1a7 187 155 6c 故输入的数字应该是5 6 1 4 3 2。
感想:
第六关感觉比较难,用了很长时间,向班级同学问,他们给我做了解答,目前只是做了大概,在他们的讲解下理解了,自己还要多练习,深刻理解汇编代码。
实验材料只有一个可执行文件,先把他用objdump-d反汇编得到汇编文件。
打开文件发现对应6个phase,对应的汇编文件十分长,因此我们直接步入phase部分。
前六关已经解决完成,还有最后一个秘密关卡,接下来解决秘密关卡。
七、phase_defused:
phase_defused的前半部分很简单,我们可以得知,必须要先解开前六关之后才能进入秘密关卡。
前:
0x0804901d<+9>:
mov%gs:
0x14,%eax
0x08049023<+15>:
mov%eax,-0xc(%ebp)
后:
0x080490bb<+167>:
mov-0xc(%ebp),%eax
0x080490be<+170>:
xor%gs:
0x14,%eax
这段汇编代码前后分别出现了如上代码,第一次见到他们,因此我在网上找到了它们的作用:
其为Linux栈溢出漏洞的利用,考查LinuxCanary绕过技术及ROP(Return-Oriented-Programming)攻击负载的构造。
0x01LinuxCanary介绍
首先了解一下Linux的Canary保护机制。
Canary是Linux众多安全保护机制中的一种,主要用于防护栈溢出攻击。
我们知道,在32位系统上,对于栈溢出漏洞,攻击者通常是通过溢出栈缓冲区,覆盖栈上保存的函数返回地址来达到劫持程序执行流的目的:
针对此种攻击情况,如果在函数返回之前,我们能够判断ret地址是否被改写,若被改写则终止程序的执行,便可以有效地应对攻击。
如何做到呢?
一个很自然的想法是在刚进入函数时,在栈上放置一个标志,在函数结束时,判断该标志是否被改变,如果被改变,则表示有攻击行为发生。
LinuxCanary保护机制便是如此,如下:
攻击者如果要通过栈溢出覆盖ret,则必先覆盖Canary。
如果我们能判断Canary前后是否一致,便能够判断是否有攻击行为发生。
说明:
上述图例仅用于说明,实际上canary并不一定是与栈上保存的BP地址相邻的。
0x02LinuxCanary实现
Linux程序的Canary保护是通过gcc编译选项来控制的,gcc与canary相关的参数及其意义分别为:
-fstack-protector:
启用堆栈保护,不过只为局部变量中含有char数组的函数插入保护代码
-fstack-protector-all:
启用堆栈保护,为所有函数插入保护代码。
-fno-stack-protector:
禁用堆栈保护,为默认选项。
在函数开始时,会取gs:
0x14处的值,并放在%ebp-0xc的地方(mov%gs:
0x14,%eax,mov%eax,-0xc(%ebp)),在程序结束时,会将该值取出,并与gs:
0x14的值进行抑或(mov-0xc(%ebp),%eax,xor%gs:
0x14,%eax),如果抑或的结果为0,说明canary未被修改,程序会正常结束,反之如果抑或结果不为0,说明canary已经被非法修改,存在攻击行为,此时程序流程会走到__stack_chk_fail,从而终止程序。
movl $0x804a200,0x4(%esp)这里读取为%D%D%S 中输入的%s为DrEvil就能开启隐藏关卡,而且这个字符串应该附在某个答案为两个数字的炸弹后面。
之前破解炸弹输入为两个参数的有3、4、5关,分别在他们后面加上DrEvil
在第四关后加上DrEvil后成功找到隐藏关。
八、disassemblesecret_phase:
0x08048c22<+7>:
call0x8049206
0x08048c3a<+31>:
call0x80488b0
开始就看到其调用了两个函数。
从而可以推断出此关卡应该是一个字符串作为参数
第二为strtol()函数。
strtol()函数的参数原型为longintstrtol(constchar*nptr,char**endptr,intbase),作用是将一个字符串转换成一个长整数,又因为参数0xa被入栈,所以推测这个函数是将输入的字符串转换成十进制长整数赋给$eax作为返回值.,且jbe8048c50
可以看到接下来调用函数fun7
fun7:
因为递归调用结束后:
*a1==a2,
0x08048c60<+69>:
cmp$0x5,%eax
0x08048c63<+72>:
je0x8048c6a
0x08048c65<+74>:
call0x80490d1
最外层返回值为5。
那么反递归可得:
(eax)*2+1=5-->eax=2 进行下面的循环 有*a1 (eax)*2==2-->eax=1 进行上面的循环有*a1>a2 a1+=4; (eax)*2+1=1-->eax=0进行下面的循环 有*a1 *a1=a2时跳出; 之前已经判断出a1=36,接下来分别查看: 地址0x804c0dc,这里存储的数字就是我们所要输入的0x2f,也就是十进制的47。 2f大于24走下面循环2f小于32走上面循环2f大于2d走下面循环2f=2f回退递归
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 二进制 炸弹 实验 报告 完整版
![提示](https://static.bdocx.com/images/bang_tan.gif)