32bitプロセスは32bitのDLLをロードし、64bitプロセスは64bitのDLをロードして使用することが出来ます。これは基本的には破ることが出来ないルールです。そのためDLLインジェクションする際にもターゲットプロセスに合わせたDLLを作成することになります。
DLLインジェクションの際にはCreateRemoteThread APIを使用して、ターゲットのプロセスに自分のDLLをロードさせるのが一般的です。このときkernel32.dllのロードアドレスが問題になってきます(実際に使用するのはLoadLibrary関数ですが、これはkernel32.dllに含まれるので)。多くのプログラムでは現時点のWindows実装ではプロセス毎にkernel32.dllの位置が変わらないために、自プログラムでLoadLibraryのGetProcAddressを行ってターゲットプロセスでのアドレスを求めたことにしてしまうのが流行りのように見えます。
しかしこの方法では問題があります。1つは今後のWindows実装において各プロセス毎にALSRが適用になった際に追従できないこと。もう1つが32bitプロセスに対してLoadLibraryのアドレスを求められていないこと、です。ある例においては、32bitのプログラムを作成し、その中でkernel32.dllのLoadLibraryを求めて、呼び出し元の64bitプロセスにそのアドレスを返すという手順をとっているものがありました(参考:64bitプロセスから32bitプロセスにDLL Injection (C言語))
上記の問題点に対応するために、もっと本気度の高い求め方を考えてみたいと思います。
必要となる情報
ここで必要になる情報は何なのか、確認してみたいと思います。「ターゲットプロセスでのLoadLibrary関数のアドレス」この情報だけです。ただ、これを分解して考えてみると、以下のようになります。
- ターゲットプロセスでのKernel32.dllのベースアドレス
- LoadLibrary関数のRVAの値(相対アドレス)
ターゲットプロセスのモジュール一覧は割と簡単に求めることが出来ます。32bit/64bitの境界をこえる際には前回の記事のように使用する関数に注意が必要ですが…。このモジュール一覧取得の際にモジュール毎のベースアドレスも求まるのでこれを利用します。
続いて LoadLibrary関数のRVAですが、これを求めるのは少々大変です。対象プロセスでロードされているkernel32.dllをファイルとして開いて、そのPEヘッダ等と解釈しながら求めるというのが必要になってきます。64bitプロセスではこの32bit kernel32.dllをLoadLibraryして関数のアドレスを解決することが出来ません。またこのような手順であれば対象がロードしているDLLから解釈することになるので、アドレスの正確性が増すように感じます。
かるく実験した結果ではうまく動作させることができています。PEヘッダから全ての情報を処理しなくともRVAは求めることが出来ます。
このような手順を利用することで今後もしもプロセス毎のALSRにkernel.dllも含まれるようになったときにもそのままで対応可能なように思います。
コメント