簡単に言えば、問題はこれです。カーネルモードのWindowsドライバーを作成しています。このドライバーは、カーネルモードのDLL(または他の実行可能モジュール)が読み込まれると通知を受け取ります。状況によっては、DLLエントリポイントルーチンをインターセプトする必要があります。つまり、ルーチンが最初に呼び出されるようにオーバーライドしてから、元のエントリポイントに制御を渡すことができます。
32ビット(正確にはx86)では、それを行うのに問題はありませんでした。モジュールのベースマッピングアドレスを取得します。これは、実際には標準のPEヘッダー(Windows実行可能ファイルで使用される)で始まります。DLLエントリポイントのRVA(イメージベースに相対的なアドレス)があります。ルーチンのアドレスからモジュールのベースアドレスを引いたもので上書きします。出来上がり!
現在、64ビットでは状況がより複雑になっています。問題は、RVAがまだ32ビット整数であるということです。このようなRVAは、イメージのベースアドレスから始まり、4GBのオフセットで終わるアドレス範囲をカバーします。同じ実行可能モジュール内のシンボルを参照するのに問題はありませんが(4GBのサイズを超えないと仮定)、これにより、モジュール間のインターセプトに問題が発生します。当然、私の実行可能モジュールとフックしようとしているモジュールは、同じ4GBの範囲に入る必要はないので、問題があります。
一時的に、元のルーチンのプロローグコードを無条件でコードにオーバーライドすることでこれを解決しましたjmp
。これは、64ビットプラットフォームでは12バイトかかります。次に、ルーチンから元のコードを呼び出すために、オーバーライドされた12バイトを復元します(つまり、上書きする前にそれらを保存します)。
これまでのところ、問題はありません。しかし、今は状況が変化しており、エントリポイントルーチンへのマルチスレッドアクセスをサポートする必要があります(理由は聞かないでください。これは、いわゆる「ユーザースペース」にロードされたマルチセッションDLLに関連しています。各ターミナルセッションに対して)。
解決策の1つはグローバルロックを使用することですが、これは避けたいと思います。
いわゆる「トランポリン機能」については知っていますが、これも避けたいと思います。これを行うには、命令境界と可能な分岐を適切に識別するために、関数プロローグコードの実行時デコードが必要です。
最近、別のアイデアを考えました。mov RAX addr
元のDLLの「不要な」部分(少なくとも12バイトの長さ( +のサイズ))が見つかった場合はどうなりますかjmp RAX
。jmp
そうすれば、この部分は私の手に上書きされる可能性があります。次に、エントリポイントRVAをこの部分に設定できます。
これが機能するために必要なのは、上書きできる適切な部分だけです。PEヘッダーには、何十年も使用されなくなった履歴フィールドが多数含まれているため、このような可能性があると思います。
このアイデアは試す価値がありますか、それともこれはよく知られた手法ですか?アンディ他の提案?
前もって感謝します。