关于windbg:利用Windbg分析高内存占用问题

大家好,我是本期的微软 MVP 实验室研究员——冯辉。本篇文章次要介绍如何利用Windbg剖析利用过程中的内存问题,从托管堆到非托管堆的摸索以及到内存的调配,接下来咱们一起来摸索吧。 近期有几位敌人应用咱们的Magicodes.IE反馈在导出过程中内存暴涨,接下来咱们通过windbg来看一下什么起因导致的。 咱们先通过address -summary来看一下以后利用内存占用量。 0:000> !address -summary--- Usage Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotalFree 581 7df8`ef0c9000 ( 125.972 TB) 98.42%<unknown> 1678 206`ffb9e000 ( 2.027 TB) 99.99% 1.58%Image 950 0`064fd000 ( 100.988 MB) 0.00% 0.00%Heap 58 0`050f6000 ( 80.961 MB) 0.00% 0.00%Stack 156 0`04380000 ( 67.500 MB) 0.00% 0.00%Other 11 0`019ad000 ( 25.676 MB) 0.00% 0.00%TEB 52 0`00068000 ( 416.000 kB) 0.00% 0.00%PEB 1 0`00001000 ( 4.000 kB) 0.00% 0.00%--- Type Summary (for busy) ------ RgnCount ----------- Total Size -------- %ofBusy %ofTotalMEM_MAPPED 282 200`038a6000 ( 2.000 TB) 98.64% 1.56%MEM_PRIVATE 1674 7`07184000 ( 28.111 GB) 1.35% 0.02%MEM_IMAGE 950 0`064fd000 ( 100.988 MB) 0.00% 0.00%--- State Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotalMEM_FREE 581 7df8`ef0c9000 ( 125.972 TB) 98.42%MEM_RESERVE 295 205`f8659000 ( 2.023 TB) 99.79% 1.58%MEM_COMMIT 2611 1`188ce000 ( 4.384 GB) 0.21% 0.00%--- Protect Summary (for commit) - RgnCount ----------- Total Size -------- %ofBusy %ofTotalPAGE_READWRITE 1595 1`0dc6c000 ( 4.215 GB) 0.20% 0.00%PAGE_EXECUTE_READ 156 0`04d66000 ( 77.398 MB) 0.00% 0.00%PAGE_READONLY 600 0`03851000 ( 56.316 MB) 0.00% 0.00%PAGE_NOACCESS 99 0`021f2000 ( 33.945 MB) 0.00% 0.00%PAGE_EXECUTE_READWRITE 19 0`0027b000 ( 2.480 MB) 0.00% 0.00%PAGE_WRITECOPY 90 0`001a0000 ( 1.625 MB) 0.00% 0.00%PAGE_READWRITE | PAGE_GUARD 52 0`0009e000 ( 632.000 kB) 0.00% 0.00%--- Largest Region by Usage ----------- Base Address -------- Region Size ----------Free 189`0413c000 7c6b`01ed4000 ( 124.418 TB)<unknown> 7dfb`2a153000 1f9`bd2ef000 ( 1.976 TB)Image 7ffc`883c1000 0`009ba000 ( 9.727 MB)Heap 183`0e9a1000 0`00f01000 ( 15.004 MB)Stack 37`62980000 0`0017b000 ( 1.480 MB)Other 183`77707000 0`01775000 ( 23.457 MB)TEB 37`62600000 0`00002000 ( 8.000 kB)PEB 37`627dd000 0`00001000 ( 4.000 kB)MEM_COMMIT占用了4.384G,接下来咱们利用eeheap -gc来查看托管堆。 ...

December 29, 2021 · 5 min · jiezi

内存泄漏的分析

一.问题描述当一个进程的内存异常大时,可能发生了内存泄漏,也可能是内存中相关队列等数据结构长度未作限制。 二.解决思路如果问题很容易出现,最好是使用umdh工具来进行判断,此工具的使用方法在其帮助手册中有详细介绍,此处不再赘述。本文中说的是另外一处思路。众所周知,进程中分为代码空间,数据空间,栈,堆等。除了堆之外,其他部分都是基本固定和有上限限制的。堆的空间大小对于64位的进程可以说基本不受限的,因此我们的思路是查看堆中的内容。 三.解决方法使用windbg attach到进程后,依次执行 0:003> !heap -sNtGlobalFlag enables following debugging aids for new heaps: stack back tracesLFH Key : 0x0000008f4b2ee335Termination on corruption : ENABLED Heap Flags Reserv Commit Virt Free List UCR Virt Lock Fast (k) (k) (k) (k) length blocks cont. heap -------------------------------------------------------------------------------------0000000002460000 08000002 1024 432 1024 1 6 1 0 0 LFH0000000000010000 08008000 64 8 64 5 1 1 0 0 0000000000020000 08008000 64 64 64 61 1 1 0 0 00000000001a0000 08001002 1088 256 1088 3 1 2 0 0 LFH00000000040a0000 08001002 512 28 512 0 2 1 0 0 0000000004080000 08001002 1088 332 1088 6 5 2 0 0 LFH-------------------------------------------------------------------------------------0:003> !heap -stat -h 0000000004080000 heap @ 0000000004080000group-by: TOTSIZE max-display: 20 size #blocks total ( %) (percent of total busy bytes) 6608 1 - 6608 (36.24) 4000 1 - 4000 (22.73) 1000 2 - 2000 (11.37) 1001 1 - 1001 (5.68) b00 1 - b00 (3.91) 2c8 3 - 858 (2.96) 200 4 - 800 (2.84) 401 1 - 401 (1.42) 3ff 1 - 3ff (1.42) 30a 1 - 30a (1.08) 178 2 - 2f0 (1.04) 28 11 - 2a8 (0.94) 38 a - 230 (0.78) 220 1 - 220 (0.75) 100 2 - 200 (0.71) 58 5 - 1b8 (0.61) 160 1 - 160 (0.49) 130 1 - 130 (0.42) 30 6 - 120 (0.40) 48 3 - d8 (0.30)0:003> !heap -flt s 401(这个是随机抽选的,当然我写的示例中正好是泄漏了这么大) _HEAP @ 2460000 _HEAP @ 10000 _HEAP @ 20000 _HEAP @ 1a0000 _HEAP @ 40a0000 _HEAP @ 4080000 HEAP_ENTRY Size Prev Flags UserPtr UserSize - state 0000000003f907e0 0043 0000 [00] 0000000003f90810 00401 - (busy)0:003> !heap -p -a 0000000003f90810 address 0000000003f90810 found in _HEAP @ 4080000 HEAP_ENTRY Size Prev Flags UserPtr UserSize - state 0000000003f907e0 0043 0000 [00] 0000000003f90810 00401 - (busy) 7700cc0d ntdll! ?? ::FNODOBFM::`string'+0x000000000001913b 71048d17 MSVCR100!malloc+0x000000000000005b 71048ddb MSVCR100!operator new+0x000000000000001f 13fa423d7 test!test1+0x0000000000000017 13fa47ed4 test!boost::detail::thread_data<long (__cdecl*)(void)>::run+0x0000000000000014 13fa52e43 test!boost::`anonymous namespace'::thread_start_function+0x0000000000000043 71001d9f MSVCR100!_callthreadstartex+0x0000000000000017 71001e3b MSVCR100!_threadstartex+0x000000000000007f 76e6652d kernel32!BaseThreadInitThunk+0x000000000000000d 76f9c521 ntdll!RtlUserThreadStart+0x000000000000001d这样就可以定位到申请内存的代码行数(需要注意的是,设置符号文件,并且执行 gflags.exe /i *.exe +ust) ...

