概述:360 DNRSP(.NET RASP 引擎)一次 w3wp 崩溃的完整取证,仅凭 minidump 的 UTF-16 字符串定位到 .NET 反射版本不匹配(
Assembly.get_Modules()在 .NET Framework 4.0 缺失)导致MissingMethodException的根因。
[toc]
0x00 TL;DR
- 崩溃进程:
w3wp.exe(IIS worker),承载DesktopMsgServer应用。 - 崩溃引擎:360 DNRSP(
DnrspEngine.dll/DnrspCore.dll/DnrspSDK.dll),.NET RASP 运行时防护引擎。 - 异常类型:
MissingMethodException(伴生一条AccessException)。 - 异常消息:
Method not found: 'System.Collections.Generic.IEnumerable`1<System.Reflection.Module>
System.Reflection.Assembly.get_Modules()'.
- 根因:DNRSP 引擎在
HookLoader初始化/遍历程序集模块时,通过反射硬编码调用Assembly.get_Modules()。该属性是 .NET Framework 4.5+ 才引入的新 API(返回IEnumerable<Module>),而客户环境是未升级的 .NET Framework 4.0.30319.1(RTMRel),反射找不到该属性 → 抛MissingMethodException→ 引擎初始化失败 → 宿主进程崩溃。
0x01 取证环境与 dump 概况
- dump 文件:
360dnrsp_crash.dmp,约 2.88 MB(minidump,非全量 dump)。 - 生成器:
D:\Program Files (x86)\360\360Safe\DumpUper.exe(360 自家 dump 工具)。 - dump 版本:
0x61b1a793,12 个 stream。
取证过程的坑(绕过损坏的结构)
本 dump 的 ExceptionStream 解析出 NumberOfParameters = 533313 这类不合理值,且 RVA 0x5dbff + size 149172 越界(超出文件大小),说明 ExceptionStream 损坏/截断;ModuleList 按 48 字节 stride 解析出乱码,暗示 dump-writer 产出的结构是 32/64 位混用。
结论:放弃 struct 逐流解析,改用字符串级取证——strings -a -e l(UTF-16LE)全量扫描,直接 grep 托管异常消息文本,一击命中根因。
strings -a -e l 360dnrsp_crash.dmp | grep -iE "method not found|get_Modules|getModules"命中(原文,出现两次):
Method not found
Method not found: 'System.Collections.Generic.IEnumerable`1<System.Reflection.Module> System.Reflection.Assembly.get_Modules()'.
ot found: 'System.Collections.Generic.IEnumerable`1<System.Reflection.Module> System.Reflection.Assembly.get_Modules()'.
0x02 根因机制
Assembly.get_Modules vs Assembly.GetModules
| API | 引入版本 | 返回类型 | 说明 |
|---|---|---|---|
Assembly.GetModules() | .NET Framework 1.0 | Module[] | 老 API,一路都有 |
Assembly.get_Modules() | .NET Framework 4.5 | IEnumerable<Module> | 新属性(C# 中写作 assembly.Modules),LINQ 友好 |
- 编译到 C# 源码里,
assembly.Modules这一语法糖在 IL 层就是get_Modules()属性 getter。 - 若引擎源码里写了
assembly.Modules,且编译目标框架为 4.5+,那么在 4.0 环境的 mscorlib 上就没有这个 getter → 反射调用抛MissingMethodException。
DNRSP 引擎侧的触发点
dump 字符串体现引擎 hook 初始化路径:
DnrspEngine.HookLoader
DnrspEngine.HookLoaderDomain
EngineMarshalLoader, dnrspengine
推断:HookLoader 在装载引擎、遍历目标程序集模块(做 IL 改写/反序列化 hook 插桩)时,调用了 assembly.get_Modules(),在 4.0 环境直接炸掉。
反序列化 hook 目标(引擎角色佐证)
AjaxPro.JavaScriptDeserializer@DeserializeCustomObject
System.Web.UI.ObjectStateFormatter@DeserializeValue
符合 DNRSP 作为反序列化 RASP 的定位——它在这些反序列化入口下 hook 点做防护,而遍历模块正是插桩的前置动作。
0x03 框架版本铁证
dump 字符串中存在:
4.0.30319.1 (RTMRel.030319-0100)
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\clr.dll
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\mscorlib.dll
4.0.30319.1 + RTMRel.030319-0100 说明客户环境是纯 .NET Framework 4.0 初始发行版,未打 4.5+ 升级(升级后 mscorlib 会带 get_Modules getter)。
0x04 与既有 DNRSP 崩溃工作链的关系(重要区分)
| 场景 | 根因 | 异常类型 |
|---|---|---|
| 广德佳联 K3Cloud(历史案例) | SuppressUnmanagedCodeSecurity 缺失 → DemandPermission CAS 栈遍历 → CLR ExecutionEngineException | EEE(进程级致命) |
| 本次 DesktopMsgServer | 反射硬编码 get_Modules() → 4.0 无该 API | MissingMethodException |
两者不是同一类缺陷:
- 前者是 .NET CAS 运行时安全机制(CAS/APTCA/SuppressUnmanagedCodeSecurity)在异常状态下的崩溃,属于”运行时安全基础设施”维度。
- 本次是引擎假设过高框架版本的纯版本兼容问题,属于”改前查现状”教训的延伸——查的是框架版本,而非编码惯例。
0x05 修复建议
- 环境侧:客户服务器升级 .NET Framework 到 4.8(
get_Modules存在,且顺带获得安全补丁)。 - 引擎侧(更稳):
HookLoader遍历模块时做降级兼容——先反射探测get_Modules是否存在,不存在则回退调用老 APIAssembly.GetModules()(4.0 一直可用),或整体 try/catch 包裹,避免单点反射失败拖垮宿主进程(符合”引擎异常永不升级宿主崩溃”的容错红线)。
0x06 小结
一次 w3wp 崩溃的精确定位,靠的不是 WinDbg(环境里没有 cdb/windbg/dotnet-dump),而是 minidump 里的托管异常消息字符串。当 dump 的异常流损坏、调试器缺失时,strings -a -e l 扫 UTF-16 字符串是 .NET dump 取证最实用的兜底手段——.NET 的异常消息、类型名、框架版本号都以 UTF-16 明文躺在 dump 里。
0x07 WinDbg 复验步骤
拿到 WinDbg 后,可按以下顺序独立复证上述结论。
7.1 定位崩溃线程与异常记录
!analyze -v:查看EXCEPTION_CODE(应为0xe0434352,即 CLR 未处理异常)与FAULTING_THREAD号。~* e .exr -1:遍历所有线程打印异常记录,寻找带0xe0434352的那条,即崩溃线程。
本 dump 的 ExceptionStream 损坏,导致
!pe直接报Not a valid exception object,但只要异常消息字符串仍在内存中,~* e .exr -1即可在异常记录里直接看到Method not found ... Assembly.get_Modules()的原文,是最快、最不依赖 SOS 的复验手段。
7.2 切换线程 + 加载 SOS
~<崩溃线程号> s
.load C:\Windows\Microsoft.NET\Framework64\v4.0.30319\sos.dll
注意:不要 .load clr.dll——clr.dll 是运行库 DLL,不是 WinDbg 扩展,.load 只用于扩展。
7.3 读托管堆栈(定位 HookLoader)
!threads:列出托管线程,找Exception列非空的线程。!clrstack/!dumpstack:看托管/混合调用栈,期望出现DnrspEngine.HookLoader及相关反射调用帧。
7.4 内存字符串独立搜证
s -u 0 L?ffffffff "Method not found"
s -u 0 L?ffffffff "get_Modules"
命中后用 du <地址> 打印完整 UTF-16 消息,与 dump 字符串取证相互印证。
7.5 验证框架版本
lmvm mscorlib
输出应落在 Framework64\v4.0.30319、版本 4.0.30319.1,佐证环境未升级 4.5+(无 get_Modules)。
结论验证清单
| 分析结论 | WinDbg 验证手段 | 期望输出 |
|---|---|---|
| .NET 未处理异常 | !analyze -v | 0xe0434352 |
异常消息含 get_Modules() | ~* e .exr -1 或 s -u | Method not found: ...get_Modules() |
| 异常类型 | !pe(需有效异常对象) | System.MissingMethodException |
| 触发点 HookLoader | !clrstack | DnrspEngine.HookLoader 帧 |
| 环境 .NET 4.0 | lmvm mscorlib | 4.0.30319.1 |