如何从DLL堆标记中受益?

时间:2014-04-24 08:57:12

标签: windbg gflags

如何使用GFlags设置Enable heap tagging by DLL并从中受益?

我知道如何激活进程的设置,但我在WinDbg的!heap -t输出中找不到有用的信息。我期待这样的输出:

0:000> !heap -t
Index   Address   Allocated by 
1:      005c0000  MyDll.dll
2:      006b0000  AnotherDll.dll

以便我可以识别哪个堆是由哪个DLL创建的,然后是确定内存泄漏的来源。

这是对“DLL堆标记”一词的误解,还是需要更多命令才能达到预期效果?

到目前为止我的研究:

  • 我搜索了有关此主题的教程,但我找不到详细说明
  • 我读过WinDbg的.hh !heap,但也没有详细解释。标记仅用于!heap -b

1 个答案:

答案 0 :(得分:5)

又是一个非常晚的答案

首先在代码中受益于HeapTagging you need to create a tag 据我所知(这是xp-sp3),有no Documented APIS to Create a tag

(从那以后我没有乱用堆,所以我不知道os中的最新api> vista重写已经完成了堆管理器所以我下面发布的许多^^^features^^^可能已经被纠正或改进了或删除错误)

在xp-sp3中,您可以使用未记录的RtlCreateTagHeapProcess HeapPrivate Heap

创建新标记

在创建标签后,您需要设置全局标志8000 | 800

htg - Enable heap tagging
htd - Enable heap tagging by DLL

theoratically all allocs and frees must get tagged

但xp-sp3中的practically only allocations > 512 kB gets tagged包含这些基本步骤

它是一个错误或功能,限制标记分配和释放> 512 kB
在分配的情况下HeapAlloc goes through ZwAllocateVirtualMemory> 32位进程中的512 kB refer HeapCreate / HeapAlloc Documentation in msdn

作为debuging aid patch ntdll.dll the fly to enable tagging all Allocations and frees cl /Zi /analyze /W4 <src> /link /RELEASE

下面的

示例代码演示了标记以及如何在windbg中查看所有内容

使用!heap * -t

进行编译

