re-dotnet について
re-dotnetは、.NETアセンブリのリバースエンジニアリングを支援するスキルです。ILSpy、dnSpy、de4dotによる逆コンパイルや難読化解除に加え、単一ファイルの展開や実行時の検証も扱います。
主な機能
- 01マネージド .NET アセンブリの識別
- 02ILSpy と dnSpy による逆コンパイル
- 03de4dot による難読化解除
- 04文字列と主要 API の検索
- 05単一ファイルバンドルの展開
- 06デバッグとメモリダンプによる検証
活用シーン
- 01.NET マルウェアの静的ロジックの復元
- 02ConfuserEx や SmartAssembly の難読化分析
- 03登録・通信・復号ロジックの特定
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ dslsdzc/ rev-skills --skill re-dotnetインストール後、Claude Code で/re-dotnetを入力して読み込まれたか確認します。
詳細
.NET CIL のリバース(dnSpy / ILSpy / de4dot)
使用する場合 / 使用しない場合
- 使用する場合:.NET アセンブリであることを確認した後(PE CLI ヘッダー /
fileの出力が「Mono/.Net assembly」)、CIL を読みやすい C# に復元します。ConfuserEx/SmartAssembly などの .NET 難読化に対処します。ライセンスキー/ネットワーク/復号ロジックを特定します - 使用する場合:悪意のある .NET サンプル([[re-malware]] → [[re-managed]] の経路)の静的復元
- 使用しない場合:アンマネージド PE(ネイティブプログラムは直接 [[re-binary-core]]。混合モードプログラムのネイティブ部分も同様に変換します)
- 使用しない場合:Java([[re-java]])、スクリプト([[re-script-deob]])
- 注意:動的な手順は、デフォルトでサンドボックスを使用します([[re-analyze/platform-tips]] の最優先原則)。まず
file/初期調査で確認してから作業してください
ツールの準備
[[re-analyze/platform-tips]] を参照してください。逆コンパイル/難読化解除は静的な手順のため、サンドボックスは不要です。動的検証は最優先原則に従ってサンドボックス内で行ってください。
dotnet SDK(.NET ツールを実行するための前提条件)
- Debian/Ubuntu:
apt install dotnet-sdk-8.0(公式 Microsoft ソース) - Fedora:
dnf install dotnet-sdk-8.0;Arch:pacman -S dotnet-sdk - macOS:
brew install --cask dotnet-sdk - Windows:
choco install dotnet-sdkまたは公式インストーラー - 確認:
dotnet --version
ILSpy(クロスプラットフォーム、GUI + CLI)
- CLI(dotnet tool):
dotnet tool install -g ilspycmd - GUI: GitHub
icsharpcode/ILSpyのリリース zip(linux-x64 / win-x64 / osx-x64 すべてあり) - 確認:
ilspycmd --help;GUI でアセンブリを開けること
dnSpy(Windows 専用、GitHub リリース)
- GitHub
dnSpyEx/dnSpyのリリース zip → 展開してdnSpy.exeを実行 - Windows 専用(WinForms + .NET Framework);Linux/WSL では ILSpy を使用してください(または Wine で dnSpy を実行)
- 確認: GUI を起動し、
File > Openでアセンブリを開けること - 利点: 逆コンパイル + 編集 + デバッグを一体化
de4dot(GitHub リリース、難読化解除)
- GitHub
de4dot/de4dotリリースのde4dot.exe。Linux/macOS では mono が必要です:apt install mono-devel/dnf install mono-devel;Arch:sudo pacman -S mono;macOSbrew install mono- upstream Git snapshot が明確に必要な場合に限り、AUR の
mono-gitを使用してください(AUR のパッケージであり、公式リポジトリではありません) - 実行:
mono de4dot.exe --help;Windows ではde4dot.exe --helpを直接実行
- 確認:
--helpが正常に出力されること
単一ファイル bundle の展開(sfextract / ILSpy 内蔵、任意だがよく使用)
- 追加ツール不要: ILSpy 7.1+ GUI で単一ファイル bundle を直接
File > Openできます。7.2+ では右クリックしてExtract package entryを選択し、内蔵アセンブリをエクスポートできます - CLI:
dotnet tool install -g sfextract(Droppers/SingleFileExtractor、MIT。dotnet-bundle-extractor/dotnet-bundle-extractの 2 つのパッケージ名は 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 单文件:
ilspycmd sample.exe(stdout 输出反编译代码) - ilspycmd 生成项目:
ilspycmd sample.exe -p -o decompiled/ - ILSpy GUI / dnSpy:
File > Open→ 左侧树展开程序集 → 双击类/方法看 C# 视图 - dnSpy 额外能力: 右键方法
Edit Method(改后File > Save Module重新编译)、断点调试
- ilspycmd 单文件:
-
字符串/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 产物- GUI 内搜索: ILSpy/dnSpy
Edit > Find(Ctrl+Shift+F),输入目标串 - 找引用: dnSpy 右键方法/字段 →
Analyze→Used By(xref 定位调用点) - 目标形态:注册码校验(
Main里if (key == ...))、网络 URL、解密 key
- GUI 内搜索: ILSpy/dnSpy
-
de4dot 去混淆(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) - 去混淆后重新在 dnSpy/ILSpy 打开
sample-cleaned.exe:字符串应为明文、控制流恢复 - 残留混淆(字符串仍密文)→ 坑 2 手动方案
- 先
-
动态侧(可选;沙箱内执行 [[re-sandbox]]):
- Frida(native 层):
pip install frida-tools;frida sample.exe -l hook.js,用Interceptorhook 底层 native 调用(如HttpClient背后的 socket 读写/SSL_read、解密函数所在 native 库)在运行时 dump 明文/URL——stock frida-tools 无法按方法名直接 hook .NET 托管方法;托管层需 CLR 桥(如 MonarchSolutions/frida-clr)或改走下方 dotnet-dump / dnSpy 调试 - dotnet-dump(进程内取运行时数据):
dotnet tool install -g dotnet-dump dotnet-dump collect -p <pid> # 收集 dump dotnet-dump analyze <dump> # clrstack / dumpheap -type System.String - Windows: dnSpy
Debug > Start断点调试(直接断方法看变量)
- Frida(native 层):
跨域联合
- [[re-managed]]:网关工作流步骤②(反编译)④(去混淆)固定调用本技能
- [[re-malware]]:.NET 恶意样本路径(re-malware → re-managed → 本技能静态还原)
- [[re-binary-core]]:底座——初勘([[re-triage]])、混合模式 native 部分
- 动态侧:[[re-frida]]、[[re-sandbox]];深层混淆转 [[re-deobfuscate]];单文件先解包再回本技能
常见坑与陷阱
- ConfuserEx 字符串加密 + 控制流:现象——反编译只见
smethod_0("密文")调用、if/while 变 switch 分发;原因——ConfuserEx 默认同时启用字符串加密与控制流混淆;对策——de4dot -p ce自动还原;残留时在解密方法返回处动态读明文(沙箱内),或还原解密循环用脚本批量解 - de4dot 对旧版本/新混淆器失效:现象——日志
Unknown obfuscator或产物仍混淆;原因——de4dot 维护停滞,混淆器新版特征未收录;对策——换社区 fork(de4dot-cex / de4dot-modified),或手动:定位字符串解密函数 → 分析算法 → python3 复刻 + 动态验证 - dnSpy 仅 Windows:现象——Linux/WSL 双击 dnSpy.exe 报错无法运行;原因——WinForms + .NET Framework 的 GUI 工具;对策——Linux/macOS 用 ILSpy(GUI 或
ilspycmd),坚持 dnSpy 可 Wine 跑 - .NET 自包含发布(单文件):现象——
file显示普通 PE、工具打开无托管结构/反编译为空;原因——self-contained single-file 把 host + 运行库 + IL 捆成一个 native 可执行;对策——先解包:sfextract sample.exe -o extracted/(或 ILSpy 直接打开/导出),对解出的程序集再反编译 - 混合模式程序集:现象——反编译只见少量托管类,主逻辑找不到;原因——C++/CLI 或 wrapper 把 native 代码与托管混合;对策——托管侧按本技能,native 侧转 [[re-binary-core]]([[re-ghidra]] / [[re-imports]])
- 薄壳启动器程序集误判导出失败(dnSpy 无头批量):现象——单个程序集反编译产物源码极少(几行到几十行),主逻辑找不到,误以为导出失败;原因——该程序集只是薄壳启动器,主逻辑在伴随程序集/库中(纯托管,区别于上一条的 native 侧);对策——目录级批量反编译拿整体视图再判断:Windows 侧用 dnSpy 控制台(
dotnet dnSpy.Console.dll)输出 solution + 每程序集一个 .csproj + 源码树,Linux 对位ilspycmd -p;批量时显式传依赖搜索路径(指向目标所在目录),否则伴随库引用解析不全;成功与否以「产物计数非零 + 退出码 0」双通道确认 - ReadyToRun(R2R)程序集:现象——
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]]) - NativeAOT 无托管结构:现象——
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) - .NET 字符串是 UTF-16LE,默认 strings 抓不到:现象——
strings sample.exe | grep 'http'无结果但反编译里有明文 URL;原因——C# 字面量在#US池以 UTF-16LE 存储;对策——strings -e l(或-el)再搜;动态侧 [[re-frida]] hook 字符串构造点也可取明文 (来源:LazyReverse(a0yami),MIT)