从 C# 崩溃异常 中研究 页堆 布局

一:背景

1.讲故事

最近遇到一位朋友的程序崩溃,发现崩溃点在富编辑器 msftedit 上,这个不是重点,重点在于发现他已经开启了 页堆 ,看样子是做了最后的挣扎。

0:000> !analyze -v
EXCEPTION_RECORD:  (.exr -1)
ExceptionAddress: 82779a9e (msftedit!CCallMgrCenter::SendAllNotifications+0x00000123)ExceptionCode: c0000005 (Access violation)ExceptionFlags: 00000000
NumberParameters: 2Parameter[0]: 00000001Parameter[1]: 8351af28
Attempt to write to address 8351af28
...
STACK_TEXT:  
00ffe0dc 827bda2a 8351ae88 00000000 00ffe174 msftedit!CCallMgrCenter::SendAllNotifications+0x123
00ffe110 827bd731 00ffe324 00ffe174 00ffe300 msftedit!CCallMgrCenter::ExitContext+0xda
00ffe120 827bde71 8351ae88 827232dc 28112f80 msftedit!CCallMgr::~CCallMgr+0x17
00ffe300 8290281f 00000102 00000067 00220001 msftedit!CTxtEdit::TxSendMessage+0x201
00ffe374 7576110b 00f20268 00000102 00000067 msftedit!RichEditWndProc+0x9cf
00ffe3a0 757580ca 82901e50 00f20268 00000102 user32!_InternalCallWinProc+0x2b
...
SYMBOL_NAME:  system_windows_forms+1c45e7MODULE_NAME: System_Windows_FormsIMAGE_NAME:  System.Windows.Forms.dll0:000> !heap -pActive GlobalFlag bits:vrf - Enable application verifierhpa - Place heap allocations at ends of pagesStackTraceDataBase @ 04c20000 of size 01000000 with 00001b18 tracesPageHeap enabled with options:ENABLE_PAGE_HEAPCOLLECT_STACK_TRACESactive heaps:+ 5c20000ENABLE_PAGE_HEAP COLLECT_STACK_TRACES NormalHeap - 5d90000HEAP_GROWABLE + 5e90000ENABLE_PAGE_HEAP COLLECT_STACK_TRACES NormalHeap - 4960000HEAP_GROWABLE HEAP_CLASS_1 ...

由于 页堆NT堆 的内存布局完全不一样,这一篇结合我的了解以及 windbg 验证来系统的介绍下 页堆

二:对 页堆 的研究

1. 案例演示

为了方便讲述,先上一段测试代码。

int main()
{HANDLE h = HeapCreate(NULL, 0, 100);int* ptr = (int*)HeapAlloc(h, 0, 9);printf("ptr= %x", ptr);DebugBreak();
}

接下来用 gflags 开启下页堆。

PS C:\Users\Administrator\Desktop> gflags -i ConsoleApplication1.exe +hpa
Current Registry Settings for ConsoleApplication1.exe executable are: 02000000hpa - Enable page heap

然后将程序跑起来,可以看到返回的 handle 句柄。

992455201373afd35d7ff4e4f1469d13.png

2. 页堆布局研究

接下来用 windbg 的 !heap -p 命令观察页堆。

0:000> !heap -pActive GlobalFlag bits:hpa - Place heap allocations at ends of pagesStackTraceDataBase @ 042e0000 of size 01000000 with 0000000e tracesPageHeap enabled with options:ENABLE_PAGE_HEAPCOLLECT_STACK_TRACESactive heaps:+ 5b0000ENABLE_PAGE_HEAP COLLECT_STACK_TRACES NormalHeap - 710000HEAP_GROWABLE + 810000ENABLE_PAGE_HEAP COLLECT_STACK_TRACES NormalHeap - 510000HEAP_GROWABLE HEAP_CLASS_1 + 56e0000ENABLE_PAGE_HEAP COLLECT_STACK_TRACES NormalHeap - 5aa0000HEAP_CLASS_1

稍微解读下上面的输出。

  1. + 56e0000**

表示 页堆 的堆句柄。

  1. NormalHeap - 5aa0000

表示 页堆 关联的 NT堆,可能有朋友要问了,既然都开启页堆了, 还要弄一个 ntheap 干嘛?大家不要忘了,windows 的一些系统api会用到这个堆。

