Bufbomb内存炸弹.docx
- 文档编号:3545298
- 上传时间:2022-11-23
- 格式:DOCX
- 页数:12
- 大小:418.56KB
Bufbomb内存炸弹.docx
《Bufbomb内存炸弹.docx》由会员分享,可在线阅读,更多相关《Bufbomb内存炸弹.docx(12页珍藏版)》请在冰豆网上搜索。
Bufbomb内存炸弹
Øwhatprocess_keys34does:
首先来看接下来的代码,显然可以看出来,我们要让程序进入并执行msg2=extract_message2(start,stride);也就是下面的第二个if循环:
那么可以考虑能不能让msg1==’\0’,很显然,第一个循环里process_key34函数不可能直接来改变msg1的值,message是全局变量(msg1的实际指向),key3是局部变量,而且,是在执行了process_key34函数之后才执行msg1赋值的,那么要保证执行第二个if语句,也可以考虑能否让process_key34函数在返回的时候直接跳转到目标代码呢?
如果要这么做,我们就需要修改程序在运行过程中的栈帧结构中的存储数据了。
现在让我们来分析一下程序在调用process_keys34之前的栈帧结构:
从汇编码中我们可以看到在调用之前整个栈帧结构中数据的存储细节:
首先是将参数key3、key4压入栈中保存,然后再传入这些参数(实际上是传递地址)。
所以可以在process_keys34中可以通过&key3的相对定位来改动调用者的栈帧数据。
来看看调用时的栈帧结构:
很显然key3下面就是返回地址,在地址上ret=&key3-1(这里减1就是地址上减4,因为int型占4字节)。
那么key3就求出来了,key3=-1,接下来就是通过地址相差的值求key4了,
还是看下汇编语言吧:
原函数执行完后返回地址为第一行也就是:
0040138E,我们想要它变成的返回地址为最后一行也就是004013BB,从process_keys34函数(*(((int*)&key3)+*key3)+=*key4;)可以看出:
原值加上key4的值就是修改后的值,那么key4=0x004013BB–0x0040138E=0x2D,转化为十进制就是45,即key4=45.
Ødescribingthelineofsourcecodethatisexecutedwhenthefirstcalltoprocess_keys34returns.
解析过程上面已经说过,第一次调用process_keys34函数后,将返回到上面截图中的最后一行代码msg2=extract_message2(start,stride)中执行。
(原因见上一小题解析)。
ØThemeaningofthethirdandfourthkeysintermsofvariablesandaddressesinthedecoderprogram.
详细解析上面解释了。
----------------------------------------------------------------------------------------------------------------------
ØSwap
开始->
Next—>
Next->在调用swap1函数时,调用函数将形参b、a的值压入栈中,然后再调用函数。
在进入被调用函数时,会自动保存返回的地址,即地址0x00401413。
这个可以通过esp的改变看出,进入函数时,esp改变小,说明栈压入了新的值,而根据这个值我们可以看到,它是一个地址,而这个地址正是我们的返回地址:
0x00401413。
压入旧的ebp,esp减小。
主要是为了保存主函数的ebp,在被调用函数返回时可以找到它
将esp的值赋给ebp由于被调用函数和调用函数的分配空间是紧邻的,所以调用函数的栈顶就是被调用函数的栈底
给被调用函数分配44h大小的空间(这个空间大小是随机的),esp变为:
0012fea0
将x的值赋给temp,我们可以观察temp的地址,它是在esp和edp之间的某个地址,也就是当前帧栈的某个空间。
调用函数结束,做一些例行操作之后,将ebp的值赋给esp,即将栈底改为栈顶,为回到调用函数做准备。
之后压出一个值,将这个值赋给ebp,还记得调用时压入的那个旧的ebp吗?
就是将这个值还原给ebp。
现在要做的就是回到以前的执行地址也就是最开始保存的返回地址0x00401413,而ret就是这个作用。
sp和ebp都回到了被调用前的值
我们可以看到,调用swap1后,a、b的值并没有交换,跟据上面的分析可以知道,其实主函数中的a,b与swap1中的a,b其实是不同的,他们分别保存在不同的地址,所以主函数中a、b的值并不会被调换。
现在我们开始来看调用swap2的过程。
前期准备和后期处理都一样这里不再赘述。
但swap2中压入的是变量b和a的地址。
现在进入被调用函数swap2。
同样的分配44h的空间,同样的改变esp和ebp。
对比swap1和swap2的汇编代码会发现不同之处,swap2是通过地址的方式来改变变量的值,而这个地址和主函数中a、b的地址相同,所以这个地址里面的值已经被改变。
所以当swap2结束后,变量a、b的值已经交换。
个人总结:
函数调用都会在栈中形成独立的栈帧,值传递的函数中,形参会在被调用函数中声明,但它的生存期也就仅仅存在于调用函数的生存期,而地址传递引用的是相同的地址空间。
----------------------------------------------------------------------------------------------------------------------
ØBufbomb
理解题目意思:
看源程序可以发现,好像无论输入什么数据,其返回结果都是1,因为在getbuf()函数中最后一行就是return1,也就是函数按照正常来说会返回1,所以理论上无论输入是什么打印出来的都是0x1。
看起来要达到预期目标:
让程序返回0xdeadbeef。
我们首先想到的方法肯定是把return语句改成returndeadbeef,但是不允许修改源码,所以仔细分析程序的编译、链接、执行的过程即可知道,我们又有一个思路了:
由函数栈帧调用过程可以知道,过程调用时要将参数和返回地址压入栈中,然后进入被调过程执行,待从被调过程返回时,弹出返回地址,将该地址存入%eip寄存器,并转到该地址开始执行。
入栈和出栈均是对%esp指向的内存操作,其基地址为%ebp。
假设,如果我们输入的是机器码,存放于该缓冲区中,注意到程序中的缓冲区只有16字节,利用缓冲区溢出,将本应存放test调用getbuf的返回地址的内存单元修改为存放可执行的机器码的地址,而这个地址就是我们的缓冲区地址,那么在调用完getbuf后会返回到缓冲区地址处执行我们输入的机器码,在机器码中让程序输出oxdeadbeef,并正确返回继续执行。
这样会不会达到目标?
直白一点说吧:
简单来讲,就是输入的时候会超过16字节(缓冲区大小),输入内容覆盖掉ebp(test程序调用getbuf后会把自己的ebp压入栈)与返回地址(getbuf的返回地址)。
想法是将存放test调用getbuf的返回地址的内存单元的内容修改为buf缓冲区的地址。
在缓冲区执行代码:
第一、给eax寄存器赋值deadbeef,也就是更改返回值;第二、实现ret语句,这一句的作用是保证程序正常运行,不会报错,但是在这之前我们已经改变了返回值!
在编译之前我们来看下test调用getbuf的栈帧结构:
栈底部->
高地址=================
Main函数的栈帧部分
=================
Test函数栈帧基址ebp1->
Test函数栈帧基址
------------------------------
+4retAddr(实际上就是test函数中printf语句地址)
=================
Getbuf函数栈帧基址ebp->ebp1(作用是保存test函数基址)
------------------------------
Getbuf函数的栈帧
^
|
Buf|
|
Bufaddr
-----------------------------
Getbuf的栈顶esp->retAddr1getbuf返回地址
==================
Getxs的栈帧
==================
地地址
栈顶->
(基本的注明上面都已经标出来了,就说下====是栈帧分隔线)
分析:
我们输入的数据存放在buf指向的一段内存中,且数据由低地址到高地址存放(图中向上箭头所示)。
如果输入的数据不覆盖ebp+4指向的内存,即buf不溢出,getbuf将永远返回1。
如果输入的数据覆盖ebp+4指向的内存呢?
也就是覆盖retAddr,但考虑到程序正常运行,不能覆盖ebp指向的内存(意思是可以覆盖,只要不改变值就行),即其中的ebp1不能被修改,如果被修改,程序将不能正确返回出现错误。
如果ebp+4指向的内存被修改为新地址,即retAddr被覆盖而存入新地址,则test调用getbuf后将返回到新地址中执行,这个新地址就可以存放一段可执行的代码,做一些我们想要发生的事了!
!
。
接下来要考虑,从程序来看,新地址只有可能是buf了(16字节长)。
如果新填入ebp+4的新地址是buf缓冲区的起始地址,而且地址中已经输入了一些我们想要执行的代码,嘿嘿,接下来就有戏了!
即test调用getbuf后将返回到buf的起始地址处执行我们想要执行的代码,并且“正常”返回,嘿嘿~。
分析完之后我们就可以确定输入了:
可执行机器码
Ebp1
新地址
好了,可以解释下了:
其中ebp1必须还是存放在ebp指向的内存中,这样才能保证程序不会出错,新地址存放在%ebp+4指向的内存中,而且新地址=buf的地址。
还有就是 输入数据的长度:
16B+4B+4B(我们期望执行的机器码不够16字节就补0)。
目标明确后就开始找值了,首先得确定机器码,到底是啥?
首先,我们应该确定可执行机器码的功能:
让程序返回0xdeadbeef
返回到%ebp+4指向的返回地址retAddr处继续执行。
我们看下源程序的返回:
我们要写改的是36下面那条语句,我们要执行的返回语句写成汇编语句就是:
Moveax,0xdeadbeef
第一点完成了,第二点,要返回到%ebp+4指向的返回地址retAddr处继续执行,看下test函数对应的汇编代码:
可以看出,实际上返回地址就是44那条printf语句,也就是0040126A(上面那条汇编代码)
所以昨晚第一步我们只要让程序返回到0040126A继续执行就ok了,就是要将该地址压入栈中,当遇到ret指令时,再将retAddr=0x0040126A从栈中弹出,并跳转到0x0040126A处继续执行,就能保证程序正确返回到test中,并继续执行。
而从getbuf中返回时,返回值在eax中,其值就是0xdeadbeef。
保证机器码执行后返回到retAddr=0x0040126A处继续执行的机器码对应的汇编代码为:
Push0x0040126A
Ret
到此为止,我们已经得到了想要执行的汇编代码,转化为机器码就行了,至于怎么变为机器码VC反汇编里直接可以看到:
Moveax,0xdeadbeef
Push0x0040126A
Ret
-------转化为机器码-------
b8efbeadde
686A124000
C3
0000000000(补齐16字节)
接下来就是确定ebp1和buf地址了:
我们在getbuf函数入口设一个端点:
也就是当程序执行到text函数将要调用getbuf函数时看下ebp值:
0018FEAC
从下图也可以看到,调用getbuf函数时首先会保存text函数的基址:
从第一句就可以看到,pushebp(保存text栈帧基址),此时ebp寄存器的只看下就知道了:
0018FEAC,所以很显然我们得到了ebp1的值了,接下来最后一步,看buf的地址了:
Buf地址为0018FE44。
到此为止,我们已得到了全部要输入的内容:
b8efbeadde686A124000C30000000000
0x0018FEAC
0x0018FE44
考虑到后面两个地址在计算机中是按小端存储的,所以我们的最终输入信息为:
b8efbeadde686A124000C30000000000ACFE180044FE1800
程序运行结果截图如下:
Wonderful!
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Bufbomb 内存 炸弹