Injection 実装メモ

32bit環境だけを想定した場合との違いについてメモ(例によって2008環境のことを書いているので、それ以外、特にXP/2003だと違う可能性あり)。

32bitにおけるDLL注入は別のプロセスにコードを割り込ませる3つの方法の「2. CreateRemoteThread+LoadLibraryのテクニック」を使うのがもっとも簡単な方法。ただし64bit環境ではこの方法は適用できない。


LoadLibraryの戻り値をスレッドの戻り値として受け渡すこの実装方法は、スレッドの戻り値(GetExitCodeThreadで受け取れる値)であるDWORDで、LoadLibraryの戻り値であるHMODULEを受け取れることを前提とする。

//↓DWORD
DWORD WINAPI ThreadProc(
  __in  LPVOID lpParameter
);

BOOL WINAPI GetExitCodeProcess(
  __in   HANDLE hProcess,
  __out  LPDWORD lpExitCode
);       //↑DWORD

64bitでは、この前提が成り立たないため、リモートスレッド内で実行されたLoadLibraryの戻り値を受け取るには別のプロセス間通信方法を実装する必要がある*1。結局64bitでは「3. CreateRemoteThread+WriteProcessMemoryのテクニック」の方法を使うのが妥当な方法となる。
この方法は自分でVirtualAllocEx/WriteProcessMemory/ReadProcessMemory/VirtualFreeExを使って通信する面倒があるが、とても自由にプロセス間通信ができる。

別の問題

CreateRemoteThreadには32bit <-> 64bit間の動作に次のような制限があるようだ。

自身 32 32 64 64
対象 32 64 32 64
結果 OK NG OK OK

PROCESS_ALL_ACCESS権限であっても32bitプロセスからは64bitプロセスにCreateRemoteThreadすることができない(VirtualAllocExとかはできるのにね!)。CreateRemoteThread内部で使用しているNtCreateThreadExがSTATUS_ACCESS_DENIEDを返してくる。NtCreateThreadExを辿って仕組みを見て行きたいところだが、それはさておき、仮に出来たとしてもあまり幸せな結果にはならない。


たとえば逆のケース、64bitプロセスから32bitプロセスにコードを注入することができる。しかしこの場合、x86用のコードを注入しないとプロセスは即死する。x64用にC言語をビルドすると当然x64用のコードが生成されてしまうので、x86(WOW64)のプロセスでは解釈できない。まーこれはchar配列でx86アセンブラ作ればいいんだけど、やっぱり良くない。というか私には無理。あとは構造体のバイトアライメントの問題とか、なにかと面倒な点が出てくる。


シンプルに、32bitプロセスからは32bitプロセスだけ、64bitプロセスからは64bitプロセスだけを対象としたほうが実装上の楽が多い。

64bitとか関係なしに

CreateRemoteThreadは異なるセッションのプロセスを対象にすること出来ない。

Therefore, CreateRemoteThread fails if the target process is in a different session than the calling process.

Vista以降はサービスプロセスがセッション0に移動してしまっているので、彼らを対象とするには自分自身をセッション0から開始させる必要がある。ツール的には psexec だけど、プログラミング的には、やっぱりCreateServiceするしかないかな? というところ。


#2008/08/30追記
セッション間はSeDebugPrivilege+スレッドコンテキスト(eip/rip)の書き換えで対応できそう。やるかやらないかはまた別として。
#2008/09/13追記
やった。でもいまいちだった。サービス経由ならセッションをまたいでプロセスを生成できるので、そのプロセスからCreateRemoteThreadすることで対応できそう。暇が出来たら実装する予定。

*1:GetExitCodeProcessも然り。DWORD64なら楽だったんですけどねぇ。