Panda Challenge 2010 Edition: last challenge を頑張りました。

えーっと。。もう提出締め切りは終わってますよね。
ということで、参戦記をば。
Panda Challengeの情報はこちらから。

Panda Challenge って何

なんかバイナリファイルがあって、それを解析して指定されたノルマをクリアしろ!
みたいな感じ。CTFっぽいね!楽しいね!

初参加!わっしょい!

Panda Challenge には初参加。といっても、参加登録とかそういうのは一切必要ないんです。
誰でも好きなときに好きなように参戦できます。チームとか個人とか関係なし。

今回のチャレンジの概要
In order to solve it, you have to create one valid license key for the game.

訳:「ゲームをプレイするために、正しいライセンスキーを作ってください。」


今回のlast challengeは、賞品がなんとiPad 3G 64Gb!太っ腹!
しかしその分難易度は跳ね上がっているようで、こんな事が書かれてます。

Note: this challenge is quite more complex than the previous one, so it may be difficult to get the solution in time. If we don’t receive any valid license, the winner will be the one who succeeds in unpacking completely and functionally the executable of the challenge. In this case you’ll have to explain how you’ve solved it.

訳すと、「今回のチャレンジは前のと比べ物にならないぐらい複雑だよ!
だから、期限内にノルマは達成できないかもね!もし誰もノルマを達成できなかったら、
完全にアンパックして機能的にチャレンジを実行できた人が優勝ってことにする!
でもそのときは、ちゃんとどうやったか説明してもらうよ!」とのこと。

結果

DIALOGリソースはアンパックできてないけど、一応テトリスをプレイできました。



DIALOGリソースがアンパックできてないので、残念ながらHelpとHall of FameとAboutは見れません。
ダメそうな気はするけど、万が一ということを考えて月曜日の早朝5時ぐらいに説明文とともに
実行するとすぐにこのテトリスが始まる実行ファイルをPandaSecurityに送信しました。
今はどきどきの結果待ち。試験がなかったらDIALOGリソースもアンパックしたんだけどなあ・・・。

分かってる限りで解説

「おまいら、テトリスをプレイしたいか!」
「おーっ!」
「そんな簡単にプレイできると思っているのか!」
「おーっ!」
「いいぜ、てめえが何でも思い通りに出来るってなら、まずはそのふざけた幻想をぶち殺す。」


Ollyで開くと、UPXやASProtectは使われてないことが分かります。すごく優しい。
コードを眺めてると、次のような事が分かります。


アイコンがテトリス(実は今気づきました。)
グローバル変数は SS:[EBP+*****] でアクセスしてる。EBPは0x798E。
・LoadLibraryA と GetProcAddress を使ってAPIを全部用意してるので、外部関数の呼び出し一覧が出ない。
参照元の抽出もできない泣きたい。
・実行コード部分はそこまで長くないので、全てを動的・静的問わず読んだ方が早そう。
げぇっ、SHA1
・license.keyは0x20バイト。
・license.keyの0x00-0x0Fと、0x10-0x1Fで扱いが分かれてる。
RC4が使われてる!


全部説明してると果てしないので、要所要所を順番に説明していきます。

最初の処理

APIが2つ読み込まれる。
[EBP+4016AB] GetProcAddress
[EBP+40167F] LoadLibraryA


409883 で CreateFileA が呼び出され、存在チェックが行われます。無ければ終了。
4098A0 で GetFileSize が呼び出され、ファイルサイズチェックが行われます。0バイトなら終了


見事チェックをかいくぐると、
4098CC で ReadFile が呼び出され、
4098BD の PUSH 20 を見ると分かるとおり、0x20バイト読み出されます。


4098E6 から各種APIのロードが始まります。ロードされるAPIは、
[EBP+401A99] EnumDeviceDrivers
[EBP+401A9D] GetDeviceDriverBaseNameA
この2つ。
このAPI名でぐぐるとサンプルコードが出てくるので、
ためしに探してコンパイルして実行してみると以後のコードが読みやすい。


