吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 9974|回复: 9
收起左侧

[调试逆向] 堆溢出研究二

[复制链接]
T_MAC仔 发表于 2017-5-14 16:02
2017/5/14 15:59:51 ## 堆溢出研究二##目录
  • 调试中认识堆表
  • 调试中识别堆的分配,释放,合并
调试中识别堆表
工具:OllyDbg 2.0版本 & vc6.0(release模式)  编译选项默认 os: windows2000   

函数的抽离
在堆中进行内存分配的时候,C语言函数调用的是malloc()函数,c++中调用new()函数,当动态调试进入函数内部的时候察觉此两个函数调用的都是底层 ntdll.dll中的 RtAllocateHeap()函数,所有的windows分配堆的函数在底层调用的都是此函数,这也死程序员可以看到的关于堆的最底层函数。因此研究堆分配,重点关注此函数即可。   
堆的调试
在此之前需要理解一个概念:调试堆与调试栈不同,不能直接加载或者attach 程序,否则堆管理策略就会采用调试状态下的堆管理策略,使用调试状态下的堆管理函数。     

正常堆和调试堆的区别:
1.调试堆只采用空表分配,不采用快表分配
2.所有的堆块末尾都加上十六个字节的用来防止程序溢出,(仅仅是用来防止程序溢出,而不是堆溢出),其中这十六个字节包括:
8 * 0xAB + 8 * 0x00
3.块首的标志标志位不同,调试状态下的堆和正常堆的区别如同debug下的PE文件和release下的PE文件类似,做堆移除实验的时候,调试器中可以v正常运行的shellcode,单独运行却不行。很可能就是调试堆与正常堆的差异造成的。   

为拉避免采用调试状态下的堆,我i们直接在程序中嵌入 int3 断点,然后调用实时调试器即可: 源码: #include <windows.h>main(){    HLOCAL h1,h2,h3,h4,h5,h6;    HANDLE hp;    hp = HeapCreate(0,0x1000,0x10000);    __asm int 3    h1 = HeapAlloc(hp,HEAP_ZERO_MEMORY,3);    h2 = HeapAlloc(hp,HEAP_ZERO_MEMORY,5);    h3 = HeapAlloc(hp,HEAP_ZERO_MEMORY,6);    h4 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8);    h5 = HeapAlloc(hp,HEAP_ZERO_MEMORY,19);    h6 = HeapAlloc(hp,HEAP_ZERO_MEMORY,24);    HeapFree(hp,0,h1); //free to freelist[2]     HeapFree(hp,0,h3); //free to freelist[2]     HeapFree(hp,0,h5); //free to freelist[4]    HeapFree(hp,0,h4); //coalese h3,h4,h5,link the large block to freelist[8]    return 0;}
step: 1. 调整ollydbg 为 just in time (实时调试器)
2. 直接进行编译链接运行程序,根据程序中的 int3 断点. ollydbg会直接断在int3断点出如图所示:

如上图所示程序断点段在拉地址 VA = 0040101D处,此时使用快捷键 ALT + M 查看内存映射窗口来到如图所示重点部分已经标注出来:

如上图所示可以、得到信息:发现进程堆地址为:  00130000 大小为0x6000  (此处可以通过函数 GetPcocessHeap()函数获得句柄)如图:

还有我们程序中创建出来的堆地址是0x00360000 size = 0x1000识别堆表
根据上图中的信息我们直接转到程序中创建出的堆地址 0x360000处在(数据窗口  直接 快捷键 ctrl + g )
  
对于上图来到地址 0x360000处后,根据和堆溢出有关的数据结构我们直接关注 空表索引区即可(即偏移地址 0x178地址处):   堆初始化时的状态

当堆刚被初始化的时候结构很简单,
1. 其中只包含一个空闲大块(称为 “尾块”)
2. 此尾块地址位于 0x178(360178)处 (未启用块表的情况下)算上基地址就是 0x360688  (又称为freelist【0】 )   
   
3.freelist[0] 指向“尾块 ‘,八个字节 (前四个字节是前向指针 后四个字节是后向指针 即:空表中的一对指针) ,其余的各项索引都指向其自身     
对堆块块首做一个简介 ####
堆块的块首占八个字节下面根据占用态和空闲态分别介绍:
  
  
共同点:   

0-2 字节代表本快的大小(包括块首)
2-4字节表示计算单位是多少字节   

不同点

Flags出 占用态标志是1  空闲态标志是 0
空闲态块首后的八个字节为一对指针,分别是前向指针和后向指针。当堆块变为占用态的时候重新回分配数据。
实际上尾块的起始位置是 0x360680   

因此根据地址 0x360680处八个字节的情况可以知道:此尾块的大小是 0x130  计算单位是 0x0008 个字节  总大小是 0x980字节。    调试中识别堆的分配,释放,合并堆块的分配
我们直接在cpu窗户 命令 F8单步执行程序到地址:0x00401028地址处也就是在源码中我们执行完:h1 = HeapAlloc(hp,HEAP_ZERO_MEMORY,3);
当h1被分配完以后直接查看地址:0x360178地址处的值:   

此时的地址0x360178处的值已经从0x360688改变为0x360698  同时跳转到 0x360698,如下图:
  
如上图所示:在地址0x360698出值为0x360178 链表的条件。
同样的根据地址0x360688处的值即:分配的h1可以发现,h1的大小是 0x002 size = 16bytes
接下来直接运行到地址 0x00401059 此时直接查看 0x360178的地址出看到 值已经更改为:0x360708.接下来直接来到0x360680处进行查看
h1 - h6的分配情况如下图所示:  

