Windows和Linux的内存结构区别

发表于:2020-1-22 14:11

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:Asc0t6e    来源:Asc0t6e's Blog

  windows 的内存结构分布
  首先是内存结构分布图, windows 的内存默认是从 0x80000000 位置开始的
  栈的增长方向是从高地址到低地址递增的,用下面一个例子来解释
  例子一:
  esp 是栈指针 ,是 cpu 机制决定的, push 、 pop 指令会自动调整 esp 的值;
  ebp 只是存取某时刻的 esp , 这个时刻就是进入一个函数内后, cpu 会将 esp 的值赋给 ebp ,此时就可以通过 ebp 对栈进行操作,比如获取函数参数,局部变量等,实际上使用 esp 也可以;
  假设执行 print 函数之前 esp=Q
main() {
//执行test前
print(int p1,int p2);
//执行test后
}
#############开始执行print函数#############
push p2; //函数参数p2入栈,esp=Q-4H
push p1; //函数参数p1入栈,esp=Q-8H
call print; //函数返回地址入栈,esp=Q-0CH
//现在进入print内,做些准备工作:
push ebp; //保护先前ebp指针,ebp入栈,esp=Q-10H
mov ebp,esp; //设置ebp等于当前的esp
// 此时,ebp+0CH=Q-4H,即p2的位置
// 同样,ebp+08H=Q-8H,即p1的位置
// 下面是print内的一些操作:
sub esp,20H; //设置长度为10H大小的局部变量空间,esp=Q-20H
// ... ...
// 一系列操作
// ... ...
add esp,20H; //释放局部变量空间,esp=Q-10H
pop ebp; //出栈,恢复原先的ebp的值,esp=Q-0CH
ret 8; //ret返回,弹出先前入栈的返回地址,esp=Q-08H,后面加操作数8H为平衡堆栈
// 之后,弹出函数参数,esp=Q,恢复执行print函数前的堆栈;
  整个的流程图如下,要执行函数之前把 p2 和 p1 压入栈中,栈向低地址递增( Q-4h ),接着把函数地址放入栈中,然后把 ebp 的值放入栈中保存
  例子二:
  EBP 是当前函数的存取指针,即存储或者读取数时的指针基地址; ESP 就是当前函数的栈顶指针。每一次发生函数的调用(主函数调用子函数)时,在被调用函数初始时,都会把当前函数(主函数)的 EBP 压栈,以便从子函数返回到主函数时可以获取 EBP 。
  下面是按调用约定 __stdcall 调用函数 test(int p1,int p2) 的汇编代码
