crackme#09 解析手引き

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


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

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

「USER32.GetDlgItemTextA」を選択して、「Enter」キーを押してください。

References in crkme09:.text to USER32.GetDlgItemTextA
アドレス  逆アセンブル  コメント 
00401203 CALL <JMP.&USER32.GetDlgItemTextA>
00401232 CALL <JMP.&USER32.GetDlgItemTextA>
00401390 JMP DWORD PTR DS:[<&USER32.GetDlgItemTextA>] USER32.GetDlgItemTextA

「References in crkme09:.text to USER32.GetDlgItemTextA」のウィンドウが表示されます。
Address 00401203 call <jmp.&USER32.GetDlgItemTextA >に「F2」でブレークポイントを設定します。

「F9」キーで実行します。
crackme #09のウィンドウが、表示されました。
Nameに、「abc」 Passに、「1234567890123456789012」と入力して、「登録」ボタンクリックしてください。


004011F7 > 6A 7E PUSH 7E ; /Count = 7E (126.)
004011F9 . 68 30304000 PUSH crkme09.00403030 ; |Buffer = crkme09.00403030
004011FE . 6A 64 PUSH 64 ; |ControlID = 64 (100.)
00401200 . FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hWnd
00401203 . E8 88010000 CALL <JMP.&USER32.GetDlgItemTextA> ; \GetDlgItemTextA       ←ココ
00401208 . 83F8 00 CMP EAX,0
0040120B . 75 19 JNZ SHORT crkme09.00401226
0040120D . 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL
0040120F . 68 95314000 PUSH crkme09.00403195 ; |Title = "登録情報"4031
00401214 . 68 B6314000 PUSH crkme09.004031B6 ; |Text = "名前を入力してください。"
00401219 . FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hOwner
0040121C . E8 7B010000 CALL <JMP.&USER32.MessageBoxA> ; \MessageBoxA
00401221 .^E9 78FEFFFF JMP crkme09.0040109E
00401226 > 6A 7E PUSH 7E ; /Count = 7E (126.)
00401228 . 68 B0304000 PUSH crkme09.004030B0 ; |Buffer = crkme09.004030B0
0040122D . 6A 65 PUSH 65 ; |ControlID = 65 (101.)
0040122F . FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hWnd
00401232 . E8 59010000 CALL <JMP.&USER32.GetDlgItemTextA> ; \GetDlgItemTextA
00401237 . 83F8 00 CMP EAX,0
0040123A . 75 19 JNZ SHORT crkme09.00401255
0040123C . 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL
0040123E . 68 95314000 PUSH crkme09.00403195 ; |Title = "登録情報"4031
00401243 . 68 CF314000 PUSH crkme09.004031CF ; |Text = "パスを入力してください。"
00401248 . FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hOwner
0040124B . E8 4C010000 CALL <JMP.&USER32.MessageBoxA> ; \MessageBoxA
00401250 .^E9 49FEFFFF JMP crkme09.0040109E
00401255 > 83F8 16 CMP EAX,16
00401258 . 74 19 JE SHORT crkme09.00401273
0040125A . 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL
0040125C . 68 95314000 PUSH crkme09.00403195 ; |Title = "登録情報"4031
00401261 . 68 9E314000 PUSH crkme09.0040319E ; |Text = "不正解です。"0319E
00401266 . FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hOwner
00401269 . E8 2E010000 CALL <JMP.&USER32.MessageBoxA> ; \MessageBoxA
0040126E .^E9 2BFEFFFF JMP crkme09.0040109E
00401273 > E8 3D000000 CALL crkme09.004012B5
00401278 . E8 AB000000 CALL crkme09.00401328
0040127D . BA 95314000 MOV EDX,crkme09.00403195 ; ASCII "登録情報"09.0
00401282 . BE AB314000 MOV ESI,crkme09.004031AB ; ASCII "正解です!"9.004
00401287 . 48 DEC EAX
00401288 . 85C0 TEST EAX,EAX
0040128A . 74 05 JE SHORT crkme09.00401291
0040128C . BE 9E314000 MOV ESI,crkme09.0040319E ; ASCII "不正解です。".00403
00401291 > 52 PUSH EDX
00401292 . 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL
00401294 . 52 PUSH EDX ; |Title
00401295 . 56 PUSH ESI ; |Text
00401296 . FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hOwner
00401299 . E8 FE000000 CALL <JMP.&USER32.MessageBoxA> ; \MessageBoxA
0040129E . 5A POP EDX
0040129F . 83EA 12 SUB EDX,12
004012A2 . 33C9 XOR ECX,ECX
004012A4 > C602 00 MOV BYTE PTR DS:[EDX],0
004012A7 . 42 INC EDX
004012A8 . 41 INC ECX
004012A9 . 83F9 11 CMP ECX,11
004012AC . 73 02 JNB SHORT crkme09.004012B0
004012AE .^EB F4 JMP SHORT crkme09.004012A4
004012B0 >^E9 E9FDFFFF JMP crkme09.0040109E