接下来有一个问题,如何观察这两个 heap 之间的关联关系呢?要回答这个问题,需要了解 页堆 的布局结构,画个简图如下:

96aaeeb02a893383497d915a439e231b.png

从图中可以看到,离句柄偏移 4k 的位置有一个 DPH_HEAP_ROOT 结构,它相当于 NTHEAP 的_HEAP,我们拿 56e0000 举个例子。

0:000> dt nt!_DPH_HEAP_ROOT 56e0000+0x1000
ntdll!_DPH_HEAP_ROOT...+0x0b4 NormalHeap       : 0x05aa0000 Void+0x0b8 CreateStackTrace : 0x042f4d94 _RTL_TRACE_BLOCK+0x0bc FirstThread      : (null)

上面输出的 NormalHeap: 0x05aa0000 就是它关联的 ntheap 句柄。

3. 堆块布局研究

页堆 有了一个整体认识,接下来继续研究堆块句柄,我们发现 ptr=0x56e5ff0 是落在 56e0000 这个页堆上,接下来我们导出这个页堆的详细信息。

0:000> !heap -p -h 56e0000_DPH_HEAP_ROOT @ 56e1000Freed and decommitted blocksDPH_HEAP_BLOCK : VirtAddr VirtSizeBusy allocationsDPH_HEAP_BLOCK : UserAddr  UserSize - VirtAddr VirtSize056e1f70 : 056e5ff0 00000009 - 056e5000 00002000unknown!fillpattern_HEAP @ 5aa0000No FrontEnd_HEAP_SEGMENT @ 5aa0000CommittedRange @ 5aa04a8HEAP_ENTRY Size Prev Flags    UserPtr UserSize - state05aa04a8 0167 0000  [00]   05aa04b0    00b30 - (free)* 05aa0fe0 0004 0167  [00]   05aa0fe8    00018 - (busy)VirtualAllocdBlocks @ 5aa009c

上面的信息如何解读呢?我们逐一来聊一下吧。

  1. _DPH_HEAP_ROOT @ 56e1000

这个已经和大家聊过了,它和 _HEAP 结构是一致的。

  1. DPH_HEAP_BLOCK :

从字面意思就能看出来和 ntheapheap_entry 是一致的,都是用来描述堆块信息, 不过有一点要注意,这个堆块是落在上图中的 DPH_HEAP_BLOCK Pool 池链表结构中的,言外之意就是它不会作为 heap_entry 的头部附加信息,接下来我们 dt 导出来看看。

0:000> dt ntdll!_DPH_HEAP_BLOCK 056e1f70 +0x000 pNextAlloc       : 0x056e1020 _DPH_HEAP_BLOCK+0x000 AvailableEntry   : _LIST_ENTRY [ 0x56e1020 - 0x0 ]+0x000 TableLinks       : _RTL_BALANCED_LINKS+0x010 pUserAllocation  : 0x056e5ff0  "???"+0x014 pVirtualBlock    : 0x056e5000  "???"+0x018 nVirtualBlockSize : 0x2000+0x01c nVirtualAccessSize : 0x20+0x020 nUserRequestedSize : 9+0x024 nUserActualSize  : 0x56e1f60+0x028 UserValue        : 0x056e1fc8 Void+0x02c UserFlags        : 0x3f18+0x030 StackTrace       : 0x042f4dcc _RTL_TRACE_BLOCK+0x034 AdjacencyEntry   : _LIST_ENTRY [ 0x56e1010 - 0x56e1010 ]+0x03c pVirtualRegion   : (null)

从字段信息看,它记录了堆块的分配首地址,栈信息等等,比如用 dds 观察一下 StackTrace。

