コンピュータウイルスの解析などに欠かせないリバースエンジニアリング技術ですが、何だか難しそうだな、という印象を抱いている人も多いのではないでしょうか。この連載では、「シェルコード」を例に、実践形式でその基礎を紹介していきます。(編集部)
第3回ではシェルコードの中身の解析に入りました。シェルコードのデコーダ部分のコードを実際に解析し、IDCによりオリジナルのアセンブリコードを復元してみました。
続く今回は、デコーダによる処理が終わった後のオリジナルコードの先頭から解析を行っていきたいと思います。
さて、アセンブリコードの解析に入る前に、まずAPI(Application Programming Interface)の話をします。
第1回で“シェルコードの役割は「ファイルをダウンロードする」「ダウンロードしたファイルを実行する」の2つである”とご紹介しました。これらファイルのダウンロード、ファイルの実行は、基本的にはWindowsのAPIを呼び出すことで行います。今回はこのAPIの呼び出しメカニズムに焦点を当てて説明し、その仕組みを理解したところでシェルコードの解析に入っていきます。
Windowsの場合、基本的にAPIは、実行ファイルとは別にDLL(Dynamic Link Library)として実装されています(注1)。Windowsのローダはファイルが実行されるとその実行ファイル中で使われているAPIを調べ上げ、そのAPIを実装しているDLLをメモリにロードし、アドレス解決を行い、実行ファイルからDLL内に実装されているAPIを呼び出せるようにします。
ではIDAとImmunity Debuggerを利用して、実行ファイルからkernel32.dll内に実装されているCreateFileA APIを呼び出すところを具体的に見てみましょう。
図1は、CreateFileAを呼び出すだけの単純なプログラムの逆アセンブリです。ソースコードはこちらになります。
図1の(1)にあるように、.text:00401020でCreateFileAを間接コールしています。この間接コールで参照している. idata:0040A004のメモリの値(図2の(2))を見てみると、まだ値が格納されていないことが分かります(図3の(3))。これは、CreateFileAの実体はDLL(kernel32.dll)内にあり、kernel32.dllは実行時にロードされるまでメモリ上のアドレスが決定していないためです。
次に、同じ実行ファイルをImmunity Debuggerで読み込んでみます。Immunity Debuggerは、デフォルトの設定では実行ファイルをローダがメモリ上に展開した後に停止させてくれるため、ローダによるアドレス解決が行われた後の様子を見ることができます(注2)。
図4の(4)(5)を見てください。IDAで見たときは空だったアドレスに「24 1A 80 7C」の値が設定されています。この値はリトルエンディアンなので、4バイト整数として解釈すると、7C801A24hになります。このアドレスの個所を詳しく見てみると、CreateFileAだということが分かります(図5の(6))。
ディスク上にファイルとして存在している実行ファイルをIDAで見たときは、まだAPIのアドレス解決は行われていません。一方、Immunity Debuggerで読み込んだ後は実行ファイルがメモリ上に展開され、ローダによりAPIのアドレス解決が行われています。そのため、IDAで見た時は空であった値が、Immunity Debuggerで見るとCreateFileAを指している、つまりアドレス解決が行われていることが見て取れると思います。
このアドレス解決は、基本的にはプログラムの起動時に、ローダにより一度だけ行われます。そのため、起動済みのプロセスの脆弱性を突いて挿入されるシェルコード内からAPIを呼び出そうとしても、APIのアドレスが分からないといった問題が生じます。
さて、シェルコードはいかにしてAPIの呼び出しを実現しているのでしょうか?
Copyright © ITmedia, Inc. All Rights Reserved.