-
-
[原创]调试技巧 | 从 Win7 到 Win10,DLL引用计数查看全攻略
-
发表于: 10小时前 210
-
前言
有两种常用的方式可以查看 dll 的引用计数。
在
windbg中通过!dlls命令查看dll的引用计数。!dlls输出结果中包含LoadCount字段。注意: 不同版本的
windbg使用的!exts.dlls命令解析方式不同,低版本的windbg在解析win10上运行程序的dll引用计数,解析结果很可能是错的。手动解析
PEB中的PEB_LDR_DATA中的模块加载列表数据查看dll的引用计数。Win7及之前系统:
找到PEB中的PEB_LDR_DATA(Ldr字段),遍历其InLoadOrderModuleList链表。链表项为_LDR_DATA_TABLE_ENTRY结构,其中的LoadCount字段即为引用计数。Win8及之后系统:
结构发生变化,需在_LDR_DATA_TABLE_ENTRY中找到DdagNode字段(它是一个指向_LDR_DDAG_NODE的指针),该结构中的LoadCount字段为当前引用计数。
win7 查看方法
进程的 PEB 中保存了 PEB_LDR_DATA,对应的字段名为 ldr,可以通过 dt _PEB @$peb -b ldr 命令查看 ldr 的内容。
0:000> dt _PEB @$peb -b ldr |
ldr 中有三个链表,分别是 InLoadOrderModuleList,InMemoryOrderModuleList 和 InInitializationOrderModuleList
0:000> dt ntdll!_PEB_LDR_DATA 0x77290200 |
每个链表项的类型是 LDR_DATA_TABLE_ENTRY,结构如下:
0:000> dt _LDR_DATA_TABLE_ENTRY 0x6b27a0 |
可以通过如下命令显示出每个 dll 的引用计数。
!list -t nt!_LIST_ENTRY.FLink -x "dt nt!_LDR_DATA_TABLE_ENTRY LoadCount BaseDllName @$extret" 0x76ff5da0+0x00c,其中 0x77b80200 为 ldr 的基址。
命令解释
关于以上命令的 AI 解释如下:
!list- 专门用于遍历链表的
Windbg扩展命令 - 可以遍历
LIST_ENTRY结构定义的双向链表 -t nt!_LIST_ENTRY.FLink-t指定链表节点的类型nt!_LIST_ENTRY.FLink指定链表结构体中的前向指针字段- 表示链表通过
FLink字段连接 -x选项及其参数-x "dt nt!_LDR_DATA_TABLE_ENTRY LoadCount BaseDllName @$extret"
-x表示对每个链表节点执行指定的命令- 执行的命令是:
dt nt!_LDR_DATA_TABLE_ENTRY LoadCount BaseDllName @$extret @$extret是一个伪寄存器,包含当前遍历到的节点地址起始地址:
0x76ff5da0+0x00c- 链表的起始地址
0x76ff5da0是基地址+0x00c是偏移量- 最终地址是:
0x76ff5dac(0x76ff5da0 + 0xc)
这个命令会:
- 从地址
0x76ff5dac开始遍历链表 - 对链表中每个节点:
- 将节点地址传递给
@$extret - 将该地址解释为
_LDR_DATA_TABLE_ENTRY结构 - 显示该结构中的
LoadCount和BaseDllName字段
说明: 以上查看方法只适用于
win7及以前的系统,从win8开始,结构体发生了改变。
win8 及 win10 查看方法
从 win8 开始,_LDR_DATA_TABLE_ENTRY 结构体发生了变化,引入了 LDR_DDAG_NODE 结构体。
0:008> dt _LDR_DATA_TABLE_ENTRY |
LDR_DDAG_NODE 结构体定义如下:
0:000> dt ntdll!_LDR_DDAG_NODE |
其中的 LoadCount 表示引用计数。
快捷命令
按上面的操作步骤,每次查看引用计数都需要执行几次 dt 命令,效率太低,可以通过 dx 命令查看,非常灵活高效。
在 win7 系统下查看指定 dll 引用计数
dx @$modules = Debugger.Utility.Collections.FromListEntry(@$peb->Ldr->InLoadOrderModuleList, "ntdll!_LDR_DATA_TABLE_ENTRY", "InLoadOrderLinks") |
在 win10 系统下查看指定 dll 引用计数
dx @$modules = Debugger.Utility.Collections.FromListEntry(@$peb->Ldr->InLoadOrderModuleList, "ntdll!_LDR_DATA_TABLE_ENTRY", "InLoadOrderLinks") |
说明:
FromListEntry(ListEntry, [<ModuleName | ModuleObject>], TypeName, FieldExpression)第一个参数是链表地址,第二个参数可省略,第三个参数是链表节点类型,第四个参数是节点类型中指向下一个节点的成员名。以下输出是
dx Debugger.Utility.Collections.FromListEntry给出的说明。
Debugger.Utility.Collections.FromListEntry [FromListEntry(ListEntry, [<ModuleName | ModuleObject>], TypeName, FieldExpression) - Method which converts a LIST_ENTRY specified by the 'ListEntry' parameter of types whose name is specified by the string 'TypeName' and whose embedded links within that type are accessed via an expression specified by the string 'FieldExpression' into a collection object. If an optional module name or object is specified, the type name is looked up in the context of such module]
总结
- 可以使用
!exts.dlls扩展命令查看所有dll的引用计数。!exts.dlls -c addr可以查看指定dll的引用计数。 - 可以手动查看,需要解析
_LDR_DATA_TABLE_ENTRY结构。 dx命令非常灵活,可以方便的查看dll的引用计数。