文字列の生成


さて、いつも通りですが、解析用のプログラムをば…。
base64->rar
UmFyIRoHAM+QcwAADQAAAAAAAACqjnQggi8A2wIAAAAMAAACwdd3XwAAcjgdNQ8AIAAAAGNya21l
MDMuZXhlAAHACecYF00Psy1t5ix6Z3l0xOWaF9uR2gHpZcSxgy1WbaFIQfKMIqZhT0j0au+TQfBM
yfwbZ3k21zXbo6ZxAroDnryWpZhnlDueOQG4HT/3zAw74ZkoHsHrY7ErXcS4TpAcMbHMFZatGVwu
3Ol79fHpqFf1oj2Z2UFDBIty2GcsCTLwePAGIwpoR3DCW1Y/XqVCFne+WFm1dhXwd/OC4Rw61Z9p
51v1LJQnrEXauFHOM2WzJi6Xkq1CrxaQL5GfpBhhNMCLxdmC1RFjFGUR1X672kBtOdRu6E7rav91
9ieSjTonejYJeHnMnx4gEJt4HTDsFaQsLtaMyW+ahMNEYeef+1imYKWWUAd+taqft9r/SuTN2I9o
iCu8JNCZiYDVc2Iykw19BLthSGFsq4972Za2Lttw8Xjx/yTzfeilelo7Ik4OEorYFajjBVt4fXRt
FJYXf0ek1xQ+asf7nMZW8BNi7ZFIPv1kbWCFT4miQejMpsKCmoqZHm3s9XmLE2H0fr2jnn9lDTr2
OnVSQ3/uIC5+hn3M2y3goRt4cf3zSj2MHHFjsWrgr2q5ZzvaG3JzRwOgHcQhb15k3URpbcJ4oe19
ZrU6kT0L2FI9WGeVwWxrCEP9LXZJaTCM/UgAESiF+wFk/GiMGK7VLJNM3JmRzcxwe/fpuw+gJZ1S
12iyULopmZxJ6uTleVL6VuzrNEOI5BIG42UOljc5mlGUM53tggMosqSiHpLJbB+QnJ4KtCkgXVPK
sdriVr5yBusCLqoIkqmAmNko47c3iUZoflCwQrmc+j/T/oo23GKPHvCk7oipp6/CfF/NUMCFFUAr
PXxUMRX6C8mZN47VnPmHkdphVLSkCW4eOoV7NVN9Hu2PwK0EV8pVb1T3TatZOLkW5HS0GJ0WW5MA
ck7tpxr5nKe3StJupLyaLO5om0tmhXIh0dvExFvs4dxf1kmNTNnLIlvf1lc/K7BKAAC/iGf2qf/U
xD17AEAHAA==
このcrackmeというプログラム群は随分前から作り始めたものなので、随分と処理が低級な場合がありますね。
所謂”美しくないプログラム”というやつです。
前回のプログラムも処理的にあまり美しくなかったので…。
まぁ今更作り直すくらいならもっとレベルの高いプログラムを作成して解説していきたいので、無駄な処理とかはあっても、目を瞑って下さい。

それではいつもどおりチェック開始の位置を特定してください。
GetDlgItemTextAの返り値が8と比較されているので今回は8Byteの文字列みたいですね。
次のアドレスを放置してその次を見てみると、crkme01.exeでお目にかかったlstrcmpA関数がありますね。
しかし今回はどちらも空白なので、この前のようにはいかなそうです。
とはいえ、lstrcmpAがある以上、答えはここに出てきますので、lstrcmpAにブレークポイントを置いて実行し、8文字の文字列を入力すれば答えは自ずと現れます。
でも今回は練習なのでね、ちゃんと処理の方から答えを導きましょう。

例によってCall先である4010B7hに飛びます。
するといきなりeaxに63h、edxに8hを代入していますね。
そしてebxにはどうやらアドレスを代入しているようです。
GetDlgItemTextA関数でまだ解説していなかったのですが、引数のBufferというものが入力文字列の格納先アドレスになります。
つまり格納先は403024hですね。
しかしebxに代入されたアドレスは40302Dhですから、入力文字列ではないということになります。

