dragon

第1回:初学者のためのバイナリ解析

カテゴリ:バイナリ解析、アセンブリ言語


➊ 概要/目的

スタックバッファオーバーフローを学ぶため、前提としてバイナリ解析を理解する必要がある
なるべく読者が手を動かして学びたくなるような情報を展開したい

■ ブレイクスルー観点
🈩 簡単なプログラムを実行してスタックの状態を理解する
🈔 gdbを通してバイナリ解析できる知識を身に着ける


➋ 事前準備

実際にプログラムを動かしてスタックの状態を確認していきたいと思う
以下のコードをコンパイルし、デバッグしながらflagとbufferの値を追ってみよう

$ cat stack_trace.c

#include <stdio.h>
#include <string.h>

	void test_function(int a,int b,int c,int d){
		int flag;
		char buffer[10];
	
		flag = 12345;
		buffer[0] = 'A';
	}
	
	int main(){
		test_function(1,2,3,4);
	}

$ gcc -m32 -g stack_trace.c -o stack_trace
$ gdb –q stack_trace
(gdb) となればOK

① デバッグ時はまずlistでプログラム全体を表示させる
② 任意のコード状況を把握するためにBreakPointを指定する(図では10、5、7を指定)
・10行目:test_functionを呼び出す前の状態
・5行目:flagの値に12345を代入する前の状態
・7行目:buffer[0]にAが代入された後の状態

ここまで来たら (gdb) run、continueで実行・次のブレークポイントまで移動する


❸ 10行目:test_functionを呼び出す前の状態

10行目のブレイクポイントで処理を実行しているときのスタックの状態を見てみよう

まずはesp/ebp/eipの番地を確認しておこう
(espは一番低位アドレス、ebpは一番高位アドレス、eipは次に実行するアドレスくらいのイメージ)

次にmain関数の中身を見ると、「push 0x4」を実行しようとしていることが分かる
pushはスタックへ上から積み込み、popはスタックから上から取り出すイメージを持っておく

スタックの状態は以下となる
・4⇒3⇒2⇒1の順番でpushされる
・test_function関数がcall命令で呼ばれる

test_functionへ移動する前に戻りアドレスとSFP(退避済みフレームポインタ)が記録される


❹ 5行目:flagの値に12345を代入する前の状態

次は5行目のブレークポイントを確認する、ちなみに12345は16進数で0x3039となる

まずはesp/ebp/eipの番地を確認しておこう

ここまでのスタック状態をイメージする、ポイントはflagが先にbufferよりスタックされる点

eip/espを確認してみると、赤枠がflagで青枠がbufferであることが分かる
現時点では値(0x3039、A)は代入される前の状態である
また緑枠がSFP、黄色枠が戻りアドレス(ret)を指す

上記から読み取らずともflagとbuffer[0]の番地と値を直接コマンド確認できる


【番外】◇ 番地の数え方
かなりややこしいが番地は4バイト分を確保している、さらに右側の方が数字が小さいようだ
番地の数え方は情報が特になかったので実際に1バイトずつ値を確認してみると以下のようになっている(真偽不明)


❺ 7行目:buffer[0]にAが代入された後の状態

次は7行目のブレークポイントを確認する、ちなみにAは16進数で0x41となる

まずはesp/ebp/eipの番地を確認しておこう(特に5行目と変化なし)

espを確認してみると、赤枠がflag(0x3039)で青枠がbuffer(A)であることが分かる
緑枠がSFP、黄色枠が戻りアドレス(ret)を指す

注目すべきは確保した4バイトの右側から「0xf7fb6808」→「0x00003039」代入されている点である
A(0x41)も同様に「0x3000f7fb」→「0x3000f741」になっている
左は「break 5」実行前、右は「break 7」実行前のespの状態である

代入前後の値を比較すると値が書き換わっている箇所が確認できる


❻ まとめ

実際にプログラムを実行してスタックの状態や値の書き換わりを理解することができた(ここまでは前段)
本題のスタックバッファオーバーフローでは主に2つの条件が揃っている
・プログラムで扱うデータのサイズを事前にチェックせず、バッファに収まらないデータを拒否できない関数(strcpy、gets)を使っている
・変数(例えばflagやbuffer)がスタック上で近い距離にある場合、任意の値に書き換えることができる


この他にASLR(アドレス空間配置ランダム化)という攻撃者がメモリ上の特定の場所を予測しづらくする技術も存在する
次回は 値が入力可能なプログラムを実行し想定外の変数値を書き換え、任意の処理を実行していきたいと思う