crackme#03 解析手引き

この解析環境はWindows98 OllyDBG1.07(日本語化)で行っています。


OllyDBGを、起動して「crkme03.exe」を読込んでください。
画面左上には逆アセンブルリストが表示されていますが、
ここで右クリックして検索→ラベル一覧 を選択して下さい。
「検索」→「ラベル一覧」(「Search for」→「Name(Label)」)で、ラベル一覧の一覧を、表示します。

Names in crkme03
Address Section Type ( Name Comment
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.GetWindowTextA」は無いですね。
他に文字列を取得するAPIを探しましょう。「USER32.GetDlgItemTextA」が見つかりました。
「USER32.GetDlgItemTextA」を選択して、「Enter」キーを押してください。


References in crkme03:.text to USER32.GetDlgItemTextA
Address Disassembly Comment
004011F4 CALL <JMP.&USER32.GetDlgItemTextA> ←ココ
00401264 JMP DWORD PTR DS:[<&USER32.GetDlgItemTex USER32.GetDlgItemTextA       


「References in crkme03:.text to user32.USER32.GetDlgItemTextA」というウィンドウが表示されます。
Address 004011F4 call <jmp.&USER32.GetDlgItemTextA>を選択して、「F2」キーでブレークポイントを設定してください。
「F9」キーで動かしてみましょう。
crackme #03のウィンドウが、表示されました。
フェークパス「12345678」と入力して、「登録」ボタンクリックしてください。
先程、ブレークポイントを仕掛けた場所で止まります。
止まりましたか?

004011E8 > 6A 7E PUSH 7E ; /Count = 7E (126.)
004011EA . 68 2C304000 PUSH CRKME03.0040302C ; |Buffer = CRKME03.0040302C
004011EF . 6A 64 PUSH 64 ; |ControlID = 64 (100.)
004011F1 . FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hWnd
004011F4 . E8 6B000000 CALL <JMP.&USER32.GetDlgItemTextA> ; \GetDlgItemTextA
004011F9 . BE 2C304000 MOV ESI,CRKME03.0040302C ; ASCII "12345678"
004011FE . 33C9 XOR ECX,ECX
00401200 > BF 08314000 MOV EDI,CRKME03.00403108 ; ASCII "vP12NGgoQa"
00401205 . 8A0439 MOV AL,BYTE PTR DS:[ECX+EDI]
00401208 . 8BD9 MOV EBX,ECX
0040120A . 83E3 01 AND EBX,1
0040120D . 03C3 ADD EAX,EBX
0040120F . 3A0431 CMP AL,BYTE PTR DS:[ECX+ESI]
00401212 . 75 1F JNZ SHORT CRKME03.00401233
00401214 . 41 INC ECX
00401215 . 83F9 0B CMP ECX,0B
00401218 .^72 E6 JB SHORT CRKME03.00401200
0040121A . 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL
0040121C . 68 E7304000 PUSH CRKME03.004030E7 ; |Title = "登録情報"4030
00401221 . 68 FD304000 PUSH CRKME03.004030FD ; |Text = "正解です!"4030F
00401226 . FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hOwner
00401229 . E8 42000000 CALL <JMP.&USER32.MessageBoxA> ; \MessageBoxA
0040122E .^E9 6BFEFFFF JMP CRKME03.0040109E
00401233 > 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL
00401235 . 68 E7304000 PUSH CRKME03.004030E7 ; |Title = "登録情報"4030
0040123A . 68 F0304000 PUSH CRKME03.004030F0 ; |Text = "不正解です。"030F0
0040123F . FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hOwner
00401242 . E8 29000000 CALL <JMP.&USER32.MessageBoxA> ; \MessageBoxA
00401247 .^E9 52FEFFFF JMP CRKME03.0040109E
0040124C $-FF25 28204000 JMP DWORD PTR DS:[<&USER32.CallWindowProcA>]
00401252 $-FF25 18204000 JMP DWORD PTR DS:[<&USER32.DialogBoxParamA>]
00401258 $-FF25 1C204000 JMP DWORD PTR DS:[<&USER32.EndDialog>]
0040125E $-FF25 14204000 JMP DWORD PTR DS:[<&USER32.GetDlgItem>]
00401264 $-FF25 0C204000 JMP DWORD PTR DS:[<&USER32.GetDlgItemTextA>] ←ココ
0040126A $-FF25 10204000 JMP DWORD PTR DS:[<&USER32.LoadIconA>]
00401270 $-FF25 2C204000 JMP DWORD PTR DS:[<&USER32.MessageBoxA>]
00401276 $-FF25 30204000 JMP DWORD PTR DS:[<&USER32.SendMessageA>]
0040127C $-FF25 20204000 JMP DWORD PTR DS:[<&USER32.SetWindowLongA>]
00401282 $-FF25 24204000 JMP DWORD PTR DS:[<&USER32.ShowWindow>]
00401288 $-FF25 04204000 JMP DWORD PTR DS:[<&KERNEL32.ExitProcess>]
0040128E $-FF25 00204000 JMP DWORD PTR DS:[<&KERNEL32.GetMo


ここで、OllyDBGのCPUウィンドウに、注目してみましょう。
Address 00401200 mov edi, crkme03.00403108 ; ASCII "vP12NGgoQa"っと表示されてます。
なんか、正解パスみたいですね。 でも、ここでだまされては行けません!

Address 00401200 ~ 00401218 jb short crkme03.00401200 の処理で正解パスを比較しているようです。


004011F9 . BE 2C304000 MOV ESI,CRKME03.0040302C ; ASCII "12345678"
004011FE . 33C9 XOR ECX,ECX
00401200 > BF 08314000 MOV EDI,CRKME03.00403108 ; ASCII "vP12NGgoQa"
00401205 . 8A0439 MOV AL,BYTE PTR DS:[ECX+EDI]
00401208 . 8BD9 MOV EBX,ECX
0040120A . 83E3 01 AND EBX,1
0040120D . 03C3 ADD EAX,EBX
0040120F . 3A0431 CMP AL,BYTE PTR DS:[ECX+ESI]
00401212 . 75 1F JNZ SHORT CRKME03.00401233
00401214 . 41 INC ECX
00401215 . 83F9 0B CMP ECX,0B
00401218 .^72 E6 JB SHORT CRKME03.00401200


しかし、単純に比較している様子は無いですね。
Address 0040120F cmp al, byte ptr ds:[ecx+esi] で、パス1文字の比較ですが、前3行の処理が、どぉ~しても気になります。

まずは、動かして変化を見ていきましょう。
「F8」でcall <jmp.&USER32.GetDlgItemTextA>を飛ばします。
Address 004011F9 mov esi, crkme03.0040302C ; ASCII "12345678" フェークパスが読込まれて表示されてます。
Address 004011FE xor ecx, ecx ここでecxレジスタをクリアしてます。(これは、比較処理のループ・カウンタのようです。)
つまり、文字列の何文字目の値が入っています。
「F7」キーで、Address 00401205 8A0439 mov al, byte ptr ds:[ecx+edi]まで、実行させます。

Address 00401205まで実行させました。
ここでば、alレジスタ(eaxレジスタの最下位1バイト部分)に、正解パス?の文字列1文字を代入してます。
「F7」キーで、次に行きましょう。


00401205 . 8A0439 MOV AL,BYTE PTR DS:[ECX+EDI]    ←ココ 
00401208 . 8BD9 MOV EBX,ECX
0040120A . 83E3 01 AND EBX,1
0040120D . 03C3 ADD EAX,EBX

ebxレジスター値は、0(ゼロ)なので、eaxレジスタも変化ありません。
「F7」キーで、次に行きましょう。


0040120F . 3A0431 CMP AL,BYTE PTR DS:[ECX+ESI]


Address 0040120F cmp al, byte ptr ds:[ecx+esi] ここで正解パスと比較しています。
CPUウィンドウの下のほうに、ds:[0040302C]=31('1')と、al=76('v')が表示されてます。
表示されてますか?


ds:[0040302C]=31('1') ←これは、入力したフェークパスです。
al=76('v')←これが正解パスです。


前回までは、比較後フラグレジスターを変えていましたが、今日はフェークパスの部分を書換えます。
CPUウィンドウの下のほうに、ds:[0040302C]=31('1') の行を選択します。
その後、右クリックで、データ修正(Modify Data)を選択します。
「Modify byte at ・・・・・」ってウィンドウが表示されます。
Hexadecimal(16進)の値に、alレジスターと同じ値(76)を入力後、「OK」をクリックします。


「F7」キーを何回か押して、Address 0040120D add eax, ebx まで実行させます。


0040120D . 03C3 ADD EAX,EBX


ADD は算術演算命令で、この場合はEAXとEBXを足して、EAXに格納しています。

and ebx, 1 の結果は、1になってます。
よって、0x50 + 1 となり、ASCII文字では、'Q'となります。
「F7」キーで、次に行きましょう。


0040120F . 3A0431 CMP AL,BYTE PTR DS:[ECX+ESI]


cmp al, byte ptr ds:[ecx+esi] CPUウィンドウの下のほうに、ds:[0040302D]=32('2')と、al=51('Q')が表示されてます。
また、ds:[0040302D]の値を、0x51に変更します。

現在、止まっている行 Address 0040120F cmp al, byte ptr ds:[ecx+esi] に、「F2」キーでブレークポイントを仕掛けます。
BP掛けおわりましたか?

「F9」で実行して、ds:[ecx+esi]のメモリー値をalレジスタと同じに変更
上記を、繰返して行います。

「正解です!」とダイアログ表示されたら、Address 004011F9 mov esi, crkme03.0040302C のコメント部分に正解パスが表示されてます。

ここで、Address 0040120F cmp命令の前3行の処理を、簡単に説明します。


00401208 . 8BD9 MOV EBX,ECX
0040120A . 83E3 01 AND EBX,1
0040120D . 03C3 ADD EAX,EBX


MOV とはデータ転送命令で、この場合はEBXにECXの内容を転送しているということです。
AND とは論理演算命令で、積(掛算)の事です。
この場合は、EBXと1の論理積なので、
EBXが0なら
0∧1=0 になります。
また、EBXが1ならば、
1∧1=1 になります。

0x50 とは16進数ですので、ASCIIコード表を見ればASCII文字に置き換えれます。
チュートの文を借りると、

>0x50 + 1 となり、ASCII文字では、'Q'となります。

といった具合になるわけです。
後は実際にOllyを動かしながら数値を見ていけば少しずつ分かるようになってくると思います。


始めに表示されていた、正解?パス"vP12NGgoQa"の偶数文字を変更しています。
変更方法は、ASCIIコード+1となっています。