4

タイトルが少し複雑だと気付いたので、私がやろうとしていることを説明しましょう。

書き込もうとしている概念実証のために、単純なDLLインジェクションを書き終えたところです。プログラムは、現在のプロセスのスナップショットを取り、プロセスツリーを列挙し、DLLを直接の親プロセスに挿入します。現在、理想的な条件下では、これは正常に機能します。32ビットバージョンのインジェクターは32ビットの親プロセスに注入でき、64ビットバージョンのインジェクターは64ビットの親プロセスに注入できます。

ただし、現在私が探しているのは、x64インジェクターから32ビットの親プロセスに32ビットのDLLを挿入することです。そのDLLが挿入されたら、挿入されたDLLによってエクスポートされた関数の1つへの呼び出しを挿入したいと思っていました。しかし、それが実際に可能かどうかはわかりません。(親プロセスが32ビットプロセスであるか64ビットプロセスであるかを識別するためのコードをすでにまとめているので、問題にはなりません)

さて、プリコンパイルされたマシンコードをプロセスに挿入することで最初の部分を実行しているように見えるコードをすでに見つけました。(少なくとも、それが実行されていると思います)通常、LoadLibraryWへの呼び出しを挿入した後、その呼び出しによって返されたアドレスを取得し、呼び出したいエクスポートされた関数に相対オフセットを追加し、それに呼び出しを挿入します働き。ただし、この場合、32ビットライブラリを64ビットインジェクターにロードできないため、通常のようにGetProcAddressを使用して関数の相対オフセットを見つけることができません。私は次のことを行うことでこの問題を乗り越えました:

通常の方法では32ビットDLLの関数オフセットを見つけることができないため、現在、ファイルをバッファーに読み込み、そのバッファーを使用してIMAGE_NT_HEADERS32構造体にデータを入力し、IMAGE_EXPORT_DIRECTORYを列挙して名前と相対オフセットを見つけています。エクスポートされたすべての関数の。

したがって、この時点で、私は次のようになります。

  • 32ビットプロセスにロードされた32ビットDLL
  • 32ビットプロセスで次のコードを実行する場合のfuncAddrと同等の値:

コード:

HMODULE hInjectedDLL = LoadLibrary("mydll.dll");
DWORD funcAddr = (DWORD)GetProcAddress(hInjectedDLL, "ExportedFunc") - (DWORD)hInjectedDLL;

理論的には、今必要なのはhInjectedDLLの値だけであり、その関数を呼び出すことができるはずです。残念ながら、アセンブリやマシンコードについては、その値を取得する方法を知るのに十分な知識がありません。

何か案は?

(また、2つのバージョンのインジェクターをコンパイルし、親プロセスのプロセッサーアーキテクチャーが一致しない場合に一方を実行するだけで、多くの問題を回避できることを認識しています。ただし、このルートです。)

編集:この概念実証で私が実際に達成しようとしていることを説明するのに役立つかもしれないと考えました。

元のプロセスが子プロセスの終了を待つ必要なしに、現在のコンソールで子プロセスの実行を許可する必要があるという考えを実験しています。コンソールアプリケーションでこれを行うための組み込みAPIがないため、通常はプロセスのツリーで立ち往生しており、すべてがそれぞれの子プロセスが完了するのを待っています。この機能を容易にするために、私は次のことをしたいと思います。

注入

DLLインジェクションは、「プロセスの実行」の役割を果たします。(通常、子プロセスが終了するまで待機する必要があるプロセス)実行時に、親プロセスのプラットフォームを判別し、親プロセスがコンソールベースのアプリケーションであるかどうかを判別します。そうでない場合、プロセスは単にexecファミリーの関数を使用して目的のサブプロセスを実行し、すぐに終了します。親プロセスがコンソールアプリケーションの場合、インジェクターは使用するDLLを決定し、インジェクタープロセスを最初に作成したスレッドを一時停止してから、DLLを親プロセスに挿入します。

私たちの機能を解決する

