かっぱの沼

かっぱの沼

かっぱが技術的な何かを書きます

Xamarinでも難読化のすゝめ

この記事は Qiita [初心者さん・学生さん大歓迎!] Xamarin その1 Advent Calendar 2017 11日目 に向けたものです。

書いていたらチュートリアル記事になってしまったので、「ほへぇ難読化しゅごい」などと画像だけを眺めていくことをおすすめします。

はじめに

みなさん、難読化してますか?(挨拶)

入門記事を見てXamarinでアプリ作った!早速公開しよう!と思ったアナタ。

ちょっとまってください。あなたのコードの中に秘密のパスワードや暗号化キー、代々受け継がれてきた社外秘な処理は含まれていませんか?
完成した .apk ファイル(Androidの場合)からリバースエンジニアリングによって、簡単にあなたの書いたコードが丸裸にされてしまいますよ。

「えっ、コンパイラによってコードは低水準の言語に変換されるから読めないのでは?」
=> C#では中間言語に変換されるだけです。中間言語からあっさりとコードを復元できてしまいます。

APKファイルの中身を覗いてみる

ツールを使って、実際にコードを覗いてみましょう。

その前に、簡単なアプリを作ってみます。

……はい、できました!

f:id:kmz_kappa:20171201210015p:plain

アプリを起動した時刻と、時間に合わせたメッセージを表示するアプリです。

時間によってメッセージを変更する処理はこんな感じです。
6~9時台では「Good Morning!」、10~17時台では「Hello!」、それ以外の時間帯では「Good Evening!」と返すだけの単純な処理です。
今回は、このコードを覗いてみます。

    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();

            var now = DateTime.Now;
            labelTime.Text = now.ToString("HH:mm:ss");
            labelMessage.Text = GetMessage(now);
        }

        private string GetMessage(DateTime time)
        {
            if (time.Hour >= 6 && time.Hour < 10)
            {
                return "Good Morning!";
            }
            else if (time.Hour >= 10 && time.Hour < 18)
            {
                return "Hello!";
            }
            return "Good Evening!";
        }
    }

アプリを作ったら、さくっとReleaseビルドしてAPKファイルまで作ってしまいます。
f:id:kmz_kappa:20171201210843p:plain

できましたか?できましたね?

そうしたら、拡張子 .apk.zip に変更して解凍します。

中に assemblies というフォルダがあるので、開いてみます。

f:id:kmz_kappa:20171201211257p:plain

すると中には作成したアセンブリ(今回は ObfuscateSample.dll)が入っています。

f:id:kmz_kappa:20171201211356p:plain

このDLLファイルを、ILSpy というツールで開いてみましょう。 ILSpyオープンソースアセンブリブラウザ・デコンパイラです。

f:id:kmz_kappa:20171207210538p:plain

はい、APKファイルを展開して中のファイルをツールにかけたら、書いたコードがほとんど元通りに復元されました。
特に GetMessage メソッドなんてほぼ変わっていません。

あなたはこんなAPKファイルをリリースしますか?秘密のコードは書かれていませんか?本当に大丈夫ですか?

難読化のすゝめ(無償版編)[PCLプロジェクトの場合]

幸いにも Visual Studio にはコード難読化ツール Dotfuscator CE が付いてきます。これを使ってみましょう。

Dotfuscatorのインストール

Visual Studio 2017 では、Visual Studio Installer から Dotfuscator を導入します。
画面右のオプション項目 PreEmptive Protection - Dotfuscator にチェックを入れインストールしてしまいましょう。

f:id:kmz_kappa:20171201214436p:plain

インストールが完了すると、Visual Studio上で ツール → PreEmptive Protection - Dotfuscator が選択できるようになるはずです。

f:id:kmz_kappa:20171201214950p:plain

こいつをクリックしてやります。
すると「起動前にユーザー登録せよ」などと言われるので登録してください
DotfuscatorにはGUI版とコマンドライン版がありますが、Xamarinではコマンドライン版を使用します。 コマンドライン版を使うためには登録が必要です。(無料)

