これは私であり、リバースエンジニアリングのトピックを再び大衆にもたらします。 なんらかの理由で、私は記事で商用保護者やプログラムの分析をカバーできないため、今日は実験用ウサギが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
すべてが正しく見つかった場合、次のウィンドウが表示されます。

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