動的解析すると分かりやすいけど、GetDeviceDriverBaseNameAを順次呼び出して、
4099CA で変な値とCMPしてる。値を1バイトごとに区切ってみると見るからにアスキー(笑)。
アスキー文字に直すと、"PavP"と比較してることが分かる。
さらに 4099DF を見ると、またしても変な値が。これもアスキーで、".sys"を表している。
2つのキーワードでググると、PavProc.sysというワードが浮かぶ。
[EBP+401975] にはlicense.keyの中身がロードされてて、
[EBP+401987] に "PavP" で始まるDeviceDriverBaseNameを7バイト書き込んでる。
その直後の場所に、".sys"を加えてるので、license.keyの中身は次のように書き換えられることとなる。

****************
**PavP+++.sys***


どう見ても "PavProc.sys" です。本当にありがとうございました。
4099F2 で GetModuleHandleA が呼び出されて、
409A0A で [EBP+401992] から3バイト分、ModuleHeaderが書き込まれる。
これによって、license.keyの中身はさらに次のように書き換えられる。

****************
**PavProc.sysMZ・
ライセンスキーの正誤判定

SHA1関係のAPIロード部分は省略します。
409A6C の CALL で license.key の 0x00-0x0F 部分が使われて、何かXORとか使ってごそごそしてます。
そのごそごそしたデータのSHA1ハッシュを取得し( 409B3E A_SHAFinal呼び出し )、
[EBP+401939] にある正しいハッシュ値と比較。
409B52 JNZ で間違っていると飛ばされます。


もう1箇所は、合計2回か3回呼び出されます。
409BFD の CALL で license.key の 0x10-0x1F 部分が使われて
409C26 の CALL でごそごそして、ごそごそしたデータのSHA1ハッシュを取得し( 409C6A A_SHAFinal呼び出し )、
[EAX+EBP+401939] にある正しいハッシュ値と比較。
409C8A JNZ で間違っていると飛ばされます。


この正誤判定をつぶすと、不正な処理が云々で強制終了されます。
強制終了される部分を見ると、
409CC2 LoadLibraryA 呼び出しで死んでます。引数を見ると、確かにおかしい。
さて、これをどうするか、です。

最初の処理を使ってみる

PavProc.sysが動いてると、license.key が書き換えられてました。
書き換えられた状態で正誤判定をつぶすと、どうなるかというと!
次のようなエラーが表示されるように変わります。
(うろ覚えなので多少違うかも。。)


"Cannot load WINMM.・・l"


おやおや。2文字、文字化けしてます。
どう見ても "WINMM.dll" が正しいそう。
試しに、license.key の 0x10と0x11バイト目をいじくると、文字化けしてるところが変わります。
試行錯誤して WINMM.dll になるように値を調整すると、いろいろDLLやらAPIやらのロードが始まります!


これで、license.key の 0x10-0x1F が正しく復号できました。
2つ目の正誤チェックは、もうつぶさなくても通るようになってます。やったね!
復号された部分を見ると、UnicodeTetris とか見えてます。テトリスだー。

しかしテトリスは実行されない。

そのままコードを進めていくと、次のところで新しい実行コードにジャンプします。


409D11 MOV EAX,309C
409D16 ADD EAX,DWORD PTR SS:[EBP+4016EB] ;[EBP+4016EB] は ModuleHandle で 0x400000
409D1C CALL NEAR EAX


409D1C で新しいコードに行きますが、行った先はぐっちょぐちょ。
それもそのはずで、401000-のコードは license.key の 0x00-0x0F バイトで複合化されてます。
よって、正しくない license.key で複合化されてるので、ぐっちょぐちょ。

license.key の最初0x10バイトは、別のところの復号でも使われている!

