qidao123.com技术社区-IT企服评测·应用市场

标题: .NET程序启动就报错,如何截获初期化时的问题json [打印本页]

作者: 怀念夏天    时间: 7 天前
标题: .NET程序启动就报错,如何截获初期化时的问题json
一:配景

1. 讲故事

前几天练习营里的一位朋侪在复习课件的时间,程序一跑就报错,截图如下:

从给出的错误信息看大概是因为json格式无效导致的,在早期的练习营里曾经也有一例这样的报错,末了定位下来是公司的电脑安全软件导致的,一旦有非托管调试器,安全软件就会加密 runtimeconfig.json,末了导致程序无法正常被调试执行。
此时相信有很多人想搞清楚,windbg 在那个时刻到底读到了什么脏东西?到底经历了怎样的惊魂时刻?
二:WinDbg 到底遇到了什么

1. 一个小案例

为了方便演示,写一个简单的 hello world 程序,代码如下:
  1.     internal class Program
  2.     {
  3.         static void Main(string[] args)
  4.         {
  5.             Console.WriteLine("hello world!");
  6.             Debugger.Break();
  7.             Console.ReadLine();
  8.         }
  9.     }
复制代码
接下来将程序编译之后观察 runtimeconfig.json 内容,截图如下。

2. 如何用 windbg 观察文件内容

熟悉 win32 api 的朋侪都知道,C# 的 ReadFile 底层会调用 win32 的 ReadFile 方法,方法签名如下:
  1. BOOL ReadFile(
  2.   [in]                HANDLE       hFile,
  3.   [out]               LPVOID       lpBuffer,
  4.   [in]                DWORD        nNumberOfBytesToRead,
  5.   [out, optional]     LPDWORD      lpNumberOfBytesRead,
  6.   [in, out, optional] LPOVERLAPPED lpOverlapped
  7. );
复制代码
方法的 lpBuffer 参数存放的就是读取到的文件内容。
这里还有一个问题就是 ReadFile 是在 dotnet 进程被初始化的时间读取到内存的,在这个初始化过程中会有一系列的加载,那到底在哪个时刻埋点呢?这时间可以借助 procmon 工具,观察 runtimeconfig.json 的加载时机,截图如下:

