詳解crackme #1
eagle0wl氏のcrackmeでバイナリ解析を学んでいる。
基本的に本家の手引きをなぞっている。環境はWindows 10, OllyDgb 1.10である。但しメニューには日本語化パッチを適用していない。明らかに読みにくくなる。
crackme#1の概要
crackme#1を起動すると,次のような入力画面が表示される。文字を入力して登録ボタンを押すと,それが正しいか否かを表すダイアログが表示される。 要するに,パスワード解析,認証回避の練習問題のようだ。後者は前者を内包するため,後者の問題と捉えることにする。
解の方針
今回の目的は,パスワード(と呼ぶことにする)の認証を回避することだ。従ってユーザの入力と真のパスワードの文字列を比較する箇所(以下,比較箇所)があるなら(あるはずであり),その返り値を書き換えれば良い。問題はその箇所がどこにあるか分からないことだ。そこで「どこかの命令」にブレークポイントを設置し,そこを足掛かりに解析を進める。
ではどの命令にブレークポイントを設置すれば良いか。ユーザの入力した文字列を得るには,「テキストボックスからの文字列取得」APIが使われているはずだ。これを足掛かりにすれば良い。
従って方針は次のようになる。
- テキストボックスからの文字列取得時のブレークポイントの設置
- そこからステップ実行し比較箇所を特定する
- 比較結果を書き換える
このように,プログラムの解析は実際の動作から内部の仕組み推測しながら行う。なお,上記は数ある方針の1つに過ぎない。
Olly Dgbの画面構成
解説に先立ち,Olly Dbgの画面構成について,呼び方の定義をしながら説明をする。Olly Dbgのウィンドウ(CPUウィンドウ)には図のような4つの画面(ペイン)が存在する。
図中の「C」の文字がCPUウィンドウであることを表している。ツールバーに「L」「E」「M」などとあるように,他にも色々なウィンドウがある。全く知らないウィンドウになってしまった,という時は落ち着いて「C」を選択して戻る。
解析手順
0. プロセスのアタッチ
まずはcrackme#1, Olly Dgbを起動する。Olly Dgbの「File > Attach」からcrackme#1のプロセスをアタッチする。crackme#1は動かなくなるので注意。
次に実行命令ペイン(先述)を右クリックし「View > Module 'crkme01'」を選択する。すると,crackme#1の解析結果が表示される。
タイトルバーが「module crkme01」になった点に注意する。時々誤操作で別のプロセスに飛んでしまうことがあるので,ここで確認すると良い。
ちなみに実行命令ペインを少し下にスクロールすると「登録情報」や「不正解です」といった文字列が確認できる。実はその付近に平文のパスワードも書かれており,見え見えである。あくまでそれらの情報は知らない体で進めることにする。
1. テキストボックスからの文字列取得時のブレークポイントの設置
まずはcrackme#1が使用しているAPIを確認する。そこから今探している「テキストボックスからの文字列取得」らしきAPIを見つければ良い。
crackme#1が使用しているAPIは,実行命令ペインを右クリックし「Search for > Names in current module」から確認できる。
「USER32.GetWindowTextA」が目的のAPIであると推測する。更にこのウィンドウ上で右クリックし「Find references to import」から呼び出している箇所を見つける。今回は小さいプログラムのため参照2つしかなく楽だが,実際はもっと大変だと思う。
ここでEnterキーを押すと,それを実行する命令(CPUウィンドウの実行命令ペイン)に飛ぶことができる。
2. ステップ実行し比較箇所を特定する
「004011F5」に飛んだら,ここでF2でブレークポイントを設置する(当該アドレスの表示が赤くなる)。更にF9でアタッチ対象を実行再開し,crackme#1を操作できるようにする。 今,「テキストボックスからの文字列取得」APIにブレークポイントを設置したから,適当な文字列を入力し登録を押せば,うまくいけばそこで止まるはずである。それを確認する。
「登録」を押す
無事にブレークポイントで止まることができた。
ここで,その先にWinAPIの文字列比較関数lstrcmpAがあることに気づく。直前のPUSH2つは引数渡しだろうから,ここが比較箇所で,片方の文字列「doomo」が真のパスワードではないかと推測できる。
一応,続いてF8でステップ実行を行う。
入力した文字列abcが積まれた。やはりここが比較箇所のようだ。
ここで終わっても良し。本記事では当初の方針通り,この状態(abcと入力した状態)で認証を回避したいと思う。
3. 比較結果を書き換える
lstrcmpAの実行時は次のような状態にある。
実行結果はどこかのレジスタに保管されるはずだ。実行結果は-1, 0, 1だろうから,EAXレジスタ(-1)だと判断できる。従って,ここを適切に書き換えれば,認証を回避できそうだ。という訳で0に書き換える。
やったねー!! (^^)