実は他のところでも使われています。
exeScopeなんかで見ると分かるんですが、このEXEはDIALOGリソースが4つあります。
しかし、まともに見れません。暗号化されてるからです。
これを復号するときに、license.key の最初0x10バイトが使われてます。
(これは、409E27 にブレークポイントをかけると分かります。)


ダイアログリソースの最初の方は、ある程度予測ができます。
というのも、構造が決まっているからです。
ダイアログには2種類あり、今回のダイアログも2種類が入り混じってます。
(詳しくは普通のDIALOG拡張DIALOGを参考。)


これより、どちらかのダイアログのバイナリは、01 00 FF FF 00 00 00 00 で始まるであろうことが分かる。
バイナリはXORで複合化されるので、2種類のダイアログリソースに対してこれに複合化できるように
キーを計算する。同じキーで実行コードも複合化されているので、さらに実行コードも8バイトだけ復号してみる。
すると、片方はこんなコードになる!

00409E19    A1 A87C4000     MOV EAX,DWORD PTR DS:[407CA8]
00409E1E    8B0D AC000000   MOV ECX,DWORD PTR DS:[AC]


それっぽいの(・∀・)キター!!!
もう一種類の普通のダイアログは、こんな感じのバイナリになっている。
C0 08 C8 80 00 00 00 00
それっぽくなってる!やったね!


後は、実行コードから [407CAC] を参照してるだろうと予想がつくので、
さらに3バイトは 7C 40 00 になるだろう、と予想がつく。
その結果、普通のダイアログのバイナリは
C0 08 C8 80 00 00 00 00 05 00 00
になっている。ダイアログのdumpとにらめっこすると、ダイアログ上のアイテム数が5だと分かる。
その後は位置なので、00 00 00 だと仮定してさらに4バイト進めると、実行コードがこうなる。

00409E19    A1 A87C4000     MOV EAX,DWORD PTR DS:[407CA8]
00409E1E    8B0D AC7C4000   MOV ECX,DWORD PTR DS:[407CAC]
00409E24    56              PUSH ESI
00409E25    8B35 00000000   MOV ESI,DWORD PTR DS:[0]


またまたそれっぽいことになってる!!
PUSH ESI で ESI を退避した後に、MOV ESI ときてるので、正しいと考えられる。
こんな感じで、ダイアログのバイナリ、拡張ダイアログのバイナリ、実行コードを見て予測しながら
キーを求めていく。キー長は0x100バイトしかないので、手動で気合入れて試行錯誤すれば、最初の方は結構求まる。

実行コードの復元

しかし、0x100バイト全てを戻すのはなかなか大変。
ここで、あることに気づく。

00409A1B    C785 03174000 2>MOV DWORD PTR SS:[EBP+401703],20


これ。実は、実行コードに限り、0x20バイトごとにキーテーブルがリセットされる。
(これはコードを読んでいけば分かる。)


ということは!
最初の0x20バイトが分かれば、実行コードは復元できる。
しかも、最初の0x10バイトが分かれば、0x10バイトごとに実行コードは復元されているということになる!
これを利用して、実行コードを予測しながら0x20バイトまで復元する。気合で。


そうすると、テトリスができるようになるというわけでした。

リソースは?

残念ながらダイアログリソースは0x20でリセットされない。
(409DBA を参照。)
そのため、ダイアログリソースを完全に復元するには、より一層の気合と根性で復元するしかない。
(あるいは、license.keyの最初の0x10バイトを求めるか。)


XORするキーがこれだけ分かっていても、RC4はどうやら100バイト近く分かってないと
最初の鍵を攻撃で求められない、みたいな論文を見かけたので、
残念ながら翌日に試験が待ち構えてた自分はここであきらめました。

まとめ

・Ollyさん、いちいちブレークポイントを解除するのやめて
・Ollyさん、uddファイルにパスを含ませるのやめて
・Ollyさん、ありがとう


・手動解析は大事、自動化も大事。見極めが肝心。
・時差が分かりにくい。
テトリス楽しい。


・結果が気になる。


