OllyDbg を起動して、「crackme02.exe《を読み込んで下さい。 ラベル一覧を開いて、その中から GetWindowTextA を選んで下さい。 ラベル ネーム crkme02 アドレス セクション タイプ ( ネーム 00402028 .rdata Import ( USER32.CallWindowProcA 00402018 .rdata Import ( USER32.DialogBoxParamA 0040201C .rdata Import ( USER32.EndDialog 00402004 .rdata Import ( KERNEL32.ExitProcess 00402014 .rdata Import ( USER32.GetDlgItem 00402000 .rdata Import ( KERNEL32.GetModuleHandleA 0040200C .rdata Import ( USER32.GetWindowTextA ←ココ 00402010 .rdata Import ( USER32.LoadIconA 0040202C .rdata Import ( USER32.MessageBoxA 00402030 .rdata Import ( USER32.SendMessageA 00402020 .rdata Import ( USER32.SetWindowLongA 00402024 .rdata Import ( USER32.ShowWindow 00401000 .text Export <モジュールエントリーポイント> GetWindowTextA上で右クリック -> 「インポート関数の一覧表示《を選択して、二個あるのでその内の上の1つだけに右クリック -> 「ブレークポイントセット《をクリックします。GetWindowTextA にブレークポイントがセットされるはずです。下のもう1個方までブレークポイントを仕掛けてしまうと、後々少し面倒のので、やめておいたほうがいいです。 逆アセンブラ画面に戻って、F9 を押します。 すると、crackme#02 ウィンドウが現れるはずです。。 crackme#02 ウィンドウ上のパス入力欄に適当なパスワードを入れて登録ボタンを押します。 OllyDbg に制御が移り、先ほど仕掛けたブレークポイントで止まります。 004011E8 > 6A 7E PUSH 7E ; /Count = 7E (126.) 004011EA . 68 2C304000 PUSH crkme02.0040302C ; |Buffer = crkme02.0040302C 004011EF . FF35 04304000 PUSH DWORD PTR DS:[403004] ; |hWnd = 009B07FC (class='Edit',parent=003E07DE) 004011F5 . E8 90000000 CALL ; ¥GetWindowTextA ←ココ 004011FA . 83F8 08 CMP EAX,8 004011FD . 75 22 JNZ SHORT crkme02.00401221 004011FF . E8 36000000 CALL crkme02.0040123A 00401204 . 85C0 TEST EAX,EAX 00401206 . 74 19 JE SHORT crkme02.00401221 F8を押して、cmp eax,8 の行に行って下さい。 004011FA . 83F8 08 CMP EAX,8 ←ココ 004011FD . 75 22 JNZ SHORT CRKME02.00401221 004011FF . E8 36000000 CALL CRKME02.0040123A 00401204 . 85C0 TEST EAX,EAX 00401206 . 74 19 JE SHORT CRKME02.00401221 ここで eax レジスタと 8 を比較しています。 eax レジスタには GetWindowTextA の戻り値の入力した文字数が入っています。 次の行を見てください。jnz 命令はzeroフラグが0でない時(つまり1の時)に指定されたアドレスにジャンプします。 では実際にこのアドレスの 00401221 を見てみましょう。 00401221 > 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL 00401223 . 68 EF304000 PUSH crkme02.004030EF ; |Title = "登録情報" 00401228 . 68 F8304000 PUSH crkme02.004030F8 ; |Text = "上正解です。" 0040122D . FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hOwner 00401230 . E8 61000000 CALL ; ¥MessageBoxA 見ればわかるように上正解の処理をしています。 つまり 004011FD の jnz 命令でジャンプすると上正解になってしまいます。 よって、ジャンプしないように eax = 8 にしてzeroフラグを0にしなければなりません。 そのためには文字数が8文字である必要があります。 次の call はひとまず置いておき、さらに次の test を見ます。 test命令は ソースオペランドとディスティネーションオペランドでAND(論理積)をして、演算結果を保存せず、フラグ変更のみ行う命令です。 この場合 eax = 0 のとき zeroフラグが立ち、それ以外だと立ちません。 このあとの je命令は zeroフラグが1の時にジャンプします。このジャンプ先アドレスを見ると先程の上正解の処理を行っていたのと同じアドレスです。 よって、ジャンプしてはならない -> zeroフラグが1 -> eax = 0以外 がわかります。 ゆえに、call内での処理で最終的に eax != 0 (逆に言えば eax = 0 にならない)になるような処理が正解パスです。 ここで、call内部へ入りたいため一回再スタートし、パス入力欄に8文字の適当なパスワードを入れましょう。 ブレークポイントで止まったら、F7 で call で飛ぶ先まで進めて下さい。 0040123A /$ 33C0 XOR EAX,EAX 0040123C |. BE 2C304000 MOV ESI,crkme02.0040302C ; ASCII "12345678" <- 入力したパス 00401241 |. 803E 35 CMP BYTE PTR DS:[ESI],35 00401244 |. 75 2B JNZ SHORT crkme02.00401271 00401246 |. 807E 01 45 CMP BYTE PTR DS:[ESI+1],45 0040124A |. 75 25 JNZ SHORT crkme02.00401271 0040124C |. 807E 02 48 CMP BYTE PTR DS:[ESI+2],48 00401250 |. 75 1F JNZ SHORT crkme02.00401271 00401252 |. 807E 03 39 CMP BYTE PTR DS:[ESI+3],39 00401256 |. 75 19 JNZ SHORT crkme02.00401271 00401258 |. 807E 04 56 CMP BYTE PTR DS:[ESI+4],56 0040125C |. 75 13 JNZ SHORT crkme02.00401271 0040125E |. 807E 05 33 CMP BYTE PTR DS:[ESI+5],33 00401262 |. 75 0D JNZ SHORT crkme02.00401271 00401264 |. 807E 06 51 CMP BYTE PTR DS:[ESI+6],51 00401268 |. 75 07 JNZ SHORT crkme02.00401271 0040126A |. 807E 07 57 CMP BYTE PTR DS:[ESI+7],57 0040126E |. 75 01 JNZ SHORT crkme02.00401271 00401270 |. 40 INC EAX 00401271 ¥> C3 RETN ここがパス解析部分です。 順を追って見てみましょう。 最初の xor は eax の初期化です。つまり、eax = 0 です。 次に esi に 0040302C を代入しています。 0040302C とは何でしょうか? これは GetWindowTextA で得た入力パスの文字列の先頭アドレスです。 004011EA で push して GetWindowTextA に渡しています。 よって、0040302C から順に文字列が格紊されることになります。 つまり、ここで文字列の先頭アドレスを esi にコピーしていることになります。 次に移ります。 cmp と jnz の繰り返しから、一回でもcmp の結果が0でない、つまり二つのオペランドが等しくない場合に 00401271 へジャンプします。 さてここで一回でもジャンプしてしまうと eax = 0 になります。直前の処理に inc eax というのがあります。inc命令はオペランドを1だけ増加させる命令です。 一回もジャンプしなければ、この命令が実行されるので eax != 0 になるためには一回もジャンプしてはならないことがわかります。 最後にパスの確定をしましょう。全ての cmp命令でソースオペランドとディスティネーションオペランドが一致すればジャンプしません。よって、正解パスは35 45 48 39 56 33 51 57、つまり5EH9V3QWです。