crackme#10 解析手引き 上巻

作家さんからの連絡 #10は、長いので2回に分けての解析になるそうです。


まず、始めにUPXで、crkme10.exeを解凍します。
「upx -d crkme10.exe」です。(コマンドプロンプト等で実行してください。)
もしくは、UPXMASKを使って解凍します。GUIなので簡単だと思います。
ファイルサイズが、33,792バイトになります。



OllyDBGを、起動して「crkme10.exe」を読込んでください。
CPUウィンドウで、右クリックをして、「検索」→「ラベル一覧」(「Search for」→「Name(Label)」)で、ラベル一覧の一覧を、表示します。


Names in crkme10
アドレス  セクション  タイプ ( 名前 コメント 
00401000 .text Export <ModuleEntryPoint>
00402000 .rdata Import ( KERNEL32.GetVolumeInformationA
00402004 .rdata Import ( KERNEL32.GetModuleHandleA
00402008 .rdata Import ( KERNEL32.ExitProcess
00402010 .rdata Import ( USER32.LoadIconA
00402014 .rdata Import ( USER32.MessageBoxA
00402018 .rdata Import ( USER32.GetDlgItem
0040201C .rdata Import ( USER32.EndDialog
00402020 .rdata Import ( USER32.SetWindowTextA
00402024 .rdata Import ( USER32.ShowWindow
00402028 .rdata Import ( USER32.GetWindowTextA ←ココ
0040202C .rdata Import ( USER32.DialogBoxParamA
00402030 .rdata Import ( USER32.CallWindowProcA
00402034 .rdata Import ( USER32.SendMessageA
00402038 .rdata Import ( USER32.SetWindowLongA

「USER32.GetWindowTextA」を選択して、「Enter」キーを押してください。
「References in crkme10:.text to USER32.GetWindowTextA」のウィンドウが表示されます。

References in crkme10:.text to USER32.GetWindowTextA
アドレス  逆アセンブル  コメント 
00401282 CALL <JMP.&USER32.GetWindowTextA>        ←ココ
0040150C JMP DWORD PTR DS:[<&USER32.GetWindowText USER32.GetWindowTextA

Address 00401282 call <jmp.&USER32.GetWindowTextA> にブレークポイントを設定します。

「F9」キーで実行します。
crackme #10のウィンドウが、表示されました。
今回のウィンドウでは、ID値が表示されてます。
よって、パスが同じでないことを心得といてください。
フェークパス「1234567890123456789」と入力して、「登録」ボタンクリックします。
Address 00401282 call <jmp.&USER32.GetWindowTextA> で止まります。


00401275 > 6A 7E PUSH 7E ; /Count = 7E (126.)
00401277 . 68 30304000 PUSH crkme10.00403030 ; |Buffer = crkme10.00403030
0040127C . FF35 04304000 PUSH DWORD PTR DS:[403004] ; |hWnd = 0005010A (class='Edit',parent=000900E4)
00401282 . E8 85020000 CALL <JMP.&USER32.GetWindowTextA> ; \GetWindowTextA         ←ココ
00401287 . 85C0 TEST EAX,EAX
00401289 . 74 1E JE SHORT crkme10.004012A9
0040128B . 83F8 13 CMP EAX,13
0040128E . 74 34 JE SHORT crkme10.004012C4
00401290 > 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL
00401292 . 68 F1304000 PUSH crkme10.004030F1 ; |Title = "登録情報"4030
00401297 . 68 1F314000 PUSH crkme10.0040311F ; |Text = "不正解です。"0311F
0040129C . FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hOwner
0040129F . E8 74020000 < CALL JMP.&USER32.MessageBoxA> ; \MessageBoxA
004012A4 .^E9 F5FDFFFF JMP crkme10.0040109E
004012A9 > 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL
004012AB . 68 F1304000 PUSH crkme10.004030F1 ; |Title = "登録情報"4030
004012B0 . 68 FA304000 PUSH crkme10.004030FA ; |Text = "シリアルナンバーを入力してください。" 900E4
004012B5 . FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hOwner
004012B8 . E8 5B020000 CALL <JMP.&USER32.MessageBoxA> ; \MessageBoxA
004012BD .^E9 DCFDFFFF JMP crkme10.0040109E
004012C2 90 NOP
004012C3 90 NOP
004012C4 > 33C9 XOR ECX,ECX
004012C6 . BE F2314000 MOV ESI,crkme10.004031F2
004012CB . BF 50314000 MOV EDI,crkme10.00403150 ; ASCII "BINARYEDIT"
004012D0 . B8 30304000 MOV EAX,crkme10.00403030
004012D5 . 8B00 MOV EAX,DWORD PTR DS:[EAX]
004012D7 > 50 PUSH EAX
004012D8 . 33C0 XOR EAX,EAX
004012DA . 02044E ADD AL,BYTE PTR DS:[ESI+ECX*2]
004012DD . 02444E 01 ADD AL,BYTE PTR DS:[ESI+ECX*2+1]
004012E1 . 33D2 XOR EDX,EDX
004012E3 . BB 0A000000 MOV EBX,0A
004012E8 . F7F3 DIV EBX
004012EA . 0FB61417 MOVZX EDX,BYTE PTR DS:[EDI+EDX]
004012EE . 58 POP EAX
004012EF . 38D0 CMP AL,DL
004012F1 .^75 9D JNZ SHORT crkme10.00401290
004012F3 . C1E8 08 SHR EAX,8
004012F6 . 83C7 10 ADD EDI,10
004012F9 . 41 INC ECX
004012FA . 83F9 04 CMP ECX,4
004012FD . 73 04 JNB SHORT crkme10.00401303
004012FF .^EB D6 JMP SHORT crkme10.004012D7
00401301 90 NOP
00401302 90 NOP
00401303 > BE 30304000 MOV ESI,crkme10.00403030
00401308 . 83C6 04 ADD ESI,4
0040130B . 0FBE06 MOVSX EAX,BYTE PTR DS:[ESI]
0040130E . 83F8 2D CMP EAX,2D
00401311 . 74 07 JE SHORT crkme10.0040131A
00401313 .^E9 78FFFFFF JMP crkme10.00401290
00401318 90 NOP
00401319 90 NOP
0040131A > 46 INC ESI
0040131B . 6A 04 PUSH 4
0040131D . 56 PUSH ESI
0040131E . E8 93010000 CALL crkme10.004014B6
00401323 . 83F8 FF CMP EAX,-1
00401326 .^0F84 64FFFFFF JE crkme10.00401290
0040132C . 50 PUSH EAX
0040132D . BE E6314000 MOV ESI,crkme10.004031E6 ; ASCII "ID:46484833"
00401332 . 83C6 03 ADD ESI,3
00401335 . 6A 04 PUSH 4
00401337 . 56 PUSH ESI
00401338 . E8 79010000 CALL crkme10.004014B6
0040133D . 5A POP EDX
0040133E . 33C2 XOR EAX,EDX
00401340 . 66:C1C0 04 ROL AX,4
00401344 . 35 DCFE0000 XOR EAX,0FEDC
00401349 . 66:C1C8 04 ROR AX,4
0040134D . 35 98BA0000 XOR EAX,0BA98
00401352 . C0C4 04 ROL AH,4
00401355 . 35 54760000 XOR EAX,7654
0040135A . C0C8 04 ROR AL,4
0040135D . 35 10320000 XOR EAX,3210
00401362 . 50 PUSH EAX
00401363 . BE E6314000 MOV ESI,crkme10.004031E6 ; ASCII "ID:46484833"
00401368 . 83C6 07 ADD ESI,7
0040136B . 6A 04 PUSH 4
0040136D . 56 PUSH ESI
0040136E . E8 43010000 CALL crkme10.004014B6
00401373 . 59 POP ECX
00401374 . 3BC1 CMP EAX,ECX
00401376 . 74 07 JE SHORT crkme10.0040137F
00401378 .^E9 13FFFFFF JMP crkme10.00401290
0040137D 90 NOP
0040137E 90 NOP
0040137F > BE 30304000 MOV ESI,crkme10.00403030


「F8」キーで、実行します。

00401287 . 85C0 TEST EAX,EAX

これは、パスの未入力チェックです。
未入力だと入力画面に戻っています。
「F7」キーを2回押してください。

0040128B . 83F8 13 CMP EAX,13

ここは、パス長のチェックです。
パス長は、16進数で 0x13(10進数で19)文字となります。
「F7」キーを3回押してください。

004012C6 . BE F2314000 MOV ESI,crkme10.004031F2

ここで止まってます。
crkme10.004031F2の内容を見てみましょう。
右クリックでメニュー表示して、「内容表示ダンプ→直接の定数」と指定してください。
アセンブラリストの下のダンプ部分に表示されます。
ここには、crackme #10のウィンドウで表示されていた、IDの値が1桁毎に、バイナリーで保存されてます。
「F7」キーを押してください。

004012CB . BF 50314000 MOV EDI,crkme10.00403150 ; ASCII "BINARYEDIT"

ここは、パス生成用のテーブルのアドレスを、ediレジスタに代入してます。
「F7」キーを押してください。

004012D0 . B8 30304000 MOV EAX,crkme10.00403030 ; ASCII "1234567890123456789"

ここは、入力パス域のアドレスをeaxレジスタに代入してます。
「F7」キーを押してください。

004012D5 . 8B00 MOV EAX,DWORD PTR DS:[EAX]

ここで、パス上位4バイトを、eaxレジスタに読込んでます。
アセンブラリストをよく見ると、Address 004012D7 ~ 004012FF でループしているのが分かります。
このループで、IDの値を使用して算出した値で、crkme10.00403150 ; ASCII "BINARYEDIT"のテーブルを参照、入力されたパスと比較しています。
「F7」キーを9回押してください。

004012EF . 38D0 CMP AL,DL

ここで止まってます。
alレジスタ=フェークパスの1文字 dlレジスタ=正解パスの比較を行っています。
まず、1文字目のパスが、dlレジスタ値となります。
EAXレジスタの最下位1バイトを、dlレジスタと同じに変更します。
ここで、効率良くパスを出す為に、

004012EF . 38D0 CMP AL,DL                    ←ココと
004012F1 .^75 9D JNZ SHORT crkme10.00401290
004012F3 . C1E8 08 SHR EAX,8
004012F6 . 83C7 10 ADD EDI,10
004012F9 . 41 INC ECX
004012FA . 83F9 04 CMP ECX,4
004012FD . 73 04 JNB SHORT crkme10.00401303
004012FF .^EB D6 JMP SHORT crkme10.004012D7
00401301 90 NOP
00401302 90 NOP
00401303 > BE 30304000 MOV ESI,crkme10.00403030 ; ASCII "1234567890123456789"  ←ココ(ループの出口)

にブレークポイントをセットします。

「F9」を押して、cmp al,dl で alレジスタ値をdlレジスタと同じに変更しながら、

00401303 > BE 30304000 MOV ESI,crkme10.00403030 ; ASCII "1234567890123456789"  

(ループの出口) まで、実行させます。

ここで、パスの4文字が出ました。
そしてここで、入力パス域のアドレスを、esiレジスタに代入してます。
「F7」キーを押してください。

00401308 . 83C6 04 ADD ESI,4

ここで、esiレジスタ+4 esiレジスタが指してるアドレスは、パスの5文字となります。
「F7」キーを2回押してください。

0040130E . 83F8 2D CMP EAX,2D

ここは、パスの5文字目の比較を行ってます。
パスの5文字目は、0x2D(ASCII '-')となります。
「F7」キーを押して、フラグレジスタのZを、0(ゼロ)→1に変更した後、もう一度「F7」キーを押してください。

0040131A > 46 INC ESI ; crkme10.00403034

ここで止まってます。
ここで、esiレジスタ+1 esiレジスタが指してるアドレスは、パスの6文字となります。
「F7」キーを3回押してください。

0040131E . E8 93010000 CALL crkme10.004014B6

このコールで止まってます。
「F8」キーでコールを飛ばします。

ここで、EAXレジスタに注目です。 0x00006789 っとなってます。
これは、フェーク・パスの6文字目からの4文字を、バイナリに変換した値です。
「F7」キーを2回押してください。

0040132C . 50 PUSH EAX

ここで、eaxレジスタをスタック域に待避してます。
「F7」キーを押してください。

0040132D . BE E6314000 MOV ESI,crkme10.004031E6 ; ASCII "ID:XXXXXXX"

ここで、ASCII文字の「ID:XXXXXXXX」の文字列のアドレスを、esiレジスタに代入してます。
ID:以降は、皆さん違うので、XXXXXXXXっと表現してます。
「F7」キーを押してください。

00401332 . 83C6 03 ADD ESI,3

esiレジスタ+3 ASCII文字列の数字部分を参照するようにしてます。
「F7」キーを3回押してください。

00401338 . E8 79010000 CALL crkme10.004014B6

このコールで止まってます。
このcallは、先程と同じ処理で、ASCII数字文字列をバイナリに変換する処理です。
今回は、ID値の前4桁を変換します。
「F8」でコールを、飛ばします。

eaxレジスタに、ID文字列の前4文字をバイナリ変換した値が入ってます。

0040133D . 5A POP EDX

ここで、Address 0040132C で待避した値を、edxレジスタに入れてます。(フェーク・パスの6文字目からの4文字のバイナリ値)
「F7」キーを押してください。

0040133E . 33C2 XOR EAX,EDX
00401340 . 66:C1C0 04 ROL AX,4
00401344 . 35 DCFE0000 XOR EAX,0FEDC
00401349 . 66:C1C8 04 ROR AX,4
0040134D . 35 98BA0000 XOR EAX,0BA98
00401352 . C0C4 04 ROL AH,4
00401355 . 35 54760000 XOR EAX,7654
0040135A . C0C8 04 ROR AL,4
0040135D . 35 10320000 XOR EAX,3210


Address 0040133E ~ 0040135D までが、パスの計算処理です。
ここでの計算処理は、入力パスの6文字目からの4文字と、ID値の上位4文字の排他的論理和(xor)を行ってから
右もしくは、左のローテイトと、定数とのxorを行ってます。
「F7」キーを9回押してください。

00401362 . 50 PUSH EAX

ここで止まってます。
ここで、入力パスから算出した値を、スタック域に待避します。
「F7」キーを押してください。

00401363 . BE E6314000 MOV ESI,crkme10.004031E6 ; ASCII "ID:XXXXXXXX"

ここで、ASCII文字の「ID:XXXXXXXX」の文字列のアドレスを、esiレジスタに代入してます。
次の行で、esiレジスタ+7(ID値の5文字目を指します。)
「F7」キーを4回押してください。

0040136E . E8 43010000 CALL crkme10.004014B6

ID値の後4桁を変換します。
「F8」キーを押して、コールを飛ばします。

00401373 . 59 POP ECX

入力パスから算出した値を、スタック域よりecxレジスタに入れてます。
「F7」キーを押してください。

00401374 . 3BC1 CMP EAX,ECX

ここで、正解の計算値(IDの後半4文字のバイナリ値)と、入力パスから算出した値の比較です。
ここで、パスの6文字目からの4文字は、、Address 0040133E ~ 0040135D の計算結果が、IDの後半4文字のバイナリ値となる値になります。
「F7」キーを押して、フラグレジスタのZを、0(ゼロ)→1に変更した後、もう一度「F7」キーを押してください。

0040137F > BE 30304000 MOV ESI,crkme10.00403030 ; ASCII "1234567890123456789"

ここで止まってます。

正解パス 9文字目まではこれで終わりです。
後は、次回に続きます・・・。