ollydbg で次の数行のアセンブリを見つけました。
MOV ECX,DWORD PTR DS:[xxxxxxxx] ; xxxxxxxx is an address
MOV EDX,DWORD PTR DS:[ECX]
MOV EAX,DWORD PTR DS:[EDX+116]
CALL EAX
誰かステップスルーして、ここで何が起きているのか教えてくれませんか?
これは、構造体に格納された関数ポインターの呼び出しです。
この最初の行は、 address に格納されているポインターを取得しますDS:xxxxxxxx。角括弧は、Cと同様にアドレスの逆参照*を示します。メモリからの値がポインタとして使用されようとしています。それはレジスターに入れられecxます。
MOV ECX,DWORD PTR DS:[xxxxxxxx] ; xxxxxxxx is an address
2 行目は、上記で取得したポインターを逆参照します。その値 fromecxは、逆参照されるアドレスとして使用されるようになりました。メモリ内で見つかった値は別のポインターです。この 2 番目のポインターは、edxレジスターに配置されます。
MOV EDX,DWORD PTR DS:[ECX]
3 行目もメモリを逆参照します。今回は、上記で取得したポインタから 0x116 バイトずれたアドレスにアクセスが発生します。これは 4 で割り切れないため、この関数ポインターは C++ vtable からのものではないようです。メモリから取得した値は、今度はレジスタに格納されますeax。
MOV EAX,DWORD PTR DS:[EDX+116]
最後に、 が指す関数eaxが実行されます。これは、関数ポインタを介して関数を呼び出すだけです。関数は引数を取りませんが、回答の改訂について質問がありPUSHます。このスニペットに先行する指示はありますか? それらは関数の引数になります。クエスチョン マークは、この関数が値を返す可能性があることを示しています。
CALL EAX
全体として、コード スニペットは、プラグイン ライブラリから OllyDbg への拡張関数の呼び出しのように見えます。OllyDbg ABIstructは、いくつかの関数ポインターを含むさまざまな を指定します。関数ポインターの配列もありますが、保持されたポインターに到達するための二重の間接参照 (偶数倍でアライメントされていないオフセットも) があるため、これは関数ポインターの配列または C++ ではなく、そうでedxはないと思います。structクラスの vtable。
つまり、関数ポインタを含むxxxxxxxxへのポインタへのポインタです。struct
OllyDbg ソース ファイル PlugIn.h には、いくつかの候補struct定義があります。次に例を示します。
typedef struct t_sorted { // Descriptor of sorted table
char name[MAX_PATH]; // Name of table, as appears in error
int n; // Actual number of entries
int nmax; // Maximal number of entries
int selected; // Index of selected entry or -1
ulong seladdr; // Base address of selected entry
int itemsize; // Size of single entry
ulong version; // Unique version of table
void *data; // Entries, sorted by address
SORTFUNC *sortfunc; // Function which sorts data or NULL
DESTFUNC *destfunc; // Destructor function or NULL
int sort; // Sorting criterium (column)
int sorted; // Whether indexes are sorted
int *index; // Indexes, sorted by criterium
int suppresserr; // Suppress multiple overflow errors
} t_sorted;
これらの例は許可されており、asm スニペットは関数ポインターのポインターをNULLチェックしません。NULLしたがって、DRAWFUNCfromt_tableまたはSPECFUNCof である必要がありt_dumpます。
ヘッダー ファイルを含む小さなプロジェクトを作成し、 と を使用printf()しoffsetof()て、それらのいずれかが 0x116 のオフセットにあるかどうかを判断できます。
そうでなければ、OllyDbg の内部はこのスタイルで書かれていると思います。そのため、OllyDbg 内でさまざまな目的に使用されるプライベートstructな定義 (Plugin.h ファイルで公開されていない) が存在する可能性があります。
付け加えておきたいのですが、OllyDbg のソースが利用できないのは残念だと思います。そこに含まれる静的にリンクされた逆アセンブラは、ある種の ?GPL ライセンスの下にあるという印象を受けましたが、OllyDbg のソースを入手することができませんでした。
アドレス xxxxxxx から 32 ビットの数値を取得し、ECX レジスタに入れます。次に、この値をアドレスとして使用し、値を読み取って EDX レジスタに入れ、最後にこの数値に 116 を加えて、そのアドレスの値を EAX に読み取ります。次に、現在 EAX に保持されているアドレスでコードの実行を開始します。そのコードが戻りオペコードに遭遇すると、call 命令の後に実行が続行されます。
これはかなり基本的な組み立てです。あなたがデバッガーでやっているのと、割り当ての期限がいつなのか不思議に思います;-)
コンパイラが MSVC であるというコメントを考えると、それが仮想メソッド呼び出しであると 99% 確信しています。
MOV ECX,DWORD PTR DS:[xxxxxxxx]
クラス インスタンスへのポインタは、グローバル変数から ECX にロードされます。(注: デフォルトの __thiscall 呼び出し規約では、ECX を使用してインスタンス ポインター (別名thisポインター) を渡します)。
MOV EDX,DWORD PTR DS:[ECX]
vftable (仮想関数テーブル) ポインターは通常、クラス レイアウトの最初の項目です。ここで、ポインタが EDX にロードされます。
MOV EAX,DWORD PTR DS:[EDX+116]
テーブルのオフセット 116 (0x74) にあるメソッド ポインターが EAX に読み込まれます。各ポインターは 4 バイトであるため、これはクラスの 30 番目の仮想メソッド (116/4 + 1) です。
CALL EAX
メソッドが呼び出されます。
元の C++ では、次のようになります。
g_pObject1->method30();
仮想メソッドを含む MSVC の C++ クラスの実装について詳しくは、こちらの記事を参照してください。
私が ASM (1997) をやってからしばらく経ちましたが、それでも私は i386 ASM しかやっていませんでした。
残念ながら、これらの 4 行のコードではあまりわかりません。ほとんどの場合、CPU レジスタにデータをロードして関数を呼び出すだけです。
具体的には、データまたはポインターがそのアドレスから CX レジスターにロードされているようです。次に、その値が CX から DX にコピーされます。したがって、DX にある CX のポインターの値があります。次に、DX の値に 116 のオフセットを加えた値が AX レジスタ (アキュムレータ?) にコピーされます。
次に、AX にコピーされたそのアドレスにある関数が実行されます。