はじめに
この出版物は、いくつかのリバースエンジニアリング技術の研究を目的としています。 すべての資料は情報提供のみを目的としており、個人的な利益のために使用することを意図したものではありません。最初の部分の後の推奨読書
外科医が人の働き方を教えられ、メスを与えられたとしても、この知識を誰かの不利益に使用するという意味ではなく、知識のあるアセンブラーはスーパーウイルスを書くことを夢見ていません。
したがって、これらのレッスンでは、クラックやハッキングのヒントを探すべきではありません。
研究テーマ
Visual Studio Atomineer Proドキュメント(以降、APD)のプラグインコードの調査を続けます。 ツールとその機能を詳しく見てみましょう。
したがって、C ++でクラスがあるとします。
class ClassForReadFile { public: ClassForReadFile(); };
コメントがDoxygenスタイルになるようにAPDを構成します。
クラスにカーソルを
置き、CTRL + SHIFT + Dを押します
。 次のものが得られます。
class ClassForReadFile { public: ClassForReadFile(); };
プラグインは、クラスの説明を追加しました。 すべてが素晴らしい! 先に進みます。 クラスがライブラリに属し、エクスポートする必要があるとします。 マクロを追加してクラス定義を変更する
#ifdef DLL_EXPORTS #define DATA_READER_DLL_EXPORTS __declspec(dllexport) #else #define DATA_READER_DLL_EXPORTS __declspec(dllimport) #endif class DATA_READER_DLL_EXPORTS ClassForReadFile { public: ClassForReadFile(); };
C ++(Windows OS)の場合、状況は標準です。 プラグインをご覧ください。
CTRL + SHIFT + Dを押して、期待したものがまったく得られない
class DATA_READER_DLL_EXPORTS ClassForReadFile { };
DATA_READER_DLL_EXPORTS
定義の名前は
ClassForReadFileの代わりにクラスの名前として定義され、クラスの説明はこの名前から生成されました。 つまり、プラグインコードでは、この状況、つまりクラスのエクスポートは処理されないか、エラーで処理されます。 これが修正を試みるものです。
ステップ1
手がかりを探します。 まず、C / C ++からの関数とクラスのエクスポートは標準的な状況なので、プラグインを正しく「強制」しようとします。 DATA_READER_DLL_EXPORTS
定義の代わりに、
__ declspec命令
自体を挿入し、ドキュメントを生成します
class __declspec(dllexport) ClassForReadFile { };
そして、見よ、彼らは正しいクラスの説明を得た! したがって、APDには、クラスの説明に文字列「__declspec」が存在するかどうかをチェックし、ドキュメントを構築するためのさらなるアルゴリズムを無視するコードがあると結論付けています。
Microsoft SDKの標準のildasm.exeを使用してライブラリを逆コンパイルします。 行「__declspec」を見つけます。 2つのメソッドCmdDocComment :: aおよびCmdDocComment :: bにあります。 クラス1。 私たちはそれをさらに研究することにします。
ステップ2
私たちが探しているのはCmdDocComment ::メソッドであるとすぐに言わなければなりません
__declspecが出会うのはここです。 最も興味深い行のみが表示されます。
文字列a(CmdDocComment.GeneratorInfo A_0) これは、検証後、
リスト[num3] == "__declspec"
削除メソッドが呼び出されます
list.RemoveAt(num3);
推論(大声で考える):
- CmdDocComment ::メソッドには、文字列の配列を含むローカル変数があります
List<string> list = A_0.e;
- この配列の最初の要素は、関数、構造、クラスなどの説明の始まりを格納します。つまり、キーワード「クラス」、「構造」、「ユニオン」
list[0] == "struct"
- 配列の各要素には個別の単語が含まれます。 この場合、それは{"class"、 "DATA_READER_DLL_EXPORTS"、 "ClassForReadFile"}になります
- 配列「e」のすべての要素を巡回し、要素「__declspec」を検索し、それと角括弧内のすべてを削除するループがあります
- サイクルを終了するための追加条件があります。 これは、「where」または「:」という単語の場所です。 公式の「where」という言葉は、正直なところ、なじみがありませんが、クラスを継承する場合は「:」を使用します
新しいアルゴリズムと変更の目的を定義します。
1.変更は、残りの機能に影響を与えません
2.アルゴリズムに従って「リスト」配列の要素を削除します
-最初の要素をスキップします。
-次の要素が「:」ではなく「where」ではなく、配列の最後ではない場合は、削除します。
希望のサイクルを書く
それをプログラムするために残っています。
ステップ3
大声でプログラムします。 一般的な場合のプログラミングは、ソースコードの作成、コンパイル、リンクです。 しかし、難読化ツールはそのような機会を私たちから奪いました。 推奨される
dnSpyツールを使用し
ます 。 ライブラリ内でCILコマンドの16進コードを直接変更します。これは、非常に刺激的で有益なものです。 始めましょう。 dnSpyを開き、ライブラリをロードします。
whileを選択し、ビューをILに変更します
それはかなりかさ高いですが、私もリストを与えます
これで、CILコマンド、HEX表現、ファイルオフセット、および説明のウィンドウができました。 一箇所にすべて。 非常に便利です(
CrazyAlex25に感謝)。
「__declspec」に言及しているブロックに注目しましょう。 ブロックオフセット0x0001675A。 これが編集の始まりです。 次に、RemoveAtメソッドを見つけます。 それは私たちにとって変わらずに役立つでしょう。 ブロックの最後のバイトは0x000167BFです。 HEXエディター
Ctrl + Xに移動し、この範囲に0x00を書き込みます。 変更が何につながったかを保存して確認します。
空のループ while (num3 < list.Count && !(list[num3] == "where") && !(list[num3] == ":")) { if (list[num3] == A_0.b && num2 < 0) { num2 = num3; } list.RemoveAt(num3); num3--; num3++; }
次に、新しいロジックを実装します。 まず、条件を追加します
if (num3 != 0 && num3 < list.Count - 1)
表は、新しいコマンドとその説明を示しています。
1119 | ldloc.s | 指定されたインデックスを持つローカル変数を計算スタックにロードします(短縮形)。 |
---|
2C61 | brfalse.s | 値がfalse、null参照、またはゼロの場合、制御を最終ステートメントに渡します。 注 :num3 == 0の場合、ループ反復子を増やすステップに進みます。 値0x64は、命令0x000167BFへのアドレスオフセットです(リストを参照) |
---|
1119 | ldloc.s | 指定されたインデックスを持つローカル変数を計算スタックにロードします(短い形式) |
---|
07 | ldloc.1 | インデックス1のローカル変数を計算スタックにロードします |
---|
6FF700000A | コールバート | get_Count()-遅延バインドされたオブジェクトメソッドを呼び出し、戻り値を計算スタックにプッシュします |
---|
17 | ldc.i4.1 | 整数値1を計算スタックにint32としてプッシュします |
---|
59 | サブ | ある値を別の値から減算し、結果を計算スタックにプッシュします。 |
---|
2F55 | bge.s | 最初の値が2番目の値以上である場合、制御を最終命令(短い形式)に転送します。 注 :num3> list.Count-1の場合、ループ反復子を増やすステップに進みます。 値0x55は、命令0x000167BFの前のアドレスオフセットです。 |
---|
オフセット0x0001675Aから始まるこれらのバイトを書き込みます。 もう一度保存して逆コンパイルする
最初の条件 while (num3 < list.Count && !(list[num3] == "where") && !(list[num3] == ":")) { if (list[num3] == A_0.b && num2 < 0) { num2 = num3; }
次に、文字列チェック「where」と「:」を追加します。 追加のコメントなしで次のHEXコードを引用します。
07 11 19 17 58 6F F9 00 00 0A 72 A3 1D 00 70 28 70 00 00 0A 2D 3F 07 11 19 17 58 6F F9 00 00 0A 72 92 5E 00 70 28 70 00 00 0A 2D 29
逆コンパイルして、計画したものを取得する
新しいサイクル while (num3 < list.Count && !(list[num3] == "where") && !(list[num3] == ":")) { if (list[num3] == A_0.b && num2 < 0) { num2 = num3; } if (num3 != 0 && num3 < list.Count - 1 && !(list[num3 + 1] == ":") && !(list[num3 + 1] == "where")) { list.RemoveAt(num3); num3--; } num3++; }
このような変更により、プラグインは次のコードドキュメントを生成します。
class DATA_READER_DLL_EXPORTS ClassForReadFile { };
おわりに
このレッスンでは、バグを修正するために知識を適用する方法を学びました。 もちろん、この例はさまざまなエラーとその処理を反映したものではありませんが、これは「一般的なクラック」ではありません。 ソースコードを持たず、アプリケーションを再構築することなく、明らかなバグを修正しました。