DLLInjectionの利用例
動作イメージ



            パワー
            ポイント
  メモ帳

      Windows
    メモ帳

      メモ帳
                   ブラウザ
動作イメージ
各プロセスのメモリ空間内には、当該プロセ
スの独自コード・カーネル・DLLがロード
   プロセスA           プロセスB

   カーネルコード         カーネルコード


   プロセスAのコード       プロセスBのコード



     Other.dll        test.dll

    user32.dll      user32.dll

    kernel32.dll    kernel32.dll
2種類のDLL配置
ダイナミックにロードする

//test.dllをロードする
HMODULE hDll = ::LoadLibrary( "test.dll" );
if ( hDll != NULL ){

      //DLLを使用するコードをここに記述

       //test.dllをアンロードする
     ::FreeLibrary( hDll );
}
AのコードでBのメモリ空間に
ロードすればインジェクション成立
   プロセスA          プロセスB


   プロセスAのコード
  LoadLibrary()   プロセスBのコード




                    test.dll
他プロセスでスレッド実行


HANDLE CreateRemoteThread(
 HANDLE hProcess, // 挿入先プロセスハンドル
 LPSECURITY_ATTRIBUTES lpAttr,// セキュリティ属性
 DWORD dwStackSize, // スタックサイズ
 LPTHREAD_START_ROUTINE lpAddr,//関数ポインタ
 LPVOID lpParameter, // 引数ポインタ
 DWORD dwCreationFlags, // 作成フラグ
 LPDWORD lpThreadId // スレッド識別子
 );
LPTHREAD_START_ROUTINE 型

typedef DWORD (
  __stdcall *LPTHREAD_START_ROUTINE)
   ( [in] LPVOID lpThreadParameter );



HMODULE LoadLibrary(
   LPCTSTR lpFileName //モジュール名
);
BOOL FreeLibrary(
   HMODULE hModule // DLLハンドル
);
関数のアドレス

    プロセスA                     プロセスB

     カーネルコード                   カーネルコード


   プロセスAのコード                 プロセスBのコード




 0x75230000 kernel32.dll   0x75230000 kernel32.dll
       0x75230xxx                0x75230xxx
      Loadribrary()             Loadribrary()
インジェクション・コード
//kernel32のモジュールハンドル取得
IntPtr m = GetModuleHandle("kernel32.dll");
//LoadLibrary()のアドレス取得
UIntPtr a = GetProcAddress(m, "LoadLibraryA");
//スレッド実行
IntPtr t = CreateRemoteThread(
   hProcess,IntPtr.Zero, 0, a, param, 0, out b);
//スレッド完了待ち(DllMainから返るのを待つ)
WaitForSingleObject(t,INFINITE);
//DLLハンドルの取得
if (GetExitCodeThread(t, out hDll)) {
      if(hDll!=IntPtr.Zero)result = true;//成功
}
//スレッドクローズ
CloseHandle(t);
LoadLibraryのパラメータ
HMODULE hDll = ::LoadLibrary( "test.dll" );


自プロセスのメモリ空間に展開された文字列へのポインタ
プロセスBのスレッドにはプロセスB上のアドレスが必要



             プロセスA            プロセスB


             0x02583000       0x02583000
              “test.dll”       Int x=100
他プロセスでメモリ操作
VirtualAllocEx()   挿入先プロセスでメモリ確保
WriteProcessMemory()     挿入先のメモリ空間に書き込む

書き込んだメモリを使用する Loadribrary()のパラメータ
VirtualFreeEx() 確保したメモリの開放



               プロセスA           プロセスB
             VirtualAllocEx
        WriteProcessMemory
                              0x000034000
                               “test.dll”
メモリ操作・コード
UInt32 MEM_COMMIT = 0x1000;
UInt32 PAGE_EXECUTE_READWRITE = 0x40;
IntPtr m = (IntPtr)VirtualAllocEx(
       hProcess, IntPtr.Zero, (uint)len,
       MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (m != IntPtr.Zero) {
    //確保した挿入先の領域にDLL名を書き込む
    WriteProcessMemory(
      hProcess, m, dllName, (UIntPtr)len, out b);

  //確保したメモリをここで使用する
  //(挿入先でLoadLibraryを呼び出す)

  //確保したメモリの開放
  VirtualFreeEx(hProcess, m, 0, 0x8000);
64bitOSの問題(混在)
64ビットOSでは、従来の32ビットプログラムもWOW64に
よりそのまま使用することができるため、両者が混在している

64ビットEXEは、64ビットDLLのみ使用可能
32ビットEXEは、32ビットDLLのみ使用可能




 Dllインジェクションも、挿入先に合わせたDLLが必要
64bitOSの問題
(ERROR_ACCESS_DENIED)


WOW64上からは、CreateRemoteThread()は、
ERROR_ACCESS_DENIEDが発生して使用できません。

使用できないという正規のドキュメントは、見つけられませんでしたが、
同一趣旨のコメントが多数検索にヒットします。




 64bit上では、64bitのEXEしか使用できない
64bitOSの問題
(kernel32.dllのアドレスが違う)
WOW64上の32bitプロセスは、違うkernel32.dllを
ロードしている。
 c:¥>listdlls POWERPNT.EXE                          32ビットプロセス
 ListDLLs v3.1 - List loaded DLLs

 Base               Size      Path
 0x00000000776b0000 0x160000 C:¥Windows¥SysWOW64¥ntdll.dll
 0x0000000075230000 0x110000 C:¥Windows¥syswow64¥kernel32.dll

 >ListDlls notepad                                 64ビットプロセス
 ListDLLs v3.1 - List loaded DLLs

 Base               Size      Path
 0x0000000077650000 0x186000 C:¥Windows¥system32¥ntdll.dll
 0x00000000771c0000 0x12d000 C:¥Windows¥system32¥kernel32.dll


 64ビット上から32ビットプロセスのアドレスが分からない
64bitOSの問題(まとめ)
マトリックスにしてみると、下記の3種類しか動作できないこ
とが分かる




挿入先に応じたDLLが必要
WOW64で動作させない
64bitのEXE上で32bitのkernel32ロードのアドレスが必要
挿入先に応じたDLLが必要
64bitOS上では、64bitの「notepad.exe」が動作している




OSにより違う場合は、2種類用意して実行時に選択する
WOW64で動作させない
OSに合わせたEXEを用意する




.NETの「AnyCPU」で作成すれば解決

//ポインタが8バイトの場合
If(IntPtr.Size == 8){
       //64bit環境で動作中
}
64bitから32bitの
 kernel32.dllのアドレス取得
   32bitでコンパイルした下記のEXEを実行して
   アドレスを取得する

  #include <windows.h>
  int main(int, char**){
     return(int) =
       GetProcAddress(
            GetModuleHandle(“kernel32”), “LoadLibraryA”);
  }


参考「64bitプロセスから32bitプロセスにDLL Injection (C言語) 」
http://nazochu.blogspot.com/2011/09/64bit32bitdll-injection.html
4G超のアドレスに注意が必要
kernel32.dllのアドレスは幸い32bitで表現可能だった
>ListDlls notepad                                 64ビットプロセス
ListDLLs v3.1 - List loaded DLLs

Base               Size     Path
0x0000000077650000 0x186000 C:¥Windows¥system32¥ntdll.dll
0x00000000771c0000 0x12d000 C:¥Windows¥system32¥kernel32.dll


Dllハンドルは32bitで表現できない
64ビット対応Dllインジェクション

64ビット対応Dllインジェクション