首页
社区
课程
招聘
[原创]调试技巧 | 从 Win7 到 Win10,DLL引用计数查看全攻略
发表于: 10小时前 210

[原创]调试技巧 | 从 Win7 到 Win10,DLL引用计数查看全攻略

10小时前
210

前言

有两种常用的方式可以查看 dll 的引用计数。

  1. 在 windbg 中通过 !dlls 命令查看 dll 的引用计数。!dlls 输出结果中包含 LoadCount 字段。

    注意: 不同版本的 windbg 使用的 !exts.dlls 命令解析方式不同,低版本的 windbg 在解析 win10 上运行程序的 dll 引用计数,解析结果很可能是错的。

  2. 手动解析 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
ntdll!_PEB
  +0x00c Ldr : 0x77290200

ldr 中有三个链表,分别是 InLoadOrderModuleListInMemoryOrderModuleList 和 InInitializationOrderModuleList

0:000> dt ntdll!_PEB_LDR_DATA 0x77290200 
  +0x000 Length           : 0x30
  +0x004 Initialized      : 0x1 ''
  +0x008 SsHandle         : (null)
  +0x00c InLoadOrderModuleList : _LIST_ENTRY [ 0x6b27a0 - 0x6b34b0 ]    //<----
  +0x014 InMemoryOrderModuleList : _LIST_ENTRY [ 0x6b27a8 - 0x6b34b8 ]    //<----
  +0x01c InInitializationOrderModuleList : _LIST_ENTRY [ 0x6b2840 - 0x6b34c0 ]    //<----
  +0x024 EntryInProgress  : (null)
  +0x028 ShutdownInProgress : 0 ''
  +0x02c ShutdownThreadId : (null)

每个链表项的类型是 LDR_DATA_TABLE_ENTRY,结构如下:

0:000> dt _LDR_DATA_TABLE_ENTRY 0x6b27a0 
ntdll!_LDR_DATA_TABLE_ENTRY
  +0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x6b2830 - 0x7729020c ]
  +0x008 InMemoryOrderLinks : _LIST_ENTRY [ 0x6b2838 - 0x77290214 ]
  +0x010 InInitializationOrderLinks : _LIST_ENTRY [ 0x0 - 0x0 ]
  +0x018 DllBase          : 0x00fa0000 Void
  +0x01c EntryPoint       : 0x00fb1109 Void
  +0x020 SizeOfImage      : 0x1b000
  +0x024 FullDllName      : _UNICODE_STRING "C:\Users\bcn\Desktop\WaitDllUnloadExe.exe"
  +0x02c BaseDllName      : _UNICODE_STRING "WaitDllUnloadExe.exe"
  +0x034 Flags            : 0x4000
  +0x038 LoadCount        : 0xffff    //<----
  +0x03a TlsIndex         : 0
  +0x03c HashLinks        : _LIST_ENTRY [ 0x77294818 - 0x77294818 ]
  +0x03c SectionPointer   : 0x77294818 Void
  +0x040 CheckSum         : 0x77294818
  +0x044 TimeDateStamp    : 0x696f76bc
  +0x044 LoadedImports    : 0x696f76bc Void
  +0x048 EntryPointActivationContext : (null)
  +0x04c PatchInformation : (null)
  +0x050 ForwarderLinks   : _LIST_ENTRY [ 0x6b27f0 - 0x6b27f0 ]
  +0x058 ServiceTagLinks  : _LIST_ENTRY [ 0x6b27f8 - 0x6b27f8 ]
  +0x060 StaticLinks      : _LIST_ENTRY [ 0x6b3568 - 0x6b3488 ]
  +0x068 ContextInformation : 0x771cc924 Void
  +0x06c OriginalBase     : 0
  +0x070 LoadTime         : _LARGE_INTEGER 0x0

可以通过如下命令显示出每个 dll 的引用计数。

!list -t nt!_LIST_ENTRY.FLink -x "dt nt!_LDR_DATA_TABLE_ENTRY LoadCount BaseDllName @$extret" 0x76ff5da0+0x00c,其中 0x77b80200 为 ldr 的基址。

命令解释

