從 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.pswp.cn/news/282169.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/282169.shtml
英文地址,請注明出處:http://en.pswp.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項&…

《游戲大師Chris Crawford談互動敘事》一22.1 互動敘事前途無量

本節書摘來異步社區《游戲大師Chris Crawford談互動敘事》一書中的第22章&#xff0c;第22.1節&#xff0c;作者&#xff1a; 【美】Chris Crawford譯者&#xff1a; 方舟 責編&#xff1a; 陳冀康&#xff0c;更多章節內容可以訪問云棲社區“異步社區”公眾號查看。 22.1 互動…

交換兩個局部變量Integer的值

反射是很強大的&#xff0c;誰說的final修飾的就不能改變&#xff0c; 通過反射獲取成員變量&#xff0c;之后可以取消訪問修飾符&#xff0c;也就是說private的也可以訪問&#xff0c; 在修改常量&#xff08;final修飾的&#xff09;&#xff0c;之后就可以對其做任何操作了 …

搭建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;能…

5分鐘內看懂機器學習和深度學習的區別

歡迎大家前往騰訊云社區&#xff0c;獲取更多騰訊海量技術實踐干貨哦~ 本文由liuxuewen 發表于云社區專欄 在本文中&#xff0c;我們將研究深度學習和機器學習之間的差異。我們將逐一了解它們&#xff0c;然后討論他們在各個方面的不同之處。除了深度學習和機器學習的比較外&am…

《零信任的基石:使用 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;更多章節內容可以訪問云棲社區“異步社區”公眾號查看。 創建和使用自己的文件夾結構 花費約兩小時為電子郵件…

PHP 使用 OSS上傳文件

一、安裝阿里云 oss sdk 1、在網站根目錄執行下面命令&#xff0c;安裝oss sdk。 composer require aliyuncs/oss-sdk-php 安裝后&#xff0c;會在 網站根目錄/vendor 下找到一個名為 aliyuncs 的文件夾。 注&#xff1a;[阿里云 oss - sdk 文檔&#xff08;可不用關注&…

webpack常用配置

1.加載CSS 命令行輸入 npm install --save-dev style-loader css-loader webpack.config.js配置如下 const path require(path);module.exports {entry: ./src/index.js,output: {filename: bundle.js,path: path.resolve(__dirname, dist)},module: {rules: [{test: /\.css$…

《實用軟件架構:從系統環境到軟件部署 》——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;因為我一生的敵…

js截取字符串實例

// JS截取字符串可使用 substring()或者slice() // // 函數&#xff1a;substring() // 定義&#xff1a;substring(start,end)表示從start到end之間的字符串&#xff0c;包括start位置的字符但是不包括end位置的字符。 // 功能&#xff1a;字符串截取&#xff0c;…

PHP 使用 OSS 批量刪除圖片

1、控制器 OssImageController.php&#xff08;不必關注此層&#xff09; <?php declare(strict_types 1); namespace app\controllers; use app\services\OssImageService; class OssImageController extends BaseController {/*** oss 批量刪除圖片** throws null*/pub…

批處理中的IF詳解

在CMD使用IF /?打開IF的系統幫助會發現IF有3種基本的用法! 第一種用法&#xff1a;IF [NOT] ERRORLEVEL number command 這種用法現在很少用了&#xff0c;因為它需要使用到CHOICE命令&#xff0c;這個命令現在被set /p代替了&#xff0c;它是判斷CHOICE命令選擇的選項的&…

fullcalendar 顯示的時間間隔只有四十五分鐘_【體能新視點】——女子籃球運動員比賽期間的心率、血乳酸濃度和時間運動分析...

女子籃球運動員比賽期間的心率、血乳酸濃度和時間-運動分析—摘要—本研究的目的是檢驗女子籃球運動員在比賽規則改變后的生理需求和運動模式。在九場正式比賽中&#xff0c;對九名大學代表隊隊員進行了研究。每場比賽都被錄像以確定主要動作的頻率&#xff0c;連續記錄心率&am…