.NET程序启动就报错,如何截获初期化时的问题json
一:背景
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
加载之后,有了这些信息,思路就有了。
- sxe ld hostfxr 获取插入点。
- bp KERNELBASE!ReadFile 观察第二个参数。
0:000> sxe ld hostfxr
0:000> g
ModLoad: 00007ffc`29b60000 00007ffc`29b8f000 C:\windows\System32\IMM32.DLL
ModLoad: 00007ffc`03660000 00007ffc`036ba000 C:\Program Files\dotnet\host\fxr\9.0.2\hostfxr.dll
ntdll!NtMapViewOfSection+0x14:
00007ffc`2b1ad9f4 c3 ret
0:000> bp KERNELBASE!ReadFile
0:000> g
Breakpoint 1 hit
KERNELBASE!ReadFile:
00007ffc`28822670 48895c2410 mov qword ptr [rsp+10h],rbx ss:0000006c`c8f7de88=00000259c2eb0000
0:000> r
rax=0000000000000000 rbx=0000000000000000 rcx=000000000000012c
rdx=00000259c2ec8ee0 rsi=0000000000000003 rdi=0000000000000000
rip=00007ffc28822670 rsp=0000006cc8f7de78 rbp=0000000000001000
r8=0000000000001000 r9=0000006cc8f7df38 r10=00000259c2ec8ee0
r11=0000006cc8f7db38 r12=000000000000001b r13=0000000000001000
r14=0000000000000003 r15=00000259c2ec8ee0
iopl=0 nv up ei pl zr na po nc
cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
KERNELBASE!ReadFile:
00007ffc`28822670 48895c2410 mov qword ptr [rsp+10h],rbx ss:0000006c`c8f7de88=00000259c2eb0000
0:000> pt
KERNELBASE!ReadFile+0xad:
00007ffc`2882271d c3 ret
0:000> dp 0000006cc8f7df38 L1
0000006c`c8f7df38 00000000`0000010c
0:000> db 00000259c2ec8ee0 L?10c
00000259`c2ec8ee0 7b 0d 0a 20 20 22 72 75-6e 74 69 6d 65 4f 70 74 {.. "runtimeOpt
00000259`c2ec8ef0 69 6f 6e 73 22 3a 20 7b-0d 0a 20 20 20 20 22 74 ions": {.. "t
00000259`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.NET
00000259`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 "configPropert
00000259`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.S
00000259`c2ec8fa0 65 72 69 61 6c 69 7a 61-74 69 6f 6e 2e 45 6e 61 erialization.Ena
00000259`c2ec8fb0 62 6c 65 55 6e 73 61 66-65 42 69 6e 61 72 79 46 bleUnsafeBinaryF
00000259`c2ec8fc0 6f 72 6d 61 74 74 65 72-53 65 72 69 61 6c 69 7a ormatterSerializ
00000259`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?0x10c
Writing 10c bytes.
从上面的输出中果然看到了 runtimeconfig.json
中的内容,最后打开导出的文件,没毛病。
3. 有没有快捷的方式
刚才的操作确实能够完成,但还是不爽,因为介入了第三方工具,所以能不能完全通过 windbg 显示打开的 文件路径
和对应的 文件内容
呢?当然是可以的,只需关注如下两个方法 KERNELBASE!CreateFileW
和 KERNELBASE!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;g
bp 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!CreateFileW
KERNELBASE!CreateFileW:
00007ffc`33341410 4883ec58 sub rsp,58h
00007ffc`33341414 448b942488000000 mov r10d,dword ptr [rsp+88h]
...
00007ffc`3334147a c3 ret
0:000> ? 00007ffc`3334147a - 00007ffc`33341410
Evaluate 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 3
0:000> sxe ld hostfxr;g
ModLoad: 00007ffc`34250000 00007ffc`3427f000 C:\windows\System32\IMM32.DLL
ModLoad: 00007ffc`2deb0000 00007ffc`2df0a000 C:\Program Files\dotnet\host\fxr\9.0.2\hostfxr.dll
ntdll!NtMapViewOfSection+0x14:
00007ffc`35a0d9f4 c3 ret
0: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> g
WriteFile----------------------------------
0000020d`1c77ace0 "D:\skyfly\20.20250116\src\Example\Example_20_1_1\bin\Debug\net8.0\Example_20_1_1.runtimeconfig.json"
rax=0000000000000128
ReadFile----------------------------------
rsi=0000000000000128
0000020d`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之后,很多东西都会豁然开朗,而不再像以前那样人云亦云,高级调试这门救火技术
应该是高级程序员必须的进阶之路。
.NET程序启动就报错,如何截获初期化时的问题json的更多相关文章
- java程序启动redis报错ERR Client sent AUTH, but no password is set
配置文件中注释掉password一句 重启程序即可
- 启动MFC程序的时候报错:0xC0000005: 读取位置 0x00000000 时发生访问冲突
此程序的结构是 MouseCap.h #pragma once #include <afxwin.h> class MouseCapApp : public CWinApp { publi ...
- Ubuntu下安装了java但启动eclipse报错说没装java
参考资料:http://blog.csdn.net/happyteafriends/article/details/8290950 一.问题 在Ubuntu下安装了java并在~/.bashrc配置了 ...
- window下zookeeper的下载启动和报错等问题
在使用dubbo等需要用到zookeeper,之前window下本地部署,启动一直有问题,后面折腾了下才部署成功,此次记录下来. 将zookeeper下载之后,解压到指定目录即可,无需安装.例如:解压 ...
- C# 解决SharpSvn启动窗口报错 Unable to connect to a repository at URL 'svn://....'
在远程机打开sharpsvn客户端测试,结果报错 Svn启动窗口报错 Unable to connect to a repository at URL 'svn://...' 咋整,我在win10我的 ...
- maven 项目启动tomcat报错 java.lang.ClassNotFoundException: org.springframework.web.context.ContextLoaderListener
maven项目启动tomcat报错: java.lang.ClassNotFoundException: org.springframework.web.context.ContextLoaderLi ...
- 【转】Eclipse下启动tomcat报错:/bin/bootstrap.jar which is referenced by the classpath, does not exist.
转载地址:http://blog.csdn.net/jnqqls/article/details/8946964 1.错误: 在Eclipse下启动tomcat的时候,报错为:Eclipse下启动to ...
- 学习中的错误——ubuntu 14.04 LTS 启动eclipse报错
在ubuntu中启动eclipse报错:(Eclipse:15978): GLib-GIO-CRITICAL **: g_dbus_connection_get_unique_name: assert ...
- tomcat7 启动项目报错 java.lang.NoSuchMethodError: javax.servlet.ServletContext.getSessionCookieConfig()
JDK版本:jdk1.8.0_77 Tomcat 版本:apache-tomcat-7.0.47 异常重现步骤: 1.完成项目部署 2.启动Tomcat 异常头部信息:java.lang.NoSuch ...
- 使用yum安装应用程序时候,报错:[Errno 14] PYCURL ERROR 7 - "Failed to connect to 2001:da8:8000:6023::230: 网络不可达"
使用yum安装应用程序时候,报错:[Errno 14] PYCURL ERROR 7 - "Failed to connect to 2001:da8:8000:6023::230: 网络不 ...
随机推荐
- KUKA库卡机器人维修
KUKA库卡机器人作为生产线上的核心设备,一旦出现KUKA机械手故障,将直接影响整个生产线的运行效率.及时的库卡机器人维修工作不仅能够迅速恢复机器人的工作状态,减少生产停滞时间,还能通过预防性维护降低 ...
- Deepseek学习随笔(13)--- 清华大学发布第5弹:DeepSeek与AI幻觉(附网盘链接)
人工智能技术的飞速发展为我们带来了前所未有的便利,但同时也伴随着一个不容忽视的问题--AI幻觉.清华大学发布的<DeepSeek与AI幻觉>详细探讨了AI幻觉的成因.评测方法以及应对策略, ...
- 展开说说关于C#中ORM框架的用法!
Entity Framework(EF)是微软提供的一个开源的对象关系映射(ORM)框架,用于.NET应用程序.它经历了多个版本的演进,主要分为两大分支:Entity Framework 6 (EF6 ...
- 使用QT开发远程linux服务器过程
1.添加设备为通用linux 2.设置ip用户名 3.创建私钥文件,原来有的qtc那俩个文件删掉. 4.部署公钥,前提是测试链接要出现成功 5.在kits里添加编译环境设置编译器为32位或者64 6. ...
- ITSS 运维2.0 实践
最近公司在评审itss 2.0,参与了一部分工作,对工作过程中的问题梳理如下: 大的背景:评审itss 2.0会有评审 为什么公司会申请各种资质 首次申请资质后,公司可以申请一笔费用(x万元)! 申请 ...
- DBeaver 连接 mysql 报错:Public Key Retrieval is not allowed
前言 DBeaver 连接 mysql 报错:Public Key Retrieval is not allowed 遇到 "Public Key Retrieval is not allo ...
- 如何让tcxGrid左边显示序号
第一步: 设置cxgrid的属性, OptionsView.Indicator = True 第二步: 写OnCustomDrawIndicatorCell方法 procedure TForm1.cx ...
- RMQ 和 LCA 问题
# Part 1 RMQ RMQ,即区间信息维护问题 如最大值,最小值,GCD 等 RMQ 算法实现很多 具体有线段树,树状数组和 ST 表 但综合时间复杂度最好的是 ST 表 查询 O(1),预处理 ...
- 【Linux】3.6 组管理和权限管理
组管理和权限管理 1. Linux组基本介绍 Linux中每个用户属于一个组,不能独立于组以外.所以在Linux中每个文件存在组的概念: 所有者 所在组 其他组 改变用户所在组 2. 文件/目录所有者 ...
- python练习-爬虫
场景: 1.网址hppt://xxx.yyy.zzz.cn2.打开网页后显示 : 3.填上姓名 身份证和验证码,点击查询后,返回查询结果. 4.页面有cookie. 方案一: 程序中嵌入浏览器根据网址 ...