TPoDT#2からのKeygenme分析

すべての良い一日。

これは私であり、リバースエンジニアリングのトピックを再び大衆にもたらします。 なんらかの理由で、私は記事で商用保護者やプログラムの分析をカバーできないため、今日は実験​​用ウサギがTPoDTグループの重要なテーマになります。難しいことは言うまでもありませんが、それに数時間を費やすことは残念ではありませんでした。



知らない人のために:keygenmeは、他の人がそれらを分析するために一部のクラッカーによって作成されるプログラムの一種です。 原則として、目標はいくつかの興味深い数学的アルゴリズムまたは異常なコード保護を示すことです。

このリンクからkeygenmeをダウンロードできます。 著者に敬意を表する価値があります-keygenmeはいかなる方法でもパックされていないので、不必要なジェスチャーから私たちを救います。 そして、どうやら作者たちはすてきなインターフェースを作成することさえ気にかけました。



これをデバッガーにロードし、入力フィールドからテキストを取得するために使用される標準のWinAPI関数( GetDlgItem, GetDlgItemTextA, GetWindowTextA.



ブレークポイントを設定しますGetDlgItem, GetDlgItemTextA, GetWindowTextA.



プログラムを実行用にリリースし、任意の名前でドライブし、キーを押して「チェック」ボタンを押します。

不快な驚きはありません。すぐにGetDlgItemTextA



停止しGetDlgItemTextA



。 ブレークを削除し、APIを終了します-アドレス0x0040195Bにいます



ご覧のとおり、テキストは入力フィールドから読み取られており、空の場合はエラーのあるメッセージボックスが表示されます。 以下に、 CheckCode



関数への入り口をCheckCode



ます。この関数は、チェックの結果に応じて0または1を返します。 それに応じて、真と偽の検証オプションのMessageBoxペアの下。 このような保護の不条理にもかかわらず、アプリケーションのほぼ半数で見られることに注意してください。 そして、目標が単にキーではなく動作するアプリケーションを取得することである場合、呼び出しをシーケンスに置き換えることができます

  xor al、al
株式会社 


しかし、アルゴリズム自体は私たちにとって興味深いものであるため、 CheckCode



とtraceに入ります。



コードは非常にきれいで読みやすいです。 最初のステップは、 GetOnlyNumeric



と呼ばれるサブGetOnlyNumeric



を呼び出すことです。サブGetOnlyNumeric



は、入力されたコードへのポインタとしてパラメータとして渡されます。



C愛好家のために、私は特にCのような擬似コードについてコメントしました。 ループ内のコードは入力された行を読み取り、文字が'A'-'F'



または'0'-'9'



の範囲にない場合、文字をスキップします。 文字が条件を満たしている場合、新しい行で接着します。 これは、区切り文字を使用してキーを「美しい」形式で入力できるようにするためです。

CheckCode



関数のコードに戻りましょう。 0x00401457



、正規化された文字列の長さのカウントを見ることができ、24になるはずです(スクリーンショットにタイプミスがあります) 。 なぜ24? REP SCASB



は、 ECX



を減らしながら、 AL



から文字を検索します。 最初、ECXは最大値として0xFFFFFFFFを示します。 通常、この後、 NOT ECX



およびDEC ECX



コマンドが実行され、通常の形式で文字列の長さが取得されます。 私たちもやります

  -1Ah = FFFFFFE6h
 FFFFFFE6hh = 19hではない
 19時間-1 = 24dec


sscanf



関数への後続の呼び出しは、正規化された文字列を3つのバイナリDWORDに連続して変換します。 そして、スクリーンショットの一番下には、10個のバッファーDWORDの連続XOR



によって、入力された名前からハッシュをカウントする小さなループがあります。 (長さ40文字のバッファー)これは、最初の準備が完了し、チェックが開始される場所です。



個人的に、プログラムを研究するとき、私は最初に彼女が何をするかを表面的に見て、コードをトレースし、次にチェックとは反対の方向に必要なセクションを分析します。

この場合、ソリューションはEDI=ECX



正しいものになります。

ECX



から始めましょう-比較の直前に、 LEA ECX, [ECX*4+ECX]



コマンドが実行されます。

一般に、 LEA



はアドレスをカウントするためのコマンドですが、この場合は式ECX = ECX*4 + ECX



またはより単純にはECX*5



ます。 コードを0x0040154F LEA ECX, [EDX*2+EDX]



と、ECXは0x0040154F LEA ECX, [EDX*2+EDX]



ます。 繰り返しますが、これは単純な乗算です。 最終的なECX = EDX*3*5



ことがECX = EDX*3*5



ます。 しかし、 EDX



は何ですか? コードを改ざんすると、キーの最後のブロックの上部と下部の間のXOR



演算に等しいことがわかります。 将来、結果の数値をSUMと呼び、最後のブロックの侵害された2バイトをCRCと呼びます。 ブロックの理由を説明しましょう-入力された文字列をバイナリ形式に変換した後、それらに対するすべての操作はDWORDまたはWORD値で実行されます。 したがって、キータイプは次のようになります。

  1111-2222-3333-4444-5555-6666 


これは私のバリエーションです;ハイフンも異なるように配置できます。

それで、条件の正しい部分を学びました。次に、左に進んでEDIを計算します。

SUB EAX, 798134



後に何が起こるかを簡単に説明しSUB EAX, 798134



。 結果の値、次にKEY1が変数に格納され、そこから4つの半バイト(4ビット)が取得され、位置2、3、6、7(0から番号付け)に配置され、それらの間で乗算されます。 結果の値はECX



と等しくなります。 最初に頭に浮かぶのは、crcとKEY1が0になる値を計算することです。すべては問題ないようですが、このオプションでは最後のチェックを除くすべてのチェックに合格します。 しかし、それについては後で。

それでは、KEY1になる前にEDX



何が起こるか見てみましょう。 コメントから、 XOR



再び使用して、ブロックblock1とblock2の両方に代わって、ハッシュの下部から最初に考慮されることを理解できます。 次に、2つの操作が実行されます。

  XOR EAX、9F827364
サブEAX、798134 


KEY1から元の番号を取得するには、次のように逆にすることができます。

  EAXを追加、798134
 XOR EAX、9F827364 


この段階では、数字の代用を試みません;最初に、残りのチェックを調べます。



このスクリーンショットには、一度に3つあります。 最初のものは前のものをほぼ繰り返します。 違いのうち、計算アルゴリズムKEY2の変更を区別できます。

  ECXを追加、871623
 XOR ECX、4637A819 


これも簡単に変換されます

  XOR ECX、4637A819
 SUB ECX、871623 


また、ハーフバイトインデックスが変更されました。 これで、5と6はKEY2から、(!)1、2はKEY1から取得されます。 アルゴリズムに従ってさらに使用されるため、数字を拾う価値はないと言ったのはそのためです。 さらにチェックを行うと、キー番号は生成されなくなりますが、受信した2つのKEY1とKEY2が使用されます。

3番目のチェックでは、KEY1の0、1、4、5ハーフバイトを乗算します。

スクリーンショットの一番下の0x00401654



には、 CMP SI, BX



非常に小さなチェックがあります。 複雑ではありません。また、トレースによって、代わりにハッシュの下部が入力されたコードの5番目のブロックと比較されていることがわかります。 残りの3つのチェックも小規模です。

最初の2つは同じタイプであり、乗算のためにキー番号からハーフバイトを選択するだけです。 したがって、私は繰り返しません。 0x004016BF



にある検証にはさらに注意が必要です。 ここでは、彼女は最も潜行性が高く、以前のすべてのチェックが依存するcrc、またはより単純な最後のブロックのpokorsennyeバイトを比較します。 そして、実際にはKEY1とKEY2をゼロにすることはできません。

SHL EDX, 1



コマンドは、左に1ビットシフトします。これは、数値に2を掛けることに相当します。合計すると、pokorsorny数値は任意のコードの定数であり、0xB6 / 2 = 0x5Bである必要があります。

今少し数学。 最初に、チェックで比較が行われる数はCRC * 15であることがわかりました。 上記でCRC = 0x5Bであることがわかったため、SUM = CRC * 15 = 0x555 = 1365(10)を取得することは難しくありません。

各チェックで、ハーフバイトを乗算する場合、常に4つのオペランドが使用されます。つまり、1365を4つの要因に分解する必要があります。 3と5の可分性が目で見える場合、残りの7と13はもはや明白ではありません。 しかし、一般的に、タスクは難しくありません。 商用バージョンでは、より多くの要素を使用することが可能であり、それに応じて、より多くの要素を使用することが可能であり、分解にはより多くの時間がかかります。 要因を把握したので、さまざまな反復でのハーフバイト選択マップを見てみましょう。



お気づきかもしれませんが、4つの列が強調表示されています。これらは2つのチェックで交差しています。つまり、これらの値は個別に生成する必要があり、すべて異なる必要があります。 残りのハーフバイトはランダムに選択でき、各チェックには4つの異なる要因すべてが存在するという条件があります。

同時に、すべてではなく半分の値のみを生成し、残りを鏡面反射で満たすことができる対称性に気付きました。

私のニックネームの小さな例を挙げましょう。

それからのハッシュは0x6930622F



になります。 古い部分はアルゴリズムに関与しません。

KEY1とKEY2は、たとえば5d7337d5 d537735d



d537735dを選択します。

次に、block1-block4を取得するには、次の操作を実行する必要があります。

  (5d7337d5 + 798134)^ 9F827364 = C26ECA6D
 block1 = C26E ^ 622F = A041
 block2 = CA6D ^ 622F = A842

 (d537735d ^ 4637A819)-871623 = 9279C521
 block3 = 9279 ^ 622F = F056
 block4 = C521 ^ 622F = A70E 


5番目のキーブロックは、ハッシュの下部です。 私の場合-622F。 さて、最後の6番目のブロックでは、さらに選択肢があります。一般的な式は(c << 8)| (c ^ 0x5B)。 最も単純な場合、005B(00 ^ 5B = 5B)。

最後のキーは次のようになります

  A041-A842-F056-A70E-622F-005B 


すべてが正しく見つかった場合、次のウィンドウが表示されます。

キージェネレーターの私のバージョンはここにあります。



All Articles