わかったこと - アキタラヤメル[VB Decompiler Liteの出力分析]

まえがきとかを見たい人はトップページへどうぞ。

体系的に説明しているわけではないので、調べたいことがある場合はページ内検索を推奨します。

コードを見ながら想像して書いている部分が多いため、誤りが含まれる可能性がかなりあります。

プロシージャ名が変わることがある

条件はよくわかりません。イベント類はそのままになる傾向が強いみたいですね。

VBのコードほぼそのままの部分

わかりやすくていいね!

スタック

低レベル言語は、多くの場合、スタック(Stack)を使っている。

何かを計算するとき、たとえば次のように行う。

  1. 値1をスタックに積む(Push)
  2. 値2をスタックに積む(Push)
  3. 値1と値2をスタックから取り出して(Pop)計算して結果をスタックに積む(Push)

VBのPコードもこの原則に従っている。

例を挙げよう。

  loc_40551A: ILdRf var_88
  loc_40551D: LitStr vbNullString
  loc_405520: EqStr
  1. var_88をスタックに積む(Push)
  2. vbNullStringをスタックに積む(Push)
  3. var_88とvbNullStringをスタックから取り出して(Pop)等しいかどうか判定して結果をスタックに積む(Push)

これで、「var_88 = vbNullString」という計算ができたわけだ(紛らわしいが、代入の = ではない。)。

各命令については追々説明しよう。

UI1Byte型。
I2_ByteInteger型。0~255の範囲。
I2Integer型。
I4Long型。
R4Single型。
R8Double型。
StrString型。
VarVariant型。
Var(型名)内部処理(型名)型のVariant型。

命令

リテラル(Lit系)

値をスタックに積む。

Lit(型名)(型名)型のリテラル。値を指定し、スタックに積む。
LitVar(型名)(型名)型を内部形式とするVariant型のリテラル。変数名と値を指定し、変数に値を代入したうえで、スタックに積む。
LitVar_MissingVariant型のMissing値。変数名を指定し、変数にMissing値を代入したうえで、スタックに積む。省略された引数に使う。

分岐(Branch系)

基本的に、右側に書かれた番地に飛ぶ。

BranchFスタックに積んである条件が 満たされない とき、右に書かれた番地に飛ぶ。If文でEnd Ifまで飛ぶときに。
BranchTスタックに積んである条件が 満たされる とき、右に書かれた番地に飛ぶ。Select Case文で使う?
Branch無条件分岐。GoToと意味は同じ。Else直前からEnd If直後に飛ぶときなどに。

二項演算

スタックに積まれた2つの値を取り出して演算し、スタックに積む。

Eq(型名)比較。A = B
Lt(型名)比較。A < B
Gt(型名)比較。A > B
Le(型名)比較。A <= B
Ge(型名)比較。A >= B
Add(型名)加算。A + B
Sub(型名)減算。A - B
Mul(型名)乗算。A * B
Div(型名)除算。A / B

単項演算

スタックに積まれた1つの値を取り出して演算し、スタックに積む。

C(型名1)(型名2)型変換(*1)。(型名2)型から(型名1)型への変換。順番に注意。
FnInt(型名)Int関数。切捨て。
  1. コンパイル時に特定可能な型について、型変換が必要な場合は、ソース中に明示していなくても型変換のコードが生成される。
    Variant型を使っている場合は、コンパイル時に内部の型まで特定できないため、型変換のコードは生成されず、Variant型用の演算が用いられる。

その他の命令

FStVarCopyObj「FStVarCopyObj 変数名」の形式。関数の戻り値の設定に使われていた。
FLdRfVar「FLdRfVar 変数名」の形式。関数の戻り値の設定に使われていた。なぜ1つの設定に2つの命令が?
ImpAdCallFP(型名)「ImpAdCallFP型名 関数名~」の形式。関数を呼び出す。関数名の前にpushをつけると戻り値を直接スタックに積み、つけないとスタック上の参照に代入。
FLdPr「FLdPr オブジェクト名」の形式。オブジェクトの参照をスタックに積む。
MemSt(型名)「MemSt型名 変数名」の形式。スタックから値とオブジェクトを取り出し、「オブジェクト.変数名 = 値」となる。
ImpAdLd(型名)モジュールのグローバル変数をスタックに積む?
ImpAdSt(型名)モジュールのグローバル変数にスタックから代入?
VCallAd引数に書かれたコントロールのアドレスをスタックに積む。
FStAdFunc「FStAdFunc 変数名」の形式。スタックに積まれたアドレスのコントロールを変数に入れる。Funcなのにコントロール?コントロール以外のオブジェクトは?
Ary1LdPrスタックから配列と添字を取り出して配列の内容をスタックにつむ。
HardType型変換を行わずに演算を行う。Variant型関係。
ExitProcHresultプロシージャの終わり。Exit Subの意味もある。

命令に使われる単語

ImpAdグローバルスコープ。
Ld変数からスタックに積む。
Stスタックから変数に代入する。
FStスタックから一時変数に代入する。文字列やオブジェクトを関数に渡す際や、関数からの戻り値など。
Adアドレス。
Fn組み込み関数。後続の文字で何の関数かなんとなくわかる。
Popスタックにある値を明示的に取り出す。スタックから値を取り出してすぐ別の値を入れる場合に。
NoPopスタックにある値を読み出すが、値を消費しない。次取り出した時も同じ値が出てくる。

省略可能な引数

LitVar_Missingを使ってくそまじめにMissingや規定値を入れて引数をそろえている模様。