オープンソース、無料の .NET 難読化ツール。
この記事の執筆時点(2019年4月1日)では、最終更新日が2019年1月28日頃、バージョン2.2.18。
URL
なお、Obfuscar と Babel Obfuscator を混同しないようにしましょう。それぞれ別の難読化ツールです。
この記事は Obfuscar で DLL の難読化のテストをしながら書きました。そのときは問題ないと思っていたんです。
ところが、記事を書き終えてから Obfuscar で EXE の難読化をしたところ、EXE が起動すらしませんでした。
それで、いったんこの記事をゴミ箱に入れたんですが、この記事を探して来てくれる方もいますし、「結局ダメだったので使わなかった」という経験記事もそれなりの意味があると思ったので、この記事を復活させることにしました。
まぁ、Obfuscar を問題なく使ってる人も多いはずですので、「Obfuscar が使えなかった」という私の経験は、私個人の使い方や環境などに問題があった可能性を否定しません。
Obfuscar が使えないと分かったあと、Eazfuscator の試用版を試してみたんです。
昔使ってたときと変わらぬ使いやすさと安定感でした。もちろん難読化した DLL も EXE もちゃんと動きました。
そして今年リリースされた最新版では、homomorphic cryptosystem を利用してかなり優れた暗号化に成功したとのこと(記事)。
早速試してみたところ、以前のバージョンの Eazfuscator は de4dot によって難読化が解除されてしまってましたが、今回のバージョンでは解除されませんでした。
あまりお金を持ってない私にとって約5万円はちょっと痛い金額ですが、他の有料・無料の難読化ツールと比較して、それだけの価値があると判断しました。
で、結局、思い切って、Eazfuscator のライセンスを買っちゃいました。
・・・そんなこんなで、以下 Obfuscar の記事が続きます。
読む気なくなったかな?(^^;)
Obfuscar を選んだ理由
私が愛用している言語である VB.NET、つまりは .NET Framework の上に成り立ってる言語は、簡単に逆アセンブルされてしまうので対策したい。
以前、ある会社で働いてたときは、会社のお金で難読化ツールの Eazfuscator.NET を買ってもらって使ってました。開発者1人分のライセンスは今は399ドルです(2019年3月現在。後日、実際に買ってみたら税込みで49,715円でした)。Eazfuscator.NET の使い勝手はとても良かったけど、難読化したツールが正常に動かないことが(1度だけかな?)あったので、それ以来あまり使わなくなりました。
で、いまはそのとき働いてた会社とも縁が切れてるので、再び難読化ツールについて検討してみました。
個人(庶民)にとって Eazfuscator.NET の5万円近い金額は、立ち止まって考えさせられる額ですね(ノートパソコン買えちゃうじゃん!)。他の会社から出てる商用の難読化ツール(ん十万円~100万円前後)と比べれば安いとはいえ。。。無料の難読化ツールもありますしねぇ。で、他に使えるものが無いか調べて、いくつか試してみました。
私が求めた条件は・・・
- 無料、または1~2万円前後払えばずっと使えるもの
- 開発(アップデート)が止まってないこと(前回のアップデートが1年以上前のものは避ける)
- 難読化対象のアセンブリにハードコードされている文字列の暗号化に対応していること
- Visual Studio のビルドと連動して処理できること
- 別の新しいプロジェクトで利用するときになるべく(再設定などの)手間がかからないこと
・・・といったところです。
Obfuscar による難読化のサンプル
私が作成した DLL を Obfuscar で難読化して、難読化前と難読化後を ILSpy で見比べてみました(ILSpy は VB での表示ができないようなので下図は C# の表示になってます)。
難読化の前
難読化の後
クラス/メソッド/プロパティ/イベントなどの名前が暗号化されるのはもちろんですが、ハードコードされている文字列も暗号化されているのが分かりますね。
ちなみに、上図に暗号化されてない名前が残ってますが、これはアクセス修飾子が Public のものです。Public は DLL の外部から参照される名前なので、デフォルトでは暗号化しないようにしています。
Obfuscar による難読化のやり方(概要)
このやり方はちょっとだけ私流になっているので、お好みに応じてアレンジしてください。
まず、おおまかな流れを簡単に説明します。
準備と設定
準備するファイルは下記の3つです。
- Obfuscar.xml.template
Obfuscar の設定ファイルのテンプレート。 - Obfuscar.vbs
上記のテンプレートファイルを読み込んで、Obfuscar の設定ファイルを出力する VBScript。 - Obfuscar.bat
Visual Studio のビルド後イベントから呼び出すバッチファイル。上記の VBScript はこのバッチファイルから呼び出します。
上記の3ファイルを下記の場所に置きます(Windows 10 の場合)。
C:\Users\UserName\.nuget\packages
そして Visual Studio で[ビルド出力パス]を bin\ORG に変更します。
私は Debug ビルドも Release ビルドも bin に出力するようにしています。分けたい場合は、bin\Release\ORG のようにします。
最後に、プロジェクトのビルド後のイベントとして下記の1行(Obfuscar.bat を呼び出す命令)を記述します。
call “%USERPROFILE%\.nuget\packages\Obfuscar.bat” “$(ProjectDir)” $(TargetFileName)
処理の流れ
- Visual Studio でアセンブリをビルドする
- ビルドされたアセンブリがプロジェクトフォルダ直下の bin\ORG に出力される
- Obfuscar によるアセンブリの難読化処理が自動的に始まる
- 難読化されたアセンブリがプロジェクトフォルダ直下の bin に出力される
この場合、Visual Studio の[デバッグの開始(F5)]で起動するのは難読化前のアプリケーションとなります。
ではもう少し詳しく見ていきましょう。
Obfuscar による難読化のやり方(詳細)
ちょっとだけ私流になっているので、お好みに応じてアレンジしてください。
Obfuscar のインストール
まず Visual Studio の[ツール]>[NuGet パッケージ マネージャー]>[パッケージ マネージャー設定]を選択し、[既定のパッケージ管理形式]を「Packages.config」ではなく「PackageReference」に変更します。
「PackageReference」に変更できるのは Visual Studio 2017 以降のようです。
「Packages.config」のままだと、パッケージがソリューションごとにインストールされます。つまり、複数のソリューションにインストールする場合、それだけパッケージが重複してインストールされてしまいます。
管理形式を「PackageReference」に変更すると、複数のソリューションにインストールしたとしても、パッケージファイルのインストール場所が下記の場所に固定されます(つまり重複が無くなります)。
C:\Users\UserName\.nuget\packages
次に、Visual Studio の[ツール]>[NuGet パッケージ マネージャー]>[パッケージ マネージャー コンソール]を選択します。
パッケージマネージャーコンソールのプロンプト(PM>)のところで Install-Package Obfuscar と入力して Enter を押すと、Obfuscar がインストールされます。
Obfuscar.xml.template(Obfuscar の設定ファイルのテンプレート)を用意する
新規テキストファイルに下記の内容をコピペします。
<?xml version=”1.0″ encoding=”sjis” ?>
<Obfuscator>
<Var name=”InPath” value=”.\bin\ORG” />
<Var name=”OutPath” value=”.\bin” />
<Var name=”KeepPublicApi” value=”true” />
<Var name=”HidePrivateApi” value=”true” />
<Var name=”HideStrings” value=”true” />
<Module file=”$(InPath)\【アセンブリ名】” />
</Obfuscator>
ご自分の環境に合わせて上記の InPath と OutPath の値(黄色蛍光ペンのところ)を(プロジェクトフォルダに対する相対指定で)指定します。
各行の意味は次の通りです。
- InPath
Obfuscar に対する入力ファイルを置くフォルダ。つまりプロジェクトのビルド出力パス。 - OutPath
Obfuscar で難読化したアセンブリを出力するフォルダ。 - KeepPublicApi
Public の名前を難読化しない - HidePrivateApi
Private の名前を難読化する - HideStrings
ハードコードされたテキストを難読化する
設定ファイルのより詳しい情報についてはこちらをご覧ください。
ビルド出力パスと上記 XML の OutPath の値を同じにすると、アクセス違反のエラーが出てしまうと思います。
上記 XML の【アセンブリ名】のところは、後述の VBScript で自動的に対象アセンブリのファイル名に置換されます。
XML の修正が終わったら、C:\Users\UserName\.nuget\packages に Obfuscar.xml.template という名前で SJIS で保存してください。
Obfuscar.vbs(Obfuscar の設定ファイルを出力する VBScript)を用意する
新規テキストファイルに下記の内容をコピペし、C:\Users\UserName\.nuget\packages に Obfuscar.vbs という名前で SJIS で保存してください。
'=========================================================================================== ' このスクリプトと同じ場所にある Obfuscar.xml.template を読み込んで、 ' 【アセンブリ名】を対象アセンブリファイルの名前に置換し、 ' プロジェクトフォルダに Obfuscar.xml として出力する。 '=========================================================================================== If WScript.Arguments.Count <> 2 Then Call MsgBox("プロジェクトフォルダとアセンブリ名を指定してください。", 16, WScript.ScriptName) WScript.Quit(1) End if Dim ProjectFolder : ProjectFolder = WScript.Arguments(0) Dim AssemblyName : AssemblyName = WScript.Arguments(1) Dim oWShell : Set oWShell = WScript.CreateObject("WScript.Shell") Dim oFS : set oFS = createObject("Scripting.FileSystemObject") 'このスクリプトのあるフォルダにカレントを変更(バッチファイルでもやってるけど) Dim curFolder : curFolder = oFS.getParentFolderName(WScript.ScriptFullName) oWShell.CurrentDirectory = curFolder 'テンプレート xml(SJIS)を読み込む ' ちなみにだけど、VBScript で UTF-8 ファイルの読み書きをするには ADODB.Stream を使う必要があるんですよね。 Dim oInFile : Set oInFile = oFS.OpenTextFile(".\Obfuscar.xml.template") Dim templateText : templateText = oInFile.ReadAll '文字列置換 templateText = Replace(templateText,"【アセンブリ名】", AssemblyName) oInFile.Close 'プロジェクトフォルダにカレントを変更 oWShell.CurrentDirectory = ProjectFolder 'アセンブリ名を設定した xml をプロジェクトフォルダに出力する Dim oOutFile : Set oOutFile = oFS.CreateTextFile(".\Obfuscar.xml") oOutFile.WriteLine (templateText) oOutFile.Close
Obfuscar.bat(Visual Studio のビルド後イベントから呼び出す)を用意する
新規テキストファイルに下記の内容をコピペします。
@echo off REM =================================================================================== REM Visual Studio の「ビルド後イベントのコマンドライン」に下記を記述する。 REM call "%USERPROFILE%\.nuget\packages\Obfuscar.bat" "$(ProjectDir)" $(TargetFileName) REM =================================================================================== REM ★Obfuscar のバージョン(Obfuscar のインストール先フォルダに使用されているテキストに合わせる) set ObfuscarVer=2.2.18 REM %~1 を「"」で挟まないと引数が無いときにエラーになる。 REM %~1 の "~" は、全ての引用句(")を削除して %1 を展開するという意味。 if "%~1"=="" ( echo. echo プロジェクトフォルダを指定してください。 echo. exit /b 1 ) if "%~2"=="" ( echo. echo アセンブリ名を指定してください。 echo. exit /b 1 ) REM "=" の後にスペースを入れるとエラーになる(スペースまで一緒に代入されてしまう)。 REM バッチファイルでは演算子の前後にスペースを入れないよう統一したほうが安全かもしれない。 set ProjectFolder=%~1 set AssemblyName=%~2 REM このバッチファイルが存在するフォルダをカレントにする。 REM cd で /d のオプションを指定すると、ドライブの変更も同時に行うようになる(未指定だとドライブの変更はできない)。 REM %~dp0 は %~d0 (ドライブ名)と %~p0 (パス名)を組み合わせた記述方法。 cd /d %~dp0 REM Obfuscar.xml.template をスクリプトに読み込んで指定されたアセンブリファイル名を埋め込み、 REM 指定されたプロジェクトフォルダに Obfuscar.xml として出力する。 REM バッチファイルでやると面倒&可読性が低くなるので、スクリプトで処理。 .\Obfuscar.vbs "%ProjectFolder%" %AssemblyName% REM プロジェクトフォルダにカレントを変更。 cd /d "%ProjectFolder%" REM NuGet パッケージ管理形式「PackageReference」で Obfuscar をインストール済みである前提。 REM ちなみに、ビルド後イベントからパス無しで Obfuscar.Console.exe を実行できることもあるが実行できないこともある。 REM その法則が不明で動作が不安定なので、常にフルパスで指定することにした。 REM Obfuscar を実行。スクリプトから実行すると処理の経過が Visual Studio の出力ウィンドウに表示されないので REM バッチファイルから実行している。 "%USERPROFILE%\.nuget\packages\obfuscar\%ObfuscarVer%\tools\Obfuscar.Console.exe" .\Obfuscar.xml
必要に応じてバッチファイルの8行目(Obfuscar のバージョンのところ)を修正してください。
修正が終わったら、C:\Users\UserName\.nuget\packages に Obfuscar.bat という名前で SJIS で保存してください。
[ビルド出力パス]を変更する
Visual Studio の[プロジェクト]>[ProjectNameのプロパティ]を選択し、左のペインで[コンパイル]を選択します。
[構成]のところをお好みに応じて変更してください(Release のときだけ難読化するか、Release と Debug の両方で難読化するか)。私は Release でも Debug でも難読化したいので、「すべての構成」を選択しました。
次に、[ビルド出力パス]を bin\ORG\ に変更します。
[ビルド後イベントのコマンドライン]にコマンドを記述する
引き続き、[コンパイル]の画面で、画面右下のほうにある[ビルド イベント]をクリックします。
[ビルド後イベントのコマンドライン]に下記を記述します。
call “%USERPROFILE%\.nuget\packages\Obfuscar.bat” “$(ProjectDir)” $(TargetFileName)
上記コマンドの意味は、「C:\Users\UserName\.nuget\packages\Obfuscar.bat を呼び出す。その際、対象アセンブリのプロジェクトフォルダのパス、そしてアセンブリファイル名を引数として与える」ということになります。
以上で準備・設定は終わりです!
ビルドして難読化を実行する
まず、Visual Studio で Ctrl+Alt+O を押して、[出力]ウィンドウを表示しておきましょう(進行状況の確認のため)。
そして、いつものようにビルドを実行します。
下図のように、ビルドと難読化の進行状況が表示されて正常終了すればオーケーです!
ILSpy などの逆アセンブルツールを使用して、結果を確認してみましょう。