ディスアセンブルされたコードを読む場合は、下から読むことをお勧めします。

(xenowire が過去に作成した MSIL Analyzer (以下アナライザ) や パンダナス でも、下から読むこと+各OpCodeによるスタックの変化予測で動きを捉えていました。)

ilastack.png


また、インデントによりコードを視覚的に把握できるようになります。

このサイトの IL に関する各ページのインデントも、スタックの動きを視覚的に把握するため意図的に挿入されています。



次のコードは、ある関数の一部をディスアセンブルしたものです。

すべてを展開すべてを収束
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ldloc.0
ldc.i4.0
ldloc.0
callvirt   instance int32 string::get_Length()
 
ldc.i4.1
sub
callvirt   instance string string::Substring(int32, int32)
stloc.0
ldsfld     class [mscorlib]System.Collections.Hashtable Visionary::a
callvirt   instance class [mscorlib]System.Collections.ICollection class [mscorlib]System.Collections.Hashtable::get_Values()
newobj     instance void class [mscorlib]System.Collections.ArrayList::.ctor(class [mscorlib]System.Collections.ICollection)
ldloc.0
ldnull
newobj     instance void class Pandects::.ctor(string, class [System.Drawing]System.Drawing.Bitmap)
callvirt   instance int32 class [mscorlib]System.Collections.ArrayList::IndexOf(object)
stloc.1
ldloc.1
ldc.i4.0
blt.s      _fin
 
ldsfld     class [mscorlib]System.Collections.Hashtable Visionary::a
callvirt   instance class [mscorlib]System.Collections.ICollection class [mscorlib]System.Collections.Hashtable::get_Values()
newobj     instance void class [mscorlib]System.Collections.ArrayList::.ctor(class [mscorlib]System.Collections.ICollection)
ldloc.1
callvirt   instance object class [mscorlib]System.Collections.ArrayList::get_Item(int32)
castclass  class Pandects
callvirt   instance class [System.Drawing]System.Drawing.Bitmap class Pandects::a()
 
ret
 
_fin:  ldsfld     class [System.Drawing]System.Drawing.Bitmap Visionary::f
ret

引数・変数のpopと、メンバ関数を呼び出すためのpopが混ざり、非常に見づらいコードになっています。
これを下から読んでいきます。


アナライザ用のスタック定義ファイルの内容です。
(opcodeのスタックに対する挙動を調べると作成できます。)

ilaops.png


スタック定義ファイルを元に、アナライザがスタックをシミュレーションした結果が下のマトリクスです。

各行の値は、それより上のコードに求めるデータ数を示しています。
従って、0が三つ並んでいる行は、期待されたデータが全て揃った ―― 処理単位の完結 となります。

 0  0  0 // ldloc
 1  0  0 // ldc
 1  1  0 // ldloc
 1  1  1 // callvirt
 1  1  1 // ldc
 1  1  2 // sub
 1  2  0 // callvirt
 1  0  0 // stloc
 
 0  0  0 // ldsfld
 0  1  0 // callvirt
 0  1  0 // newobj
 1  0  0 // ldloc
 1  0  1 // ldnull
 1  0  2 // newobj
 1  1  0 // callvirt
 1  0  0 // stloc
 
 0  0  0 // ldloc
 1  0  0 // ldc 
 2  0  0 // blt
 
 0  0  0 // ldsfld
 0  0  1 // callvirt
 0  0  1 // newobj
 0  1  0 // ldloc
 0  1  1 // callvirt
 0  1  0 // castclass
 1  0  0 // callvirt
 1  0  0 // ret
 
 0  0  0 // ldsfld
 1  0  0 // ret



このマトリクスを元にしたアナライザによる整形結果です。

すべてを展開すべてを収束
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
    ldloc.0
        ldc.i4.0
            ldloc.0
            callvirt   instance int32 string::get_Length()
            ldc.i4.1
        sub
    callvirt   instance string string::Substring(int32, int32)
