XCHG#
xchg A,B #指令用于将A和B的值互换
0x4013d8
にマウスを置くと、xor eax,eax
命令があります。ここでは、0x4013d8
アドレスの命令を変更するデモンストレーションを行います。メニューバーのEdit-Patch program-Assemble
をクリックします。
次のウィンドウが表示されます。
変更する命令を入力すると、変更ができます。
xchg eax,esi
新しい命令を書き込んだ後、元の関数は中断されます。
0xc0
をNOP
に変更し、NOP(NO OPERATION)
命令は何もせず、何の操作もありません。変更後の内容は次のとおりです。
上記の例では、1 バイトのみが関係しており、元のデータ型は db で、値は0xc0h
です。
しかし、変更後は、関数が中断されているため、関数の開始アドレス 0x4013d8 で右クリックし、Create function(関数の作成)
を選択します。
関数が作成されると、次のような内容になります。
IDA は、前置のloc_(通常の命令を示す)
をsub_(関数の開始点を示す)
に変更します。これにより、関数として再認識され、スペースキーを押してグラフビューに切り替えることができます。
再び XCHG 命令に注目します。EAX = 12345678、ESI = 55
と仮定すると、命令の実行後、EAX = 55、ESI = 12345678 になります。
PATCH
メニューには、PATCHED BYTES
機能があり、変更されたバイトを表示し、変更をキャンセルすることもできます。
xchg 命令は、レジスタの値と他のレジスタが指すメモリに格納された値を交換することもできます。
例えば、命令:xchg eax,[esi]
もし eax=55、esi=0x10000 の場合、プログラムは 0x10000 に格納された値を検索し、アドレスに書き込み権限がある場合は 55 を格納し、元の 0x10000 の値を eax に書き込みます。
4.3 スタック操作命令#
スタック(Stack)
は、関数の実行中に一時的に使用されるメモリ領域であり、メモリアクセスモードは先入れ後出し(FILO、FIRST IN LAST OUT)
です。これにより、データの保存と読み取りが行われます。
スタックの特徴は、最後にスタックに入ったフレームが最初にスタックから出ることです(最も内側の関数呼び出しは最初に終了します)。これは後入れ先出し
のデータ構造と呼ばれます。
スタックのデータ操作には 2 つの基本的な操作命令があります。
-
PUSH:オブジェクトをスタックのトップに保存(プッシュ)します。
-
POP:最後にスタックのトップに保存されたオブジェクトを取り出します。
-
いつでも、スタックのトップまたは最後にプッシュされたオブジェクトのみを操作できます。
POP 操作は、スタックのトップに保存された最後のオブジェクトを取り出し、そのオブジェクトの下に(最後のプッシュ操作で保存された)オブジェクトを配置し、それに続く操作を行うことができます。
スタックは、メモリ領域の終了アドレスから開始し、高位(アドレス)から低位(アドレス)に割り当てられます。たとえば、メモリ領域の終了アドレスが 0x00001000 で、最初のフレームが 4 バイトである場合、次に割り当てられるアドレスは 0x00000FFC から開始されます。上記の図を参照してください。
push 命令#
通常、32 ビットプログラムでは、PUSH は関数を呼び出す前にスタックにパラメータを渡すために使用されます。たとえば、上記の図の0x40104f
での命令です。PUSH 64
命令は 64(dword)をスタックのトップにプッシュし、PUSH EAX
は EAX の値をスタックにプッシュし、以前の 64(dword)の上に保存します。これにより、EAX の値がスタックのトップになります。
PUSH は、定数以外にもアドレスを操作することができます。
文字列名の前にある OFFSET キーワードに注意してください。この命令は、この文字列または文字配列のアドレスをスタックにプッシュします。
文字列名をダブルクリックすると、次の図に移動します。
一般的に、C 言語のソースコードでは、文字配列は次のように定義されます:Char mystring[] = "Hello"
Dキー
を押すと、windowname
変数のデータ型が変更され、IDA がそれを文字配列ではなく、db またはバイトとして扱わないようにします。
変更後、このアドレスを参照するいくつかの命令が変更され、次の図のようになります。offset キーワードは、引数がまだ 0x004020e7 のアドレスであることを保証しますが、アドレスに格納されているのはもはや文字配列ではなく、バイトです。
全体の命令はpush offset byte_4020E7
に変わり、byte_4020E7
の内容を検索すると、db 型に変わります。
Aキー
を押すと、ASCII 文字列に変換され、元の表示に戻ります。
他の単独の文字列として表示されているものがある場合は、同じ操作を行うことができます。文字列の開始位置を見つけてA
キーを押すと、表示がより読みやすくなります。
次の内容:
A
キーを押すと、次の内容になります:
0x402110
でD
キーを押すと、amenu
文字列がバイトに分割されます。そして、A
キーを押すと、文字列が再度結合されます。
通常の文字列の例
X
キーを押すと、文字列の参照位置が表示されます。
通常、関数にパラメータを渡すときは、PUSH offset xxxxx
命令を使用して、文字列のアドレスを関数に渡します。offset キーワードがない場合、渡されるのは0x402110
アドレスに格納されている内容、つまり文字列の具体的なバイト55 4E 45 4D
です。ただし、API 関数はこのように動作しません。通常、ポインタまたは文字列の開始アドレスをパラメータとして受け取ります。
上記の命令では、DS: というマークは、プログラムがデータ領域(DS=DATA)のメモリに書き込むことを示しています。
pop 命令#
POP 命令は、スタックのトップにある値を読み取り、それを対象レジスタに転送します。以下の図のように、POP EDI
はスタックのトップにある最初の値を読み取り、EDI レジスタに転送し、ESP の値を読み取った値の下に指し示し、それをスタックのトップとします。
テキスト検索で pop を検索すると、pop 命令の使用方法が変わらず、値を pop してアドレスに格納することはありません。