こんにちは。
尻フェチとは言うけどおっ◯いフェチとはあんまり言わないなぁと思う今日この頃
はい。
今回は前回の記事の続きで、CTFのリバースエンジニアリング(バイナリ解析)を行っていこうかなと思います。
前回の記事は以下からどうぞ。
Capture the flagのススメ -超初心者向けバイナリ解析- その1
#注意事項#
この記事はCTF超初心者の備忘録であるとともに、少しでもCTFに興味を持ってくれたら嬉しいな♡程度の記事なので細かいところは説明しませんしできませんので予めご了承ください。
ですが、何か理解が間違っている等あればコメントなどで指摘してくれたら幸甚です。
#今回の目標#
前回までで、リバースエンジニアリングについて、バイナリ解析の概要、アセンブリ言語について理解できないこともない程度は理解できたかと思います。
今回は実際に実行ファイルを解析してFlagを回収してみよう!という企画です。
※データ型等はすべて省略しますが、dword ptrを想定しての演習になります。
※Intelの8086(x86)を例に取って説明しています。
※Ubuntuでの操作になるので一緒に実施したい方は予め用意しておいてください。
#演習#
まずは簡単な演習問題から
この演習で利用しているオペコードは2つ。
- 「mov」:第一オペランドに第二オペランドを移動(コピー)
- 「lea」:第二オペランドの値を足し算し、第一オペランドに格納
プログラミングやってる人なら簡単に理解できるかと思います。
Python的なものに置き換えて簡単にしてみましょう。
mov eax, 0x1 → eax = 0x1
※eaxに0x1を代入
2つ目
lea ebx, [eax + 0x5] → ebx = eax + 0x5
※eax+0x5の答えを代入
3つ目
mov eax, ebx → eax = ebx
※eaxにebxを代入
注意しなければならないのは、movを実行しても元のクラスタから値は消えないということです。
なので、ebxをeaxに代入してもebxはそのままです。
#Reversing問題の解法手順
Rev問題は以下の手順どおりが基本的な解き方です。
1.ファイルのダウンロード
2.権限の付与
3.実行して挙動を確認
4.objdumpやIDAなどで逆アセンブルして解析
#実際のファイルで試してみる
それでは実際のファイルを利用して解析してみたいと思います。
これ以降はUbuntuの環境を利用しています。
上述の解法手順通りにtestファイルを解いていきましょう。
##1.ファイルのダウンロード##
こちらからダウンロードしてくださいませ。
##2.権限の付与##
chmod u+x test.o
これだけ。
##3.実行して挙動を確認##
上記のファイルは練習用に作成した超絶簡単ファイルですので実行できません。
ごめんちゃい
##4.objdumpやIDAなどで逆アセンブルして解析##
今回はobjdumpコマンドを使って逆アセンブルしてみます。
以下コマンドを実行すると以下のように結果が返ってくると思います。
objdump -d test.o -M intel
それでは1つずつ説明します。
間違ってたら教えれ。
①について
これは各列の番号(アドレス)みたいなものです。
今回はJumpなど行わないので意識しなくてもOK
②について
ここは、16進数で書かれた機械側が理解する機械語のコードです。
こちらも無視して問題なし。
③について
ここが実際のアセンブリ言語となります。
ここを読み解いていきましょう!
命令一覧
push:スタック(リストのようなもの)に値を積む
※スタックは積み重ねる入れ物のようなもので、最後に入れたものが最初に取り出される。
mov:値の移動を行う
add:足し算
pop:スタックから値を取り出し、レジスタに移す。
ret:returnの意味関数から元の関数に戻る命令
※今回はnullなので終わりを示している。
DWORD PTR:32bitを示している。
では実際にわかりやすいように、頑張って説明してみます。
レジスタ(ebpとかespとか)については前記事を参考にしてね。
push ebp
mov ebp,esp
mov DWORD PTR [ebp+0x8],0x1
mov DWORD PTR [ebp+0xc],0x2
mov edx,DWORD PTR [ebp+0x8]
mov eax,DWORD PTR [ebp+0xc]
add eax,edx
pop ebp
ret
1.まず1,2行目で「push ebp」でebpレジスタをスタックに入れておき、「mov ebp,esp」でespレジスタの値をebpレジスタに代入します。
これは多分割りとお決まりなので、ebpレジスタを保持しておくためだと考えてください。
2.ebpの0x8の位置(?)に0x1を入れる。(push 1でも同様の処理が可能です。)
ebpの0xcの位置に0x2を入れる。(push 2でも同様(ry )
3.edxにebp+0x8の値を代入
eaxにebp+0xcの値を代入
4.eax+edxをeaxに代入
5.ebpから値を取り出してreturn
こんな感じです。ebp+0x8とかを位置と定義していいのかは微妙ですが、スタックに値を積んでいくイメージでいいと思います。
実際には上記のアセンブリはC言語で表現すると以下のようになるっす。
foo (int a, int b)
{
a = 1;
b = 2;
return a + b;
}
C言語があってるかどうかは微妙っすがお許しを。。
多分fooじゃなくてmainにすればいいんだよね多分。
#終わりに#
以上でバイナリ解析は終わりになります。
正直バイナリ解析は好きだけど難しい。多分マゾ向けだと思うの。
ホント中途半端な知識で記事を書いて申し訳ない気持ちはありますが、
書いて皆に添削してもらうのが一番の近道といいますか、書かないと覚えないといいますか、ホント難儀な脳みそしてるんですよすいません。
まぁこの記事で少しでもバイナリ解析に興味持ってくれたらなと思います。
違うわ。CTFに興味を持ってくれたらなと思います。
次回はForensicsについてかけたらなと思います。
あぁ、でもWebもいいなぁ・・・
さよなら!
##偉大なる参考サイト様##
kaworu.jpnサイト様
→逆アセンブルの方法について詳しく載っています。
isLandcenterサイト様
→同じく逆アセンブルの方法にについて参考にさせていただきました。
T.T Landサイト様
→アセンブリの読み方等色々詳しく載っています。すごい助けられました。
雑記帳サイト様
→同じくアセンブリの読み方について助けられました。
SCCON for Beginners様
→この無料セミナーが全ての原動力でした。ありがとうございました。
画像等もお借りしました。