windows进程中的内存结构

上传人:xiao****1972 文档编号:84829489 上传时间:2019-03-05 格式:DOC 页数:19 大小:61.50KB
返回 下载 相关 举报
windows进程中的内存结构_第1页
第1页 / 共19页
windows进程中的内存结构_第2页
第2页 / 共19页
windows进程中的内存结构_第3页
第3页 / 共19页
windows进程中的内存结构_第4页
第4页 / 共19页
windows进程中的内存结构_第5页
第5页 / 共19页
点击查看更多>>
资源描述

《windows进程中的内存结构》由会员分享,可在线阅读,更多相关《windows进程中的内存结构(19页珍藏版)》请在金锄头文库上搜索。

1、用VC写Assembly代码(6)-附录2windows进程中的内存结构 2006-06-08 23:21 1096人阅读 评论(0) 收藏 举报 在阅读本文之前,如果你连堆栈是什么多不知道的话,请先阅读文章后面的基础知识。接触过编程的人都知道,高级语言都能通过变量名来访问内存中的数据。那么这些变量在内存中是如何存放的呢?程序又是如何使用这些变量的呢?下面就会对此进行深入的讨论。下文中的C语言代码如没有特别声明,默认都使用VC编译的release版。首先,来了解一下C语言的变量是如何在内存分部的。C语言有全局变量(Global)、本地变量(Local),静态变量(Static)、寄存器变量(R

2、egeister)。每种变量都有不同的分配方式。先来看下面这段代码:#includeintg1=0,g2=0,g3=0;intmain()staticints1=0,s2=0,s3=0;intv1=0,v2=0,v3=0;/打印出各个变量的内存地址printf(0x%08x/n,&v1);/打印各本地变量的内存地址printf(0x%08x/n,&v2);printf(0x%08x/n/n,&v3);printf(0x%08x/n,&g1);/打印各全局变量的内存地址printf(0x%08x/n,&g2);printf(0x%08x/n/n,&g3);printf(0x%08x/n,&s1)

3、;/打印各静态变量的内存地址printf(0x%08x/n,&s2);printf(0x%08x/n/n,&s3);return0;编译后的执行结果是:0x0012ff780x0012ff7c0x0012ff800x004068d00x004068d40x004068d80x004068dc0x004068e00x004068e4输出的结果就是变量的内存地址。其中v1,v2,v3是本地变量,g1,g2,g3是全局变量,s1,s2,s3是静态变量。你可以看到这些变量在内存是连续分布的,但是本地变量和全局变量分配的内存地址差了十万八千里,而全局变量和静态变量分配的内存是连续的。这是因为本地变量和全

4、局/静态变量是分配在不同类型的内存区域中的结果。对于一个进程的内存空间而言,可以在逻辑上分成3个部份:代码区,静态数据区和动态数据区。动态数据区一般就是“堆栈”。“栈(stack)”和“堆(heap)”是两种不同的动态数据区,栈是一种线性结构,堆是一种链式结构。进程的每个线程都有私有的“栈”,所以每个线程虽然代码一样,但本地变量的数据都是互不干扰。一个堆栈可以通过“基地址”和“栈顶”地址来描述。全局变量和静态变量分配在静态数据区,本地变量分配在动态数据区,即堆栈中。程序通过堆栈的基地址和偏移量来访问本地变量。低端内存区域动态数据区代码区静态数据区高端内存区域堆栈是一个先进后出的数据结构,栈顶地

5、址总是小于等于栈的基地址。我们可以先了解一下函数调用的过程,以便对堆栈在程序中的作用有更深入的了解。不同的语言有不同的函数调用规定,这些因素有参数的压入规则和堆栈的平衡。windowsAPI的调用规则和ANSIC的函数调用规则是不一样的,前者由被调函数调整堆栈,后者由调用者调整堆栈。两者通过“_stdcall”和“_cdecl”前缀区分。先看下面这段代码:#includevoid_stdcallfunc(intparam1,intparam2,intparam3)intvar1=param1;intvar2=param2;intvar3=param3;printf(0x%08x/n,m1);/