0:000> dds 0x042f4dcc 
042f4dcc  00000000
042f4dd0  00006001
042f4dd4  000d0000
042f4dd8  78aba8b0 verifier!AVrfDebugPageHeapAllocate+0x240
042f4ddc  77e0ef8e ntdll!RtlDebugAllocateHeap+0x39
042f4de0  77d76150 ntdll!RtlpAllocateHeap+0xf0
042f4de4  77d757fe ntdll!RtlpAllocateHeapInternal+0x3ee
042f4de8  77d753fe ntdll!RtlAllocateHeap+0x3e
042f4dec  00ad1690 ConsoleApplication1!main+0x30 [D:\net6\ConsoleApp1\ConsoleApplication1\DisplayGreeting.cpp @ 14]
042f4df0  00ad1bc3 ConsoleApplication1!invoke_main+0x33 [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 78]
042f4df4  00ad1a17 ConsoleApplication1!__scrt_common_main_seh+0x157 [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 288]
042f4df8  00ad18ad ConsoleApplication1!__scrt_common_main+0xd [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 331]
042f4dfc  00ad1c48 ConsoleApplication1!mainCRTStartup+0x8 [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_main.cpp @ 17]
042f4e00  7646fa29 KERNEL32!BaseThreadInitThunk+0x19
042f4e04  77d975f4 ntdll!__RtlUserThreadStart+0x2f
042f4e08  77d975c4 ntdll!_RtlUserThreadStart+0x1b
...

接下来再回答一个问题,页堆的堆块有没有头部附加信息呢?当然是有的,叫做 DPH_BLOCK_INFORMATION ,即在 UserPtr-0x20 的位置,我们可以用 dt 显示一下。

0:000> ?? sizeof(ntdll!_DPH_BLOCK_INFORMATION)
unsigned int 0x200:000> dt ntdll!_DPH_BLOCK_INFORMATION 056e5ff0-0x20+0x000 StartStamp       : 0xabcdbbbb+0x004 Heap             : 0x056e1000 Void+0x008 RequestedSize    : 9+0x00c ActualSize       : 0x1000+0x010 FreeQueue        : _LIST_ENTRY [ 0x0 - 0x0 ]+0x010 FreePushList     : _SINGLE_LIST_ENTRY+0x010 TraceIndex       : 0+0x018 StackTrace       : 0x042f4dcc Void+0x01c EndStamp         : 0xdcbabbbb...

根据上面两个输出,在脑海中应该可以绘出如下图:

a3342d9f2cb2ccfc05596840959954fd.png

这里要稍微解释下 栅栏页 的概念。

4. 栅栏页

每一个 heap_entry 都会占用 8k 的空间,第一个 4k 是用户区,第二个 4k 是栅栏区,为了就是当代码越界时访问了这个 栅栏页 会立即报错,因为栅栏页是禁止访问的,我们可以提取 UserAddr 附近的内存,看看 056e6000= 056e5000+0x1000 后面是不是都是问号。

0:000> dp 056e5ff0 
056e5ff0  c0c0c0c0 c0c0c0c0 d0d0d0c0 d0d0d0d0
056e6000  ???????? ???????? ???????? ????????
056e6010  ???????? ???????? ???????? ????????
056e6020  ???????? ???????? ???????? ????????
056e6030  ???????? ???????? ???????? ????????
056e6040  ???????? ???????? ???????? ????????
056e6050  ???????? ???????? ???????? ????????
056e6060  ???????? ???????? ???????? ????????0:000> !address 056e5000+0x1000Usage:                  PageHeap
Base Address:           056e6000
End Address:            057e0000
Region Size:            000fa000 (1000.000 kB)
State:                  00002000          MEM_RESERVE
Protect:                <info not present at the target>
Type:                   00020000          MEM_PRIVATE
Allocation Base:        056e0000
Allocation Protect:     00000001          PAGE_NOACCESS
More info:              !heap -p 0x56e1000
More info:              !heap -p -a 0x56e6000Content source: 0 (invalid), length: fa000

三:总结

这就是对 页堆 的一个研究,总的来说 页堆 是一种专用于调试的堆,优缺点如下:

  • 优点:

因为 栅栏页 紧邻 用户页,一旦代码越界进入了 栅栏页,会立即报 访问违例 异常,这样我们就可以获取第一现场错误。

  • 缺点:

对空间造成了巨大浪费,即使 1byte 的内存分配,也需要至少 2 个内存页 的内存占用 (8k)。

哈哈,对调试程序崩溃类问题,非常值得一试!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/282169.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Win10笔记本不显示wifi列表

