crackme#17 解析手引き

「crackme #17」は、これまでに無かった「チェックボックス」によるボタンでのパス入力です。

OllyDBGを起動して「crkme17.exe」を読込んで下さい。
一度「F9」キーで動かしてみましょう。
左半分に四角いボタンが縦横5個ずつ並んだチェックボックスが表示されました。

ここからはボタンの位置をMSエクセル等にある「R1C1」方式で表記します。
(蛇足)R=Rowで「行」、C=Columnで「列」の事です。
では、終了ボタンを押してこのウィンドウを閉じてから、Ctrl+「F2」でOllyDBGを再スタートさせて下さい。

CPUウィンドウで右クリック、「検索」→「現在のモジュール名(ラベル)」から
Address 00402010 の「USER32.IsDlgButtonChecked」を選択し、「Enter」キーを押します。


Names in crkme17
Address Section Type ( Name Comment
00401000 .text Export <ModuleEntryPoint>
00402000 .rdata Import ( KERNEL32.GetModuleHandleA
00402004 .rdata Import ( KERNEL32.ExitProcess
0040200C .rdata Import ( USER32.LoadIconA
00402010 .rdata Import ( USER32.IsDlgButtonChecked
00402014 .rdata Import ( USER32.SendMessageA
00402018 .rdata Import ( USER32.EndDialog
0040201C .rdata Import ( USER32.MessageBeep
00402020 .rdata Import ( USER32.DialogBoxParamA
00402024 .rdata Import ( USER32.MessageBoxA


Address 004011F1 call <jmp.&USER32.IsDlgButtonChecked> を選択して、
「F2」キーでブレークポイントを設定してから、「F9」で再スタートして下さい。


References in crkme17:.text to USER32.IsDlgButtonChecked
Address Disassembly Comment
004011F1 CALL <JMP.&USER32.IsDlgButtonChecked>
0040125C JMP DWORD PTR DS:[<&USER32.IsDlgButtonCh USER32.IsDlgButtonChecked


#17のチェックボックスが表示されました。
チェックボックスでは、R1C1(左上隅)のボタン一個のみ押してから「ユーザー登録」を押します。

Address 004011F1 で止まりましたか?


004011E1 . BA 01000000 MOV EDX,1
004011E6 . B9 64000000 MOV ECX,64
004011EB > 52 PUSH EDX
004011EC . 51 PUSH ECX
004011ED . 51 PUSH ECX ; /ButtonID
004011EE . FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hWnd
004011F1 . E8 66000000 CALL <JMP.&USER32.IsDlgButtonChecked> ; \IsDlgButtonChecked
004011F6 . 59 POP ECX
004011F7 . 5A POP EDX
004011F8 . D1E2 SHL EDX,1
004011FA . 03D0 ADD EDX,EAX

004011FC . 41 INC ECX
004011FD . 83F9 7D CMP ECX,7D
00401200 .^72 E9 JB SHORT crkme17.004011EB
00401202 . 81FA 68541502 CMP EDX,2155468
00401208 . 75 16 JNZ SHORT crkme17.00401220
0040120A > 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL
0040120C . 68 0D304000 PUSH crkme17.0040300D ; |Title = "登録情報"4030
00401211 . 68 16304000 PUSH crkme17.00403016 ; |Text = "正解です!"40301
00401216 . FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hOwner
00401219 . E8 50000000 CALL <JMP.&USER32.MessageBoxA> ; \MessageBoxA

0040121E . EB 1C JMP SHORT crkme17.0040123C
00401220 > 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL
00401222 . 68 0D304000 PUSH crkme17.0040300D ; |Title = "登録情報"4030
00401227 . 68 00304000 PUSH crkme17.00403000 ; |Text = "不正解です。"03000
0040122C . FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hOwner
0040122F . E8 3A000000 CALL <JMP.&USER32.MessageBoxA> ; \MessageBoxA
00401234 . EB 10 JMP SHORT crkme17.00401246
00401236 > 837D 0C 10 CMP DWORD PTR SS:[EBP+C],10
0040123A . 75 0A JNZ SHORT crkme17.00401246
0040123C > 6A 00 PUSH 0 ; /Result = 0
0040123E . FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hWnd
00401241 . E8 10000000 CALL <JMP.&USER32.EndDialog> ; \EndDialog
00401246 > 5F POP EDI
00401247 . 5E POP ESI
00401248 . 5B POP EBX
00401249 . 33C0 XOR EAX,EAX
0040124B . C9 LEAVE
0040124C . C2 1000 RETN 10


ブレークポイントより少し上をみると、

004011E1 . BA 01000000 MOV EDX,1

ここで、初期値(ボタン1個が1ビット)を現してます。

004011F1 . E8 66000000 CALL <JMP.&USER32.IsDlgButtonChecked> ; \IsDlgButtonChecked

はブレークポイントを仕掛けた所ですが、ここで各ボタンがチェック状態であれば、1が返り

004011F8 . D1E2 SHL EDX,1

にて答えのEDXレジスタを、1ビットシフトしてます。

004011FA . 03D0 ADD EDX,EAX

EAXが、IsDlgButtonCheckedのリターン値です。
それを EDX レジスタに足しこんでます。
これをボタンの数分繰返すようです。


ブレークポイントから2~3秒「F8」を押すと、004011EB~00401200 間をループする様子が分ります。
ループ間の適当な所で止めておいて、少し先のプログラムを見ると、


00401202 . 81FA 68541502 CMP EDX,2155468
00401208 . 75 16 JNZ SHORT crkme17.00401220
0040120A > 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL
0040120C . 68 0D304000 PUSH crkme17.0040300D ; |Title = "登録情報"4030
00401211 . 68 16304000 PUSH crkme17.00403016 ; |Text = "正解です!"40301
00401216 . FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hOwner
00401219 . E8 50000000 CALL <JMP.&USER32.MessageBoxA> ; \MessageBoxA


となっています。

00401202 . 81FA 68541502 CMP EDX,2155468

ここが、正解チェックで、0x2155468をビット表記にします。

つまり、Address 00401202 で EDX=2155468 であれば,次の分岐命令を素通りしてそのまま「正解です!」の
ウィンドウを表示するルーチンへ行くようです。


では,Address 004011F1 のブレークポイント(BP)を「F2」で解除し、00401202 にBPをセットします。
Ctrl+「F2」でを再スタートさせて「F9」を押して下さい。
チェックボックスでは、先程と同じくR1C1(左上隅)のボタンのみ押してから「ユーザー登録」を押します。

00401202 . 81FA 68541502 CMP EDX,2155468

でブレークします。
CPUウィンドウの下を見ると EDX=03000000 とあります。
ここから力技で,Ctrl+「F2」&「F9」を24回繰返し、R1C2,R1C3・・・R2C1,R2C2・・・・・R5C5 と
ボタンを一つずつ押しながら EDX の値をメモって行きます。後でゆっくりやって見て下さい。
ちなみに、チェック無しだとEDX=02000000です
ここからは、ちょっと見ていて下さい。

まとめると、

   C1    C2    C3    C4    C5
R1 03000000 02800000 02400000 02200000 02100000
R2 02080000 02040000 02020000 02010000 02008000
R3 02004000 02002000 02001000 02000800 02000400
R4 02000200 02000100 02000080 02000040 02000020
R5 02000010 02000008 02000004 02000002 02000001


となります。
さて、EDX=2155468 であれば良いので、2155468=2100000+50000+5400+60+8 。さらに分解すると、
2155468=2100000+(40000+10000)+(4000+1000+400)+(40+20)+8 ですから、上の表から見比べて行くと
求めるボタンは、R1C5,R2C2,R2C4,R3C1,R3C3,R3C5,R4C4,R4C5,R5C2 となります。

分り易く書くと,■を押すボタンとして,


□□□□■
□■□■□
■□■□■
□□□■■
□■□□□



では、Address 00401202 のブレークポイントを「F2」で解除し、Ctrl+「F2」でを再スタートします。
「F9」を押し、チェックボックスのボタンを図のパターンで押してから「ユーザー登録」を押します。
「正解です!」が出ましたか?




ここで解答の一例は終わりますが、答えは一つだけではありません。
難しいタイプ(裏とも言う?)が存在します。

実は、別のチェックルーチンがあります。
もう一つのチェックルーチンで正解すると、『正解です!(Difficult)』と表示されます。


0040120A > 6A 00 push 0 ; /Style = MB_OK|MB_APPLMODAL

これは「正解です!」を表示するために使っている MessageBoxA の引数ですが、
なぜか 0040120A > ←不等号表示があります。
つまり、どこかにここに飛んでくるジャンプ命令があるはずです。とりあえずここから探してみましょう。

Address 00401202A を選択して、Ctrl + R を押すと「References in crkme17:.text to 0040120A..0040120B」
というウィンドウが表示されます。

References in crkme17:.text to 0040120A..0040120B
Address Disassembly Comment
00401191 JMP SHORT crkme17.0040120A

00401191 . EB 77 jmp short CRKME17.0040120A

を選択して、ダブルクリックして下さい。Address 00401191 に飛びます。


00401158 > 81FB D022224A CMP EBX,4A2222D0
0040115E . 75 33 JNZ SHORT crkme17.00401193
00401160 . 81F9 03A56060 CMP ECX,6060A503
00401166 . 75 2B JNZ SHORT crkme17.00401193
00401168 . BE 9EA74000 MOV ESI,40A79E
0040116D . C786 8288FFFF >MOV DWORD PTR DS:[ESI+FFFF8882],66694428
00401177 . C786 8688FFFF >MOV DWORD PTR DS:[ESI+FFFF8886],75636966
00401181 . C786 8A88FFFF >MOV DWORD PTR DS:[ESI+FFFF888A],29746C ; UNICODE "M/yy"
0040118B . 81EE 88770000 SUB ESI,7788
00401191 . EB 77 JMP SHORT crkme17.0040120A


この辺りです。
ちなみに、R1とR2のボタンを全部押したら Address 00401158 へ飛びます。

eXeScope などで、チェックボックスの ID をチェックしてみましょう。
そうすると何か見えてくるはずです。


簡単な方は押されている部分の配置をチェックしていますが、
こちらの方は押す順番をチェックしています。

始まりは Address 004010F2 からで、EAXにIDがはいってます。


004010F2 > 817D 0C 110100>CMP DWORD PTR SS:[EBP+C],111
004010F9 . 0F85 37010000 JNZ crkme17.00401236
004010FF . 66:837D 10 64 CMP WORD PTR SS:[EBP+10],64
00401104 . 0F82 9B000000 JB crkme17.004011A5
0040110A . 66:837D 10 7D CMP WORD PTR SS:[EBP+10],7D
0040110F . 0F83 90000000 JNB crkme17.004011A5


Address 40110Fまでが、チェックボックス以外のID排除になっています。


00401115 . 8B45 10 MOV EAX,DWORD PTR SS:[EBP+10]
00401118 . 83E8 64 SUB EAX,64
0040111B . 33D2 XOR EDX,EDX
0040111D . B9 05000000 MOV ECX,5
00401122 . F7F1 DIV ECX
00401124 . 8B1D 30304000 MOV EBX,DWORD PTR DS:[403030]
0040112A . 8B0D 34304000 MOV ECX,DWORD PTR DS:[403034]
00401130 . C1E3 03 SHL EBX,3
00401133 . C1E1 03 SHL ECX,3
00401136 . 0BDA OR EBX,EDX
00401138 . 0BC8 OR ECX,EAX
0040113A . 8BC3 MOV EAX,EBX
0040113C . 8BD1 MOV EDX,ECX
0040113E . C1E8 1E SHR EAX,1E
00401141 . C1EA 1E SHR EDX,1E
00401144 . 0BC2 OR EAX,EDX
00401146 . 85C0 TEST EAX,EAX
00401148 . 75 0E JNZ SHORT crkme17.00401158
0040114A . 891D 30304000 MOV DWORD PTR DS:[403030],EBX
00401150 . 890D 34304000 MOV DWORD PTR DS:[403034],ECX
00401156 . EB 48 JMP SHORT crkme17.004011A0


00401118 . 83E8 64 SUB EAX,64

ここで、振られたIDから 100引いて 0-24になってます。

00401124 . 8B1D 30304000 MOV EBX,DWORD PTR DS:[403030]
0040112A . 8B0D 34304000 MOV ECX,DWORD PTR DS:[403034]

初期状態では、[403030],[403034]には1が入ってますね。
ここにどんどん値を積み上げて、押している順番を記憶させています。
押したボタンの ID から 100 を引いて 0 - 24 の値を出しているわけですが、
これを 5 で割っています。

ここで、商と余りが出るわけですが、EDX = 押されたチェックボックスのX座標
EAX = チェックボックスのY座標、と解釈できるはずです。
次に上記の [403030](X座標),[403034](Y座標)の各値を EBX, ECX に納めています。
次に

00401130 . C1E3 03 SHL EBX,3
00401133 . C1E1 03 SHL ECX,3

で3ビット分の空白を作っています。
次に

00401136 . 0BDA OR EBX,EDX
00401138 . 0BC8 OR ECX,EAX

で、先ほど取得したチェックボックスのX座標、Y座標のデータを EBX, ECX 各レジスタに納めています。

各レジスタがどんな値を納めているかをよく考えて下さい。
これを忘れるとわからないと思います。

この EBX, EDX の各値は、押されたチェックボックスの順番(X座標、Y座標に分けて記憶させている)です。
次に

0040113A . 8BC3 MOV EAX,EBX
0040113C . 8BD1 MOV EDX,ECX

で押した順番の情報をコピーしています。

0040113E . C1E8 1E SHR EAX,1E
00401141 . C1EA 1E SHR EDX,1E

これは押した順番の情報を 30 ビット右にずらしています。
次に

00401144 . 0BC2 OR EAX,EDX
00401146 . 85C0 TEST EAX,EAX

と来ています。
30ビットは、3ビット掛ける10回の意味でした。
よって、終了条件が Address 401146 です。
そして分岐命令 jnz short ~ です。

ここで重要なのは、起動時では順番情報の初期値は 1 であるということです。
4A2222D0 と 6060A503 を3bitずつ読んでいけば良いわけです。
3 ビットずつ格納して左にずらす、ということをやっているので、
10回ボタンを押すと、値が大きくなるので条件分岐で変化が起こります。


比較情報を、分かりやすく分解・・・1 001 010 001 000 100 010 001 011 010 000 (4A2222D0)
もういっちょ、1 100 000 011 000 001 010 010 100 000 011(6060A503)
さらに分かりやすく・・・1 4 0 3 0 1 2 2 4 0 3
もうひとつ・・・1 1 2 1 0 4 2 1 3 2 0

この最初の「1」は最終チェックルーチンに導くための初期値です。

XY方式で、表すと、11 14 20 13 00 41 22 12 34 20 03 (最初の11は初期値)、
RC方式は逆にして、1足すだけです。


では、簡単にまとめ。
簡単なほうの正解配置

□□□□■  (■=押す □=押さない)
□■□■□  左図のようにしてから登録ボタンを押せば OK。
■□■□■  この配置はファミコンの名作、ロックマン2の最強パスワードを
□□□■■  元にしています。これに気づく人はどれだけいたのだろうか(笑)
□■□□□


もう一つの正解。(数字は押す順番)

4□2□□ □□9□□  こっちの方は押す順番を間違えたら crackme を
□□□□5 □□□□□  立ち上げなおして下さい。ちなみに 2 番目と 9 番目は
□□□□□→□76□□  同じ箇所ですので、押す -> 戻す という動作になります。
□3□□□ 10□□□□  上記の通りに押すと『正解です!(Difficult)』と表示されます。
□1□□□ □□□8□






補足

* (Difficult)の文字は、040116dでセットされています。

======================================================
2進数と16進数について

2進数は下記のように固定的に書いて、その下に0と1を入れて行けば解り易いです。
16進数一桁が、2進数4ビットが分かれば簡単です。

16進数→2進数への変換
   _______ C|3_______
  ↓        |        ↓
16進数で「C」      |      16進数の「3」
  ||         |         ||
 12=8+4+0+0|0+0+2+1=3 ← 10進数
    | | | ||| | | |
    ⑧ ④ 2 1|8 4 ② ①   ← 固定的に「8,4,2,1」を置く
    ↓ ↓ ↓ ↓|↓ ↓ ↓ ↓
    1 1 0 0|0 0 1 1   ← 2進数


2進数と16進数の関係
上位ビット |    下位ビット
8  4  2  1 | 8  4  2  1  ←上位,下位ビットに対して
↑  ↑  ↑  ↑ | ↑  ↑  ↑  ↑   固定的に「8,4,2,1」
bit7 bit6 bit5 bit4 | bit3 bit2 bit1 bit0   を置く


例えば、2進数によるビットパターンが分っている場合、1バイトの8ビットを上位の4ビット|下位の4ビットに分け、
それぞれのビットに上図のように「2^3,2^2,2^1,2^0」=「8,4,2,1」の数値を固定的に持たせ、
ビットパターンの「1」が立っているビットの所の数値だけを4ビット毎に合計します。

すると、その合計値が上位|下位4ビットのビットパターンをそれぞれ10進数で表した値となります。
10進数から2進数のビットパターンに直すには、上記の逆の操作を行えばよいわけです。


                      DenBoschさん編集 (はじめて読む8086 より抜粋)        
======================================================