【调试技术】.NET 调试

概述:使用 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 扩展:

1
.loadby sos clr

0x01 查看函数地址

!name2ee 查看类信息,如果不知道有哪些类,可以使用 !DumpHeap 命令,如果要查看具体的对象具有的方法但是又没有源代码时,可以使用 dnSpy 这类的反编译工具。

1
2
3
4
5
6
7
8
9
10
11
# 查看类的属性
!name2ee mscorelib.dll!Microsoft.Win32.Win32Native

# 输出如下所示
0:026> !name2ee mscorlib.dll!Microsoft.Win32.Win32Native
Module: 70931000
Assembly: mscorlib.dll
Token: 0200000e
MethodTable: 709acdcc
EEClass: 70a67be8
Name: Microsoft.Win32.Win32Native
1
2
3
4
5
6
7
8
9
10
11
# 查看具体的函数
!name2ee mscorlib.dll Win32Native.SafeCreateFile // 需要知道函数名

# 输出如下所示
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 命令用于查看当前调试的进程的堆上的对象,略微局限

  1. -stat:只输出堆上所有类型对象的统计摘要,包括它们的计数、大小和类型名称。这个选项非常有用,可以快速了解堆上对象的分布情况。
  2. -nostrings:排除字符串的输出。当与 -stat 一起使用时,这个选项可以使得输出更加简洁,专注于非字符串类型的对象。
  3. -gen X:仅输出属于指定代的对象。在 .NET 中,对象根据它们的生命周期被分配到不同的代(Generation),这个选项允许你查看特定代的对象。
  4. -min X 和 -max X:忽略小于或大于指定字节大小的对象。这两个选项可以帮助你过滤掉不感兴趣的小对象或大对象。
  5. -mt MethodTable:仅列出具有指定 MethodTable 的对象。MethodTable 是 .NET 中用于描述类型的内部结构。
  6. -type type:仅列出类型名为指定子字符串的对象。这个选项允许你按类型名称过滤输出。
  7. -live:仅输出仍然存活的对象,即那些还没有被垃圾回收器标记为可回收的对象。
  8. -dead:仅输出已死亡的对象,这些对象将在下一次完全垃圾回收(Full GC)中被回收。
  9. -cache:将对象保存在内部缓存中以供以后使用,这有助于加快后续扫描堆的速度。
  10. -lx:只打印每个堆中的指定数量的项,而不是所有对象。这可以帮助你限制输出的数量。
  11. -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 (等同于上面两条命令)

0x06 其他命令补充

  1. 帮助命令

    1
    !help
  2. 清屏

    1
    !cls
  3. 查看当前托管线程已执行时间

    1
    !runaway
  4. 查看当前线程池情况

    1
    !threadpool

【调试技术】.NET 调试
https://hodlyounger.github.io/wiki/调试技术/【调试技术】.NET 调试/
作者
mingming
发布于
2024年8月31日
许可协议