次の4010C6hでは何やら見たことの無いALというレジスタを使っていますね。
これは実はeaxの下位1Byteを示します。
例えばeaxが12345678hだとすれば78h、ということになります。
現在のeaxは00000063hなので、40302Dhに63hを格納する、というものになります。

次アドレスのSub命令は減算命令です。
第二オペランドの数値を第一オペランドから減算します。
さて、それではWindows付属の電卓に活躍してもらいましょうか。
起動したら表示→関数電卓にチェックが入っているかどうか確認してください。
それでは左のほうにある16進数というラジオボックスを選択してください。
画面が簡単なので解説はいらないでしょう。
52hになりましたね。
さて、ではStrlingを起動し、新しいバイナリデータを作成(白い紙のアイコン)して、63h 52hを入力してみましょう。

今回の根幹となる処理は計算処理ですので、ガンガンこなしていきましょう。
Mov命令については今更必要はないと思うので割愛します。
次のAdd命令はSub命令の反対、加算命令です。
Sub命令と同様、第二オペランドの数値を第一オペランドに加算します。
では電卓で加算、と…61hですね。

次のアドレスのMul命令は乗算命令ですね。
これは若干特殊で、第一オペランドしかありません。
なぜかというと、掛けられる対象となるものがeaxレジスタ、と固定されているからです。
計算結果の格納先も勿論eaxになります。
edxは最初に8hを代入していますので、電卓で8を掛けてください。
そしてその直後に減算命令があるので、2C5hを減算してください。
43hとなりましたので、バイナリエディタに入力していきます。
文字列の形が現れてきましたね。

サクサク進めていきますが、ecxに2を代入後、Div命令ですね。
これは除算…割り算の命令です。
これもMul命令と同様、対象と格納先はeaxで固定です。
そして0Fhを加算、と…30hになりましたね。

次はecxに3を代入後、eaxに1を加算しているようですが、その直後のLoopd命令を見てください。
これは指定された範囲をループさせる命令なのですが、その回数の指定はecxによって行われます。
つまりこの時点でecxは3なので3回処理を行うのですが、処理としてはecxが0かどうか確認し、0であればループ先へジャンプせずに次のアドレスへ処理を移し、0でなければecxから1を減算する、という処理になっています。
今まで使ってきた命令で書き直すと以下のようになります。
TEST ECX,ECX
JE 4010F3
DEC ECX
JMP 4010F0

これだけの処理を1つの命令でやっているのですから、すごいですよね。
ああ、今までに使った命令で、とか言ってるくせに今までに使ってないのまで使っちゃってますね。
Jmp命令は無条件でアドレスジャンプを行う命令、Dec命令は第一オペランドを1減算する、という命令です。
まぁ何はともあれ、ここは3回1を加算するので3を加算する、という処理ですね。
つまりここの処理結果は33h、ということになります。

えっと…次の行からは筆者のネタ切れが見受けられます。(笑
通常の計算で用いる命令はかなりの割合で使いましたからね…。
まぁここの解説は割愛します。
処理結果はそれぞれ、4Dh、65hとなります。

つまり40302Dhから始まる文字列は"cRaC03Me"となります。
Call元に戻れば、lstrcmpA命令がありますね。
1つめの引数のアドレスのディスアセンブル列をみてください。
PUSH 40302Dとなっているはずです。
これは…少し今の段階では説明しにくいので意訳しますが、40302Dから始まる文字列を引数に用いる、という意味です。
GetDlgItemTextA関数のBufferのアドレスのディスアセンブル列も似たようになっているはずです。
lstrcmpA関数の2つ目の引数は403024…これは入力文字列ですね。
つまりlstrcmpA関数では入力文字列と"cRaC03Me"を比較していますので、正解は"cRaC03Me"です。

今回は前回の説明を大いに生かすことで随分コンパクトに収められました。
なので前回の内容を参照した方が理解しやすい箇所が多々ありますので、わからないところがあったらすぐ前回の読み直しをしてみてください。


Copyright © 2008 Rapidsy. All Rights Reserved.