crackme01_x64ソリューションの詳細分析

この記事は、リバースエンジニアリングに興味があり、CPU、アセンブリ言語の動作を基本的に理解している初心者を対象としています。 このクラックメは比較的古く単純ですが、それを解決するとき、基本的にはより複雑なものを解決するときと同じ方法が使用されます。 Webで、彼の分析を含むこの記事のようないくつかの記事を見つけることができます。 またここでも言及しています (歴史のあるクラック)。しかし、それらの決定はこれほど詳細ではありません。 かつては、このような行ごとの解析が本当に不足していたので、混乱したり、コードのこの部分が何をするのか理解できなかったときに調べることができました。 この投稿が少なくとも1人に役立つと判明した場合、私が試したのは無駄ではありませんでした。 すべてのスクリーンショット(最初のスクリーンショットを除く)はクリック可能です。 読書をお楽しみください。



したがって、単純なクラックメットになる前に、それを実行して、それがどのように機能するかを確認してください。









ええ、それはかなり簡単です。正しいシリアルを入力する必要があります。 次に、逆アセンブラでプログラムを開きます。 原則として、逆アセンブルされたリストは、比較的単純なプログラムでさえ、非常に膨大です。 シリアルの入力をチェックするコードの部分を決定するために、「Fail、Serial is invalid !!!」というエラーメッセージのある行がプログラムメモリのどこに格納されているか、どのコードがこの行を参照しているかを見つけます。







データセクションに目的の行が表示され、その下に逆アセンブラーがその行を参照するコードセクションへのリンクを形成し、リンクをクリックします。







Loc_140001191はトランジションのラベルです。下に「 call cs:MessageBoxA 」という行があります。 これが、「Fail、Serial is invalid !!!」というテキストを含むウィンドウを描画する関数の呼び出し方法であるとします。 間違ったシリアルを入力すると、ラベルLoc_140001191によって制御が正確に転送されることがわかります。 さて、掘り続けて、同じラベルにまだアクセスできる場所を見つけましょう。そのラベルの右のリンクをたどります。逆アセンブラーはそこに注意深く配置しました。







これは非常に重要なコードです。詳しく見てください。 GetDlgItemTextA関数の呼び出しがあります。名前にはgettextという単語含まれており、明らかにこの関数は入力されたシリアルを読み取ります。 関数が機能した後、コマンド「 lea rcx、[rsp + 78h + String] 」がスタックから何かアドレスをrcxレジスタにロードします。 次に、 eaxからedxに値を書き込み、 sub_140001000関数を呼び出します。 呼び出し前のレジスタの操作はランダムではないため、関数に引数を渡します。関数が完了した後、コマンド「 test eax、eax 」はeaxの内容をチェックし、 0でない場合、すべてを報告する行のアドレスが読み込まれます約 その後、ウィンドウは対応する通知で描画を開始します。 eaxの操作sub_1400010000の場合、制御は以前に考慮されたラベルに転送され、エラーメッセージが表示されます。 結論: sub_140001000eaxレジスタの値を返します。 これで、このcrackmeのロジックを復元できます。GetDlgItemTextA関数が呼び出されます。おそらく、シリアルを読み取り、その後、 sub_140001000関数が呼び出されます。sub_140001000関数が呼び出されます。 「ゼロ」の場合、正しいシリアル番号が入力され、「ゼロ」の場合は正しくありません。 入力確認するのがsub_140001000であると仮定し、 便利でわかりやすいようにリストのラベルと機能の名前を変更します。 これで、リストは次のようになります。







これでフロアが完成しました。 check_func関数が引数として受け取るものを把握しましょう。これを行うには、関数を呼び出す前にデバッガーでプログラムを実行し、 rcxedxに格納されているものを確認します。







12345と入力しました。 チェック機能の呼び出しで実行が停止しました。 レジスタを調べます。rcxには12F660という数字があります。このアドレスを知っているので、下のウィンドウでメモリダンプを調べます。そうです、入力があります。edx(rdxの若い部分)に5があり、これがシリアル番号。 そこで、どの引数と検証関数が返すかを見つけました。今では、そのプロトタイプがint check_func(* char、int)のように見えると仮定できます。 それを勉強する時間です。 逆アセンブラでチェック機能を開きます。 非常に大きいので、部分的に分析します。







