最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C#?崩潰異常中研究頁(yè)堆布局的詳細(xì)過(guò)程

 更新時(shí)間:2022年10月08日 09:19:09   作者:一線碼農(nóng)  
最近遇到一位朋友的程序崩潰,發(fā)現(xiàn)崩潰點(diǎn)在富編輯器 msftedit 上,這個(gè)不是重點(diǎn),重點(diǎn)在于發(fā)現(xiàn)他已經(jīng)開(kāi)啟了 頁(yè)堆,由于 頁(yè)堆 和 NT堆 的內(nèi)存布局完全不一樣,這一篇結(jié)合我的了解以及 windbg 驗(yàn)證來(lái)系統(tǒng)的介紹下 頁(yè)堆,需要的朋友可以參考下

一:背景

1.講故事

最近遇到一位朋友的程序崩潰,發(fā)現(xiàn)崩潰點(diǎn)在富編輯器 msftedit 上,這個(gè)不是重點(diǎn),重點(diǎn)在于發(fā)現(xiàn)他已經(jīng)開(kāi)啟了 頁(yè)堆 ,看樣子是做了最后的掙扎。

0:000> !analyze -v
EXCEPTION_RECORD:  (.exr -1)
ExceptionAddress: 82779a9e (msftedit!CCallMgrCenter::SendAllNotifications+0x00000123)
   ExceptionCode: c0000005 (Access violation)
  ExceptionFlags: 00000000
