脳内テキストの垂れ流し

プログラミングとか絵とか色々

リバースエンジニアリング初心者向け HackThisSite アプリケーションチャレンジ 1

HackThisSiteというサイトでは基礎的なセキュリティ問題にチャレンジできます。というのを今日思い出した。

 

大分昔にやってたのを思い出したのでまたやってみようと思いますー

いろんなジャンルがありますが、とりあえず今回はアプリケーションミッション1をやろうと思います。4だか5までやった記憶はあるんですがもう内容覚えてない・・・

こういうCrackMe的なヤツは久々で緊張。まあミッション1シリーズなんて公式がバカテストとか言ってるくらいだから簡単なんでしょうw

なんとなく初心者向けなんて書いちゃってるけど自分も初心者です、PhotoShopクラックできないうちは素人だー

 

環境はWindows10です。たしかMacLinuxで出来る問題は最初の方だけなのでWinちゃん使いましょう。

まずはx64dbgと好きなバイナリエディタをダウンロードしましょう。

でも本気でやるならImmunity Debuggerが一番いいかもしんない。今回はインストール不要のx64dbg使います。(次回からはImmunityかな・・・)

 

バイナリエディタHxDをダウンロードしてきました。初心者にはStirlingがいいと思う。でもさっき入れたらメニューとかの日本語が化けて使いづらいんで自分はHxD使います。

 

OS:Windows10

デバッガー:x64dbg

バイナリエディター:HxD

 

・最初は超簡単

HackThisSiteのApplication Missionsからapp1.zipをダウンロードして解凍しましょう。

そしたらひとまず実行

f:id:ibuninngu:20170924000143p:plain

「シリアルナンバーを入れてボタンを押しやがれ」とのこと。

好きに入れましょう。

f:id:ibuninngu:20170924000359p:plain

 

 

f:id:ibuninngu:20170924000915p:plain

"Sorry, you entered an incorrect serial number. Please re-enter."

まあ通るわけない。

 

さてどうしましょう、事前知識無しだとここで手詰まりですね。なのでデバッガに触れたこともない超ビギナーの方に答えを教えちゃいます。

一番簡単な問題ということで、バイナリエディタで覗けばシリアルナンバーなんてすぐに見つかりますw

f:id:ibuninngu:20170926184944p:plain

バイナリエディタでapp1win.exeを開き、"Sorry"で検索かけるとそれっぽいのが見つかります。

 

"7692-3349-1914-4567"を試しに入れてみましょう。

f:id:ibuninngu:20170924001758p:plain

f:id:ibuninngu:20170926185000p:plain

 

終わり!・・・では勉強にならないので、もうすこし掘り下げましょうw

 

・デバッガーを使う

バイナリエディタで検索かけるだけなんて誰にでも出来るので、次はデバッガーを使って"Contratulations!"してみましょう。これって誤字ったのかな?

 

x32dbg.exe(x64dbgの32bit版)を起動してapp1win.exeをぶっこみましょう。

多分問題なく読み込めると思うので、F9を押してブレークポイントまで進みます。

もしアドレスとか違ってたら上手いこと読み替えてくださいー

f:id:ibuninngu:20170924004048p:plain

初期設定では、自動的にエントリーポイントにINT3が設定されているハズです。

ちなみにこのINT3は「ソフトウェアブレークポイント」と言って、割り込み番号3を発生させてCPUを一時停止させる命令です。

試しにエントリーポイントの命令をINT3に書き換えてみましょう。

f:id:ibuninngu:20170924004148p:plain

命令が表示されているところの左に16進数で"CC"と表示されているのが見えます。これは「オペコード」と呼ばれる物です。Intelプロセッサがそのまま解釈できる完全な機械語と言っていいと思います。

CPUはこのオペコードを読み込みながら動作します。

デバッガーはソフトウェアブレークポイントをかける際、裏でオペコードを"CC"に書き換えます。

ユーザーがブレークポイントを抜けた際には、そのままでは正常に動かないので"CC"を元のオペコードに戻して再開します。意外と単純

 

INT3は元に戻しときましょう、多分スタックが狂いますw

 

ではCTRL + Gで"0046DC4A"に飛びましょう。

f:id:ibuninngu:20170924024515p:plain

 

飛んだ先の命令では"CompareStringW"という関数が呼び出されます。ここをどうやって見つけるのかはそのうち話すとして、とりあえずこの関数のリファレンスマニュアルを見てみましょう。

