crackme#06 解析手引き

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


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

Names in crkme06
アドレス  セクション  タイプ ( 名前 コメント 
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 crkme06:.text to USER32.GetDlgItemTextA」というウィンドウが表示されます。

References in crkme06:.text to USER32.GetDlgItemTextA
アドレス  逆アセンブル  コメント 
004011F4 CALL <JMP.&USER32.GetDlgItemTextA>
0040128C JMP DWORD PTR DS:[<&USER32.GetDlgItemTex USER32.GetDlgItemTextA

004011F4 call <jmp.&USER32.GetDlgItemTextA>を選択して、「F2」キーでブレークポイントを設定してください。

「F9」キーで動かしてみましょう。
crackme #06のウィンドウが、表示されました。
フェークパス「12345678」と入力して、「登録」ボタンクリックしてください。
先程、ブレークポイントを仕掛けた場所で止まります。

004011C3 . 6A 00 PUSH 0 ; /lParam = 0
004011C5 . 6A 02 PUSH 2 ; |wParam = 2
004011C7 . 68 A1000000 PUSH 0A1 ; |Message = WM_NCLBUTTONDOWN
004011CC . FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hWnd
004011CF . E8 CA000000 CALL <JMP.&USER32.SendMessageA> ; \SendMessageA
004011D4 >^E9 C5FEFFFF JMP crkme06.0040109E
004011D9 > 6A 00 PUSH 0 ; /Result = 0
004011DB . FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hWnd
004011DE . E8 9D000000 CALL <JMP.&USER32.EndDialog> ; \EndDialog
004011E3 .^E9 B6FEFFFF JMP crkme06.0040109E
004011E8 > 6A 7E PUSH 7E ; /Count = 7E (126.)
004011EA . 68 2C304000 PUSH crkme06.0040302C ; |Buffer = crkme06.0040302C
004011EF . 6A 64 PUSH 64 ; |ControlID = 64 (100.)
004011F1 . FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hWnd
004011F4 . E8 93000000 CALL <JMP.&USER32.GetDlgItemTextA> ; \GetDlgItemTextA   ←ココ
004011F9 . 33DB XOR EBX,EBX
004011FB . 33D2 XOR EDX,EDX
004011FD . 83F8 0A CMP EAX,0A
00401200 . 74 05 JE SHORT crkme06.00401207
00401202 . BB 01000000 MOV EBX,1
00401207 > BE 2C304000 MOV ESI,crkme06.0040302C
0040120C . 0FBF06 MOVSX EAX,WORD PTR DS:[ESI]
0040120F . B9 02000000 MOV ECX,2
00401214 . 66:3D 454F CMP AX,4F45
00401218 . 74 05 JE SHORT crkme06.0040121F
0040121A . BB 01000000 MOV EBX,1
0040121F > 0FB60431 MOVZX EAX,BYTE PTR DS:[ECX+ESI]
00401223 . 3C 30 CMP AL,30
00401225 . 72 0F JB SHORT crkme06.00401236
00401227 . 3C 39 CMP AL,39
00401229 . 77 0B JA SHORT crkme06.00401236
0040122B . 83F9 07 CMP ECX,7
0040122E . 72 04 JB SHORT crkme06.00401234
00401230 . 2C 30 SUB AL,30
00401232 . 03D0 ADD EDX,EAX
00401234 > EB 05 JMP SHORT crkme06.0040123B
00401236 > BB 01000000 MOV EBX,1
0040123B > 41 INC ECX
0040123C . 83F9 0A CMP ECX,0A
0040123F .^72 DE JB SHORT crkme06.0040121F
00401241 . 8BC2 MOV EAX,EDX
00401243 . B9 0A000000 MOV ECX,0A
00401248 . 33D2 XOR EDX,EDX
0040124A . F7F1 DIV ECX
0040124C . 83FA 00 CMP EDX,0
0040124F . 74 05 JE SHORT crkme06.00401256
00401251 . BB 01000000 MOV EBX,1
00401256 > 83FB 00 CMP EBX,0
00401259 . 75 14 JNZ SHORT crkme06.0040126F



「F8」キーを押して、

004011F4 . E8 93000000 CALL <JMP.&USER32.GetDlgItemTextA> ; \GetDlgItemTextA

を飛ばします。
この時の、EAXレジスタに入っている値=読込んだパスの長さです。
Address 004011FD cmp eax,0A を見ると、パスの長さ=10文字か比較しています。
今のフェークパスは、8文字なので、ここで、EAXレジスタの値を、10に変更しましょう。
変更の仕方は、EAXレジスタの値の部分を、Wクリックで、「Modify EAX」というウィンドウが表示されます。
Hexadecimal(16進)の値を、0x0Aに変更して、OKボタンをクリックです。
または「符号あり」の数値を10に変更しても可。


「F7」キーを数回押して下さい。

0040120C . 0FBF06 MOVSX EAX,WORD PTR DS:[ESI]

まで、実行させます。
まず、ここで先程のパス文字数を合わせます。
ESIレジスタに、フェークパス読込みエリアのアドレスが代入されてます。
ESIレジスタの値を選択して、右クリック、(「Follow in Dump」)を選択してください。
日本語メニューでは、「ダンプ画面へ」です。

CPUウィンドウの左下のダンプ画面部分に、フェークパスが表示されてます。
'12345678'の次、0x00(アドレスは、403034だと思います。)を選択して、右クリック、(「Binary」→「Edit」)を選択。
日本語メニューは、「バイナリー」→「編集」です。
「Edit data at 00403034」のウィンドウが表示されます。
ASCIIの入力部分に'0'(ゼロ)を入力して、OKボタンをクリックしてください。(Hex 00:30)
同じように、アドレスは、403035の値も、変更します。

プログラムの説明に戻ります。
「F7」キーを数回押して、Address 0040120C まで実行します。
ここでは、ESIレジスタのアドレスから、EAXレジスタにワード(2バイト)の読込みを行います。
この時、EAXレジスターの値は、0x3231となっています。(文字では'21')
フェークパスの頭2文字が反転されて読込まれています。

この現象は、インテルのプロセッサが扱うワード以上のバイトオーダ(バイトの順番)の処理方法によるものです。
インテルでは、リトルエンディアンと言われる方式であり、最下位のバイトから順番に並べられてます。
例として、0x12345678の値を、メモリなどの記録媒体の値に変換すると、0x78563412となります。


で、話を元に戻しまして、メモリ上(Hexダンプ)では、フェークパスが、0x3132333435363738と並んでます。
先頭2バイトを、ワード値として読込んだので、0x3231となります。
「F7」キーを押して、Address 00401214 cmp ax,4F45 まで、実行させます。
ここで、フェークパスの2文字を比較してます。

ASCIIコード表見て、表示可能文字で無い場合、16進数と見た方が良いようです。
ASCIIの01~0Fあたりは、制御文字で16進と見た方が良いようです。
80**だと日本語の可能性もありますが、余り出てこないです。


0x4f45を、ASCII文字に変換すると、'OE'となります。
次のステップに進める為、EAXレジスタ値を、0x4f45に変更します。
パスは、10文字で最初の2文字は、固定で、'EO'となります。

「F7」キーを数回押して、
0040121F > 0FB60431 MOVZX EAX,BYTE PTR DS:[ECX+ESI]
まで、実行させます。

0040121F > 0FB60431 MOVZX EAX,BYTE PTR DS:[ECX+ESI]
00401223 . 3C 30 CMP AL,30
00401225 . 72 0F JB SHORT crkme06.00401236
00401227 . 3C 39 CMP AL,39
00401229 . 77 0B JA SHORT crkme06.00401236
0040122B . 83F9 07 CMP ECX,7
0040122E . 72 04 JB SHORT crkme06.00401234
00401230 . 2C 30 SUB AL,30
00401232 . 03D0 ADD EDX,EAX
00401234 > EB 05 JMP SHORT crkme06.0040123B
00401236 > BB 01000000 MOV EBX,1
0040123B > 41 INC ECX
0040123C . 83F9 0A CMP ECX,0A
0040123F .^72 DE JB SHORT crkme06.0040121F

までをよく見ると、

00401223 . 3C 30 cmp al, 30
00401225 . 72 0F jb short crkme06.00401236
00401227 . 3C 39 cmp al, 39
00401229 . 77 0B ja short crkme06.00401236

まず、この4行ですが
1文字が、0x30~0x39の範囲か、判定してます。
ようは、数字かどうかです

00401223 . 3C 30 CMP AL,30
00401227 . 3C 39 CMP AL,39

この2行から、数字'0'~'9'の文字を処理すると考えられます。

0040122B . 83F9 07 cmp ecx, 7
0040122E . 72 04 jb short crkme06.00401234
00401230 . 2C 30 sub al, 30
00401232 . 03D0 add edx, eax
あと、この4行です。
今、何文字目かは、ecxレジスタに入っています。
ecxレジスタが、7以下の場合は、subとaddの実行はしていません。ジャンプするようになっています。
subとaddが、実行される場合は、ecxが8~10の時です。
7より大きくなった時に、数字に変換して、EDXレジスタに足しこんで行ってます。
subは引き算、addは足し算、divは割り算です(多分判ってるとは思うけども)
8ー10文字目までは数字のアスキー文字のうちまず30を引き算しています。例)38(8)→8
これで残りの8文字は、全て数字とわかります。
その判定は”EO”のあとの3文字目~7文字目まで続きます。


00401241 . 8BC2 MOV EAX,EDX
00401243 . B9 0A000000 MOV ECX,0A
00401248 . 33D2 XOR EDX,EDX
0040124A . F7F1 DIV ECX
0040124C . 83FA 00 CMP EDX,0
0040124F . 74 05 JE SHORT crkme06.00401256
00401251 . BB 01000000 MOV EBX,1
00401256 > 83FB 00 CMP EBX,0
00401259 . 75 14 JNZ SHORT crkme06.0040126F
0040125B . 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL
0040125D . 68 EB304000 PUSH crkme06.004030EB ; |Title = "登録情報"4030
00401262 . 68 F4304000 PUSH crkme06.004030F4 ; |Text = "正解です!"4030F
00401267 . FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hOwner
0040126A . E8 29000000 CALL <JMP.&USER32.MessageBoxA> ; \MessageBoxA


0040124Cを見ます。

0040124C . 83FA 00 CMP EDX,0

で、余りが0(ゼロ)か判定してます。
「F7」キーを押して、割り算を行います。
EDXレジスタの値は、0でないと思います。 ここで、EDXレジスタの値を、0に変更します。

「F7」キーを数回押して、Address 00401241 mov eax,edx まで、実行させます。
ここで、EDXレジスタ(可変パス8文字'#####XXX'の'X'部分の加算結果)を、EAXレジスタに代入してます。
「F7」キーを数回押します。

0040124C . 83FA 00 CMP EDX,0

まで、実行させます。
ここでは、EAXレジスタ ECXレジスタ = 商 EAXレジスタ 余り EDXレジスタ となります。

「F7」キーを数回押します。

00401256 > 83FB 00 CMP EBX,0

まで、実行させます。
ここでは、EBXレジスタが、0か比較してます。
EBXレジスタに、値が代入される場所は、Address 00401202 、0040121A 、00401236 、00401251 の4ヶ所です。
(これは、比較条件を満たしていない時のエラー・フラグです。)
今は、EBXレジスタ=0です。
ゼロになってますか?

もう、迷わずに、「F9」キーで一気に実行します。 「正解です!」と表示されたはずです。

ここで、今までのパス構成をまとめて見ましょう。
最終的に、このCrackMe06の、パスは'E0#####XXX'の形式になります。( #=何でも可 X=数値に変換して加算)となってます。
X=数字で、3つの数字(3文字)の合計が、10進で10の倍数になれば正解です。
つまり、この正解パスは、何通りかあります。


http://ash.jp/code/ 文字コード表はこういう所が便利です。
http://ash.jp/code/ctrltbl.htm
http://hp.vector.co.jp/authors/VA008536/data/ascii.html