アセンブラ C# で作られたプログラムをデコンパイル デコンパイルとは 大雑把に言うと コンパイルされたプログラム (exe dll 等 ) から 元のコードを復元する行為の事で有る 特に C# や Java の様に中間言語に翻訳された状態でコンパイルされる様な言語の場合は元のコードに可成り忠実に復元される 本稿では C# に限らず.NET に関わる言語全般のデコンパイルをする方法を紹介する デコンパイルに依るリバースエンジニアリングが禁止されて居るアプリケーションには使用しない様にしなければ成らない 中間言語 (IL) C# や VB.NET 等では コンパイル時に直接ネイティブコードにするのではなく 一旦中間言語 ( 以下 IL) と謂う形にし 実行時にネイティブコードを生成する JIT コンパイル方式を採用して居る 事前コンパイルと謂う手段も用意されて居るが基本的には IL を出力する 処で ネイティブコンパイルをしないと謂う事は JIT コンパイラが存在する環境でさえ有れば 実行する環境を選ばないと謂うメリットが有る 今迄は Windows 環境で実行される事が殆どだったので余り気にするする機会は無かったが 最近の Docker 隆盛も相俟って Linux 環境で実行する機会も増えて居り 遂に JIT コンパイル形式の強みが本当の意味で生かされる事に成りそうで有る IL はバイトコードの為 其の儘では人間が読む事が出来ないが Visual Studio に付属して居る ildasm.exe を使う事でアセンブリ言語を高級にした様な形式で確認する事が出来る 1
逆アセンブル :IL( バイトコード ) から人が読める形の IL へ 先ずは,IL( バイトコード ) から人が読める形に出力する 逆アセンブルに付いて紹介する Ildasm.exe(IL 逆アセンブラー ) 先程挙げた Visual Studio に付属して居る逆アセンブラーで有る dll や exe に対して使用する事で IL をテキストとして出力する事が出来る ildasm.exe は SDK 内に存在して居る C: Program Files Microsoft SDKs Windows v7.0a bin C: Program Files Microsoft SDKs Windows v7.0a x64 bin C: Program Files (x86) Microsoft SDKs Windows v7.0a bin C: Program Files (x86) Microsoft SDKs Windows v7.0a x64 bin 試しに起動して観ると下図の様なウィンドウが表示される 2
今回は GUI で操作をするが コマンドラインからも実行出来るので 一括で処理し度い場合等は其方を利用すると良い 其れでは試しに適当なコードを書いてコンパイルした物を ildasm.exe に通して観るとする var word = args[0]; return string.format("hello 0", word); 此れを作成して Debug ビルドする 生成された dll を先程のウィンドウで開いて観ると 以下の様に展開される 上から順に名前空間 クラス名 クラス情報 (class) コンストラクタ情報 (ctor) HelloHogehoge メソッドの情報で有る 夫々をダブルクリックする事で中身を見る事が出来る (DUMP も出来る ) クラス名の配下に有る情報に付いて観て行く 先ずは.class.class public auto ansi beforefieldinit Hogehogee.SampleClass extends [mscorlib]system.object // end of class Hogehogee.SampleClass 3
普段余り気にしないが 仕様通り System.Object が暗黙的に継承されて居る事が見て取れる 次に.ctor.method public hidebysig specialname rtspecialname instance void.ctor() cil managed // コードサイズ 7 (0x7).maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]system.object::.ctor() IL_0006: ret // end of method SampleClass::.ctor 此方も暗黙的に基底クラス (System.Object) のデフォルトコンストラクタが呼ばれて居る事が何んとなく解る C# の仕様で スーパークラスのデフォルトコンストラクタが暗黙的に呼ばれると謂う仕様通りで有る 最後に HelloHogehoge メソッド.method public hidebysig instance string HelloHogehoge(string[] args) cil managed // コードサイズ 21 (0x15).maxstack 2.locals init ([0] string word, [1] string CS$1$0000) IL_0000: nop IL_0001: ldarg.1 IL_0002: ldc.i4.0 IL_0003: ldelem.ref IL_0004: stloc.0 IL_0005: ldstr "Hello 0" IL_000a: ldloc.0 IL_000b: call string [mscorlib]system.string::format(string, object) IL_0010: stloc.1 IL_0011: br.s IL_0013 IL_0013: ldloc.1 IL_0014: ret // end of method SampleClass::HelloHogehoge 何んとなく "Hello 0" 文字列を使って string.format( ) を呼び出して居る様に見える 更に 何んとなくだが word と謂う変数らしい物が見える ( 今回は Debug ビルドだったのでメソッド内で宣言した変数名の情報が残って居る 試しに Release ビルドにして結果の違いを確認して欲しい ) 何んとなく解るが C# のコードからは可成り程遠い物が出て来る IL の仕様を理解しないと たった此れ丈でも割と四苦八苦しそうで有る 実際のプロダクトのコードを全部 IL で読もうとすれば可成り厳しい 併し 後述する様に C# のコード其の物を復元するデコンパイラも開発されて居るので コードからは読み取れない様な謎の動きを解析する場合以外は此方を使う事は余り無いかも知れない 4
LINQPad 少し趣向は違うが LINQPad でも人が読める形式の IL を確認する事が出来る 此方は dll や exe をデコンパイルするのではなく コードを書いて 其れに依って生成される IL を確認する事が出来る Item クラスに Indexer が存在出来ない理由が IL を眺めると解る (MSDN にも書かれて居る ) デコンパイル :IL( マシン語 ) から元のコードを復元 扨て 此処からが本記事の本題で有る 前述の様に 人に読める形で IL を出力して下れるのも便利だが 矢張り通常の C# のコードとして読めた方が早いし嬉しい 其処で IL を解釈して C# のコードを出力して下れるデコンパイラの出番で有る.NET Reflector に始まり dotpeek, ILSpy 等々 大体の場合は此等を使う事で可成り読み易いコードが出力される 変数名の情報 ( 誤って Debug ビルドで提供されて居ない限り ) は残って居ないので str1, str2 等と成るが 其れは仕方ない 以下に幾つかのデコンパイラを記載するので 色々試してみて頂ければ良い.NET Reflector 以前は此方が無償提供されて居たので此れ一択だった 豊富なプラグインが有り 今でも愛好家が多い様で有る 2011 年の 3 月に有償化され 気軽には使えなく成ったが 其の際幾つかの競合プロダクトが無償化 公開された 以下に其等競合プロダクトを挙げて行く dotpeek Free.NET decompiler :: JetBrains dotpeekjetbrains 社製のデコンパイラ ReSharper に付属して居て 定義へ移動する際に dotpeek を通してデコンパイルした物を表示したりする UI が Visual Studio ライクで可成り馴染み易いのではないだろうか ILSpy MIT ライセンスで公開されて居るオープンソースで有る スタンドアロンで利用出来る為 普段は専ら此方を利用して居る 試しに最初に作った dll を此れを使ってデコンパイルした結果を載せて置く // Debug ビルド版 using System; 5
string word = args[0]; return string.format("hello 0", word); // Release ビルド版 (pdb ファイル有 ) using System; string word = args[0]; return string.format("hello 0", word); // Release ビルド版 (pdb ファイル無 ) using System; string arg = args[0]; return string.format("hello 0", arg); Release ビルドで pdb ファイルが削除された物以外では暗黙的に付与される using System; が明示された丈で最初に書いた物と粗同等の物が出力された 此処迄見事に戻されると センシティブな情報をコード内に埋め込む事は非常に怖いと謂う処も容易に理解出来るかと思う プログラムを提供する際には気を付けて欲しい 其の他 Telerik JustDecompiler free.net decompiler CodeReflect Free.NET Decompiler 6
纏め オープンソースなプロダクトも増えて来て居るので 活用の機会は多少減って居るが 未だ未だ.NET でクローズドなプロダクトを扱う事は多々有る 寧ろ其の方が多いと想う 其の際には 規約を良く読み 問題が無ければ大いに使用すれば良い 勉強にも成るので 今回は取り上げて居ないが VB.NET の生成物から C# C# の生成物から VB.NET F# の生成物から C# と謂う事も出来るので 試して欲しい F# を C# や VB.NET に変換すると F# が何の様にしてパターンマッチを実現して居るのかが解り面白い 7