ここではすべてが比較的簡単です。 edxレジスタは入力されたシリアル番号のサイズを保存し、そのアドレスをメモリにrcxします。 まず、 check_func関数が入力された数値の長さをチェックしていることがわかります。 それは19 (16進法では13 )である必要があり、そうであれば、検証は続行( good_sizeラベルに切り替え)、そうでなければ制御はbad_serialラベルに渡されます。終了機能。 結論: 19文字の有効なシリアル番号と、それがさらに理解されます。 入力された番号のアドレスは、すぐにレジスタR8に配置されることに注意してください。



検証メカニズムの調査を続けています。







R8はシリアルの最初の要素を指していることを覚えています。つまり、5番目の要素のアドレスはR8 + 4に格納され、 RAXに記録されます。 さらに、非常に奇妙な一連のコマンドがあり、それらは2回以上会います。



xchg ax, ax db 66h, 66h xchg ax, ax
      
      





より詳細に:



Xchg ax、ax」は、 x86プラットフォームの「 nop 」コマンドに相当します。

次の行はいわゆるプレフィックスであり、次の命令の長さを示すために使用されます。 このコードは無視できます。 これらの指示の後、次の種類のチェックが行われます。RAXアドレス(およびシリアルの5番目の要素のアドレス)にあるものは、2D(16進法で)の数と等しくなければなりません。 2DはASCIIコード記号「-」です。



1.入力を読み取る関数は、数値ではなく文字列を読み取ります。

2.シリアルには、ハイフンで区切られた4つの数字のグループがいくつかあります。



5番目の文字が「-」ではない場合、シリアル番号が正しくなく、bad_serialラベルは0の値で終了しますが、ハイフンで5番目の位置にある場合、 RAXは10を指すときに5増加しますこの文字はこの位置にある要素がチェックされます。 合計で、このようなチェックは3回実行され、シリアルの各5番目の文字は「-」であり、19文字しかありません。つまり、XXXX-XXXX-XXXX-XXXXという形式になります。 入力形式が適切な場合、検証は続行されます。



関数の次のセクションは、特に関心を引くものではありません。







どうぞ







次のことに注意してください: R9は常にブロックの最初の要素を示し、 RCXはブロック内にオフセットを保存します。したがって、チェック後、 R9は次のブロックに到達するために5増加し、ブロックをチェックした後RCXはゼロになります。RDXにはゼロがあることに注意してください。したがって、 zero_to_rcxへのゼロ化が発生します。 「 add eax、0FFFFFFD0h 」という行は、見た目ほど単純ではありません。 実際、これは加算ではなく減算です。 はい、あなたの目を信じないでください、コンピューターの0FFFFFFD0hは-30hの数字です。明らかに、コマンド「 add eax、0FFFFFFD0h 」は、コンパイラーの観点から「 sub eax、30h 」よりも最適です。 エレメントのASCIIコードはeaxに格納され、30hから減算され、9と比較されます。意味は次のとおりです。0〜9の数字のASCIIコードはそれぞれ30h〜39hです。したがって、数字コードから30hを減算しても、結果が9を超えても9を超えることはありません、したがって、eaxではコードは数字ではなく、他の記号であり、それからwrong_serialラベルを調べます。 シリアルは数字とハイフンで構成する必要があります。 次に、ブロックの4つの要素すべてのコードが要約され、4番目の要素のコードが3回加算され、150hが減算されます。 結果はスタックにプッシュされ、R10で合計したすべてのブロックの合計が4で除算されます。







これで、4つのブロックすべての金額がチェックされます。 各ブロックの値は、合計を4で割った値に等しくなければなりません。つまり、最初の文字のASCIIコードと2番目、3番目のコード、4番目のコードの3倍、マイナス150hの合計は、すべてのブロックで同じである必要があります。

検証の最終段階は残ります。







ここではすべてが明確であり、シリアルは同じブロックを持つべきではありません。 R8は入力のアドレスを格納し、 RAXはブロック内の文字を選択するために使用され、 R8はシリアル番号内のブロックを選択するために使用されることを忘れないでください。



これで検証の実行方法がわかり、keygenを作成できます。 ソリューションをできるだけ詳細に説明しようとしましたが、コメントで質問することができます。読んでくれてありがとう。



All Articles