Windbg
+ -

Windbg 跟踪句柄泄漏(!htrace)

2021-07-01 50 0

!htrace(Handle Trace) 扩展用于显示一个或多个句柄的堆栈回溯信息。
直接用!htrace -?可以看到简单使用说明:

0:000> !htrace -?  
!htrace [handle [max_traces]]  
!htrace -enable [max_traces]  
!htrace -disable  
!htrace -snapshot  
!htrace -diff
  • Handle: 指定要显示堆栈回溯的句柄。如果Handle 为0 或者省略,则显示进程中所有句柄的堆栈回溯。
  • Process: (仅内核模式)指定要显示句柄的进程。如果Process 为0或者省略,则使用当前进程。用户模式下总是使用当前进程。
  • Max_Traces:指定要显示的堆栈回溯的最大层数。用户模式下如果省略该参数,则显示目标进程中的所有堆栈回溯。
  • -enable:(仅用户模式) 启用句柄跟踪,并且为-diff 选项使用的初始状态产生第一次句柄信息的快照。
  • -snapshot:(仅用户模式) 抓取当前的句柄信息的快照用作-diff 选项的初始状态。.
  • -diff:(仅用户模式) 将当前的句柄信息和上一次句柄快照的信息进行对比。显示所有仍然打开的句柄。
  • -disable:(仅用户模式;仅Windows Server 2003和之后的系统) 禁止句柄跟踪。在Windows XP中,只有结束目标进程才能禁用句柄跟踪。
  • -?:在调试器命令窗口中显示一些简要的帮助文本。

Htrace查找handle泄露点操作步骤

http://blog.csdn.net/tpriwwq/article/details/9082709

  1. 使用Windbg -File-OpenExecutable,打开要调试exe. 程序启动后,系统会暂停程序一次.这个是正常的.
  2. 启用htrace: (在windbg的命令窗口中输入)!htrace -enable
  3. 运行程序: (在windbg的命令窗口中输入)g
    4.某个时刻创建一次句柄快照:(在windbg的命令窗口中输入)!htrace -snapshot
  4. 观察到handle增长,暂停进程(Debug - Break)
  5. 继续运行后进行一次句柄快照对比:(在windbg的命令窗口中输入)!htrace -diff
  6. 使用(在windbg的命令窗口中输入)!htrace (此处为handle的地址),即可跟踪到handle泄露点
  7. 根据handle的输出信息,如果有源码,则可以使用(在windbg的命令窗口中输入)lsa 传递指定位置对应的代码,定位到handle泄露点

windbg-!htrace(跟踪句柄泄漏)示例

首先需要用!htrace -enable来告诉操作系统启用栈回溯(这是前提)

!htrace -snapshot  
!htrace -diff  
0:000> !htrace -enable  
Handle tracing enabled.  
Handle tracing information snapshot successfully taken.

可以看到,-enable是一个两步操作,首先,启动栈回溯(Handle tracing enabled),

然后,它根据句柄来抓取进程当前状态的快照(Handle tracing information snapshot successfully taken.)
在栈回溯被启用后,windows将立即开始记录所有的句柄创建调用和句柄删除调用,在下一次抓取快照时(-snapshot),!htrace将向操作系统查询所有句柄
创建调用和句柄删除调用的栈回溯,并且把它们显示出来,
这时直接用!htrace会输出以下内容:

0:001> !htrace  
--------------------------------------  
Handle = 0x000007bc - CLOSE  
Thread ID = 0x000014fc, Process ID = 0x000013f0  

0x7c8135dd: kernel32!GetLongPathNameW+0x00000249  
0x7854287c: MSVCR90!_check_manifest+0x0000009c  
0x78542c22: MSVCR90!__CRTDLL_INIT+0x0000008e  
0x78542d5e: MSVCR90!_CRTDLL_INIT+0x0000001e  
0x7c92118a: ntdll!LdrpCallInitRoutine+0x00000014  
0x7c93b5d2: ntdll!LdrpRunInitializeRoutines+0x00000344  
0x7c93fbdc: ntdll!LdrpInitializeProcess+0x0000114b  
0x7c93fad7: ntdll!_LdrpInitialize+0x00000183  
0x7c92e457: ntdll!KiUserApcDispatcher+0x00000007  
--------------------------------------  
Handle = 0x000007bc - OPEN  
Thread ID = 0x000014fc, Process ID = 0x000013f0  