意味はあんまりないけど、解析中に書いたメモ帳のメモを貼り付けておきます。
グローバル変数の中身とか大体全部書いてあるので、これから解析する人は便利かも。
(ただし、ノー編集なので見づらさ100%です :D)
(ついでにuddファイルもどうぞ。
ただし、パスは適当に書き換えてください(じゃないと読み込んでくれない)。)

[panda challenge PlayMe]
license.keyは0x20 (32.)バイト読み込む。

EBP = 798E

401677 ADVAPI32 MZ
40167B user32 MZ
40167F LoadLibraryA
401683 MessageBoxA
401687 CreateFileA
40168B GetFileSize
40168F VirtualAlloc
401693 ReadFile
401697 CloseHandle
40169F ExitProcess
4016A3 GetModuleHandleA
4016A7 VirtualProtect
4016AB GetProcAddress
4016AF int nVirtualAddress
4016B3 int nSize
4016B7 A_SHAInit
4016BB A_SHAUpdate
4016BF A_SHAUpdate
4016C3 license.keyのファイルハンドル
4016C7 license.keyのファイルサイズ
4016CF license.keyの読み込まれたバイト数
4016E3 kernel32 MZ

4016E7 int nTmp = 20 OR -1      (減らしていき、0になったら再びCreateTable)
4016EB long lModuleHandle (400000)

4016F3 lpflOldProtect(バッファ)

4016FB リソースのアドレス lResourceBaseAddr

401703 20 OR -1
401707 int nResourceAddress
40170B int nSize
40170F nCounter
401723 WORD nNumOfSecs
401725 buf1[6][36]
4017FD buf2[180]       先頭0x10バイトは、key[17..32] ラスト4バイトは0

4018B1 SHA1 hash object
  4018B1 ULONG SomeJunk[6]
  4018C9 ULONG State[5]
  4018DD ULONG Count[2]
  4018E5 UCHAR Buffer[64]
  -401924
401925 sha1 hash[20]
401939 CORRECT HASH 1
401953 CORRECT HASH 2    OK
401957 CORRECT HASH 3    OK


401975 license.keyの中身 key[32]
  401987 key[19..29] = PavProc.sys   PavP***.sys
  401992 key[30..32] = 4D 5A 90 (MZ・)
401995 char tbl[256]

401A95 psapi MZ
401A99 EnumDeviceDrivers
401A9D GetDeviceDriverBaseNameA

401AA1 ロードアドレスを格納するために必要な配列のバイト数
401AA5 VirtualAllocしたバッファ
401AA9 カウンタ(4ずつ増やしてロードアドレスからドライバ名を取得する為)

401AAD "advapi32.dll"
401ABA "user32.dll"
401AC5 "A_SHAInit"
401ACF "A_SHAUpdate"
401ADB "A_SHAFinal"
401AE6 "license.key"
401AF2 "CreateFileA"
401AFE "GetFileSize"
401B0A "ReadFile"
401B13 "LoadLibraryA"
401B20 "ExitProcess"
401B2C "MessageBoxA"
401B38 "GetModuleHandleA"
401B49 "VirtualProtect"
401B58 "VirtualAlloc"
401B65 "CloseHandle"
401B71 "GetProcAddress"
401B80 "Error loading function"
401B97 "Error loading keyfile"
401BAD "Error loading the imports"
401BC7 "Invalid license file"
401BDC "ERROR!"
401BE3 NullString(文字列バッファ) buf[12]
401BEF "psapi.dll"
401BF9 "EnumDeviceDrivers"
401C0B "GetDeviceDriverBaseNameA"
401C24 "Error getting address of function %s"
401C49 "Error loading library %s"
401C62 "sprintf"
401C6A "msvcrt.dll"
401C75 "What are you looking for? ;)"





404000 RegQueryValueExW
404004 RegCreateKeyExW
404008 RegSetValueW
40400C RegCloseKey