6、打印出各个变量的内存地址printf(0x%08x/n,m2);printf(0x%08x/n/n,m3);printf(0x%08x/n,&var1);printf(0x%08x/n,&var2);printf(0x%08x/n/n,&var3);return;intmain()func(1,2,3);return0;编译后的执行结果是:0x0012ff780x0012ff7c0x0012ff800x0012ff680x0012ff6c0x0012ff70函数执行时的栈顶(ESP)、低端内存区域var1var2var3RET“_cdecl”函数返回后的栈顶(ESP)parameter1par

7、ameter2parameter3“_stdcall”函数返回后的栈顶(ESP)栈底(基地址EBP)、高端内存区域上图就是函数调用过程中堆栈的样子了。首先,三个参数以从又到左的次序压入堆栈,先压“param3”,再压“param2”,最后压入“param1”;然后压入函数的返回地址(RET),接着跳转到函数地址接着执行(这里要补充一点,介绍UNIX下的缓冲溢出原理的文章中都提到在压入RET后,继续压入当前EBP,然后用当前ESP代替EBP。然而,有一篇介绍windows下函数调用的文章中说,在windows下的函数调用也有这一步骤,但根据我的实际调试,并未发现这一步,这还可以从param3和v

8、ar1之间只有4字节的间隙这点看出来);第三步,将栈顶(ESP)减去一个数,为本地变量分配内存空间,上例中是减去12字节(ESP=ESP-3*4,每个int变量占用4个字节);接着就初始化本地变量的内存空间。由于“_stdcall”调用由被调函数调整堆栈,所以在函数返回前要恢复堆栈,先回收本地变量占用的内存(ESP=ESP+3*4),然后取出返回地址,填入EIP寄存器,回收先前压入参数占用的内存(ESP=ESP+3*4),继续执行调用者的代码。参见下列汇编代码:;-func函数的汇编代码-:0040100083EC0Csubesp,0000000C/创建本地变量的内存空间:004010038B

9、442410moveax,dwordptresp+10:004010078B4C2414movecx,dwordptresp+14:0040100B8B542418movedx,dwordptresp+18:0040100F89442400movdwordptresp,eax:004010138D442410leaeax,dwordptresp+10:00401017894C2404movdwordptresp+04,ecx(省略若干代码):0040107583C43Caddesp,0000003C;恢复堆栈,回收本地变量的内存空间:00401078C3ret000C;函数返回,恢复参数占用的

10、内存空间;如果是“_cdecl”的话,这里是“ret”,堆栈将由调用者恢复;-函数结束-;-主程序调用func函数的代码-:004010806A03push00000003/压入参数param3:004010826A02push00000002/压入参数param2:004010846A01push00000001/压入参数param1:00401086E875FFFFFFcall00401000/调用func函数;如果是“_cdecl”的话,将在这里恢复堆栈,“addesp,0000000C”聪明的读者看到这里,差不多就明白缓冲溢出的原理了。先来看下面的代码:#include#include

11、void_stdcallfunc()charlpBuff8=/0;strcat(lpBuff,AAAAAAAAAAA);return;intmain()func();return0;编译后执行一下回怎么样?哈,“0x00414141指令引用的0x00000000内存。该内存不能为read。”,“非法操作”喽!41就是A的16进制的ASCII码了,那明显就是strcat这句出的问题了。lpBuff的大小只有8字节,算进结尾的/0,那strcat最多只能写入7个A,但程序实际写入了11个A外加1个/0。再来看看上面那幅图,多出来的4个字节正好覆盖了RET的所在的内存空间,导致函数返回到一个错误的内

12、存地址,执行了错误的指令。如果能精心构造这个字符串,使它分成三部分,前一部份仅仅是填充的无意义数据以达到溢出的目的,接着是一个覆盖RET的数据,紧接着是一段shellcode,那只要着个RET地址能指向这段shellcode的第一个指令,那函数返回时就能执行shellcode了。但是软件的不同版本和不同的运行环境都可能影响这段shellcode在内存中的位置,那么要构造这个RET是十分困难的。一般都在RET和shellcode之间填充大量的NOP指令,使得exploit有更强的通用性。低端内存区域由exploit填入数据的开始buffer填入无用的数据RET指向shellcode,或NOP指令的范围NOP填入的NOP指令,是RET可指向的范围NOPshellcode由exploit填入数据的结束高端内存区域windows下的动态数据除了可存放在栈中,还可以存放在堆中。了解C+的朋友都知道,C+可以使用new关键字来动态分配内存。来看下面的C+代码:#include

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 大杂烩/其它

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号