stloc.0
 
        ldsfld     class [mscorlib]System.Collections.Hashtable Visionary::a
        callvirt   instance class [mscorlib]System.Collections.ICollection class [mscorlib]System.Collections.Hashtable::get_Values()
    newobj     instance void class [mscorlib]System.Collections.ArrayList::.ctor(class [mscorlib]System.Collections.ICollection)
            ldloc.0
            ldnull
        newobj     instance void class Pandects::.ctor(string, class [System.Drawing]System.Drawing.Bitmap)
    callvirt   instance int32 class [mscorlib]System.Collections.ArrayList::IndexOf(object)
stloc.1
 
    ldloc.1
    ldc.i4.0
blt.s      _fin
 
            ldsfld     class [mscorlib]System.Collections.Hashtable Visionary::a
            callvirt   instance class [mscorlib]System.Collections.ICollection class [mscorlib]System.Collections.Hashtable::get_Values()
        newobj     instance void class [mscorlib]System.Collections.ArrayList::.ctor(class [mscorlib]System.Collections.ICollection)
            ldloc.1
        callvirt   instance object class [mscorlib]System.Collections.ArrayList::get_Item(int32)
    castclass  class Pandects
    callvirt   instance class [System.Drawing]System.Drawing.Bitmap class Pandects::a()
ret
 
_fin:
    ldsfld     class [System.Drawing]System.Drawing.Bitmap Visionary::f
ret

引数/変数のpopと、メンバ関数を呼び出すためのpopが区別されてインデントされています。


メンバ関数を呼び出すためのpop と callvirt のインデントの深さが同じであることは、
C++ でメンバ関数を呼び出すときの . あるいは -> 、静的メンバであれば :: の意味を表すものとなっています。

 ldloc.0
 callvirt   instance int32 string::get_Length()



引数/変数は一段階深くインデントされており、どの opcode のための引数/変数であるのかが分かるようになっています。

 	ldloc.1
 	ldc.i4.0
 blt.s      _fin



castclass は、pop して push するというスタックの積載量には特に変化を与えない処理であるため、インデントの深さは直後の行と変わりません。

 castclass  class Pandects
 callvirt   instance class [System.Drawing]System.Drawing.Bitmap class Pandects::a()



callvirt は 引数の個数分 pop しますが、返り値の無いものは push 無しであるため、
callvirt 自身のインデントの深さは直後の行と変わりません。
返り値のあるものは引数と同じく、直後の行より一段階深くインデントされています。


sub は 2回 pop しますが必ず 1回 push するため、直後の行に対して一段階深くインデントされています。

 		sub
 	callvirt   instance string string::Substring(int32, int32)
 stloc.0



最後にアナライザによる C# へのディスコンパイル結果です。

ilacode.png
すべてを展開すべてを収束
  1
  2
  3
  4
  5
  6
  7
 
 
 
 
 
 
 
V_0 = V_0.Substring( 0, V_0.get_Length() - 1 );
V_1 = new System.Collections.ArrayList( Visionary.a.get_Values() ).IndexOf( new Pandects( V_0, null ) );
 
if( V_1 < 0 )
    return Visionary.f;
 
return ( ( class Pandects ) new System.Collections.ArrayList( Visionary.a.get_Values() ).get_Item( V_1 ) ).a();

以上から、

  1. V_0 の 最後の 1文字を除去し V_0 に格納
  2. V_0 を元に Pandects オブジェクトを生成し、 Visionary.a (ArrayList) の中のインデックス番号を V_1 に格納
  3. Visionary.a に探している値が存在しない場合は Visionary.f を return
  4. Visionary.a に探している値が存在した場合はその値を Pandects にキャストし、メンバ関数a()を呼び出してその返り値を return

していることが分かります。


なお、下から読むこの方法は、スタックシミュレーションマトリクスに 0 が並ぶことを利用しているため、
pushされる順番がゴチャゴチャになっているコードに対しても有効です。


トップ 編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード 新規 一覧 単語検索 最終更新 ヘルプ 最終更新のRSS xenowire
Last-modified: 2018-01-25 (木) 15:08:49