July 1, 2019 · 2 min · jiezi

callstack的恢复

一.问题的现象程序崩溃的时候有时候会产生dump,或者弹出错误框“×××已停止工作”,这时候用windbg打开dump文件或者attach到进程,查看异常的线程,有时候线程的调用栈已经看不到,如何最大限度的手动恢复栈呢?我也曾经为了解决这个问题查了不少资料,现在也没找到完美的方法,限于自己的知识,只能尽力去恢复。 环境和工具主备操作系统是windows server 2012(64)64,异常的程序是64位,使用的调试工具windbg也是64位。 三.问题追踪首先,调用k命令,输出如下: 0:013> kChild-SP RetAddr Call Site00000000`3c98db18 00007ffc`749213ed ntdll!ZwWaitForMultipleObjects+0xa00000000`3c98db20 00007ffc`772c8241 KERNELBASE!WaitForMultipleObjectsEx+0xe100000000`3c98de00 00000000`00000000 KERNEL32!WerpReportFaultInternal+0x581通过这个callstack,我们很难知道异常在哪。接着,执行!teb指令,查看线程栈的起始位置,输出如下: 0:013> !tebTEB at 00007ff6df4ae000 ExceptionList: 0000000000000000 StackBase: 000000003c990000 StackLimit: 000000003c98d000 SubSystemTib: 0000000000000000 FiberData: 0000000000001e00 ArbitraryUserPointer: 0000000000000000 Self: 00007ff6df4ae000 EnvironmentPointer: 0000000000000000 ClientId: 000000000000148c . 0000000000001c08 RpcHandle: 0000000000000000 Tls Storage: 0000000000e9acd0 PEB Address: 00007ff6df5f6000 LastErrorValue: 0 LastStatusValue: c0000017 Count Owned Locks: 0 HardErrorMode: 0找到线程栈的起始地址和结束地址后,执行命令: dps 000000003c98d000 000000003c990000输出的内容如下: 0:013> dps 000000003c98d000 000000003c99000000000000`3c98d000 00000000`0000000000000000`3c98d008 00000000`0000000000000000`3c98d010 00000000`0000000000000000`3c98d018 00007ffc`776bd702 ntdll!EtwEventWriteNoRegistration+0x92...00000000`3c98d0b8 00007ffc`7770be5a ntdll!WaitForWerSvc+0x8a00000000`3c98d0c0 00000000`0000000100000000`3c98d0c8 00007ffc`7770bf20 ntdll!WerpAllocateAndInitializeSid+0xac00000000`3c98d0d0 00000000`0000000000000000`3c98d0d8 00000000`0000000000000000`3c98d0e0 00000000`0048004600000000`3c98d0e8 00007ffc`776d8380 ntdll! ?? ::FNODOBFM::`string'00000000`3c98d0f0 00000000`0000100000000000`3c98d0f8 00007ffc`7770bfa9 ntdll!WerpFreeSid+0x41 ...00000000`3c98ec38 00007ffc`776d262a ntdll!KiUserExceptionDispatcher+0x3a ... 上面的内容输出很长,内容也很丰富,只列了一部分,执行下面的命令: ...

June 26, 2019 · 1 min · jiezi

new内存太大引发进程崩溃

一.简介win32下应用程序的内存最大限制为2G,如果new一块很大的内存即使没有超过系统限制也可能会引发崩溃,因为new出来的内存是连续的。 二.问题现象和跟踪分析在调试一个内存越界的问题时,开启了gflags -p /enable *.exe /full,之后进程再也无法启动成功,查看此时进程内存,亦只有300多M,还剩余很多。另外产生的dump文件中,显示用户可用内存有100多M。通过调试发现,此时进程停在了new的地方,具体调用栈如图1:图1 异常调用栈 查看03帧下size大小为0x20ce7000,大概为550多M,因为gflags设置内存对齐后,需要更大的内存。申请不到,因此程序在此时出现异常。(查看此值大小需要设定操作系统符号文件路径)图2 申请内存大小 三.问题解决申请的内存大小变小后,未再出现异常。

April 22, 2019 · 1 min · jiezi

windbg常用命令详解

说明:此文章不定期编辑,对工作中常遇到的命令进行补充,使用方法大部分来自windbg帮助文档,具体使用请参考帮助手册。 一.断点ba(break on access),当指定的内存被访问时触发例子如下:r指read或者write后面的变量g_nCacheCapacity地址,大小为4个字节时,触发断点。 ba r4 g_nCacheCapacity

April 22, 2019 · 1 min · jiezi

使用umdh工具检测windows进程内存泄漏

一.简介内存泄漏指由于编码错误导致进程的内存未能释放,从而不断增加,严重的情况可导致进程崩溃。二.umdh工具检测内存泄漏的方法umdh是windbg自带的一个检测内存泄漏的工具,用于检测windows下进程的内存泄漏。具体步骤如下:1.设置用户态栈跟踪数据库(user mode stack trace database ),跟踪的进程为test.exegflags /i test.exe +ust2.设置符号文件路径set _NT_SYMBOL_PATH=C:\symbols3.启动test.exe或者重新启动test.exe4.打开服务管理器找到test.exe对应的进程ID,假设为1234。5.首次运行umdhumdh -p:1234 -f:c:\log\test1.txt6.待该进程内存增加后再执行umdh -p:1234 -f:c:\log\test2.txt7.计算内存增加umdh c:\log\test1.txt c:\log\test2.txt > c:\log\test_comp.txt查看test_comp.txt中,内存增加的代码行数即可定位了。三.存在某些进程不能使用umdh检测内存泄漏的情况返回错误码299,暂时没找到解决方法。

April 18, 2019 · 1 min · jiezi

栈缓冲区溢出(stack Buffer Overrun)引发进程崩溃

一.问题描述程序崩溃,如果1所示图1 进程崩溃图二.使用windbg调试使用windbg attach上崩溃的进程,执行命令~*kn,观察所有的线程,其中某个线程(09s)的调用栈如图2所示图2 9号线程的调用栈在该线程的调用栈中,出现了__report_gsfailure函数,此函数的存在是判断此线程出问题的原因。三.cookie机制VS2005版本开始引入cookie机制来检查是否存在缓冲区溢出,cookie是一个32位的整数,存在栈帧起始处EPB-4的位置,当函数返回时,此值与函数刚开始进入时的cookie值,如果不同,说明是栈缓冲区被破坏了。这是一个比较严重的错误,因此进程报错,程序停止运行。在此进程中,EBP的值为2c87d780,执行命令dd可以看到此时栈上cookie值,父函数的EBP值和函数返回值。0:009> dd 2c87d780-4 l42c87d77c 00000000 2c87d7c8 74ee19f8 2c87d734其中,00000000即为cookie值。这个问题由于最后一个栈帧中保存了出问题的代码行数,此处代码经排查有较多的memcpy,然而没有对第三个参数长度进行判断,因此造成了栈缓冲区溢出。后记字符串复制或者内存复制的时候,一定记得不要超过源或者目标的最小长度。

April 18, 2019 · 1 min · jiezi