关于以上命令的 AI 解释如下:

  1. !list

    • 专门用于遍历链表的 Windbg 扩展命令
    • 可以遍历 LIST_ENTRY 结构定义的双向链表
  2. -t nt!_LIST_ENTRY.FLink

    • -t 指定链表节点的类型
    • nt!_LIST_ENTRY.FLink 指定链表结构体中的前向指针字段
    • 表示链表通过 FLink 字段连接
  3. -x选项及其参数

    -x "dt nt!_LDR_DATA_TABLE_ENTRY LoadCount BaseDllName @$extret"
    • -x 表示对每个链表节点执行指定的命令
    • 执行的命令是:dt nt!_LDR_DATA_TABLE_ENTRY LoadCount BaseDllName @$extret
    • @$extret 是一个伪寄存器,包含当前遍历到的节点地址
  4. 起始地址:0x76ff5da0+0x00c

    • 链表的起始地址
    • 0x76ff5da0 是基地址
    • +0x00c 是偏移量
    • 最终地址是:0x76ff5dac(0x76ff5da0 + 0xc)

这个命令会:

  1. 从地址 0x76ff5dac 开始遍历链表
  2. 对链表中每个节点:
    • 将节点地址传递给 @$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
ntdll!_LDR_DATA_TABLE_ENTRY
  +0x000 InLoadOrderLinks : _LIST_ENTRY
  +0x008 InMemoryOrderLinks : _LIST_ENTRY
  +0x010 InInitializationOrderLinks : _LIST_ENTRY
  +0x018 DllBase          : Ptr32 Void
  +0x01c EntryPoint       : Ptr32 Void
  +0x020 SizeOfImage      : Uint4B
  +0x024 FullDllName      : _UNICODE_STRING
  +0x02c BaseDllName      : _UNICODE_STRING
  +0x034 FlagGroup        : [4] UChar
  +0x034 Flags            : Uint4B
  +0x034 PackagedBinary   : Pos 0, 1 Bit
  +0x034 MarkedForRemoval : Pos 1, 1 Bit
  +0x034 ImageDll         : Pos 2, 1 Bit
  +0x034 LoadNotificationsSent : Pos 3, 1 Bit
  +0x034 TelemetryEntryProcessed : Pos 4, 1 Bit
  +0x034 ProcessStaticImport : Pos 5, 1 Bit
  +0x034 InLegacyLists    : Pos 6, 1 Bit
  +0x034 InIndexes        : Pos 7, 1 Bit
  +0x034 ShimDll          : Pos 8, 1 Bit
  +0x034 InExceptionTable : Pos 9, 1 Bit
  +0x034 ReservedFlags1   : Pos 10, 2 Bits
  +0x034 LoadInProgress   : Pos 12, 1 Bit
  +0x034 LoadConfigProcessed : Pos 13, 1 Bit
  +0x034 EntryProcessed   : Pos 14, 1 Bit
  +0x034 ProtectDelayLoad : Pos 15, 1 Bit
  +0x034 ReservedFlags3   : Pos 16, 2 Bits
  +0x034 DontCallForThreads : Pos 18, 1 Bit
  +0x034 ProcessAttachCalled : Pos 19, 1 Bit
  +0x034 ProcessAttachFailed : Pos 20, 1 Bit
  +0x034 CorDeferredValidate : Pos 21, 1 Bit
  +0x034 CorImage         : Pos 22, 1 Bit
  +0x034 DontRelocate     : Pos 23, 1 Bit
  +0x034 CorILOnly        : Pos 24, 1 Bit
  +0x034 ChpeImage        : Pos 25, 1 Bit
  +0x034 ReservedFlags5   : Pos 26, 2 Bits
  +0x034 Redirected       : Pos 28, 1 Bit
  +0x034 ReservedFlags6   : Pos 29, 2 Bits
  +0x034 CompatDatabaseProcessed : Pos 31, 1 Bit
  +0x038 ObsoleteLoadCount : Uint2B
  +0x03a TlsIndex         : Uint2B
  +0x03c HashLinks        : _LIST_ENTRY
  +0x044 TimeDateStamp    : Uint4B
  +0x048 EntryPointActivationContext : Ptr32 _ACTIVATION_CONTEXT
  +0x04c Lock             : Ptr32 Void
  +0x050 DdagNode         : Ptr32 _LDR_DDAG_NODE    //<----
  +0x054 NodeModuleLink   : _LIST_ENTRY
  +0x05c LoadContext      : Ptr32 _LDRP_LOAD_CONTEXT
  +0x060 ParentDllBase    : Ptr32 Void
  +0x064 SwitchBackContext : Ptr32 Void
  +0x068 BaseAddressIndexNode : _RTL_BALANCED_NODE
  +0x074 MappingInfoIndexNode : _RTL_BALANCED_NODE
  +0x080 OriginalBase     : Uint4B
  +0x088 LoadTime         : _LARGE_INTEGER
  +0x090 BaseNameHashValue : Uint4B
  +0x094 LoadReason       : _LDR_DLL_LOAD_REASON
  +0x098 ImplicitPathOptions : Uint4B
  +0x09c ReferenceCount   : Uint4B
  +0x0a0 DependentLoadFlags : Uint4B
  +0x0a4 SigningLevel     : UChar