如上所示:尾块现在的地址是:0x360700 大小是 0x120 = 0x130 - 0x2 * 4 - 0x4 * 2
以上从h1 - h6的分配情况验证啦 空表分配中的找零钱现象(从一个大块中依次一小块一小块地进行切割)堆块的释放
接着上面的程序执行,直接执行到地址:00401077地址处HeapFree(hp,0,h1); //free to freelist[2] HeapFree(hp,0,h3); //free to freelist[2] HeapFree(hp,0,h5); //free to freelist[4]
分别释放啦堆块 h1 h3 h5这样做是防止相邻堆块进行堆块的合并。直接查看地址 0x360178地址处的值重点观察变化的值如下图:
  
从上图中可以发现地址 0x360188 的值发生啦变化 从原来的指向自身现在变为指向:0x360688  0x3608A8  
地址0x360198处的值变化为: 0x003606C8 和 0x003606c8
由上图可知 h1 h3分别被释放到 freelist[2] 空表中, h5被释放到啦 freelist【4】空表中。   

根据freelist【2】 的空表索引 以及h1 h3堆块的指针组,可以发现 :

如图所示左边箭头是前向指针,顺序为 Frllist -> h1 > h3  右边是后向指针 顺序是 h3> h1 > freelist[2]
对于h5堆快倒是没啥 ,freelist【5】直接索引到 地址 0x3606c8   
堆表的合并
接着程序运行直接运行到地址 0x401080地址处,执行的是代码:HeapFree(hp,0,h4); //coalese h3,h4,h5,link the large block to freelist[8]
当释放h4的时候会发生堆块的合并现象(两个连续的空闲块就会发生合并)。首先是先从空表中将三个空闲块摘下,重新计算合并后的堆块的大小,然后合并成新的空闲块,链入空表。如下图所示分别为空表索引区状态和合并后堆块状态:  


如上图所所示:地址 0x3606A0处的值 0x0008 即是:合并后的堆块的大小。后八个字节的指针对,则指向空表的索引区。   注意事项
  • 以上是空表中的堆块的合并,并且只发生在空表中。
  • 整个过程比较费时,繁琐,在强调效率的情况下,堆块合并就会被禁止,设置为占用太。
  • 空表中第一个块的情况下不会向前发生合并,最后一个块不会向后进行合并。
快表的申请与释放
快表和空表的区别在于 HeapCreate()函数的参数的不同。   hp = HeapCreate(0,0,0);//块表hp = HeapCreate(0,0x1000,0x10000);//空表
源码:#include <stdio.h>#include <windows.h>void main(){    HLOCAL h1,h2,h3,h4;    HANDLE hp;    hp = HeapCreate(0,0,0);    __asm int 3    h1 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8);    h2 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8);    h3 = HeapAlloc(hp,HEAP_ZERO_MEMORY,16);    h4 = HeapAlloc(hp,HEAP_ZERO_MEMORY,24);    HeapFree(hp,0,h1);    HeapFree(hp,0,h2);    HeapFree(hp,0,h3);    HeapFree(hp,0,h4);    h2 = HeapAlloc(hp,HEAP_ZERO_MEMORY,16);    HeapFree(hp,0,h2);}
与空表的申请大致类似。
环境:与空表使用的环境一样
直接在dunp窗口中进行跳转到 0x360688处,此时发现快表为空。这也是为什么要反复申请释放内存的原因,接下来分别申请 8,8,16,24字节的内存,然后进行释放,(快表未满时释放到快表中)。  
先运行程序到地址 0x40109F处。此时直接观察快表中的变化,此时发现让然为空,下面运行释放程序,直接单步执行命令运行到地址:0x401106处,这是观察快表的变化如图所示:

运行程序到地址 0x40110D处观察堆块是否链如块表:
  
如上图所示h1 - h4已经链接进入块表中并且都是处于占用态。 地址 0x361e90指向下一个堆块(因为h1 h2 同时为八字节的空闲堆块)
当程序运行到地址 0x401140时(也就是执行完申请内存的代码时)   h2 = HeapAlloc(hp,HEAP_ZERO_MEMORY,16);
此时申请的堆块应该从块表中申请,此时查看堆表区的索引:
  

从以上两图中可以看到当继续申请内存的时候,是从快表lookside[2]处卸下的堆块。当释放的时候,还是将空闲堆块释放到此处执行代码:   HeapFree(hp,0,h2);
执行完后继续查看上图中地址的值:

如图所示:当释放完堆块后还是链接进入啦快表 looksize[2]
参考文档《0day安全软件漏洞的分析技术》                                                                                        2017/5/14 15:59:55

免费评分

参与人数 7吾爱币 +10 热心值 +7 收起 理由
Hmily + 5 + 1 用心讨论,共获提升!
Prick + 1 + 1 学习
海底总动员 + 1 我很赞同!
雫Hao洋洋 + 1 + 1 热心回复!
AZ1036 + 1 + 1 已答复!
lin_xop + 1 + 1 热心回复!
我只做我自己 + 1 + 1 我很赞同!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

wangp-3366 发表于 2017-5-14 16:07
我是来打酱油的,看了也白看,但是必须给你顶起!
superlukia 发表于 2017-5-14 16:42
ChunSource 发表于 2017-5-14 18:27
bsxr2017 发表于 2017-5-14 20:31
向前辈学习!
AZ1036 发表于 2017-5-14 21:47
膜拜学习一下
chenjingyes 发表于 2017-5-15 00:56
还有三吗,?  谢谢楼主分享
play910417 发表于 2017-11-14 17:07
膜拜大神,学习了。
fzalin 发表于 2018-11-11 15:21
太好了,感谢感谢
alairlee 发表于 2018-11-12 01:13
想学反编译,感觉好漫长,谢谢大佬
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-4-26 02:14

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表