fr33f0r4ll

自分用雑記

X64 pwn

x64でのpwn

Cでの話、goとかだと違う。 基本はx86と同じだが、いくつかの点で違いがある。
https://blog.techorganic.com/2015/04/10/64-bit-linux-stack-smashing-tutorial-part-1/を参考にしている。

引数の渡し方

x86では関数呼び出しするときにスタックに値をpushして引数を渡していたため、スタックの値を書き変えれば引数を操作できた。 x64ではレジスタに引数の値を指定するため、引数の操作がやりにくくなっている。

引数の順番はWinとgccで違ったりする、CTFだとだいたいgcc版になる。 gcc版だと一番目からrdi, rsi, rdx, rcx, r8, r9になる。 exe版だとrcx, rdx, r8, r9になる。 4個以上の引数がある関数はあまり見ないので知らない。

レジスタ

64bit長になった。 つまり、pushやpopは8バイト単位で動作し、アドレス長も8バイトになっている。 さらにレジスタ自体の数も増えて、R8やR9などが追加されている。 x86レジスタは、RAXやRSPのようにRを付けると64ビットでアクセスできる。 EAXやEBXで下位32ビットにアクセスできる。 それ以外はx86と同じようにアクセスできる(下位32ビットに対して)。

アドレス

バッファオーバーフローでリターンアドレスを書き換えEIPを奪うのはpwnではx86での上等手段だった。 例えば長い文字列を渡しバッファを溢れさせると、eipの値が0x41414141などになっているのがgdbなどで確認できる。 しかし、x64ではアドレス空間が64bitに拡張され、有効な命令アドレスの範囲は0x00007FFFFFFFFFFFまでに制限されている。 したがって、リターンアドレスを書き換え過ぎるとripが書き換えられないでプログラムがSIGSEGVされる。 このままだと確認しにくいので、ret命令時のスタックトップがリターンアドレスとして読み込まれるので、その値からオフセットを割り出し適切な長さに調節するとよい。

環境変数

環境変数はプログラム実行時にスタックに積まれているので、環境変数が設定できるなら文字列を設定し、そのアドレス引数にしたりするテクニックが使える。

ROP

レジスタを介して引数を渡すため、ROPを使う場面が増える。 例えばpop rsi; retという命令へのアドレスをリターンアドレスとして設定すると、スタック上でリターンアドレスの次にある値がRSIに設定できる。 これを利用してレジスタに値を設定し、