Address 00401203 call <jmp.&USER32.GetDlgItemTextA>で止まります。
「F8」キーで、実行します。

00401208 . 83F8 00 CMP EAX,0

ここでは、Nameが未入力かチェックしています。
気にせずに、「F8」キーを、7回押します。
GetDlgItemTextAを実行して Address 00401237 cmp eax,0 で止まってます。
ここも、Passの未入力チェックです。
「F7」キーを、2回押します。

00401255 > 83F8 16 CMP EAX,16

ここで、Passの長さチェックをしています。(よって、Passの長さは、0x16(22)文字です。
「F7」キーを、2回押します。

00401273 > E8 3D000000 CALL crkme09.004012B5

その下にも、callがあります。

00401278 . E8 AB000000 CALL crkme09.00401328

2つのcallの後に、cmp命令が見当たりません
コールの中を追ってみましょう。
「F7」キーで、コール先に飛んでください。


004012B5 /$ 33C0 XOR EAX,EAX
004012B7 |. BE 30304000 MOV ESI,crkme09.00403030 ; ASCII "abc"
004012BC |> 803E 00 /CMP BYTE PTR DS:[ESI],0
004012BF |. 74 17 |JE SHORT crkme09.004012D8
004012C1 |. 0FBE0E |MOVSX ECX,BYTE PTR DS:[ESI]
004012C4 |. 81F1 5B7CCB3D |XOR ECX,3DCB7C5B
004012CA |. 81C1 F56ED76F |ADD ECX,6FD76EF5
004012D0 |. C1C1 07 |ROL ECX,7
004012D3 |. 03C1 |ADD EAX,ECX
004012D5 |. 46 |INC ESI
004012D6 |.^EB E4 \JMP SHORT crkme09.004012BC
004012D8 |> BE 83314000 MOV ESI,crkme09.00403183           ASCII "ASM8SM45" ←004012F7に来ると表示
004012DD |. 50 PUSH EAX
004012DE |> 8BD8 /MOV EBX,EAX
004012E0 |. 83E3 0F |AND EBX,0F
004012E3 |. 0FBE8B E831400>|MOVSX ECX,BYTE PTR DS:[EBX+4031E8]
004012EA |. 880E |MOV BYTE PTR DS:[ESI],CL
004012EC |. 46 |INC ESI
004012ED |. C1E8 04 |SHR EAX,4
004012F0 |. 83F8 00 |CMP EAX,0
004012F3 |. 74 02 |JE SHORT crkme09.004012F7
004012F5 |.^EB E7 \JMP SHORT crkme09.004012DE
004012F7 |> C606 2D MOV BYTE PTR DS:[ESI],2D
004012FA |. 58 POP EAX
004012FB |. C1C0 0D ROL EAX,0D
004012FE |. 35 5B7CCB3D XOR EAX,3DCB7C5B
00401303 |. C1C0 07 ROL EAX,7
00401306 |. B9 09000000 MOV ECX,9
0040130B |> 8BD8 /MOV EBX,EAX
0040130D |. 83E3 0F |AND EBX,0F
00401310 |. 83C3 10 |ADD EBX,10
00401313 |. 0FBE8B E831400>|MOVSX ECX,BYTE PTR DS:[EBX+4031E8]
0040131A |. 46 |INC ESI
0040131B |. 880E |MOV BYTE PTR DS:[ESI],CL
0040131D |. C1E8 04 |SHR EAX,4
00401320 |. 83F8 00 |CMP EAX,0
00401323 |. 74 02 |JE SHORT crkme09.00401327
00401325 |.^EB E4 \JMP SHORT crkme09.0040130B
00401327 \> C3 RETN


004012B5 /$ 33C0 XOR EAX,EAX  ←ここに来ました。

そして、下に先程入力した、フェークネーム「abc」が表示されてます。
この時点での推測ですが、Nameから、Passを生成する処理のようです。
「F7」キーを何回か押して、

004012F7 |> C606 2D MOV BYTE PTR DS:[ESI],2D

ここまで実行させます。
すると、画面中央付近に、「ASCII "ASM8SM45"」と表示されてます。
Address crkme09.00403183 に生成Passが入る様です。
ここで「Ctrl」+「F9」で、callのリターンまで実行させます。

00401327 \> C3 RETN

ここで止まります。

また、画面中央付近に Address crkme09.00403183 の参照があり、その内容が表示されてます。
その結果を、メモして置きましょう。

00401357 |. BA 83314000 MOV EDX,crkme09.00403183 ; ASCII "ASM8SM45-6TBDTTPN"  ←ココ


「F7」キーで、リターンします。

00401278 . E8 AB000000 CALL crkme09.00401328

ここに戻りました。
今度は、Passの比較処理に飛ぶと推測できます。
F7」キーで、コール先に飛びます。


00401328 /$ BE B0304000 MOV ESI,crkme09.004030B0 ; ASCII "1234567890123456789012"
0040132D |. 8B0E MOV ECX,DWORD PTR DS:[ESI]
0040132F |. 81E9 32413A28 SUB ECX,283A4132
00401335 |. 81F9 11111111 CMP ECX,11111111
0040133B |. 74 03 JE SHORT crkme09.00401340
0040133D |. 33C0 XOR EAX,EAX
0040133F |. C3 RETN
00401340 |> 83C6 04 ADD ESI,4
00401343 |. 0FBE0E MOVSX ECX,BYTE PTR DS:[ESI]
00401346 |. 83E9 2E SUB ECX,2E
00401349 |. 83F9 FF CMP ECX,-1
0040134C |. 74 03 JE SHORT crkme09.00401351
0040134E |. 33C0 XOR EAX,EAX
00401350 |. C3 RETN
00401351 |> 46 INC ESI
00401352 |. BB 10000000 MOV EBX,10
00401357 |. BA 83314000 MOV EDX,crkme09.00403183 ; ASCII "ASM8SM45-6TBDTTPN"
0040135C |> 0FBE0A /MOVSX ECX,BYTE PTR DS:[EDX]
0040135F |. 0FBE0433 |MOVSX EAX,BYTE PTR DS:[EBX+ESI]
00401363 |. 2BC8 |SUB ECX,EAX
00401365 |. 75 0D |JNZ SHORT crkme09.00401374
00401367 |. 4B |DEC EBX
00401368 |. 42 |INC EDX
00401369 |. 83FB 00 |CMP EBX,0
0040136C |.^7D EE \JGE SHORT crkme09.0040135C
0040136E |. B8 01000000 MOV EAX,1
00401373 |. C3 RETN
00401374 |> 33C0 XOR EAX,EAX
00401376 \. C3 RETN


00401328 /$ BE B0304000 MOV ESI,crkme09.004030B0 ; ASCII "1234567890123456789012"

ここに来ました。
ここで、4行程アセンブラ命令を、見てみましょう。

00401328 /$ BE B0304000 MOV ESI,crkme09.004030B0 ; ASCII "1234567890123456789012" ← パス域のアドレスを、esiレジスタに代入
0040132D |. 8B0E MOV ECX,DWORD PTR DS:[ESI]      ← パス先頭の4バイトを、ecxレジスタに代入
0040132F |. 81E9 32413A28 SUB ECX,283A4132           ← ecxレジスタ - 0x283A4132
00401335 |. 81F9 11111111 CMP ECX,11111111           ← 引き算の結果が、0x11111111か比較と、先頭4バイトの比較を行っています。



よって、0x283A4132 + 0x11111111 = 0x394B5243 (ASCII "9KRC") となります。
ここで、リトルエンディアン方式を思い出し、逆から読み直して、"CRK9"となります。
これで、先頭4文字のPassが出ました。
「F7」キーを2回押して、ecxレジスタの値を、0x394B5243 に変更します。

次に、「F7」キーを、4回押します。

00401343 |. 0FBE0E MOVSX ECX,BYTE PTR DS:[ESI]

ここで止まってます。
ここ行の上で、「add esi,4」esiレジスタ + 4 を行っていますので、パス域の5バイト目の参照になります。
また、先程と同じ様に、sub と cmp ですね。(ただし、引き算の結果が、-1です。)
0x2e - 1 = 0x2d(ASCII "-") これが、Passの5文字目です。
「F7」キーを、1回押して、ecxレジスタの値を、0x2dに変更します。

「F7」キーを、数回押して、Address 00401351 inc esi まで実行させます。
ここで、esiレジスタ + 1 を行ってます。(esiレジスター値は、パス域の6文字目アドレスになります。)
さらに、Address crkme09.00403183 (以前に、Nameより生成したPass)の参照があります。
Address 0040135C ~ 0040136C ループして、6文字以降のPassを比較してます。

比較処理を、見てみましょう。


00401352 |. BB 10000000 MOV EBX,10              ←ebxレジスタに、0x10を代入(入力Pass参照用Index)
00401357 |. BA 83314000 MOV EDX,crkme09.00403183 ; ASCII "ASM8SM45-6TBDTTPN"  ←edxレジスタに、生成Pass域のアドレスを代入
0040135C |> 0FBE0A /MOVSX ECX,BYTE PTR DS:[EDX]    ←生成Pass 1文字読込み
0040135F |. 0FBE0433 |MOVSX EAX,BYTE PTR DS:[EBX+ESI]  ←入力Pass 1文字読込み(但し、ebxレジスタで修飾している為に、後ろから参照。)
00401363 |. 2BC8 |SUB ECX,EAX             ←生成Pass 1文字 - 入力Pass 1文字
00401365 |. 75 0D |JNZ SHORT crkme09.00401374     ←引き算の結果が、0(ゼロ)以外ならジャンプ(ここでジャンプすると、NG)
00401367 |. 4B |DEC EBX               ←入力Pass参照用Index - 1
00401368 |. 42 |INC EDX               ←生成Passアドレス + 1
00401369 |. 83FB 00 |CMP EBX,0              ←入力Pass参照用Index値を比較
0040136C |.^7D EE \JGE SHORT crkme09.0040135C     ←入力Pass参照用Index ≧ 0(ゼロ)なら、ジャンプ


ここで、注意するべき点は、生成Passと、入力したPassを読込む順序が逆って事です。(生成Passを、逆に見れば入力Passです。)

これで、Name:abc の時のすべてのPassが出ました。

"CRK9-NPTTDBT6-54MS8MSA"となります。

#9のPass生成部分は、32文字の配列に見えますが、配列先頭16文字からパス8文字、配列後半16文字からパス8文字を出してます。