NumberParameters: 2
   Parameter[0]: 00000001
   Parameter[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+1c45e7

MODULE_NAME: System_Windows_Forms

IMAGE_NAME:  System.Windows.Forms.dll

0:000> !heap -p

    Active GlobalFlag bits:
        vrf - Enable application verifier
        hpa - Place heap allocations at ends of pages

    StackTraceDataBase @ 04c20000 of size 01000000 with 00001b18 traces

    PageHeap enabled with options:
        ENABLE_PAGE_HEAP
        COLLECT_STACK_TRACES

    active heaps:

    + 5c20000
        ENABLE_PAGE_HEAP COLLECT_STACK_TRACES 
      NormalHeap - 5d90000
          HEAP_GROWABLE 
    + 5e90000
        ENABLE_PAGE_HEAP COLLECT_STACK_TRACES 
      NormalHeap - 4960000
          HEAP_GROWABLE HEAP_CLASS_1 
      ...

由于 頁(yè)堆NT堆 的內(nèi)存布局完全不一樣,這一篇結(jié)合我的了解以及 windbg 驗(yàn)證來(lái)系統(tǒng)的介紹下 頁(yè)堆。

二:對(duì) 頁(yè)堆 的研究

1. 案例演示

為了方便講述,先上一段測(cè)試代碼。

int main()
{
	HANDLE h = HeapCreate(NULL, 0, 100);

	int* ptr = (int*)HeapAlloc(h, 0, 9);

	printf("ptr= %x", ptr);

	DebugBreak();
}

接下來(lái)用 gflags 開(kāi)啟下頁(yè)堆。

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

然后將程序跑起來(lái),可以看到返回的 handle 句柄。

2. 頁(yè)堆布局研究

接下來(lái)用 windbg 的 !heap -p 命令觀察頁(yè)堆。

0:000> !heap -p

    Active GlobalFlag bits:
        hpa - Place heap allocations at ends of pages

    StackTraceDataBase @ 042e0000 of size 01000000 with 0000000e traces

    PageHeap enabled with options:
        ENABLE_PAGE_HEAP
        COLLECT_STACK_TRACES

    active heaps:

    + 5b0000
        ENABLE_PAGE_HEAP COLLECT_STACK_TRACES 
      NormalHeap - 710000
          HEAP_GROWABLE 
    + 810000
        ENABLE_PAGE_HEAP COLLECT_STACK_TRACES 
      NormalHeap - 510000
          HEAP_GROWABLE HEAP_CLASS_1 
    + 56e0000
        ENABLE_PAGE_HEAP COLLECT_STACK_TRACES 
      NormalHeap - 5aa0000
          HEAP_CLASS_1 

稍微解讀下上面的輸出。

+ 56e0000**

表示 頁(yè)堆 的堆句柄。

NormalHeap - 5aa0000

表示 頁(yè)堆 關(guān)聯(lián)的 NT堆,可能有朋友要問(wèn)了,既然都開(kāi)啟頁(yè)堆了, 還要弄一個(gè) ntheap 干嘛? 大家不要忘了,windows 的一些系統(tǒng)api會(huì)用到這個(gè)堆。

接下來(lái)有一個(gè)問(wèn)題,如何觀察這兩個(gè) heap 之間的關(guān)聯(lián)關(guān)系呢? 要回答這個(gè)問(wèn)題,需要了解 頁(yè)堆 的布局結(jié)構(gòu),畫(huà)個(gè)簡(jiǎn)圖如下:

從圖中可以看到,離句柄偏移 4k 的位置有一個(gè) DPH_HEAP_ROOT 結(jié)構(gòu),它相當(dāng)于 NTHEAP 的_HEAP,我們拿 56e0000 舉個(gè)例子。

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 就是它關(guān)聯(lián)的 ntheap 句柄。

3. 堆塊布局研究

對(duì)頁(yè)堆 有了一個(gè)整體認(rèn)識(shí),接下來(lái)繼續(xù)研究堆塊句柄,我們發(fā)現(xiàn) ptr=0x56e5ff0 是落在 56e0000 這個(gè)頁(yè)堆上,接下來(lái)我們導(dǎo)出這個(gè)頁(yè)堆的詳細(xì)信息。

0:000> !heap -p -h 56e0000
    _DPH_HEAP_ROOT @ 56e1000
    Freed and decommitted blocks
      DPH_HEAP_BLOCK : VirtAddr VirtSize
    Busy allocations
      DPH_HEAP_BLOCK : UserAddr  UserSize - VirtAddr VirtSize
        056e1f70 : 056e5ff0 00000009 - 056e5000 00002000
          unknown!fillpattern
    _HEAP @ 5aa0000
      No FrontEnd
      _HEAP_SEGMENT @ 5aa0000
       CommittedRange @ 5aa04a8
      HEAP_ENTRY Size Prev Flags    UserPtr UserSize - state
        05aa04a8 0167 0000  [00]   05aa04b0    00b30 - (free)
      * 05aa0fe0 0004 0167  [00]   05aa0fe8    00018 - (busy)
       VirtualAllocdBlocks @ 5aa009c

上面的信息如何解讀呢?我們逐一來(lái)聊一下吧。

_DPH_HEAP_ROOT @ 56e1000

這個(gè)已經(jīng)和大家聊過(guò)了,它和 _HEAP 結(jié)構(gòu)是一致的。

DPH_HEAP_BLOCK :

從字面意思就能看出來(lái)和 ntheapheap_entry 是一致的,都是用來(lái)描述堆塊信息, 不過(guò)有一點(diǎn)要注意,這個(gè)堆塊是落在上圖中的 DPH_HEAP_BLOCK Pool 池鏈表結(jié)構(gòu)中的,言外之意就是它不會(huì)作為 heap_entry 的頭部附加信息,接下來(lái)我們 dt 導(dǎo)出來(lái)看看。

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
...

接下來(lái)再回答一個(gè)問(wèn)題,頁(yè)堆的堆塊有沒(méi)有頭部附加信息呢?當(dāng)然是有的,叫做 DPH_BLOCK_INFORMATION ,即在 UserPtr-0x20 的位置,我們可以用 dt 顯示一下。

0:000> ?? sizeof(ntdll!_DPH_BLOCK_INFORMATION)
unsigned int 0x20

0: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
   ...

根據(jù)上面兩個(gè)輸出,在腦海中應(yīng)該可以繪出如下圖:

這里要稍微解釋下 柵欄頁(yè) 的概念。

4. 柵欄頁(yè)

每一個(gè) heap_entry 都會(huì)占用 8k 的空間,第一個(gè) 4k 是用戶區(qū),第二個(gè) 4k 是柵欄區(qū),為了就是當(dāng)代碼越界時(shí)訪問(wèn)了這個(gè) 柵欄頁(yè) 會(huì)立即報(bào)錯(cuò),因?yàn)闁艡陧?yè)是禁止訪問(wèn)的,我們可以提取 UserAddr 附近的內(nèi)存,看看 056e6000= 056e5000+0x1000 后面是不是都是問(wèn)號(hào)。

0:000> dp 056e5ff0 
056e5ff0  c0c0c0c0 c0c0c0c0 d0d0d0c0 d0d0d0d0
056e6000  ???????? ???????? ???????? ????????
056e6010  ???????? ???????? ???????? ????????
056e6020  ???????? ???????? ???????? ????????
056e6030  ???????? ???????? ???????? ????????
056e6040  ???????? ???????? ???????? ????????
056e6050  ???????? ???????? ???????? ????????
056e6060  ???????? ???????? ???????? ????????
0:000> !address 056e5000+0x1000
Usage:                  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 0x56e6000
Content source: 0 (invalid), length: fa000

三:總結(jié)

這就是對(duì) 頁(yè)堆 的一個(gè)研究,總的來(lái)說(shuō) 頁(yè)堆 是一種專用于調(diào)試的堆,優(yōu)缺點(diǎn)如下:

優(yōu)點(diǎn):

