锦州市广厦电脑维修|上门维修电脑|上门做系统|0416-3905144热诚服务,锦州广厦维修电脑,公司IT外包服务
topFlag1 设为首页
topFlag3 收藏本站
 
maojin003 首 页 公司介绍 服务项目 服务报价 维修流程 IT外包服务 服务器维护 技术文章 常见故障
锦州市广厦电脑维修|上门维修电脑|上门做系统|0416-3905144热诚服务技术文章
(调试逆向) 堆溢出研究二

作者: 佚名  日期:2017-05-14 20:28:59   来源: 本站整理

 堆溢出研究二##目录

  • 调试中认识堆表
  • 调试中识别堆的分配,释放,合并

调试中识别堆表
工具: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安全软件漏洞的分析技术》     



热门文章
  • 机械革命S1 PRO-02 开机不显示 黑...
  • 联想ThinkPad NM-C641上电掉电点不...
  • 三星一体激光打印机SCX-4521F维修...
  • 通过串口命令查看EMMC擦写次数和判...
  • IIS 8 开启 GZIP压缩来减少网络请求...
  • 索尼kd-49x7500e背光一半暗且闪烁 ...
  • 楼宇对讲门禁读卡异常维修,读卡芯...
  • 新款海信电视机始终停留在开机界面...
  • 常见打印机清零步骤
  • 安装驱动时提示不包含数字签名的解...
  • 共享打印机需要密码的解决方法
  • 图解Windows 7系统快速共享打印机的...
  • 锦州广厦电脑上门维修

    报修电话:13840665804  QQ:174984393 (联系人:毛先生)   
    E-Mail:174984393@qq.com
    维修中心地址:锦州广厦电脑城
    ICP备案/许可证号:辽ICP备2023002984号-1
    上门服务区域: 辽宁锦州市区
    主要业务: 修电脑,电脑修理,电脑维护,上门维修电脑,黑屏蓝屏死机故障排除,无线上网设置,IT服务外包,局域网组建,ADSL共享上网,路由器设置,数据恢复,密码破解,光盘刻录制作等服务

    技术支持:微软等