使用windbg执行应用程序并使用#include <windows.h> #include <stdio.h> //heaptags are kinda broken or they are intentionally //given only to allocations > 512 kb // allocation > 512 kb //go through VirtualAlloc Route for Heap created with maxsize //set to 0 uncomment ALLOCSIZE 0xfdfd2 and recompile to watch // tagging increase by 100% with ALLOCSIZE 0xfdfd1 only 50 allocs // and frees that are > 512 kB will be tagged these magic numbers // are related to comment in HeapCreate Documentation that state // slightly less than 512 kB will be allocated for 32 bit process // tagging can be dramatically increased by patching ntdll when // stopped on system breakpoint patch 7c94b8a4 (xpsp3 ntdll.dll) // use the below command in windbg for finding the offset of pattern // command must be in single line no line breaks // .foreach /pS 4 /ps 4 ( place { !grep -i -e call -c // "# call*RtlpUpdateTagEntry 7c900000 l?20000" } ) { ub place } // the instruction we are searching to patch is //7c94b8a1 81e3ff0fffff and ebx,0FFFF0FFFh // patch 0f to 00 at system breakpoint with eb 7c94b8a1+3 00 #define BUFFERSIZE 100 #define ALLOCSIZE 0xfdfd1 //#define ALLOCSIZE 0xfdfd2 typedef int ( __stdcall *g_RtlCreateTagHeap) ( HANDLE hHeap , void * unknown, wchar_t * BaseString, wchar_t * TagString ); void HeapTagwithHeapAllocPrivate() { PCHAR pch[BUFFERSIZE] = {}; HANDLE hHeap = 0; ULONG tag1 = 0; ULONG tag2 = 0; ULONG tag3 = 0; ULONG tag4 = 0; ULONG tag5 = 0; g_RtlCreateTagHeap RtlCreateTagHeap = 0; HMODULE hMod = LoadLibrary("ntdll.dll"); if(hMod) { RtlCreateTagHeap = (g_RtlCreateTagHeap) GetProcAddress( hMod,"RtlCreateTagHeap"); } if (hHeap == 0) { hHeap = HeapCreate(0,0,0); if (RtlCreateTagHeap != NULL) { tag1 = RtlCreateTagHeap (hHeap,0,L"HeapTag!",L"MyTag1"); tag2 = RtlCreateTagHeap (hHeap,0,L"HeapTag!",L"MyTag2"); tag3 = RtlCreateTagHeap (hHeap,0,L"HeapTag!",L"MyTag3"); tag4 = RtlCreateTagHeap (hHeap,0,L"HeapTag!",L"MyTag4"); } } HANDLE DefHeap = GetProcessHeap(); if ( (RtlCreateTagHeap != NULL) && (DefHeap != NULL )) { tag5 = RtlCreateTagHeap (DefHeap,0,L"HeapTag!",L"MyTag5"); for ( int i = 0; i < BUFFERSIZE ; i++ ) { pch[i]= (PCHAR) HeapAlloc( DefHeap,HEAP_ZERO_MEMORY| tag5, 1 ); HeapFree(DefHeap,NULL,pch[i]); } } if(hHeap) { for ( int i = 0; i < BUFFERSIZE ; i++ ) { pch[i]= (PCHAR) HeapAlloc( hHeap,HEAP_ZERO_MEMORY| tag1, 1 ); //lets leak all allocs patch ntdll to see the tagging details //HeapFree(hHeap,NULL,pch[i]); } for ( int i = 0; i < BUFFERSIZE ; i++ ) { pch[i]= (PCHAR) HeapAlloc( hHeap,HEAP_ZERO_MEMORY| tag2, 100 ); // lets leak 40% allocs patch ntdll to see the tagging details if(i >= 40) HeapFree(hHeap,NULL,pch[i]); } // slightly less than 512 kb no tagging for ( int i = 0; i < BUFFERSIZE / 2 ; i++ ) { pch[i]= (PCHAR) HeapAlloc( hHeap,HEAP_ZERO_MEMORY| tag3, ALLOCSIZE / 2 ); } // > 512 kb default tagging for ( int i = BUFFERSIZE / 2; i < BUFFERSIZE ; i++ ) { pch[i]= (PCHAR) HeapAlloc( hHeap,HEAP_ZERO_MEMORY | tag4 ,ALLOCSIZE ); } for (int i =0 ; i < BUFFERSIZE ; i++) { HeapFree(hHeap,NULL,pch[i]); } } } void _cdecl main() { HeapTagwithHeapAllocPrivate(); } 命令

进行标记
heaptag:\>cdb -c "g;!heap * -t;q" newheaptag.exe | grep Tag
 Tag  Name                   Allocs    Frees   Diff  Allocated
 Tag  Name                   Allocs    Frees   Diff  Allocated
 Tag  Name                   Allocs    Frees   Diff  Allocated
0004: HeapTag!MyTag4             50       50      0        0

使用windbg运行编译的exe,如下所示

默认执行和检查
**只有50个标签可见,所有这些标签都是&gt; 512 kB分配

cdb -c&#34; g;!heap * -t; q&#34; newheaptag.exe | grep Tag **

heaptag:\>cdb -c "eb 7c94b8a1+3 00;g;!heap * -t;q" newheaptag.exe | grep Tag
 Tag  Name                   Allocs    Frees   Diff  Allocated
0012: HeapTag!MyTag5            100      100      0        0  <-our tag in process heap
 Tag  Name                   Allocs    Frees   Diff  Allocated
 Tag  Name                   Allocs    Frees   Diff  Allocated
0001: HeapTag!MyTag1            100        0    100     3200  <--- leak all
0002: HeapTag!MyTag2            100       60     40     5120  <--- leak 40 %
0003: HeapTag!MyTag3             50       50      0        0  <--- clean < 512 kB
0004: HeapTag!MyTag4             50       50      0        0  <----clean > 512 kB

在系统断点上修补ntdll应该使所有标签可见

eb =写入字节 修补程序并在退出时运行exe检查堆标记 cdb -c&#34; eb 7c94b8a1 + 3 00; g;!heap * -t; q&#34; newheaptag.exe | grep标签

{{1}}