首页/文章列表/文章详情

.NET程序启动就报错,如何截获初期化时的问题json

编程知识1072025-05-14评论

一:背景

1. 讲故事

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

从给出的错误信息看大概是因为json格式无效导致的,在早期的训练营里曾经也有一例这样的报错,最后定位下来是公司的电脑安全软件导致的,一旦有非托管调试器,安全软件就会加密 runtimeconfig.json,最后导致程序无法正常被调试执行。

此时相信有很多人想搞清楚,windbg 在那个时刻到底读到了什么脏东西?到底经历了怎样的惊魂时刻?

二:WinDbg 到底遇到了什么

1. 一个小案例

为了方便演示,写一个简单的hello world程序,代码如下:

 internal class Program { static void Main(string[] args) { Console.WriteLine("hello world!"); Debugger.Break(); Console.ReadLine(); } }

接下来将程序编译之后观察runtimeconfig.json内容,截图如下。

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

熟悉 win32 api 的朋友都知道,C# 的 ReadFile 底层会调用 win32 的 ReadFile 方法,方法签名如下:

BOOL ReadFile( [in] HANDLE hFile, [out] LPVOID lpBuffer, [in] DWORD nNumberOfBytesToRead, [out, optional] LPDWORD lpNumberOfBytesRead, [in, out, optional] LPOVERLAPPED lpOverlapped);

方法的 lpBuffer 参数存放的就是读取到的文件内容。

这里还有一个问题就是 ReadFile 是在 dotnet 进程被初始化的时候读取到内存的,在这个初始化过程中会有一系列的加载,那到底在哪个时刻埋点呢?这时候可以借助 procmon 工具,观察 runtimeconfig.json的加载时机,截图如下:

上面的卦中runtimeconfig.json的读取是发生在hostfxr.dll加载之后,有了这些信息,思路就有了。

  1. sxe ld hostfxr 获取插入点。
  2. bp KERNELBASE!ReadFile 观察第二个参数。
0:000> sxe ld hostfxr0:000> gModLoad: 00007ffc`29b60000 00007ffc`29b8f000 C:\windows\System32\IMM32.DLLModLoad: 00007ffc`03660000 00007ffc`036ba000 C:\Program Files\dotnet\host\fxr\9.0.2\hostfxr.dllntdll!NtMapViewOfSection+0x14:00007ffc`2b1ad9f4 c3 ret0:000> bp KERNELBASE!ReadFile 0:000> gBreakpoint 1 hitKERNELBASE!ReadFile:00007ffc`28822670 48895c2410 mov qword ptr [rsp+10h],rbx ss:0000006c`c8f7de88=00000259c2eb00000:000> rrax=0000000000000000 rbx=0000000000000000 rcx=000000000000012crdx=00000259c2ec8ee0 rsi=0000000000000003 rdi=0000000000000000rip=00007ffc28822670 rsp=0000006cc8f7de78 rbp=0000000000001000 r8=0000000000001000 r9=0000006cc8f7df38 r10=00000259c2ec8ee0r11=0000006cc8f7db38 r12=000000000000001b r13=0000000000001000r14=0000000000000003 r15=00000259c2ec8ee0iopl=0 nv up ei pl zr na po nccs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246KERNELBASE!ReadFile:00007ffc`28822670 48895c2410 mov qword ptr [rsp+10h],rbx ss:0000006c`c8f7de88=00000259c2eb00000:000> ptKERNELBASE!ReadFile+0xad:00007ffc`2882271d c3 ret0:000> dp 0000006cc8f7df38 L10000006c`c8f7df38 00000000`0000010c0:000> db 00000259c2ec8ee0 L?10c00000259`c2ec8ee0 7b 0d 0a 20 20 22 72 75-6e 74 69 6d 65 4f 70 74 {.."runtimeOpt00000259`c2ec8ef0 69 6f 6e 73 22 3a 20 7b-0d 0a 20 20 20 20 22 74 ions": {.."t00000259`c2ec8f00 66 6d 22 3a 20 22 6e 65-74 38 2e 30 22 2c 0d 0a fm":"net8.0",..00000259`c2ec8f10 20 20 20 20 22 66 72 61-6d 65 77 6f 72 6b 22 3a"framework":00000259`c2ec8f20 20 7b 0d 0a 20 20 20 20-20 20 22 6e 61 6d 65 22 {.."name"00000259`c2ec8f30 3a 20 22 4d 69 63 72 6f-73 6f 66 74 2e 4e 45 54 :"Microsoft.NET00000259`c2ec8f40 43 6f 72 65 2e 41 70 70-22 2c 0d 0a 20 20 20 20 Core.App",.. 00000259`c2ec8f50 20 20 22 76 65 72 73 69-6f 6e 22 3a 20 22 38 2e"version":"8.00000259`c2ec8f60 30 2e 30 22 0d 0a 20 20-20 20 7d 2c 0d 0a 20 20 0.0".. },.. 00000259`c2ec8f70 20 20 22 63 6f 6e 66 69-67 50 72 6f 70 65 72 74"configPropert00000259`c2ec8f80 69 65 73 22 3a 20 7b 0d-0a 20 20 20 20 20 20 22 ies": {.."00000259`c2ec8f90 53 79 73 74 65 6d 2e 52-75 6e 74 69 6d 65 2e 53 System.Runtime.S00000259`c2ec8fa0 65 72 69 61 6c 69 7a 61-74 69 6f 6e 2e 45 6e 61 erialization.Ena00000259`c2ec8fb0 62 6c 65 55 6e 73 61 66-65 42 69 6e 61 72 79 46 bleUnsafeBinaryF00000259`c2ec8fc0 6f 72 6d 61 74 74 65 72-53 65 72 69 61 6c 69 7a ormatterSerializ00000259`c2ec8fd0 61 74 69 6f 6e 22 3a 20-66 61 6c 73 65 0d 0a 20 ation": false.. 00000259`c2ec8fe0 20 20 20 7d 0d 0a 20 20-7d 0d 0a 7d }.. }..}0:000> .writemem D:\testdump\1.txt 00000259c2ec8ee0 L?0x10cWriting 10c bytes.

