This request has already been treated.

  1. khsk

    するする → する

    khsk
Changes in body
Source | HTML | Preview
@@ -1,215 +1,215 @@
では、[マルウェア解析での逆アセンブルとその攻防(前編)](https://qiita.com/komi1230/items/bed26e64121657445d59)の続きを行きます。
# 前回までのあらすじ
前編ではタイトルで**マルウェア解析の攻防**と称しておきながらアセンブリ言語の解析と逆アセンブルの仕方について述べただけでした。
具体的に、*"Hello World"*と出力するコードをC言語で書き、それをコンパイルして実行ファイルを作成、それを逆アセンブルするという作業を行いました。
しかし、実際に逆アセンブルとはどうやって行われるのか。
後編ではそれに着目し、マルウェア解析ではどのような攻防戦になるのかということを見ていきます。
* 逆アセンブルのアルゴリズム
逆アセンブルにおいて、前編にて`線形逆アセンブル`と`フロー志向型逆アセンブル`があるということを述べました。
これらについて紹介していきます。
## 線形逆アセンブル
アセンブリのファイルを見たように、コンピュータへ命令を実行させるには細かくメモリにデータを割り当ててそれらをうまいこと入出力させて....というものでした。
例えば以下のアセンブリ言語のファイルを見てみましょう。
```
Disassembly of section .text:
0000000100000f60 <_main>:
100000f60: 55 push rbp
100000f61: 48 89 e5 mov rbp,rsp
100000f64: 48 83 ec 10 sub rsp,0x10
100000f68: 48 8d 3d 3b 00 00 00 lea rdi,[rip+0x3b] # 100000faa <_main+0x4a>
100000f6f: c7 45 fc 00 00 00 00 mov DWORD PTR [rbp-0x4],0x0
100000f76: b0 00 mov al,0x0
100000f78: e8 0d 00 00 00 call 100000f8a <_main+0x2a>
100000f7d: 31 c9 xor ecx,ecx
100000f7f: 89 45 f8 mov DWORD PTR [rbp-0x8],eax
100000f82: 89 c8 mov eax,ecx
100000f84: 48 83 c4 10 add rsp,0x10
100000f88: 5d pop rbp
100000f89: c3 ret
```
アセンブリの読み方としては左の数字列がメモリ番号だと言いましたが、これを見ると`100000f6f`の次が`100000f76`となっているように各命令ごとにある程度ブロックがあるわけです。
その特性を利用して逆アセンブルするのが**線形逆アセンブル**というアルゴリズムになります。
つまりどういうことかというと、線形逆アセンブルでは「逆アセンブルされた命令が**次に何バイト逆アセンブルするべきか**を決定する」とします。
この線形逆アセンブラをテキトーに実装すると以下のようになります。
```c:LinearDissassemble.c
char buffer[BUF_SIZE];
int position = 0;
while (position < BUF_SIZE) {
x86_insn_t insn;
int size = x86_disasm(buf, BUF_SIZE, 0, position, &insn);
if (size != 0) {
char disassembly_line[1024];
x86_format_insn(&insn, disassembly_line, 1024, intel_syntax);
printf("%s\n", disassembly_line); position += size;
} else {
/* invalid/unrecognized instruction */
position++;
}
}
x86_cleanup();
```
まず最初に命令ブロックを発見してそれを逆アセンブル、その後その結果が次に何バイト分を逆アセンブルするのがいいのか決定するわけです。
## フロー志向型逆アセンブル
さて、線形逆アセンブルのアルゴリズムはちゃんと逆アセンブルできるし一見良さげです。
しかし、これには落とし穴が存在します。
その落とし穴とは、**線形逆アセンブルでは命令とデータがきちんと順序よく並んでいるのを前提としている**という点です。
これがなぜ落とし穴になりうるかを説明するために以下のようなシチュエーションを考えましょう。
```
test eax, eax
jz short near ptr loc_15+5
push Failed_string
call printf
jmp short loc_15+9
Failed_string:
inc esi
popa
loc_15:
imul ebp, [ebp+64h], 0C3C03100h
```
線形逆アセンブルでは`jmp`命令があるとき、`jmp`の飛ぶ先を先に逆アセンブルするのではなくデータの並んでる順に逆アセンブルします。
これでも逆アセンブルできるのですが、エラー除外やポインター、条件分岐など複雑なコードではゴチャゴチャになって逆アセンブル結果が妙なことになるのは想像がつくでしょう。
そこで、ブロック単位で逆アセンブルしていく線形逆アセンブルの改良として提案されたのが**フロー志向型逆アセンブル**です。
ここまで説明を読んだらだいたい察しがつくと思いますが、フロー志向型逆アセンブルのアルゴリズムとしては**基本的にはブロック単位で見ていきつつ`jmp`や`call`などの命令などがある場合は飛ぶ先を先に逆アセンブルする**というものです。
これを絵として表すと以下の通りです。
![Screen Shot 2018-11-09 at 13.42.04.png](https://qiita-image-store.s3.amazonaws.com/0/205389/fe08676d-9f85-9513-e226-d62f936c7d31.png)
# マルウェア解析での攻防
さて、ようやく**マルウェア解析の攻防**を話すための下準備が整いました。
セキュリティを勉強する上での1番の醍醐味は**知的な攻防戦**にあると思うんですよね。
その攻防戦がこのマルウェア解析においても十分見えてきます。
## マルウェア作者の気持ち
-マルウェアを作る側にとって、せっかくコードを書いて実行ファイルを作成し、ターゲットのコンピュータに忍ばせてパスワードをクラックしたりしてバックドアを仕掛けたりするするわけですが、バレて欲しくないわけですよね。
+マルウェアを作る側にとって、せっかくコードを書いて実行ファイルを作成し、ターゲットのコンピュータに忍ばせてパスワードをクラックしたりしてバックドアを仕掛けたりするわけですが、バレて欲しくないわけですよね。
つまりマルウェア作者側はマルウェア解析へ対策をするわけです。
ではどうするか?
**この逆アセンブルのアルゴリズムの脆弱性を狙ったコードを作成すればいいんです。**
## アンチ逆アセンブル
フロー志向型逆アセンブルで利用可能な脆弱性は何かというと**条件分岐においてはfalseの方から逆アセンブルを行う**という点です。
実際、if文やwhile文は**条件が満たされなければ中のコードを実行する**という性質を持っていますね。
これを利用します。
### 同一ターゲットへのジャンプ命令
`jz`という命令は条件がTrueの時にジャンプし、`jnz`は条件がFalseの時にジャンプするというものです。
この**同一ターゲットへのジャンプ命令**は`jz`と`jnz`を重ねがけして同じターゲットに飛ばさせる、というものです。
図に表すと以下のようになります。
![Screen Shot 2018-11-09 at 14.09.49.png](https://qiita-image-store.s3.amazonaws.com/0/205389/f27817a8-cbfb-b4ca-a92a-d11afddb82c2.png)
これは実質的に`jmp`命令と同義です。
しかし逆アセンブラは一度に一つの命令しか逆アセンブルしないのでこれが実質的な`jmp`命令であることを認識しません。
逆アセンブラは`jnz`に辿りついた時、**Falseから逆アセンブルを行う**ので次にセットした`call`命令を逆アセンブルしますね。
この`call`命令の呼ぶ関数を**存在しないテキトーなものにセットしておく**と逆アセンブラは読めないデータを読もうとして失敗し、最終的に**逆アセンブルに失敗**します。
### 定常条件におけるジャンプ命令
この手法も先ほどと同様にジャンプ命令を利用したアンチ逆アセンブルのテクニックです。
先ほどの同一ターゲットへのジャンプ命令は**逆アセンブラは一度に一つの命令しか逆アセンブルできない**というところを利用して**実質的な`jmp`命令を他の組み合わせで作る**というものでした。
この**定常条件におけるジャンプ命令**も同様です。
![Screen Shot 2018-11-09 at 14.27.06.png](https://qiita-image-store.s3.amazonaws.com/0/205389/2b7b19ba-c627-cf21-82ea-8385328ab7b0.png)
`xor`命令で常に真となる条件を用意します。
そこで逆アセンブラはfalseの方を先に逆アセンブルするので、`jmp`命令の方を先に読みます。
そこで`jmp`命令をテキトーにセットしておくことで逆アセンブルが失敗する、という構図です。
### 不正なバイト使用による逆アセンブル不可能化
今まで見たのはアンチ逆アセンブルのテクニックとしてはかわいいものです。
というのも、逆アセンブラ側に優秀な条件分岐をつければこれらの問題は回避することができるからです。
アンチ逆アセンブルのテクニックは非常に奥が深く、非常に巧妙な手法が数多くあります。
その中でも**不正なバイト使用**を利用したこのテクニックは非常に秀逸です。
![Screen Shot 2018-11-09 at 14.41.27.png](https://qiita-image-store.s3.amazonaws.com/0/205389/124de0fa-8118-cfa1-527f-c691856dbc42.png)
頭からこのバイト列を見たとき、最初の4バイトは2バイトの`jmp`命令です。
そしてこの`jmp`命令の飛ぶ先は`jmp`命令自身です。
FFは次の2バイトの命令`inc eax`という命令の最初の2バイトになり、このバイト列はエラーを引き起こすことはありません。
逆アセンブラが頭から逆アセンブルしようとしたとき、この命令列は`jmp`命令として解釈され正しい逆アセンブル結果が得られないのです。
こうしたバイトの不正使用を利用した応用例を見てみましょう。
![Screen Shot 2018-11-09 at 15.11.21.png](https://qiita-image-store.s3.amazonaws.com/0/205389/d9753cdc-4135-1155-1728-7aa524f1f4de.png)
この図の上の方では最初の8バイトはaxに05EBh(リトルエンディアンに注意)を代入する`mov`命令として見ることができます。
しかし最初の4バイトを無視すれば`jmp`命令として見ることができ、本当のコードが実行されるわけです。
実際、このような最初の数バイトはプログラムの一部ではないので実行時には無視されるのですが、逆アセンブラはこれらを評価をしてしまいます。
この手法は隙間をついた面白い手法と言えるのではないのでしょうか?
# まとめ
以上、マルウェア解析と解析対策における攻防を見ました。
こういう見るとめちゃくちゃワクワクしませんか?
今回Practical Malware Analysisを勉強して面白かったので、これからもセキュリティの勉強をがんばろうと思います。
ではお疲れ様でした!