この文章ではローダー(Loader)とパッカー(Packer)とカスタムリソースの話で扱ったcarckme.zipの解析を、実際に解説しながら行います。よって前記事であるローダー(Loader)とパッカー(Packer)とカスタムリソースの話を読んでいない場合は、そちらから読むことをお勧めします。ちなみに、解説するにあたってソースコードやパスワード(実際の答え)を書いていますので、もし「まだ俺はcrackをあきらめていないぜ!」と考えている方は読まない方が良いと思います。動作確認はWindowsXP SP2で行っています。
crackme.exeをOllyDbgで開くと、まずは以下のコードが出力されます。
----- crackme.exe 00401070 >/$ 81EC 04040000 SUB ESP,404 00401076 |. A1 88934000 MOV EAX,DWORD PTR DS:[__security_cookie] 0040107B |. 338424 0404000>XOR EAX,DWORD PTR SS:[ESP+404] 00401082 |. 53 PUSH EBX 00401083 |. 8B9C24 0C04000>MOV EBX,DWORD PTR SS:[ESP+40C] 0040108A |. 56 PUSH ESI 0040108B |. 68 C0714000 PUSH OFFSET crackme.??_C@_07COOAMMHI@EXE_BIN?$AA@ 00401090 |. 6A 65 PUSH 65 00401092 |. 53 PUSH EBX 00401093 |. 898424 1404000>MOV DWORD PTR SS:[ESP+414],EAX 0040109A |. FF15 18704000 CALL DWORD PTR DS:[<&KERNEL32.FindResourceA>] 004010A0 |. 8BF0 MOV ESI,EAX 004010A2 |. 85F6 TEST ESI,ESI 004010A4 |. 75 12 JNZ SHORT crackme.004010B8 004010A6 |. 68 AC714000 PUSH OFFSET crackme.??_C@_0BE@JFFMEGDH@Error?3?5Find 004010AB |. E8 50FFFFFF CALL crackme.PopMsg 004010B0 |. 83C4 04 ADD ESP,4 004010B3 |. E9 F2000000 JMP crackme.004011AA 004010B8 |> 57 PUSH EDI 004010B9 |. 56 PUSH ESI 004010BA |. 53 PUSH EBX 004010BB |. FF15 14704000 CALL DWORD PTR DS:[<&KERNEL32.SizeofResource>] 004010C1 |. 8BF8 MOV EDI,EAX 004010C3 |. 85FF TEST EDI,EDI 004010C5 |. 75 12 JNZ SHORT crackme.004010D9 004010C7 |. 68 94714000 PUSH OFFSET crackme.??_C@_0BG@DHCIIEPK@Error?3?5Size 004010CC |. E8 2FFFFFFF CALL crackme.PopMsg 004010D1 |. 83C4 04 ADD ESP,4 004010D4 |. E9 D0000000 JMP crackme.004011A9 004010D9 |> 56 PUSH ESI 004010DA |. 53 PUSH EBX 004010DB |. FF15 10704000 CALL DWORD PTR DS:[<&KERNEL32.LoadResource>] -----
詳細なプログラムはともかくとして、とりあえず、FindResource関数やLoadResource関数が呼び出されていることが確認できます。これらはつまり、実行ファイル内のリソースにアクセスしていることを意味します。
----- crackme.exe 00401116 |> 6A 40 PUSH 40 00401118 |. 68 00100000 PUSH 1000 0040111D |. 57 PUSH EDI 0040111E |. 6A 00 PUSH 0 00401120 |. FF15 08704000 CALL DWORD PTR DS:[<&KERNEL32.VirtualAlloc>] -----
次にVirtualAlloc関数を呼び出し適当なメモリ領域を確保。そして、リソース内にあるデータをVirtualAlloc関数で確保した領域へコピーしています。これらはマシン語を読んでいけば用意に理解できるはずです。さて、問題はここからです。メモリ領域にコピーした怪しげなデータは、このままではただの怪しげなデータです。というのも、このデータは実は暗号化されています。よって、このデータを復号化することが必要であり、その復号化処理をReverseData関数が受け持っています。
----- crackme.exe 0040115D |. E8 3E050000 CALL crackme.ReverseData -----
この関数が呼び出されると、メモリ内にある怪しげなデータを復号化します。そして、復号化されたデータは、PEフォーマットに従った実行ファイルのバイナリとなっていることが分かります。そして、この実行ファイルのバイナリを実行してくれる関数(loader)が、CreateExeProcess関数です。
----- crackme.exe 0040117D |. 56 PUSH ESI 0040117E |. E8 7D040000 CALL crackme.CreateExeProcess
この関数には、実行ファイルのバイナリのアドレスを渡さなければならないため、ESIレジスタにそれが格納されているのが分かります。私の環境ではESIレジスタは「003D0000」となっています。よって、このアドレスのデータが実行ファイルのバイナリデータというわけです。
あとは、アドレス「003D0000」以降のデータを取得して、実行ファイルとして保存してやれば、無事、パッキング前のファイルとなります。あとは、この実行ファイルを解析すれば(これはとても簡単)、無事パスワード取得となります。ちなみにパスワードは、実行ファイルの方は「2006020515」で、ZIPファイルの方は「200602051500yen」です。
とりあえず、「解答編」ということで、アンパッキング(?)に的を絞って解説させていただきました。詳細な動作説明や仕組みについては、本編のローダー(Loader)とパッカー(Packer)とカスタムリソースの話にて解説しているので、そちらと合わせて読んでもらえたらと思います。