ちゃんと読めばこの関数には6つの引数と7種類の返り値があることが分かると思います。

 

例えば"A"と入力した場合。

この関数の返り値は"CSTR_GREATER_THAN"となり、eaxレジスタにその値が格納されて戻ってきます。

ちなみに文字の背景が赤くなってるのはINT3ブレークポイントで、F2で設定できます。

f:id:ibuninngu:20170924030432p:plain

"0046DC4A: call dword ptr ds:[<&CompareStringW>]"直前の6命令は全てスタックに値を"push"する命令です。

主にC言語では引数はこのようにして関数に渡されます。(C++は少し違う)

 

このプログラムにシリアルナンバーが正しいと思わせるにはどうすればいいでしょう?

答えは簡単、返り値を"CSTR_EQUAL"(数値は2)に書き換えればOKです。

f:id:ibuninngu:20170924032202p:plain

 

しかーし、これだけではうまくいきません。

"0046DC90"から"0046DCC8"までを見てみましょう。

f:id:ibuninngu:20170924035239p:plain

ひとつづつ読んでいくワケですが、初めのうちはものすごく疲れると思います。慣れてくださいw

 

最初の"0046DC90: mov edx, dword ptr ss:[ebp-C]" は、先ほどの返り値をedxに格納します。

"0046DC93: dec edx" edxから1を引く。

"0046DC94: cmp edx, 2" edxと2を比較

"0046DC97: ja app1win. 46DCD7" edxの値が2より大きかった場合"app1win. 46DCD7"へジャンプ

"0046DC99: jmp dword ptr ds:[edx*4+4FDCB4]" 文字列が一致する場合(edx = 2)、"edx * 4 + 4FDCB4 = 004FDCB8"となり、これをDump画面で参照すると、

ジャンプ先のアドレスは"0046DCA7"だということが分かります。

f:id:ibuninngu:20170924041203p:plain

 

なので飛んで"0046DCA7"からを見てみましょう。

f:id:ibuninngu:20170924042436p:plain

 

"0046DCA7"から"0046DCDD"までの

"mov eax, <x>"

"leave"

"ret"

がポイント、eaxに入るのは戻り値です。

関数は基本的に成功した時の戻り値は0になります。なので、eaxに0が入った状態で"ret"を踏めばよさそうです。

 

"0046DCAF: jge app1win. 46DCB8" これは前の2つの"mov"命令でスタックに保存された文字列の長さをそれぞれecxとedxに格納し、その長さを比較しています。(ecxには入力した文字列の長さが入る)

ecxの値(入力した文字列の長さ)がedx(シリアルナンバー)以上だった場合に"app1win. 46DCB8"(次の比較)へ飛ぶ。それより短い場合は戻り値"0xFFFFFFFF"で終了する。

 

"0046DCC0: jle appwin. 46DCC9" 今度はecxの値(入力した文字列の長さ)がedx(シリアルナンバー)以下だった場合に"app1win. 46DCC9"(戻り値0で終了)へ飛ぶ。それより長い場合は戻り値1で終了する。

 

ここまでで分かることは、文字列の長さも同じでなければならないということ。つまり、文字列の長さを格納したスタックをいじれば上手くいく。

f:id:ibuninngu:20170924141909p:plain

"0019EEC8"は"CompareStringW"の返り値。

"0019EECC"と"0019EED0"が文字列の長さを格納したスタックです。

 

 

次に考えたいのがココ。この最後の返り値0の"ret"はどこから来るのでしょう?

f:id:ibuninngu:20170924045358p:plain

答えは"0046DC97"の

"0046DC94: cmp edx, 2"

"0046DC97: ja app1win. 46DCD7"

f:id:ibuninngu:20170924035239p:plain

試してないから分からないけど、なんかエラー食わせばここに飛んでくるんじゃないかな( ^ω^)・・・

それか"CompareStringW"の返り値を4以上にするとか

 

・意外と長く楽しめた

全部の返り値が0になるように命令を書き換えたりレジスタやスタックいじるなりして無事返り値を0に設定することが出来ました。

 

あとはリターン先までついて行ったり、文字列比較の else if 的なループを読んでみたりしてみるといい練習になるかもしれません。

意外と楽しめるアプリケーションチャレンジ1でした!

 

クラウドソーシング「ランサーズ」