从上面的输出中果然看到了runtimeconfig.json中的内容,最后打开导出的文件,没毛病。

3. 有没有快捷的方式

刚才的操作确实能够完成,但还是不爽,因为介入了第三方工具,所以能不能完全通过 windbg 显示打开的 文件路径和对应的文件内容呢?当然是可以的,只需关注如下两个方法KERNELBASE!CreateFileWKERNELBASE!ReadFile即可,签名如下:

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( [in] HANDLE hFile, [out] LPVOID lpBuffer, [in] DWORD nNumberOfBytesToRead, [out, optional] LPDWORD lpNumberOfBytesRead, [in, out, optional] LPOVERLAPPED lpOverlapped);

在 CreateFileW 中我们提取 lpFileName和返回的handle 句柄,在 ReadFile 中提取 hFile句柄 和 lpBuffer文件内容,参考脚本如下:

sxe ld hostfxr;gbp KERNELBASE!CreateFileW+0x6a" .echo WriteFile----------------------------------; du /c100 @rbx; r @rax; gc"bp KERNELBASE!ReadFile+0x73".echo ReadFile----------------------------------; r @rsi; da poi(@rsp+0x28); gc" ; 

稍微说一下KERNELBASE!CreateFileW+0x6a是方法的ret处,可以通过uf KERNELBASE!CreateFileW计算得到,如下输出所示:

0:000> uf KERNELBASE!CreateFileWKERNELBASE!CreateFileW:00007ffc`33341410 4883ec58 sub rsp,58h00007ffc`33341414 448b942488000000 mov r10d,dword ptr [rsp+88h]...00007ffc`3334147a c3 ret0:000> ? 00007ffc`3334147a - 00007ffc`33341410Evaluate expression: 106 = 00000000`0000006a

这里的KERNELBASE!ReadFile+0x73是内部函数ntdll!NtReadFile返回的RIP处。

最后用 windbg 执行如下:

(3770.3164): Break instruction exception - code 80000003 (first chance)ntdll!LdrpDoDebuggerBreak+0x30:00007ffc`35a407a0 cc int 30:000> sxe ld hostfxr;gModLoad: 00007ffc`34250000 00007ffc`3427f000 C:\windows\System32\IMM32.DLLModLoad: 00007ffc`2deb0000 00007ffc`2df0a000 C:\Program Files\dotnet\host\fxr\9.0.2\hostfxr.dllntdll!NtMapViewOfSection+0x14:00007ffc`35a0d9f4 c3 ret0:000> bp KERNELBASE!ReadFile+0x73".echo ReadFile----------------------------------; r @rsi; da poi(@rsp+0x28); gc" ; 0:000> bp KERNELBASE!CreateFileW+0x6a" .echo WriteFile----------------------------------; du /c100 @rbx; r @rax; gc"0:000> gWriteFile----------------------------------0000020d`1c77ace0"D:\skyfly\20.20250116\src\Example\Example_20_1_1\bin\Debug\net8.0\Example_20_1_1.runtimeconfig.json"rax=0000000000000128ReadFile----------------------------------rsi=00000000000001280000020d`1c778ee0"{.."runtimeOptions": {.."t"0000020d`1c778f00"fm":"net8.0",.."framework":"0000020d`1c778f20" {.."name":"Microsoft.NET"0000020d`1c778f40"Core.App",.."version":"8."0000020d`1c778f60"0.0".. },.."configPropert"0000020d`1c778f80"ies": {.."System.Runtime.S"0000020d`1c778fa0"erialization.EnableUnsafeBinaryF"0000020d`1c778fc0"ormatterSerialization": false.."0000020d`1c778fe0" }.. }..}"ReadFile----------------------------------...

三:总结

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

一线码农

这个人很懒...

用户评论 (0)

发表评论

captcha