#始めに
初めまして、Columbineと言います。
2ヶ月ほど前にセキュリティキャンプで会った人にバイナリの面白さを布教されバイナリアンになるべく勉強を始めております。
せっかくなのでそこで得た知識を皆さんに共有しておこうと書きました。
注)このQiitaは初心者向けですが、簡単な最低限の知識については面倒なので省略して説明します。(初心者向けとは)
また簡単に説明するのが目的なのでpush rbpなどのあまり中心の処理に関係のないところの説明は雑いです。
このブログ読む前に読むといい本
デバッガによるx86プログラム解析入門[x64対応版]
・これ読んでおけば最低限の知識は手に入ります。後半はデバッガの作り方とかでしたのでそこはさらっと読みましたが分かりやすくていい本です。
ハリネズミ本
・CTFと言ったらこれ。先ほどの本よりは難しめですがアセンブラ読めるようになってCTF始めたいという人は先に目を通しておくと、どんな事を勉強するといいかの指標になるかと思います。
簡単な変数への代入と出力
//print.c
#include<stdio.h>
int main(void) {
int hoge = 9999;
printf("%d",hoge);
}
これをコンパイルします。
今回-fno-stack-protector
というオプションを使うのですが、これは出力されるアセンブラを減らすためです。説明は省きます。
###今回使用するツールの使い方
今回はradare2というツールを使用します。githubにありますのでインストールしておいてください。
radare2で解析対象のプログラムを開きます。
radare2 -dAA print.out
これでradare2の中に入れたと思いますので以下のコマンドを順に入力してください。
V
p
これで以下の写真のような表示が出ると思います。
さて、このプログラムを順に見ていきましょう。
注)メモリのアドレスは全部書くと長いので後ろから4つのみで表示する。
[1]
0x0f60〜0x0f64まではスタックポインタを使用する場所のアドレスに変更。
[2]
0x0f68〜0x0f79ではprintfで使用する引数を指定。
0x0f68では出力する型の"%d"をrdiに渡す。
0x0f6fではradare2によって名付けられたlocal_4hという変数に0x270fという値(10進数で9999)を渡し、0x0f76でesiに渡す。
[3]
0x0f7bの、call sym.imp.printfでprintf命令を呼び出す。
call命令の返り値はeaxに保持される。
[4]
0x0f80〜0x0f85は、返り値の処理とprintfの返り値をlocal_8hに収納する処理。
0x0f80で行われている xor esi, esiはesiレジスタを0にする処理である。
0x0f85でeaxにesiの値(つまり0)を渡して関数の返り値とする。
[5]
0x0f87〜0x0f8cはスタックポインタのやベースポインタの値を元に戻し関数を終了させる。
[6]
この下にsys.imp.printfのプログラムがあるが今回は触れないでおく。
読むコツとしてはcall命令に気をつけて見るというのがあります。
今回はcall sys.imp.printfというのがあるのが分かっているので、「どんな引数がどうやって渡されているのか」ということに注目したら自然とコードにまとまりが見つけられて読みやすくなります。
今回引っかかった所
以前読んだ本ではsyscallの引数をスタックを通じて渡していたが、
今回のx64ビットバイナリだとrdi,rsi,rdx…とレジスタに順番に引数を入れていく形での受け渡しとなっていた。
そのため引数の渡し方がわからず手間取りました。
まとめ
radare2はまだ色々な機能があるので色々といじって見てください。
もっと色々なプログラムの解析した奴があるのでそれも載せようかと思ったのですが、思った以上に文量があったのでまた需要があったら載せることにします。
(追記) 2018/10/24
###環境について
環境について述べていなかったので追記しました。
OS = macOS High Sierra
gcc = clang-902.0.39.2
radare2 = 3.1.0-git