概述:使用 windbg 调试 .NET 文件,虽然使用windbg是必须的技能,但是非必要情况下可以使用 dnSpy调试。
[toc]
以 C# 如下所示的代码为例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| namespace DnrspEngine.Hooks.System { class Win32Native { [StructLayout(LayoutKind.Sequential)] internal class SECURITY_ATTRIBUTES { } internal static SafeFileHandle MySafeCreateFile(string lpFileName, int dwDesiredAccess, FileShare dwShareMode, SECURITY_ATTRIBUTES securityAttrs, FileMode dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile) { }
internal static SafeFileHandle PxySafeCreateFile(string lpFileName, int dwDesiredAccess, FileShare dwShareMode, SECURITY_ATTRIBUTES securityAttrs, FileMode dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile) { } } }
|
命名空间 DnrspEngine.Hooks.System 有一个类 Win32Native。
调试 .Net 程序前,需要加载 SOS 扩展,SOS (Son of Strike) 是 .NET 调试的扩展,它提供了很多用于调试 .NET 应用程序的命令。首先,你需要加载 SOS 扩展:
0x01 查看函数地址
!name2ee
查看类信息,如果不知道有哪些类,可以使用 !DumpHeap
命令,如果要查看具体的对象具有的方法但是又没有源代码时,可以使用 dnSpy 这类的反编译工具。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| # 查看类的属性 !name2ee mscorelib.dll!Microsoft.Win32.Win32Native !name2ee System.dll!System.Diagnostics.Process
# 输出如下所示 0:026> !name2ee mscorlib.dll!Microsoft.Win32.Win32Native Module: 70931000 Assembly: mscorlib.dll Token: 0200000e MethodTable: 709acdcc EEClass: 70a67be8 Name: Microsoft.Win32.Win32Native
#查看 MethodTable !DumpMT -md 709acdcc
#查看模块 !DumpModule /d 70931000
|
1 2 3 4 5 6 7 8 9 10 11 12
| # 查看具体的函数 !name2ee mscorlib.dll Win32Native.SafeCreateFile // 需要知道函数名 !name2ee system.dll System.Diagnostics.Process.Start
# 输出如下所示 0:026> !name2ee mscorlib.dll Win32Native.SafeCreateFile Module: 70931000 Assembly: mscorlib.dll Token: 06000042 MethodDesc: 70b9db00 Name: Microsoft.Win32.Win32Native.SafeCreateFile(System.String, Int32, System.IO.FileShare, SECURITY_ATTRIBUTES, System.IO.FileMode, Int32, IntPtr) JITTED Code Address: 70d044a0
|
输出分析,以我当前要使用的字段为主:
Module: 模块句柄
MethodTable:方法表。给出了我们对应的类的方法表的地址
!DumpHeap
!DumpHeap
命令用于查看当前调试的进程的堆上的对象,略微局限
- -stat:只输出堆上所有类型对象的统计摘要,包括它们的计数、大小和类型名称。这个选项非常有用,可以快速了解堆上对象的分布情况。
- -nostrings:排除字符串的输出。当与
-stat
一起使用时,这个选项可以使得输出更加简洁,专注于非字符串类型的对象。
- -gen X:仅输出属于指定代的对象。在 .NET 中,对象根据它们的生命周期被分配到不同的代(Generation),这个选项允许你查看特定代的对象。
- -min X 和 -max X:忽略小于或大于指定字节大小的对象。这两个选项可以帮助你过滤掉不感兴趣的小对象或大对象。
- -mt MethodTable:仅列出具有指定 MethodTable 的对象。MethodTable 是 .NET 中用于描述类型的内部结构。
- -type type:仅列出类型名为指定子字符串的对象。这个选项允许你按类型名称过滤输出。
- -live:仅输出仍然存活的对象,即那些还没有被垃圾回收器标记为可回收的对象。
- -dead:仅输出已死亡的对象,这些对象将在下一次完全垃圾回收(Full GC)中被回收。
- -cache:将对象保存在内部缓存中以供以后使用,这有助于加快后续扫描堆的速度。
- -lx:只打印每个堆中的指定数量的项,而不是所有对象。这可以帮助你限制输出的数量。
- -short:仅打印出对象的地址。这个选项通常与其他命令(如
.foreach
)组合使用,用于自动化处理或进一步分析。
0x02 查看方法有哪些
!dumpmt
查看方法有哪些
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| !dumpmt -md %MethodTable% # 查看方法有哪些 --------------------------------------- !dumpmt -md 00007ffd79054540
# 输出如下所示: 0:024> !dumpmt -md 00007ffd79054540 EEClass: 00007ffd792da338 Module: 00007ffd79051810 Name: DnrspEngine.Hooks.System.Win32Native mdToken: 000000000200000b File: DnrspEngine, Version=1.0.0.1031, Culture=neutral, PublicKeyToken=null BaseSize: 0x18 ComponentSize: 0x0 Slots in VTable: 7 Number of IFaces in IFaceMap: 0 -------------------------------------- MethodDesc Table Entry MethodDesc JIT Name 00007ffdd5c957a0 00007ffdd579c7a0 PreJIT System.Object.ToString() 00007ffdd5d0d2b0 00007ffdd5935560 PreJIT System.Object.Equals(System.Object) 00007ffdd5cfaba0 00007ffdd5935588 PreJIT System.Object.GetHashCode() 00007ffdd5d0aac0 00007ffdd5935590 PreJIT System.Object.Finalize() 00007ffd79159448 00007ffd79054538 NONE DnrspEngine.Hooks.System.Win32Native..ctor() 00007ffd792e1440 00007ffd79054518 JIT DnrspEngine.Hooks.System.Win32Native.MySafeCreateFile(System.String, Int32, System.IO.FileShare, SECURITY_ATTRIBUTES, System.IO.FileMode, Int32, IntPtr) 00007ffd792e1810 00007ffd79054528 JIT DnrspEngine.Hooks.System.Win32Native.PxySafeCreateFile(System.String, Int32, System.IO.FileShare, SECURITY_ATTRIBUTES, System.IO.FileMode, Int32, IntPtr)
|
输出分析:
可以看到输出内容是比较多的,并且输出了方法表示以及各个方法对应的地址。这里就可以对函数下断点了,以 DnrspEngine.Hooks.System.Win32Native.MySafeCreateFile
为例 (注意只有 JIT 的函数才可以下断点)
0x03 添加断点
1 2 3 4 5 6
| !bpmd -md 00007ffd79054518
# 输出如下所示: 0:024> !bpmd -md 00007ffd79054518 MethodDesc = 00007ffd79054518 Setting breakpoint: bp 00007FFD792E1440 [DnrspEngine.Hooks.System.Win32Native.MySafeCreateFile(System.String, Int32, System.IO.FileShare, SECURITY_ATTRIBUTES, System.IO.FileMode, Int32, IntPtr)]
|
分析:
可以看到断点设置信息,已经函数参数等。等待函数执行就可以触发断点。
0x04 查看堆栈
1 2 3 4 5 6 7
| !clrstack
# 查看所有线程的堆栈 !dumpstack #或者 ~*e!dumpstack
|
添加初始断点
1 2 3 4 5
| 0:000> sxe ld:clrjit
0:000> .load C:\Windows\Microsoft.NET\Framework\v4.0.30319\sos.dll 0:000> .load C:\Windows\Microsoft.NET\Framework\v4.0.30319\clr.dll 0:000> .loadby sos clr (等同于上面两条命令)
|
查找某个函数
1 2
| # 如下所示,查找AppDomain.CreateDomain !name2ee *!AppDoamin.CreateDomain
|
0x06 其他命令补充
-
帮助命令
-
清屏
-
查看当前托管线程已执行时间
-
查看当前线程池情况