さて、Dotfuscator は起動したでしょうか。早速ですがGUI版のツールは使用しないので終了してください。

プロジェクトの難読化設定

Xamarinの場合、APKファイルを生成する前に難読化を行う必要があるため、少し設定が面倒です。

まず、PreEmptive のサイト から MSBuild の targets ファイルを入手してください。

ダウンロードした PreEmptive.Dotfuscator.Xamarin.targets ファイルをソリューションのルートにでも置いてください。

f:id:kmz_kappa:20171201220537p:plain

難読化の対象とするプロジェクトの .csproj ファイルをテキストエディタ等で開いてください。
そしてファイルの終わり </Project> の直前に <Import Project="..\..\PreEmptive.Dotfuscator.Xamarin.targets" /> を挿入してください。(.targetsファイルのパスが異なる場合は適宜変更)

f:id:kmz_kappa:20171207211407p:plain

次に、Dotfuscator のCLI版(dotfuscatorCLI.exe)のパスを調べてください。Visual Studio のインストール先のどこかに入っているはずです。
私の場合は C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE\Extensions\PreEmptiveSolutions\DotfuscatorCE\dotfuscatorCLI.exe にありました。

パスを調べたら、対象 .csproj<PropertyGroup> タグ(PropertyGroup は複数ありますが、PropertyGroup の後に Condition が付いていないもの)の内側に以下の3行を挿入してください。
このとき、1行目のパスは実際のインストール先に合わせるようにしてください。

    <DotfuscatorXamarinCliPath>C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE\Extensions\PreEmptiveSolutions\DotfuscatorCE\dotfuscatorCLI.exe</DotfuscatorXamarinCliPath>
    <DotfuscatorXamarinConfigFileName>DotfuscatorConfig.xml</DotfuscatorXamarinConfigFileName>
    <DotfuscatorXamarinGenerateNewConfigFile>true</DotfuscatorXamarinGenerateNewConfigFile>

f:id:kmz_kappa:20171207211845p:plain

次に、ビルドの対象とするターゲット(Debug, Releaseなど)の <PropertyGroup> の中に <DotfuscatorXamarinEnabled>true</DotfuscatorXamarinEnabled> を挿入してください。 今回は Release ビルド時に難読化が行われるように設定します。

f:id:kmz_kappa:20171207212202p:plain

最後に、「最後の」<ItemGroup> タグの次に <ItemGroup><None Include="DotfuscatorConfig.xml" /></ItemGroup> と挿入してください。

f:id:kmz_kappa:20171207212503p:plain

はい、これでようやく設定が完了です。お疲れ様でした。

難読化ビルドの実行

Visual Studio にて、プロジェクトを再読込してください。
ソリューション エクスプローラー に DotfuscatorConfig.xml が追加されているはずです。
これは Dotfuscator のプロジェクトファイルです。難読化関連の設定を変更したい場合は、このファイルを Dotfuscator のGUIツールで開きます。

f:id:kmz_kappa:20171201223758p:plain

さて、早速プロジェクトをビルドしてみましょう。

ビルドログにこんなメッセージが表示されたら成功です。これでアセンブリの中身が難読化されたはずです。

1>------ ビルド開始: プロジェクト: ObfuscateSample, 構成: Release Any CPU ------
1>  ObfuscateSample -> C:\dev\ObfuscateSample\ObfuscateSample\ObfuscateSample\bin\Release\ObfuscateSample.dll
1>  Running Dotfuscator with config file 'DotfuscatorConfig.xml'...
1>  Finished running Dotfuscator.

難読化結果

さて、生成されたアセンブリを ILSpy で見てみましょう。

f:id:kmz_kappa:20171207212903p:plain

フィールドやメソッドの名称が 「a」「b」のように変更されています。

さて、フィールドやメソッドの名前が変更されたので、リバースエンジニアリングによって解読がされづらくなりました。
……が、頑張ればまだ解読が可能な気がします。難読化ツールの力はこの程度なのでしょうか?

違います。元のコードが全く推測できないくらい複雑にすることが可能です。

