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つ目の正誤チェックは、もうつぶさなくても通るようになってます。やったね!
復号された部分を見ると、Unicodeで Tetris とか見えてます。テトリスだー。
しかしテトリスは実行されない。
そのままコードを進めていくと、次のところで新しい実行コードにジャンプします。
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