Log.i53

Themidaのアンパックを目指すブログ改め使い物になるえんじにゃを目指すブログ

マニュアルアンパック:PECompact

導入

 PECompactは速度とパフォーマンスに焦点を当てて設計された商用パッカーです。無償の学生版はマルウェア作者によって使用されることが多かったため廃止され、使用用途や組織名による登録が必要な試用版に置き換えられています。このパッカーでパックされたプログラムは、アンチデバッグ例外や難読化コードを含んでいるためアンパックするのが難しくなっています。PECompactはサードパーティ製のツールを組み込むことが可能となるプラグインフレームワークが実装されており、マルウェア作者の多くはアンパックの難易度を上げるためのサードパーティ製のツールを付加しています。
 PECompactのマニュアルアンパックはUPXのマニュアルアンパックと基本的に同じです。ただし、プログラムがいくつかの例外を発生させるので、OllyDbg上でプログラムに例外を渡す設定をする必要があります。
 最終的にはテールジャンプを探すことでOEPを見つけることができます。いくつかの関数をステップオーバーすると、多くの0x00バイトが続くjmp eax命令からなるテールジャンプを確認することができます。

パックサンプルを入手する

forum.tuts4you.com
試用版の登録が面倒くさいので今回はこちらのUnpackmeサンプルを使用します。

f:id:i53:20150711043142p:plain
例によってPEiDで確認すると,PECompactによりパックされたバイナリであることが確認できます。

OEPの探索

 OllyDbgでロードすると早速興味深い命令が確認できます:
f:id:i53:20150711044315p:plain

 The Art of Unpackingでも説明されていたDebugger Interruptsというアンチデバッグ技術が適用されていることが分かります。意図的に例外を発生させることで例外ハンドラへの分岐を構築しています:

00401000 > MOV EAX, 0048CF1C          ; 例外ハンドラ=0x0048CF1C
00401005   PUSH EAX
00401006   PUSH DWORD PTR FS:[0]
0040100D   MOV DWORD PTR FS:[0],ESP   ; 例外ハンドラ設定
00401014   XOR EAX,EAX                ; EAX <- 0
00401016   MOV DWORD PTR DS:[EAX],ECX ; アクセス違反例外を生成

 OllyDbgの設定であらかじめ例外情報をOllyDbgから例外ハンドラに自動的に渡すように設定しておきましょう:
f:id:i53:20150712035121p:plain

 設定を終えたら例外ハンドラ0x0048CF1Cにブレークポイントを設置して実行再開(F9)することで例外ハンドラにてブレークが発生します:
f:id:i53:20150711054926p:plain

; Patch:0x0048CF40
0048CF1C   MOV EAX,F048BCA1                    ; EAX <- 0xF048BCA1
0048CF21   LEA ECX,DWORD PTR DS:[EAX+1000129E] ; ECX <- EAX + 0x1000129E = 0x0048CF3F
0048CF27   MOV DWORD PTR DS:[ECX+1],EAX        ; [0x0048CF40] <- 0xF048BCA1
; Get Exception Address
0048CF2A   MOV EDX,DWORD PTR SS:[ESP+4]        ; EDX <- ExceptionRecord
0048CF2E   MOV EDX,DWORD PTR DS:[EDX+C]        ; EDX <- ExceptionRecord.ExceptionAddress = 0x00401016
; Patch:0x00401016
0048CF31   MOV BYTE PTR DS:[EDX],0E9           ; [0x00401016] <- 0xE9
0048CF34   ADD EDX,5                           ; EDX <- 0x00401016 + 5 = 0x0040101B
0048CF37   SUB ECX,EDX                         ; ECX <- 0x0048CF3F - 0x0040101B = 0x8BF24
0048CF39   MOV DWORD PTR DS:[EDX-4],ECX        ; [0x00401017] <- 0x0008BF24
0048CF3C   XOR EAX,EAX
0048CF3E   RETN

 例外ハンドラ内では次のステップに進むために自身のコードに対していくつかのパッチを行っています。例えば、例外ハンドラに渡されるEXCEPTION_RECORD構造体から例外が発生したアドレス(この場合、0x00401016)を取得してそのアドレスの命令をjmp命令に書き換えています。この時ジャンプ先(0x0048CF3F)の相対アドレスの計算も例外ハンドラ内で行われています。

 例外ハンドラのRETN命令の直前まで実行した後で、例外が発生したアドレス0x00401016を確認するとJMP命令に書き換わっていることが分かります:
f:id:i53:20150711062721p:plain

 例外ハンドラ最後のRETN命令により例外が発生したアドレスに復帰してこのJMP命令が実行されることになります。 

 では、ジャンプ先の0x0048CF3Fにブレークポイントを設置して実行を再開(F9)します。後はしばらくステップオーバー(F8)実行を続けていると現れるJMP EAX命令がOEPに遷移するテールジャンプとなります:
f:id:i53:20150711064321p:plain

アンパックされたバイナリのメモリダンプ

 OEP(0x004667DC)までステップ実行したら【Plugin】➤【OllyDump】➤【Dump debugged process】でOllyDumpを起動しましょう。赤枠の部分にOEPのオフセット0x667DCを入力します:
f:id:i53:20150711065300p:plain

 また、左下の【Rebuild import】がチェックしてあればダンプ時にインポートテーブルを自動で再構築してくれます。ダンプしたファイルを実行して正常に起動したならばアンパック成功となります。

PECompact OEP Finder

 上記の操作を自動化するOllyScriptの例を以下に示します:

//////////////////////////////////////////////////
//
//  FileName    :  PECompact OEP Finder
//  Author      :  i53
//  Date        :  2015-07-11
//
//////////////////////////////////////////////////
INIT:
  bc
  bphwc

FIND_EXCEPTION_HANDLER:
  find eip, #B8????????5064FF3500000000#
  cmp $RESULT, 0
  je NOT_FOUND
  add $RESULT, 1
  mov $RESULT, [$RESULT] 
  bphws $RESULT, "x"
  eob EXCEPTION_HANDLER_FOUND 
  run

EXCEPTION_HANDLER_FOUND:
  bphwc $RESULT
  rtr
  bp eip+1  
  eob FIND_TAIL_JMP
  run

FIND_TAIL_JMP:
  bd eip
  gci eip, COMMAND
  cmp $RESULT, "JMP EAX"
  sto
  jne FIND_TAIL_JMP
  cmt eip, "This is the OEP!"
  msg "OEP is found :)"
  ret

NOT_FOUND:
  msg "OPE Finding is failed :("
  ret

実行すると自動的にOEPに遷移します:
f:id:i53:20150712043920p:plain

OllyScriptの各種コマンドについては以下のリファレンス翻訳記事を参照して下さい。i53.hatenablog.jp

ハッピーアンパッキング:)