LDR_DDAG_NODE 结构体定义如下:

0:000> dt ntdll!_LDR_DDAG_NODE
  +0x000 Modules          : _LIST_ENTRY
  +0x008 ServiceTagList   : Ptr32 _LDR_SERVICE_TAG_RECORD
  +0x00c LoadCount        : Uint4B       // <----
  +0x010 LoadWhileUnloadingCount : Uint4B
  +0x014 LowestLink       : Uint4B
  +0x018 Dependencies     : _LDRP_CSLIST
  +0x01c IncomingDependencies : _LDRP_CSLIST
  +0x020 State            : _LDR_DDAG_STATE
  +0x024 CondenseLink     : _SINGLE_LIST_ENTRY
  +0x028 PreorderNumber   : Uint4B

其中的 LoadCount 表示引用计数。

快捷命令

按上面的操作步骤,每次查看引用计数都需要执行几次 dt 命令,效率太低,可以通过 dx 命令查看,非常灵活高效。

在 win7 系统下查看指定 dll 引用计数

dx @$modules = Debugger.Utility.Collections.FromListEntry(@$peb->Ldr->InLoadOrderModuleList, "ntdll!_LDR_DATA_TABLE_ENTRY", "InLoadOrderLinks")

dx -r2 @$modules.Select(m => new { Name = ((wchar_t*)m.BaseDllName.Buffer).ToDisplayString("su"), Base = m.DllBase, Count = m.LoadCount }).Where(m => m.Name.ToLower().Contains("your_dll_name"))

在 win10 系统下查看指定 dll 引用计数

dx @$modules = Debugger.Utility.Collections.FromListEntry(@$peb->Ldr->InLoadOrderModuleList, "ntdll!_LDR_DATA_TABLE_ENTRY", "InLoadOrderLinks")

dx -r2 @$modules.Select(m => new { Name = ((wchar_t*)m.BaseDllName.Buffer).ToDisplayString("su"), Base = m.DllBase, Count = m.DdagNode->LoadCount }).Where(m => m.Name.ToLower().Contains("your_dll_name"))

说明: 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 的引用计数。

参考资料

d2dK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2K6k6h3y4#2M7X3W2@1P5i4S2H3L8r3!0V1k6h3c8Q4x3X3g2U0L8$3#2Q4x3V1k6V1L8r3I4J5k6h3k6U0L8%4g2F1N6q4)9J5k6i4m8Z5M7l9`.`.

401K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6K6N6r3q4U0K9$3!0$3k6i4u0X3L8r3!0%4i4K6u0W2j5$3!0E0i4K6u0r3M7i4g2W2M7%4c8A6L8$3&6K6i4K6u0r3x3K6f1#2x3K6t1K6x3g2)9J5c8X3S2G2N6#2)9J5k6s2c8G2i4K6u0V1j5$3S2W2j5$3E0Q4x3X3c8V1L8r3I4K6i4K6u0V1M7X3g2X3k6i4u0W2L8X3y4W2i4K6u0V1j5$3!0#2L8Y4c8Q4x3X3c8Z5L8%4N6Q4x3X3c8@1L8#2)9J5k6r3E0F1L8%4N6Q4x3X3c8%4K9r3g2J5k6g2)9J5k6s2c8Z5k6g2)9J5k6r3c8D9L8q4)9J5k6s2N6S2M7#2)9J5k6r3I4G2j5h3c8W2k6l9`.`.

7ccK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2Y4k6h3!0X3k6X3y4Z5j5i4m8H3k6h3I4D9i4K6u0W2j5$3!0E0i4K6u0r3M7%4c8#2k6r3W2W2M7#2)9J5c8Y4N6A6L8X3c8G2N6%4y4Q4x3V1k6C8L8g2)9J5c8X3&6@1L8%4y4C8M7X3&6D9i4K6u0r3K9h3&6U0i4K6u0r3j5i4m8A6i4K6u0r3L8Y4c8D9k6s2u0Q4x3V1k6D9k6s2u0Q4y4h3k6V1j5i4c8S2i4K6g2X3N6r3q4T1L8r3g2Q4y4h3k6W2L8Y4c8J5P5g2)9J5k6h3S2@1L8b7`.`.



[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!

收藏
免费 2
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回