上面的卦中 runtimeconfig.json 的读取是发生在 hostfxr.dll 加载之后,有了这些信息,思路就有了。
  1. 0:000> sxe ld hostfxr
  2. 0:000> g
  3. ModLoad: 00007ffc`29b60000 00007ffc`29b8f000   C:\windows\System32\IMM32.DLL
  4. ModLoad: 00007ffc`03660000 00007ffc`036ba000   C:\Program Files\dotnet\host\fxr\9.0.2\hostfxr.dll
  5. ntdll!NtMapViewOfSection+0x14:
  6. 00007ffc`2b1ad9f4 c3              ret
  7. 0:000> bp KERNELBASE!ReadFile
  8. 0:000> g
  9. Breakpoint 1 hit
  10. KERNELBASE!ReadFile:
  11. 00007ffc`28822670 48895c2410      mov     qword ptr [rsp+10h],rbx ss:0000006c`c8f7de88=00000259c2eb0000
  12. 0:000> r
  13. rax=0000000000000000 rbx=0000000000000000 rcx=000000000000012c
  14. rdx=00000259c2ec8ee0 rsi=0000000000000003 rdi=0000000000000000
  15. rip=00007ffc28822670 rsp=0000006cc8f7de78 rbp=0000000000001000
  16. r8=0000000000001000  r9=0000006cc8f7df38 r10=00000259c2ec8ee0
  17. r11=0000006cc8f7db38 r12=000000000000001b r13=0000000000001000
  18. r14=0000000000000003 r15=00000259c2ec8ee0
  19. iopl=0         nv up ei pl zr na po nc
  20. cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
  21. KERNELBASE!ReadFile:
  22. 00007ffc`28822670 48895c2410      mov     qword ptr [rsp+10h],rbx ss:0000006c`c8f7de88=00000259c2eb0000
  23. 0:000> pt
  24. KERNELBASE!ReadFile+0xad:
  25. 00007ffc`2882271d c3              ret
  26. 0:000> dp 0000006cc8f7df38 L1
  27. 0000006c`c8f7df38  00000000`0000010c
  28. 0:000> db 00000259c2ec8ee0 L?10c
  29. 00000259`c2ec8ee0  7b 0d 0a 20 20 22 72 75-6e 74 69 6d 65 4f 70 74  {..  "runtimeOpt
  30. 00000259`c2ec8ef0  69 6f 6e 73 22 3a 20 7b-0d 0a 20 20 20 20 22 74  ions": {..    "t
  31. 00000259`c2ec8f00  66 6d 22 3a 20 22 6e 65-74 38 2e 30 22 2c 0d 0a  fm": "net8.0",..
  32. 00000259`c2ec8f10  20 20 20 20 22 66 72 61-6d 65 77 6f 72 6b 22 3a      "framework":
  33. 00000259`c2ec8f20  20 7b 0d 0a 20 20 20 20-20 20 22 6e 61 6d 65 22   {..      "name"
  34. 00000259`c2ec8f30  3a 20 22 4d 69 63 72 6f-73 6f 66 74 2e 4e 45 54  : "Microsoft.NET
  35. 00000259`c2ec8f40  43 6f 72 65 2e 41 70 70-22 2c 0d 0a 20 20 20 20  Core.App",..   
  36. 00000259`c2ec8f50  20 20 22 76 65 72 73 69-6f 6e 22 3a 20 22 38 2e    "version": "8.
  37. 00000259`c2ec8f60  30 2e 30 22 0d 0a 20 20-20 20 7d 2c 0d 0a 20 20  0.0"..    },..  
  38. 00000259`c2ec8f70  20 20 22 63 6f 6e 66 69-67 50 72 6f 70 65 72 74    "configPropert
  39. 00000259`c2ec8f80  69 65 73 22 3a 20 7b 0d-0a 20 20 20 20 20 20 22  ies": {..      "
  40. 00000259`c2ec8f90  53 79 73 74 65 6d 2e 52-75 6e 74 69 6d 65 2e 53  System.Runtime.S
  41. 00000259`c2ec8fa0  65 72 69 61 6c 69 7a 61-74 69 6f 6e 2e 45 6e 61  erialization.Ena
  42. 00000259`c2ec8fb0  62 6c 65 55 6e 73 61 66-65 42 69 6e 61 72 79 46  bleUnsafeBinaryF
  43. 00000259`c2ec8fc0  6f 72 6d 61 74 74 65 72-53 65 72 69 61 6c 69 7a  ormatterSerializ
  44. 00000259`c2ec8fd0  61 74 69 6f 6e 22 3a 20-66 61 6c 73 65 0d 0a 20  ation": false..
  45. 00000259`c2ec8fe0  20 20 20 7d 0d 0a 20 20-7d 0d 0a 7d                 }..  }..}
  46. 0:000> .writemem D:\testdump\1.txt 00000259c2ec8ee0 L?0x10c
  47. Writing 10c bytes.
复制代码
从上面的输出中果然看到了 runtimeconfig.json 中的内容,末了打开导出的文件,没弊端。

3. 有没有快捷的方式

刚才的操纵确实能够完成,但还是不爽,因为介入了第三方工具,所以能不能完全通过 windbg 显示打开的 文件路径 和对应的 文件内容 呢?当然是可以的,只需关注如下两个方法 KERNELBASE!CreateFileW 和 KERNELBASE!ReadFile 即可,签名如下:
  1. HANDLE CreateFileW(  [in]           LPCWSTR               lpFileName,  [in]           DWORD                 dwDesiredAccess,  [in]           DWORD                 dwShareMode,  [in, optional] LPSECURITY_ATTRIBUTES lpSecurityAttributes,  [in]           DWORD                 dwCreationDisposition,  [in]           DWORD                 dwFlagsAndAttributes,  [in, optional] HANDLE                hTemplateFile);BOOL ReadFile(
  2.   [in]                HANDLE       hFile,
  3.   [out]               LPVOID       lpBuffer,
  4.   [in]                DWORD        nNumberOfBytesToRead,
  5.   [out, optional]     LPDWORD      lpNumberOfBytesRead,
  6.   [in, out, optional] LPOVERLAPPED lpOverlapped
  7. );
复制代码
在 CreateFileW 中我们提取 lpFileName 和返回的 handle 句柄,在 ReadFile 中提取 hFile句柄 和 lpBuffer 文件内容,参考脚本如下:
  1. sxe ld hostfxr;g
  2. bp KERNELBASE!CreateFileW+0x6a " .echo WriteFile----------------------------------; du /c100 @rbx; r @rax; gc"
  3. bp KERNELBASE!ReadFile+0x73 ".echo ReadFile----------------------------------; r @rsi; da poi(@rsp+0x28); gc " ;  
复制代码
稍微说一下 KERNELBASE!CreateFileW+0x6a 是方法的ret处,可以通过 uf KERNELBASE!CreateFileW 计算得到,如下输出所示:
  1. 0:000> uf KERNELBASE!CreateFileW
  2. KERNELBASE!CreateFileW:
  3. 00007ffc`33341410 4883ec58        sub     rsp,58h
  4. 00007ffc`33341414 448b942488000000 mov     r10d,dword ptr [rsp+88h]
  5. ...
  6. 00007ffc`3334147a c3              ret
  7. 0:000> ? 00007ffc`3334147a - 00007ffc`33341410
  8. Evaluate expression: 106 = 00000000`0000006a
复制代码
这里的 KERNELBASE!ReadFile+0x73 是内部函数 ntdll!NtReadFile 返回的RIP处。
末了用 windbg 执行如下:
  1. (3770.3164): Break instruction exception - code 80000003 (first chance)
  2. ntdll!LdrpDoDebuggerBreak+0x30:
  3. 00007ffc`35a407a0 cc              int     3
  4. 0:000> sxe ld hostfxr;g
  5. ModLoad: 00007ffc`34250000 00007ffc`3427f000   C:\windows\System32\IMM32.DLL
  6. ModLoad: 00007ffc`2deb0000 00007ffc`2df0a000   C:\Program Files\dotnet\host\fxr\9.0.2\hostfxr.dll
  7. ntdll!NtMapViewOfSection+0x14:
  8. 00007ffc`35a0d9f4 c3              ret
  9. 0:000> bp KERNELBASE!ReadFile+0x73 ".echo ReadFile----------------------------------; r @rsi; da poi(@rsp+0x28); gc " ;  
  10. 0:000> bp KERNELBASE!CreateFileW+0x6a " .echo WriteFile----------------------------------; du /c100 @rbx; r @rax; gc"
  11. 0:000> g
  12. WriteFile----------------------------------
  13. 0000020d`1c77ace0  "D:\skyfly\20.20250116\src\Example\Example_20_1_1\bin\Debug\net8.0\Example_20_1_1.runtimeconfig.json"
  14. rax=0000000000000128
  15. ReadFile----------------------------------
  16. rsi=0000000000000128
  17. 0000020d`1c778ee0  "{..  "runtimeOptions": {..    "t"
  18. 0000020d`1c778f00  "fm": "net8.0",..    "framework":"
  19. 0000020d`1c778f20  " {..      "name": "Microsoft.NET"
  20. 0000020d`1c778f40  "Core.App",..      "version": "8."
  21. 0000020d`1c778f60  "0.0"..    },..    "configPropert"
  22. 0000020d`1c778f80  "ies": {..      "System.Runtime.S"
  23. 0000020d`1c778fa0  "erialization.EnableUnsafeBinaryF"
  24. 0000020d`1c778fc0  "ormatterSerialization": false.. "
  25. 0000020d`1c778fe0  "   }..  }..}"
  26. ReadFile----------------------------------
  27. ...
复制代码
三:总结

有了windbg之后,很多东西都会豁然开朗,而不再像以前那样人云亦云,高级调试这门救火技术 应该是高级程序员必须的进阶之路。


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 qidao123.com技术社区-IT企服评测·应用市场 (https://dis.qidao123.com/) Powered by Discuz! X3.4