MyUtils

View on GitHub

バイナリ解析について

このページは初めて学ぶバイナリ解析についての備忘録である。

アセンブラ出力の方法

gcc -S <ファイル名>.c



バイトオーダー

0x01234567という数字を扱う場合

ビッグエンディアン

アドレスの下位に高い桁の数字をあてがっていく方式 (数値の高い桁から順に書き込んでいく)

ビッグエンディアン

リトルエンディアン(一般的にこれが使われている!!)

アドレスの高位に低い位の数字をあてがっていく方式 (数値の低い桁から順に書き込んでいく) リトルエンディアン



スタックについて

基本的なことだが、メモリ領域においてプログラム部とスタック部に分けられる。 グローバル変数はプログラム部に格納され、ローカル変数はスタック部に保存される。 スタックはpushによって上位から下位のアドレスに向かって伸びていく

ここで覚えておきたいのは、ESP,EBP,EIPについてである。 スタックの一番上(一番低いアドレス)をtop、底(一番高いアドレス)をbottomと呼ぶ

トップとボトムはCPUのレジスタに保存され以下の名前がつけられている

スタックの積まれ方とEBP,ESP,EIPについて解説する

1. 引数を外側からpushしていく
2. callでRIP(EIP)の値がリターンアドレスとしてpushされる
2.1.call直後、RBP(EBP)が上位のスタックの情報としてpushされる。
3. RSP(ESP)の値をRBP(EBP)に書き換える。
4. 関数実行で、ローカル変数が積まれる。

スタックの動作1

5. RBP(EBP)の値をRSP(ESP)に書き換える。
6. 退避したRBPをpopしてRBPに書き換える。
7. retが実行され、リターンアドレスがRIP(EIP)になり、
  RSP(ESP)が書き換わる。
8. 元の関数に戻る

🚨一般的にはこんな感じで描かれるが、コンパイラでアセンブラを出力すると、
ローカル変数をpushで作成せずにESP(RBP)からのオフセットで変数を確保するような挙動をする場合もある。
また、引数を他のレジスタに格納してpushでスタックに置かないパターンもある模様

スタックの動作2



レジスタについて

接頭語にERの文字があてがわれているが、以下の認識でOK。 種類として汎用レジスタ,特殊レジスタ,フラグレジスタの3つが存在する。

レジスタに格納されている値を確認するにはGDBにてbreak中に以下のコマンドを実行することで確認できる

# レジスタの状態を表示
(gdb) info registers
(gdb) i registers
(gdb) i r

# 特定のレジスタの値を表示(例:rax)
(gdb) print/x $rax

# 逆アセンブリ結果を表示
(gdb) set disassembly-flavor intel
(gdb) set disassembly-flavor att
(gdb) disassemble main

汎用レジスタ

機能名 名称 中身
EAX アキュムレーターレジスタ 返り値(計算結果など)
EBX ベースレジスタ アドレスのベース値
ECX カウンタレジスタ ループ回数のカウンタ
EDX データレジスタ 演算に使うデータ
     

特殊レジスタ

機能名 名称 中身
ESP スタックポインタ 実行中の関数のトップを常に表す
EBP ベースポインタ 実行中の関数のボトムを常に表す
EIP インストラクションポインタ メモリのプログラム部の、次に実行する命令のアドレスを常に格納している
     

フラグレジスタ

命令や演算の結果として生じた状態を保存したレジスタ フラグレジスタの状態によって条件付き分岐命令の分岐先が決定する。 EFLAGSレジスタという32bitにまとめられていて、状態によって各ビットが1になる。 使用頻度の高いもので、ゼロフラグ(6bit目)というものがあり、 直前の命令結果が0ならフラグが立ち(1になり)0以外ならフラグが下がる(0になる)

EFLAGS