0x7c80ef97: kernel32!FindFirstFileW+0x00000016  
0x7c8135c5: kernel32!GetLongPathNameW+0x00000231  
0x7854287c: MSVCR90!_check_manifest+0x0000009c  
0x78542c22: MSVCR90!__CRTDLL_INIT+0x0000008e  
0x78542d5e: MSVCR90!_CRTDLL_INIT+0x0000001e  
0x7c92118a: ntdll!LdrpCallInitRoutine+0x00000014  
0x7c93b5d2: ntdll!LdrpRunInitializeRoutines+0x00000344  
0x7c93fbdc: ntdll!LdrpInitializeProcess+0x0000114b  
0x7c93fad7: ntdll!_LdrpInitialize+0x00000183  
--------------------------------------  
Handle = 0x000007c0 - CLOSE  
Thread ID = 0x000014fc, Process ID = 0x000013f0  

0x7c8135dd: kernel32!GetLongPathNameW+0x00000249  
0x7854287c: MSVCR90!_check_manifest+0x0000009c  
0x78542c22: MSVCR90!__CRTDLL_INIT+0x0000008e  
0x78542d5e: MSVCR90!_CRTDLL_INIT+0x0000001e  
0x7c92118a: ntdll!LdrpCallInitRoutine+0x00000014  
0x7c93b5d2: ntdll!LdrpRunInitializeRoutines+0x000003  
.........................

格式一般是句柄值,进程线程, OPEN表示打开句柄的栈回溯,CLOSE表示关闭句柄的栈回溯,那么关键是要找到那些栈回溯打开了句柄,却没有相应的栈回溯来关闭句柄,这可以通过!htrace -diff来实现:

0:000> !htrace -diff  
Handle tracing information snapshot successfully taken.  
0xfa new stack traces since the previous snapshot.  
Ignoring handles that were already closed...  
Outstanding handles opened since the previous snapshot:  
--------------------------------------  
Handle = 0x000005b0 - OPEN  
Thread ID = 0x000007f4, Process ID = 0x000013f0  

0x00401657: test1!CServer::GetToken+0x00000047  
0x0040136f: test1!CServer::GetSID+0x0000001f  
0x004010de: test1!ThreadWorker+0x0000007e  
0x7c80b729: kernel32!BaseThreadStart+0x00000037  
--------------------------------------  
Handle = 0x000005b4 - OPEN  
Thread ID = 0x00000c34, Process ID = 0x000013f0  

0x00401657: test1!CServer::GetToken+0x00000047  
0x0040136f: test1!CServer::GetSID+0x0000001f  
0x004010de: test1!ThreadWorker+0x0000007e  
0x7c80b729: kernel32!BaseThreadStart+0x00000037  
--------------------------------------  
Handle = 0x000005b8 - OPEN  
Thread ID = 0x00001650, Process ID = 0x000013f0  

0x00401657: test1!CServer::GetToken+0x00000047

很明显了,我们看到在GetToken中有句柄未关闭,所以在使用!htrace时采用的步骤一般是:

  1. 在重现泄漏问题之前,启用句柄跟踪(!htrace -enable)
    2.执行重现过程,并且让进程句柄泄漏
    3.通过!htrace -diff来找出有问题的栈

自己写了下DML方便使用:

.printf "nnfor trace handle leakn"  
.block  
{  
    as ${/v:ScriptName} c:cmdtreescriptUser-ModetraceHandle.txt  
}  

.printf /D "<link cmd="!htrace -enable;ad ${/v:ScriptName};$$><${ScriptName}">trace handle :<col fg="changed"bg="wbg"><b>snap-enable</b></col></link>nn"  
.printf /D "<link cmd="!htrace -snapshot;ad ${/v:ScriptName};$$><${ScriptName}">trace handle :<col fg="changed"bg="wbg"><b>snap-snapshot</b></col></link>nn"  
.printf /D "<link cmd=".echo you should get snapshot more than twice!!!!!!!!!!!!!!!!!!!!!!!!!;!htrace -diff;ad ${/v:ScriptName};$$><${ScriptName}"nn>trace handle :<col fg="changed"bg="wbg"><b>diff</b></col></link>nn"

原文地址:https://blog.csdn.net/bcbobo21cn/article/details/68935814
参考:
http://blog.csdn.net/yockie/article/details/40603511
http://blog.csdn.net/tpriwwq/article/details/9082709

0 篇笔记 写笔记

作者信息
我爱内核
Windows驱动开发,网站开发
好好学习,天天向上。
取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

您的支持,是我们前进的动力!