ただし、ここから先は Professional Edition(製品版)の機能です。
Professional Edition は評価版があるので、これを試してみましょう。 (評価版の試用期間は意外と短いので注意してください。)

難読化のすゝめ(製品版編)[PCLプロジェクトの場合]

というわけ以下のサイトから評価版をダウンロードしましょう。

PreEmptive Solutions

さくっとインストールとユーザー登録を済ませてしまってください。GUIアプリを起動するとユーザー登録を求められるかと思います。

終わりましたか?終わりましたね?

さて、それではプロジェクトの .csproj ファイルを書き換えましょう。
無償版の解説で、.csproj ファイルに dotfuscatorCLI.exe のパスを書きました。これを先程インストールした Professional Edition (dotfuscator.exe) のパスに変更してください。
私の環境では C:\Program Files (x86)\PreEmptive Solutions\Dotfuscator Professional Edition Evaluation 4.33.0\dotfuscator.exe でした。

f:id:kmz_kappa:20171207213449p:plain

次に、スタートメニューなどから Dotfuscator のGUIツール(Dotfuscator Professional Edition)を起動してください。

そして File → Open Project... から、Visual Studio のプロジェクトの中にある DotfuscatorConfig.xml を開いてください。

f:id:kmz_kappa:20171201232653p:plain

さて、ここから先の設定はお好みです。このツールから、どのような難読化処理を加えるかを設定します。

今回は、例として次の3つの処理を有効(No)に設定します。

  • Control Flow(処理の内容をぐちゃぐちゃにする)
  • Renaming(フィールドやメソッドの名前を変更する)
  • String Encryption(文字列リテラルを暗号化する)

f:id:kmz_kappa:20171201233233p:plain

String Encryption を有効にするには、Setting タブで値を有効(No)に設定した後、String Encryption タブで対象のアセンブリ・クラス・メソッドなどにチェックを付ける必要があります。

f:id:kmz_kappa:20171201233618p:plain

ここまで設定をしたら、 File → Save Project でプロジェクトファイルを保存してください。
次に Visual Studio で再度プロジェクトを行います。

すると……

f:id:kmz_kappa:20171207214943p:plain

中身がぐちゃぐちゃです。
文字化けしているように見える箇所は、元々 string のリテラルだった場所です。

ここまで複雑に難読化をされたら解読をしようとは思いませんね。 (世の中には 難読化されたコードを復元するツールが存在すると聞いたこともありますが…)

注意

今回は例として3種類の難読化を行いましたが、実際にはこれを行うとアプリが正常に動作しない場合があります。
特に XAML に Binding を書いた場合や文字列を使用したリフレクションを行う場合などに Dotfuscator が対応できないことがあります。
この場合は問題となる箇所を難読化の対象から除外する必要があります。
(ツールを使うだけだから簡単だと思っていると、意外と上手く行かず痛い目を見ます…)

難読化のすゝめ [.NET Standardの場合](次回へ)

ここまでXamarinプロジェクトをPCLで作成した場合のの難読化方法を説明しましたが、Visual Studio 2017 15.5 がリリースされたことによりPCLという選択肢が無くなってしまいました。
そこで今後主流となる(であろう).NET Standard を使用したプロジェクトでの難読化方法を説明…したいところですが、長くなってしまったので次回に。.NET Standard 編は近いうちに書きます。

おわりに

そんな感じで Xamarin アプリも難読化しましょう、というお話でした。(チュートリアル記事になってしまいましたが…)
少なくとも何も対策をしないと処理が丸見えになってしまうことが分かったかと思います。

Visual Studio 付属の無償版では機能が弱いですし、個人で使うことはあまり無いかもしれませんが(買うとそれなりのお値段ですし)特に Xamarin アプリの場合は Google PlayApp Store にで配布をすることが多いため、難読化の需要は高いかと思います。
(既存のアプリを改造して不正な処理を埋め込んで偽アプリとして配布したり…という話も聞きます。)
クセはありますが強力なツールなので、無償版の範囲でも使えるところはしっかり使って身を守っていきましょう。

明日は

明日は @crocus7724 さんです。よろしくお願いします。