12558网页游戏私服论坛

 找回密码
 立即注册
游戏开服表 申请开服
游戏名称 游戏描述 开服状态 游戏福利 运营商 游戏链接
攻城掠地-仿官 全新玩法,觉醒武将,觉醒技能 每周新区 经典复古版本,长久稳定 进入游戏
巅峰新版攻 攻城掠地公益服 攻城掠地SF 新兵种、新武将(兵种) 进入游戏
攻城掠地公 散人玩家的天堂 新开 进入游戏
改版攻城掠 上线即可国战PK 稳定新区 全新改版,功能强大 进入游戏
少年江山 高福利高爆率 刚开一秒 江湖水落潜蛟龙 进入游戏
太古封魔录 开服送10亿钻石 福利多多 不用充钱也可升级 进入游戏
神魔之道 签到送元宝 稳定开新区 送豪华签到奖励 进入游戏
神奇三国 统帅三军,招揽名将 免费玩新区 激情国战,征战四方 进入游戏
龙符 三日豪礼领到爽 天天开新区 助你征战无双 进入游戏
王者之师 免费领豪华奖励 免费玩新区 6元送6888元宝 进入游戏
三国霸业 战车-珍宝-觉醒-攻城掠地SF-全新玩法 免费玩新区 攻城掠地私服 进入游戏
手游私服盒子 各类免费游戏 0.1折送海量资源 各类手游私服 进入游戏
皇家MU2 《奇迹 2:传奇》韩国网禅公司《奇迹》正统续作。 3D锁视角Mmrpg 暗黑3+传奇+流放之路+奇迹 进入游戏
查看: 338|回复: 0

堆溢出研究二

[复制链接]

345

主题

345

帖子

700

积分

实习版主

Rank: 7Rank: 7Rank: 7

积分
700
发表于 2020-2-9 03:03:46 | 显示全部楼层 |阅读模式
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 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 #include 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
来源:http://www.12558.net
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
楼主热帖
回复

使用道具 举报

*滑块验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|12558网页游戏私服论坛 |网站地图

GMT+8, 2024-11-25 00:26 , Processed in 0.078125 second(s), 31 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

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