crackme#01 解析手引き

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

Ollyを立ち上げてcrkme01.exeを開いてください。
画面左上には逆アセンブルリストがズラズラ表示されていますが、
ここで右クリックして検索→ラベル一覧 を選択して下さい。
(OllyDBG1.07b以降は、検索→現在のモジュール名。)

すると、なにやら命令のリストが出力されるので、その中のGetWindowTextAを選択して下さい。

00401000 .text Export <ModuleENtryPoint>
00402000 .rdate Inport KERNEL32.LstrcmpA
00402004 .rdate Inport KERNEL32.GetModuleHandleA
00402008 .rdate Inport KERNEL32.ExitProcess
00402010 .rdate Inport user32.LoadIconA
00402014 .rdate Inport user32.DialogBoxParamA
00402018 .rdate Inport user32.GetDlgItem
0040201C .rdate Inport user32.SetWindowLongA
00402020 .rdate Inport user32.ShowWindow

00402024 .rdate Inport use32.GetWindowTextA    ←ココ
00402028 .rdate Inport user32.EndDialog
0040202C .rdate Inport user32.MessageBoxA
00402030 .rdate Inport user32.CallWindowProcA
00402034 .rdate Inport user32.SendMessageA


英語メニューの方は、「search for」→「label」です。
GetWindowTextA上でエンターキーを押して下さい。
すると、「References in crkme01:.text to USER32.GetWindowTextA」というウィンドウが開きます。

