次の x86 アセンブラー命令は何をしますか?
call dword ptr ds:[00923030h]
これは間接的な呼び出しだと思いますが、呼び出しのアドレスを正確にどのように計算するのでしょうか?
次の x86 アセンブラー命令は何をしますか?
call dword ptr ds:[00923030h]
これは間接的な呼び出しだと思いますが、呼び出しのアドレスを正確にどのように計算するのでしょうか?
[編集]更新
のようなメモリオペランドが表示される場合は常にds:0x00923030
、それがセグメント相対アドレッシングモードです。ds
tpで参照される実際のアドレスは、セグメントレジスタのベースアドレスを基準にした線形アドレス0x00923030にあります。
x86アーキテクチャのメモリセグメンテーションはやや混乱を招きます。ウィキペディアはそれをうまく説明していると思います。
基本的に、x86にはいくつかの特別なセグメントレジスタがありますcs
:(コードセグメント)、(ds
データセグメント) 、、、、、および(スタックセグメント)。すべてのメモリアクセスは、特定のセグメントレジスタに関連付けられています。通常、セグメントレジスタは指定せず、メモリへのアクセス方法に応じて、デフォルトのセグメントレジスタが使用されます。たとえば、レジスタは命令の読み取りに使用されます。es
fs
gs
ss
cs
各セグメントレジスタには、特定のベースアドレスと制限があります。ベースアドレスは線形アドレス0x00000000が対応する物理アドレスを決定し、制限はそのセグメントの最大許容線形アドレスを決定します。たとえば、ベースアドレスが0x00040000で、制限が0x0000FFFFの場合、有効な線形アドレスは0x00000000〜0x0000FFFFのみであり、対応する物理アドレスは0x00040000〜0x0004FFFFになります。
したがって、呼び出されるサブルーチンが存在する物理アドレスは、セグメントレジスタに格納されているベースアドレスds
に0x00923030を加えたもので与えられます。しかし、まだ完了していません。命令には単語が含まptr
れています。これにより、間接レベルが追加されるため、サブルーチンの実際のターゲットは、場所に格納さds:0x00923030
れているアドレスになります。
AT&T構文(GNUアセンブラによって受け入れられる)では、命令は次のように記述されます。
lcall *ds:0x00923030
指示の内容の詳細については、80386リファレンスマニュアルを参照してください。命令のこの特定の変形は"CALL r/m16"
(near register indirect / memory indirectを呼び出す)です。
この特定のオペコードは、論理アドレスが指す場所に存在する仮想アドレス (ここでは 32 ビット) を介して呼び出しを行いますds:[00923030h]
。
論理アドレスは、次の 2 つのコンポーネントで構成されます。
上記の計算は、物理アドレスではなく線形アドレスを示していることに注意してください(インテルのマニュアルボリューム 3a、図 2.2 を参照)。これは、4KB ページングの標準メカニズムを介して変換されます。つまり、アドレスは、ページ ディレクトリへのインデックス、ページで構成されます。テーブルと選択したページへのオフセット。ただし、すべてのメイン ストリーム オペレーティング システムがいわゆるフラット メモリ モデルを使用していることに注意してください。つまり、すべてのセグメント セレクターがアドレス 0x00000000 を指し、制限が 0xFFFFFFFF に設定されています。これが、すべてのセグメント間でキャストし、最終的にリードできる理由です。バッファオーバーフローの(簡単な)悪用。
あなたが与えたアセンブラー命令は、実行可能ファイルの Import Address Table (詳細については、この素晴らしい記事を参照) を介した呼び出しである可能性が非常に高いです。つまり、これが序数のサブルーチン呼び出しである可能性はほとんどありません。
このようなコードは、外部 dll からインポートされた関数の最終的な仮想アドレスをコンパイル時に一般的に知ることができないため (dll のリベースが原因で)、コンパイラによって生成されます。このような呼び出し構造を使用することにより、OS ローダーは論理アドレスによってアドレス ポインターに正しい仮想アドレスを挿入でき、コンパイラーは最終関数がどのアドレスを持っているかを気にする必要がなくなります。
IIRCは、DSレジスタの値を取得し(そしてそれを4ビット左にシフトし)、指定された即値に加えて、結果のメモリ位置からdword値をフェッチします。これが、呼び出すアドレスになります。(編集:これは16ビットリアルモードにも当てはまります。プロテクトモードについては、他の回答を参照してください。)