「リバースエンジニアリングバイブル」勉強メモ#6
よく使われるパターン
2014/11/13 : CTF
前回に続き、「リバースエンジニアリングバイブル」の勉強メモ第6弾です。
実行環境は、Windows7(64bit版)です。実行環境は、Windows7(64bit版)です。
6章 よく使われるパターン
[条件文]
[誤植]
p.120 逆アセンブルコード内
誤
cmp eax,100h
jnz short loc_401038
push offset aX100 ; "x = 100\n"
正
cmp eax,0
jnz short loc_401038
push offset aX0 ; "x = 0\n"
jle、jnz などのジャンプ命令は、if 文の条件に応じて変わる。
逆アセンブルされた結果から、cmp+jxx の部分は元が条件文だったと判断できる。
条件文の後に登場する、何も条件のないジャンプ命令は else に対応する。
&& などを使って1つの if 文の中に複数の条件を含めた場合は、<処理>に対するコードが生成されず、cmp + jxx のコードが連続して登場する。
||( OR 演算子)を使ったときも大きな違いはなく、ただジャンプ命令の形が変わるだけ。
条件文の後に<処理>が出るか出ないかがポイントになる。
[ループ]
var_8 は IDA が付けた変数の名前で、2番目のローカル変数を意味する。
4バイトずつに分けると var_4 が1番目のローカル変数だと考えてよい。
メモリ上で直接演算することはできないので、いったんレジスタに入れて演算してから再び元の場所に値を入れた。
最初の条件を満たした場合にループの最初の処理を実行するようにし、満たしていない場合はすぐにループを抜け出すようにするコードで、このようにジャンプ命令が連続するパターンのコードは、break 文に対応すると考えてよい。
break の例では、現在の番地より結構遠い後部の番地にジャンプするコードが含まれていたが、continue の例では、前のコードに戻るコードになっている。
ループが始まる地点に戻るようにするコードは、continue コードである。
スタックをクリアする必要があるので、いきなり retn を使うわけにはいかない場合もあるが(もちろん、スタックを使わないコードであった場合、その場で retn を使用することもある)、ループの途中で突然抜け出してコードが終わる部分は return だと考えればよい。
[文字列制御]
BOF:Buffer Over Flow
マルウェアをリバーシングしてみると、多くのウイルスは strcpy などの安全でないランタイムライブラリを多く使っているという事実がわかる。
repxx 系命令は ECX が正の値である間繰り返すもの。
scac 命令は、現在 AL/AX/EAX に格納されている値と右のオペランドで指定された場所の値を比較するための命令。
次のようなコードのパターンが出てきたら、Xの長さを求めるためだと考えても問題ない。
mov edi,X
repne scas byte ptr es:[edi]
not ecx ; 0xFFFFFFFF から変化した ecx を反転して値を得る
shr は右シフト演算をする命令。
shl は左シフト演算をする命令。
余りは表現されないが、商は ECX に入る(余りは後で and で求める。)
movs 命令を用いて値をコピーする。
単に1バイトずつコピーすればよさそうに思われるが、そうすれば4倍の時間を無駄にすることになる。
「shr ecx,2(
4で割る)」と「and ecx,3(
4で割った商)」という命令は、商と余りを求める際によく使われるアセンブラ命令なので、覚えておくと便利である。
[誤植]
p.138 4 行目
誤:「shr, ecx,2」
正:「shr ecx,2」
リバースエンジニアリングを職業としている筆者にとって、strcat() はウイルスやマルウェアでよく見るランタイム関数である。
→システム系ディレクトリのパスを問い合わせ結果に偽システムモジュール名を連結するのに使われる。
[まとめ]
重要なのは、(一般的な)スタイルを基に、アセンブラのパターンを身に付けていくことである。
1行ずつ読みながらデバッガーで流れをたどっていくと、どのようなコードでも解析できるし、パターンを定型化できる能力が育つ。
記述に際しては、細心の注意をしたつもりですが、間違いやご指摘がありましたら、こちらからお知らせいただけると幸いです。
→「リバースエンジニアリングバイブル」勉強メモ#7
←「リバースエンジニアリングバイブル」勉強メモ#5
« 戻る