References in crkme01:.text to USER32.GetWindowTextA
Address Disassembly Comment
004011F5 CALL <JMP.&USER32.GetWindowTextA>
00401258 JMP DWORD PTR DS:[<&USER32.GetWindowText USER32.GetWindowTextA


ところで、このGetWindowTextAとは何か?
これは、画面上のテキストボックスから文字列を取得するAPIのひとつです。
これと似たAPIにGetDlgItemTextAがありますが、この命令は非常に重要なので覚えて下さい。

それではブレークポイントについて簡単に説明します。
ブレークポイントを仕掛けて、仕掛けた部分が実行されると、デバッガが処理を奪います。
キーチェックで使われるだろうAPIに仕掛けることでキーチェックルーチンを特定できます。


では、「004011F5 CALL <JMP.&USER32.GetWindowTextA>」にブレークポイントを仕掛けて下さい。
仕掛け方は、選択した後に「F2」を押します。(もしくは、右クリックで、ブレークポイントセット)
アドレスが赤くなればOKです。


そこまで出来ましたら、F9を押してプログラムを実行します。

crackme#01ウィンドウが現れました。
crackme#01ウィンドウ上のパス入力欄に適当なパスワード(フェイクパス:12345)を入れて登録ボタンを押しましょう。
OllyDbgに制御が移ります。

004011E8 > 6A 7E PUSH 7E ; /Count = 7E (126.)
004011EA . 68 2C304000 PUSH CRKME01.0040302C ; |Buffer = CRKME01.0040302C
004011EF . FF35 04304000 PUSH DWORD PTR DS:[403004] ; |hWnd = 00000AE0 (class='Edit',wndproc=0040102B,parent=00000AD4)
004011F5 . E8 5E000000 CALL <JMP.&USER32.GetWindowTextA> ; \GetWindowTextA   ←ココ


004011F5で止まりました。
今入力した「12345」を取得しています。

ここからトレースしていきます。

トレースはF7、F8で出来ますが、F7はcall内部に潜り、F8はcall内部に潜りません。
ここでF7を押すとGetWindowTextAの内部に潜ってしまいますので、1回F8を押しましょう。

*もし「References in crkme01:.text to USER32.GetWindowTextA」のウィンドウで、2つともブレークポイントを仕掛けてしまうと、
アドレス 00401258 で一度止まります。
 NT系のOS(2kやXP)環境では、ここで2回目のF8を押すと、OS部分(kernel32.dllの中)にはいってしまいます。
 その時は、F8を押さずに、慌てずすぐ [Ctrl]+[F9] を押しましょう。rtnまで自動で実行されます。
 その後、[F7]キーで、呼び出し元に戻れます。
 よく分らない場合は、Ollyで CTRL+F2 で再起動(または OLLYのメニューの「解析」→「再スタート」)して、
 もう一度ブレークポイントを仕掛け直しましょう。

 


4011FAで止まりましたね。

004011FA . 68 AC304000 PUSH CRKME01.004030AC ; /String2 = "doomo"     ←ココ
004011FF . 68 2C304000 PUSH CRKME01.0040302C ; |String1 = "123456"
00401204 . E8 7F000000 CALL <JMP.&KERNEL32.lstrcmpA> ; \lstrcmpA
00401209 . 85C0 TEST EAX,EAX
0040120B . 74 19 JE SHORT CRKME01.00401226

ここで2つの文字列をプッシュした後、call命令でlstrcmpで呼び出しています。



ここで重要なのはlstrcmp命令です。
lstrcmp命令は命令の直前にpushされた2つの文字列を比較します。
比較結果が EAX レジスタに入っているはずです。レジスタは右上のウィンドウを参照。
lstrcmpAでは、文字列が一致する場合は EAXに0, 一致しない場合は EAXにFFFFFFFF が入ります。

ここで、再びトレース。F8を何度か押して、test eax, eax の行まで進んで下さい。


00401209 . 85C0 TEST EAX,EAX
0040120B . 74 19 JE SHORT crkme01.00401226
0040120D . 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL
0040120F . 68 F5304000 PUSH crkme01.004030F5 ; |Title = "登録情報"4030
00401214 . 68 FE304000 PUSH crkme01.004030FE ; |Text = "不正解です。"030FE
00401219 . FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hOwner
0040121C . E8 43000000 CALL <JMP.&USER32.MessageBoxA> ; \MessageBoxA



test eax, eax 、これは比較命令です。
test eax, eax の後に je ~ がくる場合、eax = 0 ならジャンプ、それ以外はジャンプしません。
比較命令があるということは直後に必ず分岐命令があります。

さらに je short~ は何か。je は分岐命令です。
分岐命令は j で始まります。je とか jne とか ja などいろいろあります。

shortについて。je short crkme01.00401226 ってありますよね?
このshortは左の16進数と関係があります。
je short ~の場合は 74 19 ですが、 short のついていない方は E9 5FEFFFF てな感じです。
先頭の 74 は je を示しています。次の 19 は飛び先のアドレス(相対位置)です。
short の場合は飛び先のアドレスは 0 ~ FF までしか設定できません。
jeなどのジャンプ命令はジャンプ先に限度があります。
それに対し、short の付いていない方は E9 (jmp) 78FEFFFF と長めです。

基本的に左の16進数の束が短いほど実効速度が速くなるので、
ジャンプ命令の飛び先が短い場合はshortを使って処理速度の向上を図っているわけです。
j**系は-128~+127番地以内にしか条件ジャンプできません。その外はj**+JMPの組み合わせを使います。

話しが戻って、EAX=0 ならアドレス 00401226 に飛んでいきます。
00401226は、ちょっと下なので見えますね?


00401221 .^E9 78FEFFFF JMP crkme01.0040109E
00401226 > 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL
00401228 . 68 F5304000 PUSH crkme01.004030F5 ; |Title = "登録情報"4030
0040122D . 68 0B314000 PUSH crkme01.0040310B ; |Text = "正解です!"40310
00401232 . FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hOwner
00401235 . E8 2A000000 CALL <JMP.&USER32.MessageBoxA> ; \MessageBoxA


0040122Dに"正解です!"とみえますか?
そこが正解になるわけで、ようするにアドレス 00401226 に行けばいいわけです。

で、逆に
00401226の2個上を見て下さい

00401221 .^E9 78FEFFFF JMP crkme01.0040109E

JMPというのは、強制的にどっかにジャンプしてしまいます。
と、言うことは00401226に来るには、さっきのJEを使ってこなければなりません
JEで飛ぶにはTEST EAX,EAXの時点でEAXが0で無ければなりません
EAXが0になるためにはlstrcmpで、その上の2つが同じでなければなりません。

では、答えはわかりましたね?

ということは、「doomo」が正解になります。( String2="doomo")

では、Ollyで CTRL+F2 (再起動)押して下さい。
または OLLYのメニューの「解析」→「再スタート」です。

再起動したら、F9を押してプログラムを実行します。
crackme#01ウィンドウが表示されたら、パス入力欄にパスワード「doomo」を入れて登録ボタンを押しましょう。

「正解です!」のウィンドウが表示されればOKです。