さて、やっとここからアセンブリコードの解析に入ります。上で説明した知識を頭に入れておくと、今回のアセンブリコードはスラスラ読めると思います。
それでは今回も、前回までと同様に、Metasploitで生成できるdownload_exec.binのシェルコードを解析対象とします(注4)。第3回目でdownload_exec.binのデコーダ部分の解析を行いましたので、今回はデコード処理後のシェルコードを見ていきます。
まず前準備として、fsレジスタの値を修正します。デフォルトの設定だとfsレジスタが0x00000000を指しているため、0000001Dで「fs:30h」と表示したいところを、IDAが自動的に紛らわしい参照を作ってしまい、「fs:loc_2F+1」と表示してしまいます(図8)。
fsレジスタの値を修正して、この紛らわしい参照を消します。メニューの[Edit]→[Segments]→[Set default segment register value]を選び、fsの値を0から、無効な値0xFFFFFFFFに修正してください(図9)。
これで「fs:[30h]」となると思います(図10)。
それではアセンブリコードの解析に入ります。まずは少しトリッキーな動作からです。
loc_17: jmp loc_F1 ;loc_F1にジャンプ sub_1C proc near pop edx mov eax, large fs:30h …… loc_F1: call sub_1C
loc_17(00000017)ではloc_F1にジャンプしています。そして、loc_F1(000000F1)を見てみると、すぐにcall命令でsub_1Cに戻ります。
loc_F1の周辺でIDA proのメモリダンプ画面(HexView-Aのタブ)を見てみてください。「GetProcAddress」や「WinExec」などの文字列が確認できると思います(図11)。
これは、call sub_1C命令より下の領域では、コードではなくデータ(文字列)が格納されているためです。
ここで可読性を高めるために、データ部分を逆アセンブリのウィンドウで文字列として定義します。
まずは、コードとして解釈されている部分をUndefinedします(注5)。000000F6にカーソルを合わせて、キーボードの「u」を押してください。コードとしての解釈が解かれて、単なるバイト列になったと思います。
次にこれを文字列として解釈させていきます。000000F6にカーソルを合わせてキーボードの「a」を押してください。“GetProcAddress”という文字列がひとまとまりになりました(図12)。
以下、「a」を押して文字列として解釈できるところを全部変換してください。
この文字列を見ているだけで、ある程度シェルコードの動作は予想できますが、ここでは詳細を追っていきたいと思います。アセンブリコードのsub_1Cに戻ります。
sub_1C proc near pop edx ;スタックに積んだアドレス(文字列データの先頭アドレス)を取得 mov eax, large fs:30h ;PEBのアドレスを取得 mov eax, [eax+0Ch] ; LoaderDataのアドレスを取得 mov esi, [eax+1Ch] ;InInitializationOrderModuleListのアドレスを取得(ntdll.dllのエントリ取得) lodsd ;ntdll.dllのLDR_MODULEのエントリ内のInInitializationOrderModuleList.Flink(kernel32.dllのLDR_MODULEエントリへのポインタ)を取得
まず、最初のpop edxは000000F1で、call sub_1Cが呼ばれた時に積まれたリターンアドレス(000000F6)を取り出して、edxに格納しています。このedxは後ほど利用します(注6)。
次に、fs:30hの値をeaxに格納しています。これは、前で説明したとおりPEBデータ構造体へのポインタになります。ここから先は、先ほどの図7を見ながら読むと分かりやすいかもしれません。
このPEBデータ構造体のオフセット+0Chのアドレスに格納されている値を、eaxに格納しています。これは先ほどのデータ構造体の定義で確認すると、LoaderDataに相当します。
続いて、LoaderDataのオフセット+1Chのアドレスに格納されている値を、esiに格納します。この値はInInitializationOrderModuleListに相当します。InIntializationOrderModuleListはnttdll、kernel32.dll……の順番で、リンクリストの形で各モジュールの情報を保持しています。
lodsd命令は、DS:ESIの指している内容を、4バイト分eaxにコピーする命令です。
この時esiは、InIntializationOrderModuleListの1つ目のエントリ(ntdll.dll)のFlinkを指しています。Flinkの指す先は2つ目のエントリになりますので、lodsd命令を実行することで、2つ目のエントリのデータ構造体の先頭アドレスがeaxに格納されます。InInitializatoinOrderModuleListの2つ目の要素はkernel32.dllなので、オフセット+8hにアクセスし、kernel32.dllのベースアドレスを取得しています。
これでkernel32.dllのベースアドレスを取得することができました。
次回はこのベースアドレスからAPIのアドレスを取得する方法について解析したいと思います。
川古谷 裕平(かわこや ゆうへい)
2005年 日本電信電話株式会社入社。入社以来、ハニーポット技術やマルウェア解析技術の研究開発に従事。国内外のカンファレンスや学会で研究成果の発表を行っている。「アナライジング・マルウェア」の著者。
Copyright © ITmedia, Inc. All Rights Reserved.