【Unity】ソースコードを難読化して改造対策をする「Obfuscator」を使ってみる
ソースコードの難読化にObfuscatorを購入したのですが、導入するのになかなか苦戦したので、試して分かったことを書いていきます。
ちなみにAndroidのIL2CPPビルドで試しています。Monoでは試していません。
- ソースコード難読化の必要性
- Obfuscatorの有効化
- 「Il2CppDumper」で難読化の結果を確認する
- 難読化の対象を変更する
- 難読化されないクラスがあるんだけど
- 特定の属性をつけるとリネームされなくなる
- 「SkipRename」属性で特定の名前をリネームしない
- コンパイラが生成したコードを難読化しない
- おわりに
ソースコード難読化の必要性
いろんなサイトで詳しく説明されているので、そちらで確認を。
www.slideshare.net
「IL2CPPビルドにしておけば機械語になるんだし、大丈夫だろ」と最初は思ってたんですが、そうじゃないんですね、IL2CPPビルドでもメタデータにクラスのデータ構造がばっちり残っていました。
名前とアドレス値がわかれば、どういう動作がどの場所で処理されてるか分かってしまってやばいので、この名前の方をObfuscatorで難読化(リネーム、フェイク関数を追加)します。
解析のヒントになる情報を片っ端からつぶしていく感じです。
Obfuscatorの有効化
パッケージをインポートすると、そのままObfuscatorが有効化されます。
設定ファイルはEditor/Beebyte/Obfuscator/ObfuscatorOptionsにあります。
Obfuscatorを有効化すると必要な名前までリネームしてアプリが動かなくなる可能性があるので、要注意です。
リフレクションとかで必要な名前をリネームしてしまうと、実行時にアプリがクラッシュします。
後からObfuscatorを導入するとアプリが動かなくなった時の問題部分の切り出しが困難になるので、開発初期から導入しておいた方がよさげ。
「Il2CppDumper」で難読化の結果を確認する
Il2CppDumperを使うと難読化した結果を確認することができます。
Il2CppDumperの使い方は、以下のサイト参照。
public class NewBehaviourScript { private int a; void Hoge() { } }
難読化の対象を変更する
難読化の対象はAssembly-CSharp.dllに設定してあります。
Assembly-CSharp.dllには特殊フォルダ(PluginsとかStandard Assetsとか)以外にあるスクリプトファイルが含まれています。
なので、一般公開されてて難読化するメリットがあまりない外部ライブラリやUnityアセットも、Assets直下に置いてある場合、問答無用で難読化されるので注意です。
自分は、外部ライブラリやUniyアセットはPluginsに移動させて、難読化の対象外にしています。
名前空間で難読化をスキップする指定もできますが、管理が面倒くさくなると思って今のところ使っていません。
難読化されないクラスがあるんだけど
MonoBehaviourなクラスでリネームされない
MonoBehaviourを継承しているクラスのクラス名、公開関数、公開フィールドは設定でリネームされないようになっています。
また、AwakeやUpdate等のUnity固有のイベント関数もリネームされないようになっています。
using UnityEngine; public class NewBehaviourScript : MonoBehaviour { public string Name; private int a; private void Awake() { } public void Hoge() { } private void Fuga() { } }
これらの名前も設定変更でリネームするようにできますが、エンジン側で参照する名前の可能性大なので、要注意です。
基本はそのままの設定でいいかと思います。
特定の属性をつけるとリネームされなくなる
たとえば、[SerializeField]をつけると名前がリネームされなくなるようです。
他にもリネームされなくなる属性があるかもしれません。
using UnityEngine; public class NewBehaviourScript { [SerializeField] private int a; }
設定ファイルに、これらのリネームされない属性が表示されてる項目はないっぽい。まじか。
「SkipRename」属性で特定の名前をリネームしない
SkipRename属性を使うと、クラス名、関数名やフィールド名をリネームしないようにできます。
using Beebyte.Obfuscator; [SkipRename] public class NewBehaviourScript { [SkipRename] private int a; [SkipRename] void Hoge() { } }
コンパイラが生成したコードを難読化しない
ラムダ式やUnityコルーチンやasyncを使うと、コンパイル時にコンパイラがコードを生成するのですが、これにも難読化がかかります。
コンパイラが生成したコードを難読化したくない場合は、Equivalent Attributes For SkipにSystem.Runtime.CompilerServices.CompilerGeneratedを設定します。
using System.Collections; using UnityEngine; public class NewBehaviourScript { IEnumerator Hoge() { yield return new WaitForEndOfFrame(); } }
難読化してるとUniTaskを使っているときに例外が発生したことがあったので、自分はコンパイラの生成コードは難読化しないようにしています。
おわりに
上記の内容で自分がやりたいことは大体できたのですが、もし他にわからないことがあったら、Assets/Editor/Obfuscator.pdfを確認してみてください。(ただし、英語)
個人アプリで難読化が必要かと言うと、うーんな感じですが、やっとくに越したことはないんじゃないかな。
ちなみにMonoビルドは多分リスクしかないのと思うので、公開するアプリに使うのはやめといたほうがいいと思います。(某ひまつぶゲームが改造されて配布されたのを見かけましたし)