假设执行函数前堆栈指针ESP为0xAAAAAAA ;EBP为0xAAAAAB0
push   p2    ;参数2入栈, ESP -= 4h , ESP = 0xAAAAAAA - 4h =    0xAAAAAA6
push   p1    ;参数1入栈, ESP -= 4h , ESP = 0xAAAAAAA - 8h =    0xAAAAAA2
call test    ;压入返回地址 ESP -= 4h, ESP = 0xAAAAAAA- 0Ch  = 0xAAAAA9D,注意:这里是test函数的返回地址,即在代码段中的地址(偏移)。
;//进入函数内
{
push   ebp                           ;保护先前EBP指针, EBP入栈(即0xAAAAAB0入栈,注意与返回地址区别), ESP-=4h, ESP = 0xAAAAA99
mov    ebp, esp                   ;设置EBP指针指向栈顶 0xAAAAA99
mov    eax, dword ptr  [ebp+0ch]   ;ebp+0ch为0xAAAAAA6即参数2的位置
mov    ebx, dword ptr  [ebp+08h]   ;ebp+08h为0xAAAAAA2,即参数1的位置
sub    esp, 8                     ;局部变量所占空间ESP-=8, ESP = 0xAAAAA91
...
add    esp, 8                     ;释放局部变量, ESP+=8, ESP =  0xAAAAA99
pop    ebp                        ;出栈,恢复EBP, ESP+=4, ESP = 0xAAAAA9D,即把栈中地址0xAAAAA99的内容pop到ebp中
ret    8                     ;ret返回,弹出返回地址,ESP+=4, ESP=0xAAAAAA2, 后面加操作数8为平衡堆栈,ESP+=8,ESP=0xAAAAAAA, 恢复进入函数前的堆栈.
  }
  原来 ESP 就是一直指向栈顶的指针,而 EBP 只是存取某时刻的栈顶指针,以方便对栈的操作,如获取函数参数、局部变量等。
  堆分为堆块和堆表
  (1)引用大佬的图
  (2)堆表
  为了合理地组织堆区中的空闲堆块,提出了堆表的概念。堆表的数据结构决定了整个堆区的组织方式,一般位于堆区的起始位置,用于索引堆区中空闲堆块的重要信息,包括堆块的位置、大小、状态(空闲或占用)。
  (3)堆块
  传统内存统计单位往往是以字节位标准,但处于性能的考虑,堆内存按照大小不同组成不同的块,以堆块为单位进行标识。一个堆块包括两个部分: header 部分和 data 部分。 header 是一个堆块头部的几个字节,用来标识这个堆块自身的信息。 data 是用来在最终分配给用户使用的数据区。
  1)堆块分配
  堆块的分配可以分为三类, Lookaside 分配、普通 Freelist 分配以及 0 号 Freelist(free[0]) 分配。
  Lookaside 分配:寻找到大小匹配的空闲堆块 -> 修改状态为占用 -> 从堆表中解链 -> 给程序返回一个指向堆块的指针
  普通 Freelist 分配:寻找最优的空闲堆块 -> 若失败,寻找次优空闲堆块分配
  0 号 Freelist 分配:从 free[0] 反向寻找最后一个堆块(最大的堆块) -> 若满足要求,再正向搜索最小的满足要求的空闲堆块。
  堆块分配中的“找零钱”现象:当在 Freelist 中无法找到刚好合适的堆块时,此时会分配一个稍微大一点的空闲堆块给程序使用,其过程是首先在这个大块中分配出大小刚好等于请求堆块大小的堆块给程序,然后剩下的部分修改堆块的 header 信息,重新链入到 Freelist 合适的位置。这种方法节约了内存的使用,不会造成大量的内存浪费。
  由于 Lookaside 只有在精确匹配时才会分配,因此不存在“找零钱”现象。
  2)堆块释放
  堆块的释放主要是将堆块修改为空闲状态,然后将堆块链入相应的堆表。所有的释放块都链入堆表的末尾,分配的时候也会首先从堆表末尾分配。
  3)堆块合并
  为了减少内存中的内存碎片,合理有效地利用内存,堆管理系统还需要进行堆块合并操作。
  当两个空闲堆块彼此相邻的时候就会进行堆块合并操作。其过程大致为:
  将两个块从 Freelist 中解链 -> 合并堆块 -> 调整合并后堆块的 header 信息 -> 将合并后的堆块放入 Freelist 合适的位置
  (4)堆上的漏洞
  1)堆溢出漏洞
  堆溢出与栈溢出在本质上是相通的,都是精心构造特制的数据去覆盖正常数据,覆盖到某个特定位置后跳转到自己的shellcode的地址去执行shellcode。
  但从技术层面来讲,堆溢出比栈溢出难度更大。而且现在基本很少有软件存在典型的栈溢出漏洞,相反由于堆的复杂性,很多软件仍然存在诸多的堆溢出漏洞。
  2)UAF 漏洞
  Use After Free(UAF),释放后重引用漏洞, 一块内存已经被释放后,在程序中仍然存在对该块内存的引用,并且在一定情况下可能使用内存中的数据。
  由于这块原本已经被释放不应该再使用的内存被程序中的其他地方进行了使用,因此该块内存中的数据是不可信的。这种方式甚至会造成内存崩溃或者任意代码执行。此类型的漏洞在浏览器中比较常见。
  UAF漏洞比较有名的是CVE-2013-1347 Microsoft IE CGenericElementUAF漏洞,该漏洞被用在了当时著名的“水坑”事件中,影响巨大。
  3)Double Free 漏洞
  双重释放漏洞,主要是由于对同一块内存进行二次重复释放。在释放过程中,邻近的已释放的堆块存在合并动作,这会导致原有的堆header信息发生改变,同时前向指针和后向指针也会发生改变,随后再对其中的地址进行引用,就会导致访问异常,最终导致程序崩溃或者任意代码执行。
  从另外一个角度来说,由于发生了对释放后的堆块内存的引用,因此Double Free漏洞也是UAF漏洞的一个子集。
  双重释放漏洞比较经典的是CVE-2014-1767,该漏洞位于Windows AFD.sys文件中。
  在2014年的Pwn2Own上,Siberas团队使用该漏洞进行内核提权,绕过了Windows 8.1平台上的IE11沙箱,并在随后获得了Pwnie Awards的“最佳提权漏洞奖”。该漏洞通杀Windows系统,影响较大。
  linux 的内存结构分布
  首先依旧是先放个内存结构图
  栈地址也是依旧是高地址到低地址递增,但是栈的整体地址是可以向高地址增加的,不像 windows 一样 ebp 直接就在最高地址了
  堆(待更新)

      本文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理。
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号