404014 SetBkMode
404018 DeleteObject
40401C TextOutW
404020 GetStockObject
404024 CreateFontW
404028 SetTextColor
40402C CreateCompatibleDC
404030 CreateSolidBrush
404034 DeleteDC
404038 GetDeviceCaps
40403C BitBlt
404040 CreateCompatibleBitmap
404044 GetTextExtentPoint32W
404048 SelectObject

404050 InterlockedExchange
404054 GetCurrentProcessId
404058 GetCurrentThreadId
40505C QueryPerformanceCounter
405060 ReleaseMutex
404064 CloseHandle
404068 MulDiv
40406C GetTickCount
404070 CreateMutexW
404074 GetLastError
404078 IsDebuggerPresent
40407C SetUnhandledExceptionFilter
404080 UnhandledExceptionFilter
404084 Sleep
404088 GetCurrentProcess
40408C TerminateProcess
404090 GetStartupInfoA
404094 InterlockedCompareExchange
404098 GetSystemTimeAsFileTime

4040A0 _initterm
4040A4 _acmdln
4040A8 exit
4040AC _initterm_e
4040B0 _XcptFilter
4040B4 _exit
4040B8 _cexit
4040BC __getmainargs
4040C0 _configthreadlocale
4040C4 __setusermatherr
4040C8 _adjust_fdiv
4040CC __p__commode
4040D0 _encode_pointer
4040D4 __set_app_type
4040D8 _crt_debugger_hook
4040DC ?terminate@@YAXXZ
4040E0 _unlock
4040E4 memset
4040E8 __dllonexit
4040EC _lock
4040F0 _onexit
4040F4 _decode_pointer
4040F8 _except_handler4_common
4040FC _invoke_watson
404100 _controlfp_s
404104 _ismbblead
404108 __p__fmode
40410C ??3@YAXPAX@Z
404110 wcscpy_s
404114 swprintf_s
404118 ??2@YAPAXI@Z
40411C iswspace
404120 rand
404124 srand
404128 _amsg_exit

404130 SetClassLongW
404134 PostQuitMessage
404138 PeekMessageW
40413C GetDlgItemTextW
404140 FillRect
404144 SetDlgItemTextW
404148 BeginPaint
40414C UpdateWindow
404150 ReleaseDC
404154 GetDlgCtrlID
494158 PostMessageW
40415C DrawTextW
404160 GetDlgItem
404164 EndDialog
404168 WaitMessage
40416C GetDC
404170 DefWindowProcW
404174 TranslateMessage
404178 DialogBoxParamW
40417C InflateRect
404180 EndPaint
404184 RegisterClassExW
404188 DestroyWindow
40418C ShowWindow
404190 LoadIcon
404194 LoadCursorW
404198 DispatchMessageW
40419C GetAsyncKeyState
4041A0 SetRect
4041A4 CreateWindowExW
4041A8 SetFocus
4041AC AdjustWindowRectEx
4041B0 DrawEdge
4041B4 MessageBoxW
4041B8 SystemParametersInfoW

4041C0 timeGetTime
4041C4 timeEndPeriod
4041C8 timeGetDevCaps
4041CC timeBeginPeriod


1A CF 00 8A 80 1E EF BA

DB CF 37 F5 80 1E EF BA    CB 43 E5  C9 0B    97     76 11 99  D7 E7
02 B7 E2 E7 ED F4 06 E8 F7 1A 26 33  41 50 60 65  7D 90 A4 B9  08 E6 27 F5  


DB CF 37 F5 80 1E EF BA    CB 43 E5  C9 0B    97     76 11 99  D7 E7
02 B7 E2 E7 ED F4 EE 

DB CF 37 F5 80 1E EF BA    CB 43 E5  C9 0B    97     76 11 99  D7 E7
02 B7 E2 E7 ED F4 EE 

DB C7
02 CF


