バージョン11.8.0.0
Babel Obfuscator
機能強化
- FIPS が有効なホスト — マネージド AES 暗号化:
--use encryption=aesmanaged(MSBuild からは<Use>encryption=aesmanaged</Use>) で選択する、オプトインのマネージド AES 復号ルーチンを追加しました。コード (MSIL)、文字列、値/配列、リソースの各暗号化で保護したアセンブリが、プラットフォームの暗号プロバイダーを利用できないホストでも起動するようになります。代表的なのは OpenSSL の FIPS 状態が壊れた Linux コンテナーで、従来は埋め込まれた復号ランタイムが、ユーザーコードの実行前に型初期化子の中でOpenSslCryptographicExceptionをスローしていました。難読化ツール自体は (正常な) ビルドホスト上でこれまでどおりプラットフォームのプロバイダーを使って暗号化し、出力される暗号文も同じです。変わるのは、アセンブリに埋め込まれる自己完結型の復号ルーチンだけです。.NET Framework と .NET (Core) でサポートされ、netstandard と Mono は従来の動作のままです。FIPS 準拠 のドキュメントページも参照してください。なお、マネージド復号ルーチンのスループットは約 40 MB/s です。ホットパス上のメソッドに対してcache=falseのコード暗号化と組み合わせるのは避けてください。 - パフォーマンス — 大規模アセンブリ: コードと属性が非常に多い大規模アセンブリの難読化で支配的だった 3 つのホットパスを書き直しました。出力は 11.7.0 とバイト単位で同一であることを確認済みです。
- カスタム属性のリネーム (
RenameTypeAttributes) は、O(型数 × 属性数) ではなくインデックスを使う方式になりました。属性の多い 41 MB / 約 69,000 型のアセンブリ (たとえば Microsoft.Graph) は、これまでリネームフェーズが終わらなかったものが約 20 秒で完了します。 - 呼び出しグラフの
FindCallVerticesは O(頂点数 × シグネチャ数) ではなく O(頂点数) になり、DevExpress.XtraEditors / Spire.Pdf で 7〜11 倍高速化しました。 - リネームフェーズでの仮想メンバーのグループ化を名前でバケット化しました (DevExpress.XtraEditors で 2074 ms → 17 ms)。 全体として、7.9 MB の DevExpress アセンブリを全機能で難読化する時間が約 24% 短縮されました。
- カスタム属性のリネーム (
- ターゲットランタイムの動的検出: 難読化対象アセンブリのターゲットランタイムバージョンを動的に検出する機能を追加しました。
- ハードウェアドングル連携のサンプル: コード暗号化で保護した .NET アプリケーションをハードウェアライセンスドングル (KEYLOK、SafeNet Sentinel、Wibu CodeMeter、Marx CrypToken など) に結び付け、ドングルの DLL をすり替えたりスタブに置き換えたりしても保護を回避できないようにする方法を示す、実行可能なクロスプラットフォームのリファレンス実装を追加しました。
- [新機能] 警告 OB0009 は、出力からは診断のしようがなかったデモモードの事例を報告します。マップファイルを指定しているのにアセンブリ間難読化のライセンスがない場合、マップはスキップされ、リネーム済みの外部シンボルへの参照は元の名前のままになるため、アセンブリは実行時に
TypeLoadExceptionで初めて失敗していました。これがビルド時に報告されるようになりました。
バグ修正
- Avalonia — リソース暗号化: リソース暗号化を有効にすると、難読化した Avalonia アプリケーションが
avares://のアセットや XAML でFileNotFoundExceptionを起こして起動時にクラッシュする問題を修正しました。Avalonia はアセンブリの XAML とアセットをすべて単一の!AvaloniaResourcesBLOB に格納し、アセットローダーがそれを直接読み取るため、Babel が挿入する復号フックを迂回してしまいます。この BLOB は暗号化しないようになり (それ以外のリソースは引き続き暗号化されます)、リソース暗号化を有効にしたまま Avalonia アプリを完全に難読化できます。WPF には影響しません。単一ファイルの Avalonia アプリを配布する場合は、マージではなくアセンブリの埋め込みを使用してください。 - WPF — テーマディクショナリのマージ: 同じパス (通常は
themes/generic.baml) に BAML のリソースディクショナリを持つアセンブリ同士をマージすると、コントロールのテーマが失われる問題を修正しました。衝突するニュートラルカルチャーのResourceDictionaryストリームは一意の名前で保持され、生成したディクショナリからMergedDictionaries経由でリンクされるため、主アセンブリの参照優先順位が維持されます。 - MAUI/XAML —
x:言語プリミティブ: XAML のリネーム処理がx:TypeArguments内の言語プリミティブからx:プレフィックスを落としてしまう問題 (例:x:Double→Double) を修正しました。これにより、実行時に XAML を読み込む .NET MAUI の iOS リリースビルドがXamlParseException ("Type Double not found")でクラッシュしていました。 - リネーム — 自己参照する型参照: リネームした型の自己参照
TypeReference(自モジュールスコープ) が古い名前のまま残り、型の初期化時にSystem.TypeLoadExceptionが発生する問題を修正しました。ArxOne.MrAdvice のような IL ウィーバーや、内部のProgram型を介した C# のトップレベルステートメントはldtokenのエイリアスを出力するため、解決に失敗していました。自モジュールの TypeRef の修正処理が、リネームされたすべての型に対して実行されるようになりました。 - リネーム — PropertyChanged の文字列書き換え: リネーム後に
OnPropertyChanged("PropName")を動作させ続けるためのldstr書き換えが、INotifyPropertyChangedを実装する基底型を継承したあらゆる型で、無関係な文字列キーの API 引数 (XAML のコントロール検索、ディクショナリのキー、独自のレジストリなど) まで書き換えてしまう問題を修正しました。書き換えは、認識済みの PropertyChanged の呼び出し先に限定されます。8.7 で報告され、11.7 で再現しました。 - 制御フロー — 外部依存を持つホスト型: 実行時に不規則かつ断続的に発生する
FileNotFoundException(たとえば SSIS の0xC0011008) を修正しました。原因は、制御フローのディスパッチャーや switch フラット化のヘルパーメソッドが、読み込み時に外部アセンブリを引き込む型に配置されていたことです。ホスト型の選択では、値型のフィールドを持つ型 (ジェネリック引数や入れ子の値型を含めて再帰的に確認)、モジュール外やコアライブラリ外の基底型/インターフェイスを持つ型、およびジェネリック型を除外するようになりました。 - ライセンス — 無効なライセンスが黙ってデモモードにフォールバックする: 明示的に指定したライセンスファイルが実行中のバージョンでは有効でない場合に、そのことが尊重も報告もされない問題を修正しました。従来は黙ってデモモードのまま処理が続き、ライセンス対象の機能を含まない時限付きアセンブリが生成されるため、利用者がそれに気付くのはずっと後になりがちでした。明示的に指定したライセンスは、この製品とバージョンに対して有効でなければならなくなり、そうでない場合、Babel は
Error: the license provided is not valid for Babel Obfuscator <version>.を表示してアセンブリを生成せずに停止し、LicensingFailure(1、--strict-exit指定時は40) で終了します。ライセンスをまったく指定せずに実行した場合は、従来どおり機能を制限したデモモードで起動します。 - Linux でのトライアルライセンス: 実行時間やインスタンス数のトライアル制限を持つライセンスが、Linux ホスト上で検証に失敗することがありました。Linux コンテナー内の Babel Obfuscator NuGet タスクも同様です。詳細は Babel Licensing 11.8.0.0 のリリースノートを参照してください。
変更
- 自己難読化のツールチェーン: Babel Obfuscator の NuGet パッケージ (および babel/lic/HardwareId/Babel.Win の各バイナリ) は、マネージド AES アルゴリズムで自己難読化されるようになりました。これにより、配布されるツール自体が FIPS の壊れたホストでも起動します。
Babel Licensing
機能強化
- FIPS が有効なホスト — 起動時の耐性: 認定済みの OpenSSL FIPS プロバイダーを持たないコンテナーが FIPS 有効ホスト上で動作する環境 (たとえば RHEL ホスト上の Ubuntu ベース .NET コンテナー、dotnet/dotnet-docker#5849) でも、Babel Licensing が動作するようになりました。従来、こうした環境ではライセンス検証が
BabelLicenseException: Internal errorでクラッシュしていました。原因は、.NET のIsolatedStorageの ID ハッシュ計算がSHA1.Createでerror:03000086を起こしていたことです。ライセンスストアは、IsolatedStorage を初期化できない場合にマネージドなファイルストアへフォールバックし、暗号化された日付ストアのプラットフォーム AES/DES 暗号をビット互換の BouncyCastle 実装に置き換え (既存のストアファイルは双方向で読み取れます)、どのストレージも使えない場合はメモリ上での日付管理に縮退します。これにより、ライセンス検証が OS の暗号スタックに触れることはなくなりました。Babel Licensingサービスを Babel Obfuscator のマネージド AES アルゴリズム (--use encryption=aesmanaged) で難読化した場合、難読化後のサービスも FIPS の壊れたホストで起動します。 - アクティベーションライセンス — 検証時の機能反映: アクティベーションライセンスに機能を追加すると、完全な非アクティブ化と再アクティベーションを行わなくても、次回の検証時にアクティベーション済みのクライアントへ反映されるようになりました。アクティベーションと検証の両方の経路で、親ライセンスの情報 (機能、フィールド、制限、日付) とハードウェアバインディングから、共通のヘルパーを使ってマシンごとのノードロックライセンスを生成し、再署名します。
バグ修正
- gRPC の UserKey 認証: 11.7.0 のセキュリティ強化以降、gRPC の UserKey ログインがすべて失敗していた問題を修正しました。
GrpcAuthenticator.AuthenticateAsyncがユーザー名パラメーターを無視し、常にコンストラクターで取得したクライアント ID を送信していたため、UserKey のログインが 2 引数の ID ログイン経路に流れていました。すでに多数のクライアントアプリケーションを配布している運用者向けに、オプトインのEnableLegacyUserKeyFallbackブリッジ (既定はオフ) を用意し、再配布せずに互換性を回復できるようにしています。新規インストールでは修正後の経路が使われます。 - Webhook ペイロードの JSON キー: ライセンス、注文、レポートの Webhook ペイロードが、シンボルのリネームを有効にして難読化した Licensingサービスでは 1 文字の JSON キー (
p、q、rなど) になってしまう問題を修正しました。ペイロードは、すべてのキーを[JsonPropertyName](camelCase) で固定した名前付き DTO を使うようになり、Webhook の仕様はシンボルのリネームや文字列暗号化の影響を受けなくなりました。 - トライアルライセンス — 共有メモリ領域のない Linux ホスト: 実行時間やインスタンス数のトライアル制限を持つライセンスが
Object reference not set to an instance of an objectで検証に失敗し、Windows や macOS では正しく検証される同じライセンスでIsValidがfalseを返す問題を修正しました。Linux コンテナーで報告されたものです。Application.StartTimeとApplication.InstanceCountは無条件に共有メモリ領域を読んでいましたが、この領域は開けた場合にのみ内容が設定されるため、作成できない環境では両メンバーが例外をスローし、TrialRestriction.Validateが原因の分からないエラーで失敗していました。この領域は任意のものとして扱われるようになり、2 つの値は例外をスローする代わりにプロセス単位の値 (プロセスの開始時刻と、既存のプロセス数フォールバック) に縮退します。作成時刻がゼロの場合も同様に扱われます。そうしないと、どのトライアルも実行時間を使い切ったように見えてしまうためです。これはリグレッションではなく、11.6.0、11.7.0、11.7.0.3、11.8.0 のいずれでも同じ動作でした。 - Linux — POSIX セマフォライブラリのバインド: 上記の失敗の根本原因を修正しました。glibc の Linux ホストでは共有メモリ領域をそもそも開けませんでした。これを保護する
sem_*のエントリポイントがlibrtからインポートされており、.NET ランタイムはこの名前をlibrt.soとliblibrt.soとして探しますが、実際にインストールされているlibrt.so.1という soname では探さないためです (バージョンなしのシンボリックリンクは開発パッケージに含まれており、コンテナーイメージには入っていません)。その結果、すべての呼び出しがDllNotFoundExceptionでバインドに失敗していました。この問題はトライアル制限を壊すだけでなく、すべての Linux ホストでApplication.StartTimeとApplication.InstanceCountを黙ってプロセス単位の値に縮退させていました。インポートは soname を使うようになり、さらにDllImportResolverがlibpthread.so.0、libc.so.6、musl libc の名前も試すため、sem_*が libc に統合された glibc 2.34 の前後と musl の両方をカバーします。プロセスをまたいだインスタンス数のカウントと、共有されるアプリケーション開始時刻は、Linux でも Windows と同じように動作するようになりました。Alpine/musl のホストは影響を受けていませんでした。またSharedMemory.Openは呼び出し元に例外をスローする代わりに領域が利用できないことを報告するため、名前付きセマフォを持たないホストでもプロセス単位のフォールバックで動作し続けます。 - Babel Licensingサービス — 自動生成される JWT 署名キー:
Application:SigningKeyを空のままにしていると、発行済みトークンを使った呼び出しがすべて401で失敗し、結果としてどのライセンスもアクティベーションできなくなる問題を修正しました。起動時にランダム生成されたキーはApplication構成セクションの切り離されたコピーにしか設定されていませんでしたが、TokenServiceはIOptionsSnapshot<ApplicationSettings>を解決して構成から再バインドするため、トークンはパディングの空白から成るキーで署名され、生成されたキーで検証されていました。サービスログは401の直前に認証成功を記録していたため、原因の特定も困難でした。生成されたキーはPostConfigure<ApplicationSettings>を通じて適用されるようになり、バインドされたすべてのオプションインスタンスが同じキーで署名と検証を行います。なお、本番環境では明示的にキーを設定することを引き続き推奨します。生成されたキーは再起動のたびに変わり、複数インスタンス間で共有されないためです。

