-
Notifications
You must be signed in to change notification settings - Fork 0
MS_DecompileAndObfuscation
- 戻る(その他、開発の色々)
逆コンパイル・難読化などの質問が稀にあるため。
難読化ソリューションは、
- 実行時コンパイラや
- スクリプト言語が
出てきて、必要になってきたんじゃないでしょうか?
補足(原文の見立ては本質を突いている): 「実行時コンパイラや
スクリプト言語が出てきて、必要になってきた」——
この推測は正しい。理由を整理する。【なぜ中間コード・スクリプトだと逆コンパイルされやすいのか】 ネイティブ コンパイル(C/C++) ソース ──最適化──▶ 機械語 ・変数名、型名、構造が【完全に失われる】 ・レジスタ割り当て、インライン展開、ループ変形で原型を留めない ・【元に戻す情報がそもそも残っていない】★ 中間コード(.NET / Java) ソース ──▶ IL / バイトコード ──実行時──▶ 機械語 ・【メタデータ(型名・メソッド名・シグネチャ)が必須】 → リフレクション、GC、型安全性のために【消せない】★ ・IL は高水準(スタックマシン、構造化された分岐) → 元のソースに近い形に復元できる「消せない」のが要点である。
.NET は実行時に型情報を必要とする設計(.NET の Reflection、
ガベージ コレクション、DI など)であるため、
メタデータを削ることが原理的にできない。
だから**「消す」のではなく「読みにくくする」=難読化**しかない。
C 言語のプリプロセッサに通した後のコードは ≒ リバースできないもよう。
-
奇跡的に C/C++ の逆コンパイラを手に入れられたとしても
それが吐き出すソースを理解できる可能性は全くない。 -
C++ の逆コンパイラは C よりもはるかに困難 (事実上不可能)
※ 機械語自体を読む人がいるかも知れないので、保護したいレベルによるかもしれませんが。
補足(この判断は現在は少し慎重に): 原文の
「理解できる可能性は全くない」「事実上不可能」は
当時としては妥当だが、現在はやや状況が変わった。
ツール 内容 Ghidra NSA が 2019 年に公開。無償。C 風の擬似コードを出力 ★ IDA Pro + Hex-Rays 商用の定番。デコンパイラの品質が高い Binary Ninja 商用。中間表現が扱いやすい radare2 / rizin OSS 【現在の実際】 ・変数名・型名は【依然として復元されない】 → 出力は sub_401000(a1, a2) のような形 ・しかし【制御フローとアルゴリズムは読める】 ・シンボル(デバッグ情報 / PDB)が残っていれば、かなり読める ★ ・機械学習で関数名を推定する研究・ツールも出ている 【結論】 「不可能」ではなく【非常に手間がかかる】が正しい → 費用対効果が攻撃者側で見合うかどうかの問題原文の※(「保護したいレベルによる」)が、
現在も最も的確な締め方である。
- スクリプト・ファイルがそのまま動く。
- インタプリタでソースコードを、機械語プログラムに解釈・変換しながら処理・実行する。
以下の理由で、逆コンパイルが容易であるケースが多いらしい。
-
中間コードはネイティブコードに比べて命令の種類や複雑さが少ない。
-
逆コンパイルされ易い言語
-
ガーベージ・コレクションやリフレクション機能のある言語
-
その他、
- 共用体が無い
- ポインタ演算・キャストの制限
- class に定義情報が含まれる。
- goto 文を禁止している
- , etc.
-
-
言語毎のツール
-
Java:
JAD(Jad Decompiler) -
.NET:
-
IL Disassembler(ildasm.exe)
Visual Studio に付属 -
.NET Reflector
昔は無償で一択だったが、2011 年の 3 月に有償化された。 -
dotPeek
ReSharper に付属 -
ILSpy
MIT の OSS でスタンドアロンで利用可能。 -
Telerik JustDecompile
Telerik 社製 -
.NET CodeReflect
-
-
補足(挙げられた理由は的確): 「逆コンパイルされ易い言語」の
特徴の一覧は、なぜ復元できるのかを正しく説明している。【共用体(union)が無い】 C の union は「同じ領域を別の型として解釈する」 → 逆コンパイラはどの型か判断できない .NET には(原則)無いので、型が一意に決まる ★ 【ポインタ演算・キャストの制限】 C では p + 3 が何を指すか、静的に分からない .NET は型安全なので、参照が何を指すか追える 【class に定義情報が含まれる】 メタデータそのもの。前述の通り消せない ★ 【goto を禁止している】 構造化された制御フロー(if / while / for)に戻しやすい → 任意の goto があると、元の構造を推定できない移行メモ(各ツールの現況):
ツール 現況 ildasm.exe 現役(IL の逆アセンブル。C# には戻らない) ILSpy 現役・活発。MIT。VS 拡張版もある ★ dotPeek 現役・無償(JetBrains。ReSharper 不要になった) .NET Reflector 有償(Redgate)。現役 Telerik JustDecompile 提供終了(2023 年) .NET CodeReflect 提供終了 dnSpy 開発停止(2020)。dnSpyEx がフォークとして継続 JAD(Java) 開発停止。現在は CFR、Procyon、Fernflower 【現在の .NET での定番】 ・ILSpy(無償・OSS)★ ・dotPeek(無償) ・dnSpyEx(デバッガ付き。動的解析ができる)なお、逆コンパイルは「攻撃」だけの手段ではない。
【正当な用途】 ・ソースを失った自社製 DLL の調査 ・NuGet パッケージの挙動確認([NuGetパッケージのデバッグ](MS_NuGetPackageDebugging)) ・.NET ランタイム自体の実装確認 → 現在は【source.dot.net / GitHub】でソースが公開されている ★ ・障害解析(配置されたバイナリが期待の版か)ライセンス上の注意:
・第三者の製品を逆コンパイルする行為は、 【使用許諾契約(EULA)で禁止されている】ことが多い ・著作権法上も、リバース エンジニアリングの可否は 目的・国によって扱いが異なる → 業務で行う場合は【契約と法務を確認する】★
-
難読化は単なる時間稼ぎに過ぎず、プログラムの逆コンパイルを不可能とするものではない。
-
商用の難読化ソリューションはソースコードの難読化や Java や .NET などのプラットフォーム中立な
バイトコードの変換が大部分を占めるが、中にはコンパイルされたバイナリに直接作用するものも存在する。
そもそも、逆コンパイルができないので難読化も不要。
そもそも、コンパイルがないので難読化もできない。
移行メモ(スクリプト言語も難読化はできる): 「コンパイルがないので
難読化もできない」は正確ではない。
難読化はコンパイルの有無とは独立である。【JavaScript の難読化は実際に広く行われている】 ・変数名の短縮・無意味化(Minify + Mangle) → terser / esbuild が標準で行う → [ASP.NET の BundleConfig](MS_ASPNETBundleConfig) の Minification ・文字列の分割・エンコード ・制御フローの平坦化(javascript-obfuscator 等) 【ただし】 ブラウザで動く以上、【最終的には必ず読める】 → デバッガで実行時の値を見れば分かる → やはり「時間稼ぎ」である原文の主張の核——
「難読化は時間稼ぎに過ぎない」——は、
スクリプト言語でも中間コードでも等しく正しい。
-
逆コンパイルが容易なため、難読化が必要になるケースもある。
-
言語毎のツール
-
Java:
- ProGuard
- JODE
- JavaGuard
- RetroGuard
- jarg
- yGuard
-
.NET:
- ConfuserEx(無料)
- NanDoKu(無料)
- Phoenix Protector(無料)
- Dotfuscator(有料 – 簡易版有り)
- Eazfuscator.NET(有料 – 試用版有り)
- babelfor.net(Babel)(有料 – 試用版有り)
-
補足(難読化が実際に何をするか): 「読みにくくする」の中身を
具体化しておくと、限界と副作用が理解しやすい。
手法 内容 副作用 識別子の改名 CalcTax→a、bリフレクションで名前解決している箇所が壊れる ★ 制御フローの平坦化 if/forをswitch+ 状態変数に変形性能低下、スタック トレースが無意味に 文字列の暗号化 文字列リテラルを実行時に復号 起動が遅くなる。復号キーはバイナリ内にある 不要コードの注入 意味のない分岐を混ぜる サイズ増、性能低下 メタデータの除去 未使用の型・メンバを削る 動的読み込みが壊れる 対デバッガ デバッガ検知で終了する 正当な障害解析も困難になる 【難読化で壊れる典型的なもの】★ 実務で必ず当たる ・リフレクション(型名・メソッド名を文字列で指定している箇所) ・シリアライズ/デシリアライズ(JSON、XML のプロパティ名) ・DI コンテナの型名による解決 ・Entity Framework のマッピング ・WPF / XAML のバインディング(プロパティ名を文字列で指定)★ ・単体テスト(internal を見るテスト) → 【除外設定(exclude)を丁寧に書く】必要がある → 難読化後に【必ず結合テストを通す】スタック トレースの問題:
難読化すると、本番で出る例外のスタック トレースが at a.b(c d) のようになり【解析できない】 【対策】 マッピング ファイルを保存し、復元ツールで戻す ・Dotfuscator: Map ファイル ・ProGuard: mapping.txt → 【リリースごとに保管する】。失うと解析不能になる ★移行メモ(ツールの現況):
ツール 現況 ConfuserEx 開発停止。フォークの ConfuserEx2 が継続 Dotfuscator Community Visual Studio に同梱(現役)★ Eazfuscator.NET 現役(有償) Babel 現役(有償) NanDoKu / Phoenix Protector 更新停止 ProGuard(Java) 現役。Android では R8 が後継
補足(現在の「保護」の考え方): 難読化の議論は、
前提から見直すべき段階にある。① そもそも守るべきものは何か
【クライアントに配るバイナリに置いてはいけないもの】 ・API キー、接続文字列、暗号鍵 ★ → 難読化しても【実行時にはメモリ上に平文で存在する】 → デバッガを付ければ取れる ・認可のロジック → クライアント側の判定は【必ず迂回される】 → 【サーバ側に置く】のが唯一の解② 守れるもの / 守れないもの
対象 難読化の効果 アルゴリズムの機密(独自の計算ロジック) 一定の効果(時間稼ぎ) ライセンス チェック 限定的(クラックの主戦場) 秘密情報(鍵、パスワード) 効果なし ★ 認可・課金の判定 効果なし(サーバでやる) ③ 現在の代替手段
・【サーバ側に置く】(SaaS 化) → そもそもクライアントに渡さない。最も確実 ・【Native AOT でネイティブ化する】★ → IL が残らないため、逆コンパイルの難度が跳ね上がる → .NET 7 以降で実用的に。副次的な保護効果がある → ただしリフレクション等に制約([自作CUI(CLI)の話](MS_BuildingYourOwnCLI)) ・【署名して改竄を検知する】(Authenticode) → 「読まれない」のではなく「書き換えを検知する」 ・【重要処理を HSM / セキュア エレメントに置く】【判断の順序】 ① その情報は本当にクライアントに置く必要があるか? → No なら サーバへ移す(これで大半が解決する)★ ② 置くなら、漏れた場合の影響はどれくらいか? ③ 影響が大きいなら、難読化 + 検知 + ローテーションを組み合わせる ④ 難読化は【多層防御の 1 層】であり、単独では機能しない原文の「難読化は単なる時間稼ぎに過ぎず」という一文が、
20 年近く経った現在も最も正確な要約である。
- .NET Tools:.NET逆コンパイラとコードを難読化するDotfuscator(3/4) - @IT
https://atmarkit.itmedia.co.jp/fdotnet/tools/dotfuscator/dotfuscator_03.html
-
逆コンパイラ - Wikipedia
https://ja.wikipedia.org/wiki/%E9%80%86%E3%82%B3%E3%83%B3%E3%83%91%E3%82%A4%E3%83%A9 -
Cの逆コンパイラはどこまで実現可能か,Javaはなぜ逆コンパイルされやすいのか?
http://www5d.biglobe.ne.jp/~noocyte/Programming/Decompile.html -
既にコンパイルされたアセンブリをデバッグできる.NET Reflector
https://www.infoq.com/jp/news/2012/08/precompiled-net-reflector/ -
C#で作られたプログラムをデコンパイルしてみよう - ほげほげー
https://tyheeeee.hateblo.jp/entry/CS-Advent-Calendar-2014-Day-15
-
難読化コード - Wikipedia
https://ja.wikipedia.org/wiki/%E9%9B%A3%E8%AA%AD%E5%8C%96%E3%82%B3%E3%83%BC%E3%83%89 -
.NETプログラムの難読化ツールの紹介と使ってみた感想
https://rabbitfoot.xyz/code-obfuscation/ -
ニュートン製品案内 Spices.NET JP
(.NET アプリケーション難読化ツール/逆コンパイラ)
https://www.newtone.co.jp/productsnjp00.html -
Dotfuscator の機能 - Visual Studio (Windows) | Microsoft Docs
https://learn.microsoft.com/ja-jp/visualstudio/ide/dotfuscator/capabilities
- Native AOT の配置
https://learn.microsoft.com/ja-jp/dotnet/core/deploying/native-aot/ - .NET のソース(source.dot.net)
https://source.dot.net/ - アセンブリの厳密名と署名
https://learn.microsoft.com/ja-jp/dotnet/standard/assembly/sign-strong-name
Tags: 移行, プログラミング, その他、開発の色々, .NET開発
このWikiは「Open棟梁Project」,「OSSコンソーシアム 開発基盤部会」によって運営されています。