DLLが配置されると、インジェクターはDLLによってエクスポートされた関数のアドレスを判別します。(通常、これを行うには、 CreateRemoteThreadを呼び出して最初のインジェクションを実行し、次にそのスレッドでGetExitCodeThreadを使用して、親プロセスのDLLのベースアドレスを取得します。これを取得したら、次のアドレスを見つけるのは簡単な計算です。エクスポートされた関数。これを使用して、その関数への2番目の呼び出しを挿入できます。

私たちの関数を呼び出す

エクスポートされた関数は、次のようになります。

BOOL RewriteHProcess(HANDLE hProcess)

インジェクターは再びCreateRemoteThreadを使用して、親プロセスのコンテキストからこの関数を呼び出します。hProcessはインジェクタープロセスへのハンドルです。DLL側では、関数は2つのことのいずれかを実行します(スレッド間のメモリアクセスのセキュリティ制限を考えると、最初のアイデアが可能かどうかはよくわかりません。そのため、フォールバックする2番目のアイデアをまとめました。最初はうまくいきません。)

  1. RewriteHProcessは、以前に中断されたスレッドを読み取りと書き込みのために開き、 ReadProcessMemoryを使用して、プロセスのメモリでインジェクタープロセスへのハンドルを検索します。(親プロセスが現在、 WaitForSingleObjectを使用してそれ以上の実行をブロックしていると想定しています。働き。コマンドプロンプトが少なくとも実行することはわかっています。当面はそれが私の焦点です)次に、DLLは内部関数を呼び出して目的の子プロセスを作成し、古いハンドルを閉じて、メモリを新しいハンドルで上書きします。子プロセス。この時点で、可能なことをクリーンアップして戻ります。次に、インジェクターは必要な残りのクリーンアップを実行し、中断されたスレッドを再開し、プロセスとスレッドへのハンドルを閉じて終了し、新しい子プロセスが終了するのを待つ間、親プロセスをブロックし続けます。

  2. そのルートが不可能な場合、私のフォールバックは、インジェクターからブロッキングスレッドを一時停止し、挿入されたDLLに新しい子プロセスを作成し、インジェクターをクリーンアップして終了し、子プロセスが完了するまでDLLで待機することでした。その時点で、DLLはクリーンアップし、中断されたスレッドを再開し、それ自体をアンロードします。(ただし、このルートの欠点は、親プロセスがインジェクターから返す戻りコードが、ターゲットの子プロセスからの戻りコードと同じでない場合があることです)

4

2 に答える 2

3

VirtualAllocEx()ターゲット プロセス内に実行可能メモリのブロックを割り当てるために使用しWriteProcessMemory()、必要に応じて x86 または x64 マシン命令をそのメモリ ブロックに書き込むために使用します。それらの命令で、必要に応じて、エクスポートされた DLL 関数を呼び出すようLoadLibrary()GetProcAddress()します。次にCreateRemoteThread()、メモリブロックを実行するために使用します。別のプロセスで実行されている場合、インジェクターはエクスポートされた DLL 関数を直接呼び出すことはできません。エクスポートされた関数は、ターゲット プロセスのコンテキスト内で読み込んで呼び出す必要があります。LoadLibrary()また、 の戻り値から の戻り値を減算しないでGetProcAddress()ください。 GetProcAddress()直接呼び出すことができるように、関数への直接メモリ ポインターを返します。

更新:DLL_ATTACH_PROCESSこれのバリエーションは、理由とともに呼び出されたときに、挿入されたすべてのコードを DLL のエントリ ポイント内に配置する (またはエントリ ポイントがコードを実行するスレッドを生成する) ことです。したがって、DLL から関数をエクスポートする必要はありません。次に、 と を使用VirtualAllocEx()WriteProcessMemory()て DLL のパスをターゲット プロセスに格納し、 を使用CreateRemoteThread()して直接呼び出すことがLoadLibrary()できます。カーネル関数はプロセス間で常に同じメモリ アドレスを持つため、注入プロセスはGetProcAddress()独自のアドレス空間内で呼び出して のアドレスを取得し、LoadLibrary()そのポインタを のlpStartAddressパラメータに渡すことができますCreateRemoteThread()。このように、x86/x64 アセンブリ コードの記述について心配する必要はありません。

この手法については、この記事のセクション 3で詳しく説明しています。

コードを別のプロセスに挿入する 3 つの方法

于 2012-11-09T00:41:21.053 に答える
1

誰かがこの質問を見つけて、コメントを読んでもまだ混乱している場合は、ここで (確かに醜い) 概念実証を見つけることできます。DLL インジェクションの通常の戦略を既に知っていると仮定して、別の方法で行う必要があることについて説明します。(関連するコードを含む):


エクスポートを見つける( injdll32_64.c )

通常はこの方法で進めることができLoadLibrary/GetProcAddressますが、問題の 2 つのモジュールは異なるプロセッサを対象としているため、別の方法で処理する必要があります。Remy Lebeau が彼の回答で示唆したように、アセンブリ側でこれを完全に行うことができます。kernel32.dllしかし、私の見解では、 のベース アドレスを特定し、エクスポート テーブルを見つけて、特定するためのアセンブリを作成することは、お尻の痛みのように思えましたLoadLibrary。代わりに、各 DLL を読み込み、必要なエクスポートを検索し、後で使用するために RVA を保存するGetProcAddressことで、C 側で処理しました。IMAGE_EXPORT_DIRECTORY


RVA によるエクスポートの呼び出し( injdll32.cx86.final.asm )

この目的のために、挿入された DLL をターゲット アプリケーションのメイン スレッド内で実行する必要がありました。そのために、ターゲット アプリケーションのメイン スレッドを一時停止し、事前にアセンブルされたマシン コードにメモリを割り当て、プレースホルダーに適切なエクスポート RVA を入力し、ターゲット アプリケーションの EIP を への呼び出しで変更GetThreadContext/SetThreadContextし、一時停止したスレッドを再開しました。 . (必ずしもこの順番ではありません)残念ながら、ターゲット アプリケーションで VirtualAlloc したメモリを解放するメカニズムを書くまでには至りませんでしたが、そうするための解決策の 1 つは、ターゲット プロセスが元の状態に戻ったときにインジェクターに通知するメカニズムを実装することです。 EIP。その時点で、インジェクターが割り当てられたメモリを VirtualFree しても安全です。(あるいは、いつでも適切なコード ケーブを見つけて、メモリを解放する心配さえなくすこともできます)。

于 2013-09-22T02:51:34.080 に答える