因?yàn)?柵欄頁(yè) 緊鄰 用戶頁(yè),一旦代碼越界進(jìn)入了 柵欄頁(yè),會(huì)立即報(bào) 訪問(wèn)違例 異常,這樣我們就可以獲取第一現(xiàn)場(chǎng)錯(cuò)誤。

缺點(diǎn):

對(duì)空間造成了巨大浪費(fèi),即使 1byte 的內(nèi)存分配,也需要至少 2 個(gè)內(nèi)存頁(yè) 的內(nèi)存占用 (8k)。

哈哈,對(duì)調(diào)試程序崩潰類問(wèn)題,非常值得一試!

到此這篇關(guān)于C# 崩潰異常中研究頁(yè)堆布局的文章就介紹到這了,更多相關(guān)C# 崩潰異常內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C#實(shí)現(xiàn)串口示波器

    C#實(shí)現(xiàn)串口示波器

    這篇文章主要為大家詳細(xì)介紹了C#實(shí)現(xiàn)串口示波器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • C#使用StreamReader和StreamWriter類讀寫(xiě)操作文件

    C#使用StreamReader和StreamWriter類讀寫(xiě)操作文件

    這篇文章介紹了C#使用StreamReader和StreamWriter類讀寫(xiě)操作文件的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-05-05
  • WPF中NameScope的查找規(guī)則詳解

    WPF中NameScope的查找規(guī)則詳解

    這篇文章主要給大家介紹了關(guān)于WPF中NameScope的查找規(guī)則的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2018-10-10
  • C#絕對(duì)路徑拼接相對(duì)路徑的實(shí)例代碼

    C#絕對(duì)路徑拼接相對(duì)路徑的實(shí)例代碼

    C#絕對(duì)路徑拼接相對(duì)路徑的實(shí)例代碼,需要的朋友可以參考一下
    2013-03-03
  • C#中foreach實(shí)現(xiàn)原理詳解

    C#中foreach實(shí)現(xiàn)原理詳解

    這篇文章主要為大家詳細(xì)介紹了C#中foreach實(shí)現(xiàn)原理,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-09-09
  • C#修改圖片尺寸以及不改變?cè)袌D片比例

    C#修改圖片尺寸以及不改變?cè)袌D片比例

    文章介紹了如何使用C#中的Bitmap類來(lái)修改圖片尺寸,同時(shí)保持原有的圖片比例,作者分享了自己的經(jīng)驗(yàn),并鼓勵(lì)讀者參考和使用
    2025-01-01
  • C#中DateTime的時(shí)間加減法操作小結(jié)

    C#中DateTime的時(shí)間加減法操作小結(jié)

    本文主要介紹了C#中DateTime的時(shí)間加減法操作小結(jié),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • C# 中類型轉(zhuǎn)換方式之顯式轉(zhuǎn)換和 as 運(yùn)算符

    C# 中類型轉(zhuǎn)換方式之顯式轉(zhuǎn)換和 as 運(yùn)算符

    在 C# 中,有兩種常見(jiàn)的類型轉(zhuǎn)換方式:顯式轉(zhuǎn)換和as 運(yùn)算符,它們用于在不同類型之間進(jìn)行轉(zhuǎn)換,以下是對(duì)這兩種轉(zhuǎn)換方式的詳細(xì)解釋和示例說(shuō)明,感興趣的朋友跟隨小編一起看看吧
    2024-05-05
  • c#利用webmail郵件系統(tǒng)發(fā)送郵件示例分享

    c#利用webmail郵件系統(tǒng)發(fā)送郵件示例分享

    在C#中發(fā)送郵件的方式有2種,一種是使用webmail方式進(jìn)行發(fā)送,另外一種就是采用netmail發(fā)送的方式,這篇文章介紹了c#使用webmail方式發(fā)送郵件示例,大家參考使用吧
    2014-01-01
  • C#制作網(wǎng)站掛機(jī)程序的實(shí)現(xiàn)示例

    C#制作網(wǎng)站掛機(jī)程序的實(shí)現(xiàn)示例

    本文主要介紹了C#制作網(wǎng)站掛機(jī)程序,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-10-10

最新評(píng)論

柯坪县| 增城市| 嘉定区| 寿宁县| 博野县| 商水县| 平塘县| 望城县| 黑山县| 金乡县| 武宣县| 潞西市| 宝丰县| 昔阳县| 宝丰县| 喀喇| 元朗区| 洛阳市| 河津市| 大同县| 贺州市| 英德市| 囊谦县| 岑溪市| 莱州市| 盐亭县| 基隆市| 东乌珠穆沁旗| 彭山县| 茌平县| 清镇市| 阿合奇县| 杂多县| 安福县| 克什克腾旗| 阿合奇县| 三门县| 乐山市| 张家界市| 新和县| 印江|