AppInit_DLLs による DLL インジェクション
概要
ここでは AppInit_DLLs レジストリ値へ DLL 名を登録することによって、システムに自分の DLL を (いろんなプロセスに自動的に) ロードさせる方法を記載しています。
DLL インジェクション
API のフック、ウィンドウのサブクラス化、システムのモニタリングなどを行う目的で、自前の DLL を "よそ様" のプロセスにロードさせることを、DLL インジェクション (DLL injection, DLL の注入) と呼びます。
Windows は DLL インジェクションの方法を何種類か提供していますが、ここでは AppInit_DLLs というレジストリ値を用いる方法をご紹介します。
方法
AppInit_DLLs レジストリ値は以下の場所にあります。
HKEY_LOCAL_MACHINE
\Software
\Microsoft
\Windows NT
\CurrentVersion
\Windows
\AppInit_DLLs
こちらのレジストリに DLL を登録しておくと、user32.dll のロード時 (DllMain の DLL_PROCESS_ATACH 通知) に、user32.dll が登録された DLL をロードします。複数の DLL を登録する場合には、' ' (スペース) または ',' (カンマ) によって区切ります。
尚、user32.dll がロードするので user32.dll を使わない EXE にはロードされないことになります。
例
ここでは試しに自前の DLL (mydll.dll) を使って動作テストしてみます。mydll はロード時に、それをロードしたプロセス名をデバッグトレースに出力します。
mydll.dll のコードは次の通りです。
#include <windows.h>
BOOL APIENTRY DllMain( HANDLE hinstDLL, DWORD fdwReason,
LPVOID lpvReserved) {
TCHAR szProcessName [MAX_PATH + 2];
int nLength;
switch ( fdwReason ) {
case DLL_PROCESS_ATTACH:
if ( GetModuleFileName ( GetModuleHandle (0), szProcessName, MAX_PATH )
) {
nLength = lstrlen ( szProcessName );
szProcessName [ nLength ] = '\r';
szProcessName [ nLength + 1 ] = '\n';
szProcessName [ nLength + 2 ] = '\0';
OutputDebugString ( szProcessName );
}
else {
OutputDebugString ( "GetModuleFileName failed.\n" );
}
break;
case DLL_PROCESS_DETACH:
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
}
return TRUE;
}
mydll.dll のソースコードのダウンロード [mydll.zip, makefile]
この mydll.dll をビルドして C:\ 直下に配置し、AppInit_DLLs レジストリ値を C:\mydll.dll に設定します。さらに、この設定を反映させるために Windows を再起動します。
そこで電卓 (calc.exe)、メモ帳 (notepad.exe) 及び FireFox (firefox.exe) などを試しに起動してみました。確かに、プロセスの起動時、 DebugView にプロセス名が出力されていることが確認できます (左図, 画像をクリックすると拡大表示します)。
期待したとおりの動作になりました。
念のため、mydll.dll がロードされていることを確認しておきます。
>tasklist
/M mydll.dll
Image
Name
PID Modules
========================= ======
=============================================
winlogon.exe
664 mydll.dll
services.exe
708
mydll.dll
lsass.exe
720 mydll.dll
svchost.exe
888 mydll.dll
svchost.exe
952 mydll.dll
svchost.exe
992 mydll.dll
svchost.exe
1068 mydll.dll
svchost.exe
1100 mydll.dll
msdtc.exe
1448 mydll.dll
...
確かに mydll.dll がロードされています。
また、 mydll.dll をロードしていないプロセスがあることも確認できます。
>tasklist
/FI "MODULES ne mydll.dll"
Image
Name
PID Session Name
Session# Mem Usage
========================= ====== ================ ======== ============
System Idle
Process
0
Console
0
16 K
System
4
Console
0
236 K
smss.exe
536
Console
0
388 K
csrss.exe
584
Console
0 8,032 K
そこで user32.dll がロードされていないプロセスを見ると次のようになります。
C:\Debuggers>tasklist /FI
"MODULES ne user32.dll"
Image
Name
PID Session Name
Session# Mem Usage
========================= ====== ================ ======== ============
System Idle
Process
0
Console
0
16 K
System
4
Console
0
236 K
smss.exe
536
Console
0
388 K
つまり、 csrss.exe だけは user32.dll をロードしているにもかかわらず mydll.dll をロードしていません (が、Csrss = Windows subsystem ですからユーザーのコードがロードされないようになっているのが健全に思われます。この辺りについて詳細をご存知の方、ぜひ教えてください。)。