難読化の設定
パッケージをインストールすると、Babel Obfuscator のすべての機能を、プロジェクトファイルの MSBuild プロパティと項目で設定できます。このページでは、よく使う設定を機能ごとに紹介します。
設定が Babel に渡される仕組み
パッケージは、Babel タスクの属性ごとに MSBuild のプロパティまたは項目を 1 つ定義し、CoreObfuscate ターゲットの実行時にそれらをタスクに渡します。ほとんどのプロパティはタスクの属性と同じ名前で、対応するコマンドラインスイッチと同じ値を受け付けます。BabelLicense、MergeInternalize、BabelRules のように名前が異なるものは、プロパティの対応の表に記載されています。
設定は PropertyGroup に記述します。通常は、Debug ビルドが読みやすいままになるように、ビルド構成を条件にします。
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<StringEncryption>stream</StringEncryption>
<ControlFlowObfuscation>if=on;switch=on;call=on</ControlFlowObfuscation>
<ControlFlowIterations>3</ControlFlowIterations>
<ValueEncryption>true</ValueEncryption>
<ResourceEncryption>true</ResourceEncryption>
<DebuggingProtection>true</DebuggingProtection>
<VerboseLevel>2</VerboseLevel>
</PropertyGroup>ファイルの一覧は、ItemGroup 内の項目として指定します。BabelRules、MergeAssembly、EmbedAssembly、MapInFile、BabelPlugin です。
プロパティはどのターゲットの実行よりも前に評価されるため、プロジェクト本体で設定した値はビルド全体に適用されます。パッケージ参照の解決済みパスのように、ビルド中にしかわからない情報から設定を計算するには、代わりに BeforeObfuscate ターゲットの中で設定します。タスクの構成の上書きを参照してください。
パッケージが適用する既定値
何も設定しない場合、パッケージを参照するプロジェクトは次のように難読化されます。
- シンボルのリネームは有効です。対象は型、メソッド、プロパティ、フィールド、イベント、パラメーターで、仮想メンバーも含まれます。名前空間はフラット化されます。
SymbolsRenamingプロパティはこれらすべてを一度に切り替え、個別のObfuscateTypes、ObfuscateMethods、ObfuscateProperties、ObfuscateFields、ObfuscateEvents、ObfuscateParameters、VirtualFunctions、FlattenNamespacesプロパティはそれを 1 つずつ上書きします。 - その他の機能はすべて無効で、有効にするまで適用されません。
- デバッグシンボルは再生成されます。コンパイラーがアセンブリの隣に PDB を生成した場合が対象で、
GenerateDebugが自動的に設定されます。そのため、難読化されたビルドもデバッグでき、スタックトレースをデコードできます。 - アセンブリの検索ディレクトリはプロジェクトの参照から構築されるため、Babel はコンパイラーが参照したすべての依存関係を解決します。
- Babel の構成ファイルは無視されます(
NoConfigurationはtrue)。そのため、ビルドはプロジェクトが宣言した内容だけに依存します。 - プロジェクトフォルダーにある
babelRules.xmlファイルは、自動的に使用されます。 - MSBuild がプロジェクト用に解決した厳密名キー(
KeyOriginatorFileまたはKeyContainerName)が Babel に渡されるため、難読化されたアセンブリは元のアセンブリと同じように署名されます。
テストプロジェクトの場合や、文字列暗号化だけを適用する場合に、リネームをオフにするには次のようにします。
<PropertyGroup>
<SymbolsRenaming>false</SymbolsRenaming>
</PropertyGroup>難読化ルール
ルールは、シンボルを機能の対象から除外したり、一連のシンボルに機能を適用したりします。ルールを指定する方法は 3 つあります。
- プロジェクトフォルダーに
babelRules.xmlファイルを追加します。このファイルは自動的に使用されます。 BabelRules項目で、任意の数のルールファイルを参照します。
<ItemGroup>
<BabelRules Include="rules\renaming.xml" />
<BabelRules Include="rules\encryption.xml" />
</ItemGroup>XmlRulesプロパティにルールをインラインで記述します。プロジェクトに固有の少数のルールに便利です。
<PropertyGroup>
<XmlRules>
<Rules>
<Rule name="rename public types" feature="renaming" exclude="false">
<Access>Public</Access>
<Pattern>*</Pattern>
</Rule>
</Rules>
</XmlRules>
</PropertyGroup>Android アプリケーションのサンプルはインラインルールを使ってパブリック型をリネームし、Blazor Web アプリのサンプルはルールファイルを使って Razor ページの背後にあるクラスを難読化します。ルールは、カスタム属性でコードに付けることもできます。
文字列暗号化
StringEncryption は、--stringencryption スイッチとまったく同じように、文字列暗号化を有効にし、アルゴリズムを選択します。true は既定のアルゴリズムを使用し、xor、hash、stream、custom はそれぞれのアルゴリズムを選択します。アルゴリズムの比較については、標準アルゴリズムを参照してください。
<PropertyGroup>
<StringEncryption>stream</StringEncryption>
</PropertyGroup>STREAM Ultimate は、最近のターゲットに推奨されるアルゴリズムです。完全にマネージドコードで実装されているため、トリミング、NativeAOT、FIPS の制約があるホストでも動作し、.NET Framework から .NET 10 まで、Android、iOS、.NET MAUI も含めて検証されています。Android、iOS、MAUI のプロジェクトでは、パッケージはコンパイルの後、パッケージ化の前に Babel を実行します。パッケージ化されたアプリに暗号化された文字列を含めるには、この位置で文字列暗号化を行う必要があります。const として宣言された文字列は暗号化できません。const 文字列の暗号化を参照してください。
制御フロー難読化
ControlFlowObfuscation は --controlflow と同じキーと値の一覧を受け取り、ControlFlowIterations は反復回数を受け取ります(ILIterations もエイリアスとして使用できます)。
<PropertyGroup>
<ControlFlowObfuscation>goto=on;if=on;switch=on;case=on;call=on</ControlFlowObfuscation>
<ControlFlowIterations>3</ControlFlowIterations>
</PropertyGroup>バージョン 12 では、自動難読化解除ツールへの耐性を高めたフラット化変換として、チェーン状態アルゴリズム Ultimate が追加されました。chain=on で有効にします。単独でも、ほかのアルゴリズムと組み合わせても使用できます。
<PropertyGroup>
<ControlFlowObfuscation>switch=on;case=on;chain=on</ControlFlowObfuscation>
</PropertyGroup>フラット化には実行時のコストがあります。パフォーマンスとチューニングで説明しているとおり、ルールを使って、実行頻度の高いメソッドには適用しないようにしてください。
コード暗号化
MsilEncryption はメソッド本体を暗号化します。true は対象となるすべてのメソッドを暗号化し、正規表現を指定すると、完全名がその正規表現に一致するメソッドだけを暗号化します。
<PropertyGroup>
<MsilEncryption>MyApp\.Licensing\..*</MsilEncryption>
</PropertyGroup>実行時の動作、パスワードで保護されたコード、暗号化できないメソッドについては、コード暗号化を参照してください。
値、リソース、呼び出しの保護
<PropertyGroup>
<ValueEncryption>array=true;true</ValueEncryption>
<ResourceEncryption>true</ResourceEncryption>
<DynamicProxy>all</DynamicProxy>
<SuppressIldasm>true</SuppressIldasm>
<SuppressReflection>true</SuppressReflection>
</PropertyGroup>各プロパティは、対応するスイッチの値を受け付けます。値と配列の暗号化、リソース暗号化、動的プロキシを参照してください。
改ざん検出とデバッグ保護
<PropertyGroup>
<TamperingDetection>true</TamperingDetection>
<DebuggingProtection>true</DebuggingProtection>
</PropertyGroup>デスクトップターゲットでは、改ざんチェックはメモリに読み込まれたイメージのハッシュを計算するため、単一ファイル、トリミング、AOT で公開されたアプリケーションでは機能しません。パッケージはプロジェクトの PublishSingleFile 設定を Babel に渡し、Babel は、そのような公開に対して改ざん検出が要求されると警告を出します。改ざん検出とデバッグ保護を参照してください。
Android と iOS のパッケージの整合性 Ultimate
バージョン 12 以降、.NET for Android と iOS のターゲット(.NET MAUI を含む)では、改ざん検出はイメージではなくパッケージの整合性を検証します。Android では APK の署名証明書、iOS ではバンドル ID と Apple のチーム ID です。期待される値は、このバージョンで追加されたプロパティでピン留めします。
<!-- Android: SHA-256 fingerprint of the release signing certificate -->
<PropertyGroup Condition="$(TargetFramework.Contains('-android'))">
<TamperingDetection>true</TamperingDetection>
<TrustedSigner>2924C53EE9C511E9F26E0720FD8151064F7621681667D7AB1899E551CDB25104</TrustedSigner>
</PropertyGroup>
<!-- iOS: bundle identifier and Apple Team ID -->
<PropertyGroup Condition="$(TargetFramework.Contains('-ios'))">
<TamperingDetection>true</TamperingDetection>
<TrustedBundle>com.mycompany.myapp</TrustedBundle>
<TrustedTeam>ABCDE12345</TrustedTeam>
</PropertyGroup>TrustedSigner は、セミコロンで区切った複数のフィンガープリントを受け付けます。たとえば、アップロードキーと Google Play のアプリ署名キーです。それぞれの値を取得する方法と、ピン留めがない場合の動作は、Android(MAUI)のパッケージの整合性と iOS(MAUI)のパッケージの整合性で説明しています。Android アプリケーションのサンプルには、Android プロジェクトファイルの全体が示されています。
FIPS ホスト向けのマネージド AES
コード暗号化、文字列暗号化(XOR と HASH)、値の暗号化、リソース暗号化のために挿入される実行時の復号ルーチンは、プラットフォームの暗号化プロバイダーを使用します。そのため、OpenSSL の FIPS 構成が壊れているホストでは、そのようなアセンブリを起動できません。バージョン 11.8 以降では、encryption=aesmanaged を指定すると、代わりに、外部に依存しないマネージド復号ルーチンが選択されます。
<PropertyGroup>
<MsilEncryption>true</MsilEncryption>
<Use>encryption=aesmanaged</Use>
</PropertyGroup>Use は、--use スイッチが受け付ける任意のキーと値のペアを、セミコロンで区切って受け付けます。たとえば tagassembly=on です。マネージド復号ルーチンのトレードオフについては、FIPS 準拠を参照してください。
アセンブリのマージと埋め込み
MergeAssembly 項目と EmbedAssembly 項目には、ターゲットアセンブリにマージするアセンブリと、埋め込むアセンブリを列挙します。MergeInternalize はマージされたパブリック型を internal 化し、MergeCopyAttributes はそれらのアセンブリレベルの属性をコピーするかどうかを制御します。
<ItemGroup>
<MergeAssembly Include="$(TargetDir)Acme.View.dll" />
<MergeAssembly Include="$(TargetDir)Acme.ViewModel.dll" />
<EmbedAssembly Include="$(TargetDir)Framework.Mvvm.dll" />
</ItemGroup>
<PropertyGroup>
<MergeInternalize>true</MergeInternalize>
</PropertyGroup>Babel はコンパイルの直後、ビルドが依存関係を出力フォルダーにコピーする前に実行されるため、クリーンビルドでは $(TargetDir) の下のパスがまだ存在しないことがあります。確実な入力元は、コンパイラー用に解決された参照の一覧です。この一覧は、BeforeObfuscate ターゲットの中でフィルターできます。
<Target Name="BeforeObfuscate">
<ItemGroup>
<MergeAssembly Include="@(ReferencePathWithRefAssemblies)"
Condition="'%(Filename)' == 'Acme.View' Or '%(Filename)' == 'Acme.ViewModel'" />
</ItemGroup>
</Target>パッケージは、マージされたアセンブリと埋め込みアセンブリを .deps.json ファイルと公開対象からも削除するため、dotnet publish がそれらを重複して出力することはありません。機能についてはマージと埋め込みを、ビルド側の詳細については公開を参照してください。
マップファイルとアセンブリ間のリネーム
GenerateMapOutFile は、難読化されたアセンブリの XML マップファイルを書き出します。このファイルは、スタックトレースのデコードと、ライブラリのパブリックインターフェイスを複数のアセンブリにわたって一貫してリネームするために使用します。MapInFile 項目は、すでに難読化されたアセンブリのマップファイルを Babel に渡します。
<PropertyGroup>
<GenerateMapOutFile>true</GenerateMapOutFile>
<BabelMapOutFile>$(SolutionDir)MapOut\$(TargetFileName).map.xml</BabelMapOutFile>
</PropertyGroup>
<ItemGroup>
<MapInFile Include="$(SolutionDir)MapOut\Acme.Core.dll.map.xml" />
</ItemGroup>XML マップファイルとアセンブリ間のリネームを参照してください。単体テストのサンプルはマップファイルを使って難読化されたライブラリに対してテストを実行し、.NET アプリを公開するのサンプルは NuGet 依存関係のパブリックインターフェイスを難読化して、そのマップファイルをアプリケーションに渡します。
最適化
<PropertyGroup>
<DeadCodeElimination>true</DeadCodeElimination>
<SealClasses>true</SealClasses>
<EnumRemoval>true</EnumRemoval>
<ConstRemoval>true</ConstRemoval>
<DisgregateRemoval>true</DisgregateRemoval>
<InlineExpansion>true</InlineExpansion>
<CleanAttributes>true</CleanAttributes>
</PropertyGroup>各最適化については、最適化で説明しています。
プラグイン
BabelPlugin 項目は読み込むプラグインアセンブリを参照し、PluginsArguments はキーと値の引数をプラグインに渡します。BabelEncrypt プラグインはパッケージに同梱されており、ビルドツールと同じフォルダーにあります。
<ItemGroup>
<BabelPlugin Include="$(BabelTaskDir)BabelEncrypt.dll" />
</ItemGroup>
<PropertyGroup>
<PluginsArguments>dictionary=exclusionlist.txt</PluginsArguments>
</PropertyGroup>Babel Obfuscator のプラグインと Encrypt Plugin を参照してください。
ログと診断
<PropertyGroup>
<VerboseLevel>3</VerboseLevel>
<GenerateLogFile>true</GenerateLogFile>
<BabelLogFile>$(IntermediateOutputPath)babel.log</BabelLogFile>
<BabelProvideCommandLineArgs>true</BabelProvideCommandLineArgs>
<ShowStatistics>true</ShowStatistics>
<Trace>MyApp\.Services\..*</Trace>
<BabelWarningsToIgnore>W00008</BabelWarningsToIgnore>
<BabelWarningsAsErrors>W00000</BabelWarningsAsErrors>
</PropertyGroup>VerboseLevelは、Babel のログのうちビルド出力に表示される量を制御します。GenerateLogFileとBabelLogFileは、完全なログをファイルに保存します。BabelProvideCommandLineArgsは、パッケージが Babel に渡すコマンドラインを出力し、@(BabelCommandLineArgs)項目に格納します。Traceは正規表現を受け取り、一致したシンボルが難読化された理由、または難読化されなかった理由をシンボルごとに説明します。ルールをデバッグする最も手早い方法です。BabelWarningsToIgnore、BabelWarningsAsErrors、BabelWarningsAsInfosは警告コードの一覧を受け取り、その報告方法を変更します。MakeBabelProjectFileは、現在の構成と同等の MSBuild プロジェクトを書き出します。Babel タスクやコマンドラインツールでビルドを再現するのに便利です。
パッケージが受け付けるすべてのプロパティは、パッケージリファレンスに記載されています。