00 01 DB 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D F4 1F
20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F 30 31 32 33 34 35 36 E2 38 39 3A 3B 3C 3D 3E 3F
40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F
60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F
ED 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF B0 B1 B2 B3 B4 B5 B6 CF B8 B9 BA BB BC BD BE BF
C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE B7 D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA 02 DC DD DE DF
E0 E1 37 E3 E4 E5 E6 F5 E8 E9 EA EB EC 80 EE EF F0 F1 F2 F3 1E E7 F6 F7 F8 F9 FA FB FC FD FE FF




00 01 DB 03 04 05 EF 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D F4 1F
20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F 30 31 32 33 34 35 36 E2 38 39 3A 3B 3C 3D 3E 3F
40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F
60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F
ED 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF B0 B1 B2 B3 B4 B5 B6 CF B8 B9 E8 BB BC BD BE BF
C0 C1 C2 C3 C4 C5 C6 CF C8 C9 CA CB CC CD CE B7 D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA 02 DC DD DE DF
E0 E1 37 E3 E4 E5 E6 F5 BA E9 EA EB EC 80 EE 06 F0 F1 F2 F3 1E E7 F6 F7 F8 F9 FA FB FC FD FE FF



5F CB FF 21

9E C3 C8 5E


DIALOG PATTERN
01 00 FF FF         A1 A87C40**     △  C0 08 C8 
40 00 00 40         E0 A883FF       ×
40 00 00 46         E0 A883         ×
40 00 C8 90         E0 A8           ×
40 00 CC 80         E0 A8           ×
44 04 00 54         E4 AC83EB       ×
C0 00 C8 90         60 A8832F       ○  01 08 FF EF
C0 00 C8 80         60 A8833F       ○  01 08 FF FF     C0 08? 01 08?
C4 00 C8 80         64 A8833F       ×
C4 20 C8 90         64 88832F       △
C8 00 C8 80         68 A8833F       △


[XOR]
1A CF 00 8A 80 1E EF BA 83 CB 43 E5 09 03 04 17 BF 76 11 99 D7 E7 

[CODE]
60 A0 4B 3F 00 8B 0D AC

[DLG1]
01 00 FF FF 00 00 00 00 00 00 00 00 C0 08 C8 80 03 00 00 00 00 00 BA 00 47 00 ** ** 00 00 54 00 

[DLG2]
01 00 FF FF 00 00 00 00

[順番]
02 35 21 26 2C 33 3B 44 4E 59 65 72 2C
      36                94 3B 88 95
                           9F       A3
                        82 F3 FF 06
               F1 02 04
         92 E2 A7 D0 D9
            01
89 E2 0F 4B 9F
4B B1 2E B2 A8 D3 FC A3
            BF          2D 16 22 40
               4B 57 60
            88          62 71 A0 26 98 D3
      CE AD BF 3B 27 91 5F 24 80


XOR DATA
1A CF 00 8A 80 1E EF BA 83 CB 43 E5 09 03 04 17 BF 76 11 99 D7 E7 0C 3D 7F 66 ** ** 15 4F 8D CE
1A CF 00 8A 80 1E EF BA 83 CB 43 E5 09 03 04 17 BF 76 11 99 D7 E7 0C 3D 7F 66 00 66 15 4F 8D CE
1A CF 00 8A 80 1E EF BA 83 CB 43 E5 09 03 04 17 BF 76 11 99 D7 E7 0C 3D 7F 66 E0 66 15 4F 8D CE

409EBF
5D 8B 85 E7 16 40 00 B9 20 00 00 00 2B C8 55 8A 89 23 93 40 00 90 90 90

409EEA NOPにする。

409323にXORデータを貼り付ける。20バイト分。


01 00 FF FF 00 00 00 00 00 00 00 00 C0 08 C8 80 03 00 00 00 D7 00 BA 00

100000000
10000000000

1A CF 00 8A 80 1E EF BA
83 CB 43 E5 09 03 04 17
BF 76 11 99 D7 E7 0C 3D
7F 66 E0 66 15 4F 8D CE
48 B6 CE 2D F6 79 35 C3