一、问题描述 1、连接有线网络时&#xff0c;只显示连接到的有线网络&#xff0c;而不显示wifi列表 2、不连接有线网络时&#xff0c;同样不显示wifi列表 二、解决方案 1、Win R 打开运行&#xff0c;并输入services.msc 2、回车确定&#xff0c;找到WLAN AutoConfig项&…

搭建WeApacheb网站服务器

本地yum源安装mkdir /opt/dvd (先用mkdir去根下opt目录下建一个名字叫dvd的目录)mount /dev/sr0 /opt/dvd (用mount命令&#xff0c;挂载光盘设备&#xff08;/dev/sr0&#xff09;,将光盘挂载到刚刚建立的dvd目录下&#xff08;/opt/dvd&#xff09;)写yum源配置文件|-cd…

PHP的构成及生命周期

一、PHP开源源代码下载地址&#xff1a; https://github.com/php/php-src.git 二、PHP的构成 1、目录结构 2、目录分析 &#xff08;1&#xff09;sapi目录是PHP的应用接口层。 &#xff08;2&#xff09;main为php的主要代码&#xff0c;主要是输入/输出、Web通信、PHP框架…

《Adobe After Effects CS6完全剖析》——动画:最重要的是关系

本节书摘来自异步社区《Adobe After Effects CS6完全剖析》一书中的第2章&#xff0c;动画&#xff1a;最重要的是关系&#xff0c;作者 【美】Mark Christiansen&#xff08;马克克里斯琴森&#xff09;&#xff0c;译者 苗玉敏&#xff0c;郭圣路&#xff0c;曹玉臣&#xff…

Oracle即将发布的全新Java垃圾收集器 ZGC

Java 11的特性集合已经确定&#xff0c;其中包含了一些非常棒的特性。新版本提供了一个全新的垃圾回收器ZGC&#xff0c;它由甲骨文开发&#xff0c;承诺在TB级别的堆上实现非常低的停顿时间。在本文中&#xff0c;我们将介绍甲骨文开发ZGC的动机、ZGC的技术概览以及ZGC带来的一…

如何获取 OSS AccessKeyId、AccessKeySecret

开通阿里云oss&#xff1a;https://www.aliyun.com/product/oss 1、点击概览 — AccessKey 注&#xff1a;官方链接 2、出现下图&#xff0c;选择“开始使用子用户Access Key” 3、填写用户名&#xff0c;并点击确定 4、这时会给你的手机发送验证码确定是本人操作&#xff0c;…

【网络爬虫入门02】HTTP客户端库Requests的基本原理与基础应用

【网络爬虫入门02】HTTP客户端库Requests的基本原理与基础应用 广东职业技术学院 欧浩源 2017-10-15 1、引言 实现网络爬虫的第一步就是要建立网络连接并向服务器或网页等网络资源发起请求。urllib是目前最常用的做法&#xff0c;然而Requests会比urlib更加方便&#xff0c;能…

《零信任的基石:使用 SPIFFE 为基础设施创建通用身份》翻译电子书分享

国庆假期除了去浙江和安徽玩了一圈欣赏江南山水和徽派建筑之外&#xff0c;还抽空翻译了一本电子书。本书译自 Solving the Bottom Turtle — a SPIFFE Way to Establish Trust in Your Infrastructure via Universal Identity。你可以选择在线阅读&#xff08;推荐&#xff09…

《Outlook时间整理术》一创建和使用自己的文件夹结构

本节书摘来异步社区《Outlook时间整理术》一书中的第1章&#xff0c;作者&#xff1a; 【德】Lothar Seiwert , Holger Woeltje 译者&#xff1a; 欧阳宇&#xff0c;更多章节内容可以访问云栖社区“异步社区”公众号查看。 创建和使用自己的文件夹结构 花费约两小时为电子邮件…

《实用软件架构:从系统环境到软件部署 》——2.4 架构视图与架构视点

本节书摘来自华章出版社《实用软件架构&#xff1a;从系统环境到软件部署》一书中的第2章&#xff0c;第2.4节&#xff0c;作者&#xff1a;[印]蒂拉克米特拉&#xff08;Tilak Mitra&#xff09;著&#xff0c;爱飞翔 译&#xff0c;更多章节内容可以访问云栖社区“华章计算机…

TCP:当初取代NCP,如今害怕被取代

我叫TCP&#xff08;Transmission Control Protocol&#xff09;也叫传输控制协议。不觉回忆1983年&#xff0c;亲手将NCP协议淘汰&#xff0c;取而代之的是我&#xff0c;成了火遍大江南北的网络红人之一。现如今&#xff0c;我感受到前所未有的恐惧&#xff0c;因为我一生的敌…

批处理中的IF详解

在CMD使用IF /?打开IF的系统帮助会发现IF有3种基本的用法! 第一种用法&#xff1a;IF [NOT] ERRORLEVEL number command 这种用法现在很少用了&#xff0c;因为它需要使用到CHOICE命令&#xff0c;这个命令现在被set /p代替了&#xff0c;它是判断CHOICE命令选择的选项的&…

fullcalendar 显示的时间间隔只有四十五分钟_【体能新视点】——女子篮球运动员比赛期间的心率、血乳酸浓度和时间运动分析...

女子篮球运动员比赛期间的心率、血乳酸浓度和时间-运动分析—摘要—本研究的目的是检验女子篮球运动员在比赛规则改变后的生理需求和运动模式。在九场正式比赛中&#xff0c;对九名大学代表队队员进行了研究。每场比赛都被录像以确定主要动作的频率&#xff0c;连续记录心率&am…

《JavaScript机器人编程指南》——1.2 NodeBot是什么,基本词汇还有哪些

本节书摘来异步社区《JavaScript机器人编程指南》一书中的第1章&#xff0c;第1.2节&#xff0c;作者&#xff1a;【美】Kassandra Perch&#xff08;珀芝&#xff09;&#xff0c;更多章节内容可以访问云栖社区“异步社区”公众号查看。 1.2 NodeBot是什么&#xff0c;基本词…

解决在Python中使用Win32api报错的问题,No module named win32api

一、系统环境 操作系统: Win7 64位 Python&#xff1a;3.7.0 二、在使用import win32api时&#xff0c;报错:No module named win32api 网上查到有下面解决办法&#xff1a; 方法1&#xff1a;pip install pypiwin32或pip3 install pypiwin32 或 python -m pip install pypiwin…

《Python游戏编程入门》——1.2 初识Python

本节书摘来自异步社区《Python游戏编程入门》一书中的第1章&#xff0c;第1.2节&#xff0c;作者[美]Jonathan S. Harbour &#xff0c;李强 译&#xff0c;更多章节内容可以访问云栖社区“异步社区”公众号查看。 1.2 初识Python Python既是一个软件工具包&#xff0c;也是一种…

大数据:Parquet文件存储格式

一、Parquet的组成 Parquet仅仅是一种存储格式&#xff0c;它是语言、平台无关的&#xff0c;并且不需要和任何一种数据处理框架绑定&#xff0c;目前能够和Parquet适配的组件包括下面这些&#xff0c;可以看出基本上通常使用的查询引擎和计算框架都已适配&#xff0c;并且可以…

“数据门”事件频发 如何避免人为因素导致数据泄露?

前段时间&#xff0c;某酒店集团数据泄露引起轩然大波&#xff0c;泄露的数据中包含了用户姓名、手机号、邮箱、号等多项信息。卖家对这个约5亿条数据打包出售价格为8比特币或520门罗币。而关于此次信息泄露事件的原因&#xff0c;目前尚未定论。据悉&#xff0c;由于集团某程序…

lamda获取参数集合去空_JAVA集合框架知识

1. Vector用法和ArrayList区别(1) Vector的特有方法有哪些&#xff1f;void addElement(E obj) 将指定的组件添加到此向量的末尾&#xff0c;将其大小增加1。(2) Vector与ArrayList的区别是什么&#xff1f;①Vector的add()方法是同步方法&#xff0c;ArrayList的add()方法是非…

Blazor学习之旅(2)第一个Blazor应用

【Blazor】| 总结/Edison Zhou大家好&#xff0c;我是Edison。最近在学习Blazor做全栈开发&#xff0c;因此根据老习惯&#xff0c;我会将我的学习过程记录下来&#xff0c;一来体系化整理&#xff0c;二来作为笔记供将来翻看。本篇我们来构建第一个Blazor Web应用&#xff0c;…