executing-plans
executing-plans
Use when executing an implementation plan in the current session as the implementer yourself — your human partner chose inline execution, or no subagent tool is available
re-dotnet.NET CIL 逆向:dnSpy/ILSpy 反编译、de4dot 去混淆、ConfuserEx。触发词:.NET、dnSpy、ILSpy、de4dot、CIL、ConfuserEx
Independent GitHub snapshot · Automated classification · Original documentation governs
file 输出 "Mono/.Net assembly")后,还原 CIL 为可读 C#;处理 ConfuserEx/SmartAssembly 等 .NET 混淆;定位注册码/网络/解密逻辑file/初勘确认再动手参考 [[re-analyze/platform-tips]]——反编译/去混淆为静态步骤,免沙箱;动态验证按最高原则进沙箱。
apt install dotnet-sdk-8.0(官方 Microsoft 源)dnf install dotnet-sdk-8.0;Arch: pacman -S dotnet-sdkbrew install --cask dotnet-sdkchoco install dotnet-sdk 或官方安装器dotnet --versiondotnet tool install -g ilspycmdicsharpcode/ILSpy release zip(linux-x64 / win-x64 / osx-x64 均有)ilspycmd --help;GUI 能打开程序集dnSpyEx/dnSpy release zip → 解压运行 dnSpy.exeFile > Open 程序集de4dot/de4dot release de4dot.exe;Linux/macOS 需 mono:
apt install mono-devel / dnf install mono-devel;Arch: sudo pacman -S mono;macOS brew install monomono-git(属 AUR,非官方仓库)mono de4dot.exe --help;Windows 直接 de4dot.exe --help--help 正常输出File > Open 单文件 bundle,7.2+ 右键 Extract package entry 导出内嵌程序集dotnet tool install -g sfextract(Droppers/SingleFileExtractor,MIT;注意 dotnet-bundle-extractor/dotnet-bundle-extract 这两个包名在 NuGet 并不存在)sfextract sample.exe -o extracted/(不带 -o 只列出 bundle 内文件)sfextract --help按顺序执行,每步记录证据(路径 + sha256,见 [[re-triage]])。
识别 .NET 程序集:
file sample.exe # 含 "Mono/.Net assembly" 即 .NET
strings sample.exe | grep -i mscoree # mscoree.dll 导入是经典标识
CLI header 确认(PE 数据目录第 15 项 = COM Descriptor):
import pefile
pe = pefile.PE('sample.exe')
dd = pe.OPTIONAL_HEADER.DATA_DIRECTORY[14] # COM Descriptor (CLI header)
print(hex(dd.VirtualAddress), hex(dd.Size)) # 非 0 → 托管程序集
注意混合模式程序集(native + 托管都看得到)与自包含单文件(见坑 4)。
CLI header 结构速查(实测 .NET 10 产物):cb=0x48(72)、MajorRuntimeVersion=2、MinorRuntimeVersion=5、MetaData RVA、EntryPointToken(4 字节 union,ECMA-335 II.25.3.3:未置 COMIMAGE_FLAGS_NATIVE_ENTRYPOINT 时为 metadata token(MethodDef/File),置位时才是 native code RVA;0=无入口,即 DLL)、CorFlags(1=ILONLY、2=32BITREQUIRED、0x8=强名签名(COMIMAGE_FLAGS_STRONGNAMESIGNED)、0x20000=32BITPREFERRED)。
元数据根(MetaData RVA 处):签名 BSJB(0x424A5342) + 版本 1.1 + 保留 + 版本串长 + 版本串("v4.0.30319" 是 Roslyn/MetadataRootBuilder 的典型默认值,不是格式不变量——ECMA-335 允许 vendor 自定义串,以实际字节为准)+ flags(2) + 流数(2) + 流头(offset + size + 名字,4 字节对齐)。
流名即内容形态:#~(压缩表,Roslyn 默认)/#-(未压缩表)、#Strings(元数据字符串池)、#US(用户字符串:代码里的 C# 字面量)、#GUID、#Blob(签名/常量 blob)——字符串加密样本的明文不在 #US 就是运行时拼的;#~ 的表内容即 dnSpy/ILSpy 的类型/方法树。
dnSpy/ILSpy 反编译浏览:
ilspycmd sample.exe(stdout 输出反编译代码)ilspycmd sample.exe -p -o decompiled/File > Open → 左侧树展开程序集 → 双击类/方法看 C# 视图Edit Method(改后 File > Save Module 重新编译)、断点调试字符串/API 定位关键逻辑:
strings sample.exe | grep -iE 'http|https|api|key|secret|register'
strings -e l sample.exe | grep -iE 'http|key|secret' # UTF-16LE:#US 池里 C# 字面量是 UTF-16
grep -rn 'HttpClient\|WebRequest\|Regex\|Convert' decompiled/ # ilspycmd 产物
Edit > Find(Ctrl+Shift+F),输入目标串Analyze → Used By(xref 定位调用点)Main 里 if (key == ...))、网络 URL、解密 keyde4dot 去混淆(ConfuserEx / SmartAssembly 等):
mono de4dot.exe sample.exe # 自动检测混淆器 → 输出 sample-cleaned.exe
mono de4dot.exe -p ce sample.exe # 显式指定 ConfuserEx (ce) / SmartAssembly (sa)
-d 干跑只看检测: mono de4dot.exe -d sample.exe(日志显示 Detected: ConfuserEx)sample-cleaned.exe:字符串应为明文、控制流恢复动态侧(可选;沙箱内执行 [[re-sandbox]]):
pip install frida-tools;frida sample.exe -l hook.js,用 Interceptor hook 底层 native 调用(如 HttpClient 背后的 socket 读写/SSL_read、解密函数所在 native 库)在运行时 dump 明文/URL——stock frida-tools 无法按方法名直接 hook .NET 托管方法;托管层需 CLR 桥(如 MonarchSolutions/frida-clr)或改走下方 dotnet-dump / dnSpy 调试dotnet tool install -g dotnet-dump
dotnet-dump collect -p <pid> # 收集 dump
dotnet-dump analyze <dump> # clrstack / dumpheap -type System.String
Debug > Start 断点调试(直接断方法看变量)smethod_0("密文") 调用、if/while 变 switch 分发;原因——ConfuserEx 默认同时启用字符串加密与控制流混淆;对策——de4dot -p ce 自动还原;残留时在解密方法返回处动态读明文(沙箱内),或还原解密循环用脚本批量解Unknown obfuscator 或产物仍混淆;原因——de4dot 维护停滞,混淆器新版特征未收录;对策——换社区 fork(de4dot-cex / de4dot-modified),或手动:定位字符串解密函数 → 分析算法 → python3 复刻 + 动态验证ilspycmd),坚持 dnSpy 可 Wine 跑file 显示普通 PE、工具打开无托管结构/反编译为空;原因——self-contained single-file 把 host + 运行库 + IL 捆成一个 native 可执行;对策——先解包:sfextract sample.exe -o extracted/(或 ILSpy 直接打开/导出),对解出的程序集再反编译dotnet dnSpy.Console.dll)输出 solution + 每程序集一个 .csproj + 源码树,Linux 对位 ilspycmd -p;批量时显式传依赖搜索路径(指向目标所在目录),否则伴随库引用解析不全;成功与否以「产物计数非零 + 退出码 0」双通道确认file 报 Unknown processor 0xfd1d(PE machine 0xfd1d 即 R2R 标记),反编译正常但动态侧行为与 IL 不一致时怀疑有预编译体;原因——R2R 发布在元数据之外预编译了 native 方法体(快速启动),IL 与 CLI header 仍在(实测 .NET 10 R2R 产物 DD[14] 与元数据齐全),实际执行的是 native 体;对策——检测:file 的 0xfd1d + 二进制内 RTR\0(READYTORUN 头魔数,实测在 .text 区);分析:IL 侧逻辑一致照常反编译,性能/环境相关差异看 native 方法体(转 [[re-binary-core]])file 不显示 Mono/.Net assembly、无 CLI header,但来源声明是 .NET 程序;原因——NativeAOT 把 C# 直接编译为原生可执行(无 CLR、无元数据、无 CIL);对策——按 native 分析([[re-format-pe]]/[[re-format-elf]] + [[re-ghidra]]/[[re-imports]]);特征:静态/动态链接原生二进制 + 无 mscoree 导入,业务字符串仍可 strings -e l 提取(.NET 字符串 UTF-16)strings sample.exe | grep 'http' 无结果但反编译里有明文 URL;原因——C# 字面量在 #US 池以 UTF-16LE 存储;对策——strings -e l(或 -el)再搜;动态侧 [[re-frida]] hook 字符串构造点也可取明文
(来源:LazyReverse(a0yami),MIT)