DLL インジェクション技術: ゲーム プロセスにプラグイン DLL モジュールを挿入するなど、DLL ファイルをターゲット プロセスに強制的にロードすることで、ターゲット プロセスの命令やメモリ データの読み書きを容易にすることを目的としています。この DLL を実行したり (HOOK ゲーム関数の処理やプラグイン関数を実装するためのゲーム メモリ データの改ざんなど)、注入されたプロセスとしていくつかの操作を実行します。
システム全体のインジェクションの利点: システム メカニズムによって実装されるシステム全体のプロセス インジェクションは、ゲーム プロセス自体などのインジェクション防止保護メカニズムをバイパスできます。たとえば、リモート スレッド インジェクション ゲームはブロックされる可能性がありますが、インプット メソッド インジェクションはゲームでブロックするのが困難です。
メッセージフックインジェクション
Windows アプリケーションはメッセージ駆動型です。アプリケーションはさまざまなメッセージに応答してさまざまな機能を実装します。
メッセージ フック (メッセージ フック) は、Windows メッセージ処理メカニズムの監視ポイントであり、システムはターゲット プロセスにフックを自動的にインストールし、指定された種類のメッセージを監視する機能を実現します。つまり、システムは SetWindowsHookEx を通じてターゲット プロセスにフック DLL を自動的に挿入します。
インストールフックの関数プロトタイプは次のとおりです。
HHOOK SetWindowsHookEx(
int idHook, //钩子类型
HOOKPROC lpfn,
HINSTANCE hMod,
DWORD dwThreadId
);
このうち、dwThreadId が 0 の場合はグローバル フック、つまりシステムのすべてのウィンドウ プロセスに dll を注入します。それ以外の場合はスレッド フック、つまり、dwThreadId が 0 のプロセスにのみ dll を注入できます。対象のスレッドが所属します。グローバル フックの場合、フック プロセス HOOKPROC lpfn が DLL 内に存在する必要があります。
手順とソースコード
ソース コードでは、システム全体の DLL インジェクションを実現するための例として WH_CBT フックを取り上げます。コンピュータベースのトレーニング (CBT) ウィンドウの作成、ウィンドウの移動、その他の操作などのコンピュータベースのトレーニングでは通知を受け取ります。
次のイベントの前に、システムは WH_CBT フック サブルーチンを呼び出します。これらのイベントには次のものが含まれます。
-
アクティブ化、作成、破棄、最小化、最大化、移動、サイズ変更、その他のウィンドウ イベント。
-
システムコマンドの完了。
-
マウスとキーボードのイベントをシステム メッセージ キューから移動します。
-
入力フォーカスイベントを設定します。
-
システムメッセージキューイベントを同期します。
WH_CBT フックをインストールして CbtHook.dll をメモ帳プロセスに挿入する効果は次のとおりです。
インジェクションしたDllとフック処理を記述する
DLL を作成し、CBTProc () フック プロセスを明示的にエクスポートします。メイン コードは次のとおりです。
LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam)
{
char szファイル名[MAX_PATH];
if( (HCBT_CREATEWND == nCode) || (HCBT_ACTIVATE == nCode) )
{
memset(szファイル名, 0, sizeof(szファイル名));
::GetModuleFileNameA(NULL, szFileName, sizeof(szFileName));
DebugPrintA(0, “コード(%d) (%s)\n”, nCode, szFileName);
}
return CallNextHookEx(g_hCBT, nCode, wParam, lParam);
}
フックをインストールする
exe を作成し、SetWindowsHookEx() を使用してシステムにフックをインストールします。まず、HOOK の DLL を exe 自体のプロセスにロードして DLL のモジュール ハンドルを取得し、次に GetProcAddress( ) を使用して、DLL 内の表示およびエクスポートされた関数 MyMessageProc() を取得します。関数のアドレス、そして最後に、挿入されるプロセスのスレッド ID をトラバースして、SetWindowsHookEx() がこれらのパラメーターを使用して HOOK を実行できるようにします。メインコードを次の図に示します。
if (NULL == g_hCbtHook)
g_hCbtHook = ::LoadLibrary(szPathName);
if (NULL == g_hCbtHook)
{
lResult = GetLastError();
::DebugPrintA(0, “%s : GlobalHook モジュール '%s' のロードに失敗しました (%d)\n”, C_ModuleNameA, szPathName, lResult);
壊す;
}
CBTProc = (PCBTPROC)::GetProcAddress(g_hCbtHook, “CBTProc”);
if (NULL == g_hCbtHook)
{
lResult = GetLastError();
::DebugPrintA(0, “%s : GlobalHook 関数の取得に失敗しました (%d)\n”, C_ModuleNameA, lResult);
壊す;
}
g_hCBT = SetWindowsHookEx(WH_CBT, CBTProc, g_hCbtHook, 0);
if (NULL == g_hCBT)
{
lResult = GetLastError();
::DebugPrintA(0, “%s : GlobalHook の設定に失敗しました (%d)\n”, C_ModuleNameA, lResult);
壊す;
}
アンインストールフック
LoadLibrary()で取得したモジュールハンドルを使用して、自身のプロセスのDLLを解放します。コードは次のとおりです。
FreeLibrary(g_hCbtHook);
メッセージ フック インジェクションは、SetWindowsHookEx() と DLL エクスポート関数に精通していれば簡単に記述できるため、実装は簡単です。
レジストリインジェクション
Windows システムではレジストリ (Reg) インジェクション原理が使用されており、REG 以下のキー値に DLL ファイル パスがある場合、EXE ファイルの起動に続いて DLL ファイル パスに DLL ファイルがロードされます。
AppInit_Dlls レジストリ:
レジストリ キー HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows には 2 つの値があります。
LoadAppInit_Dlls:注入するDLLをキー値で指定
AppInit_Dlls: キー値が 1 の場合、LoadAppInit_Dlls で指定された DLL を挿入します。0 の場合、挿入しません。注: (1) LoadAppInit_Dlls の値 DLL ファイルが複数ある場合、複数の DLL ファイルのパスをカンマまたはスペースで区切る必要があるため、DLL パスにはスペースを含めないことをお勧めします。
使用範囲:
User32.DLL をロードするプログラムでは、user32.dll の DllMain が最初にレジストリ キー AppInit_Dlls に DLL をロードしようとします。すべての GUI アプリケーションは起動時に User32.dll をロードするため、この方法はすべての GUI プログラムに影響します。
Process Explorer を使用してプロセス モジュールを表示し、ターゲット DLL が挿入されているかどうかを確認します。
手順とソースコード
解決する必要があるのは、次のようなレジストリ操作用の Windows API です。
RegOpenKeyEx
レジストリキーを開きます
RegQueryValueEx
クエリキー値
RegSetValueEx
キー値を設定する
RegCloseKey
閉じるキー
メインのコードは次のとおりです。
BOOL AddRegItem(CHAR* szInjectFilePath)
{
// キー値を開きます
LSTATUS nReg = ERROR_SUCCESS;
HKEY hKey;
CHAR szRegPath[MAX_PATH] = “SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows”;
nReg = RegOpenKeyEx(
HKEY_LOCAL_MACHINE、
szRegPath、
0、
KEY_ALL_ACCESS、
&hキー);
if (nReg != ERROR_SUCCESS)
{
FALSEを返します。
}
// クエリキーの値
DWORD dwReadType;
DWORD dwReadCount;
TCHAR szReadBuff[1024] = { 0 };
nReg = RegQueryValueEx(hKey,
_T(“AppInit_DLL”),
ヌル、
&dwReadType、
(バイト*)&szReadBuff,
&dwReadCount);
if (nReg != ERROR_SUCCESS)
{
FALSEを返します。
}
// DLL 名が既にコンテンツに含まれている場合は、繰り返し追加する必要はありません
if (StrStrI(szReadBuff,szInjectFilePath))
{
printf(“dll はすでに reg=%s\n にあります”, szReadBuff);
FALSEを返します。
}
//元の既存のコンテンツにはスペースが追加され、その後新しい DLL 文字列が追加されます
if (0 != _tcscmp(szReadBuff, _T(“”)))
{
_tcscat_s(szReadBuff, _T(" "));
}
_tcscat_s(szReadBuff, szInjectFilePath);
//1. DLL パスをレジストリに設定します
nReg = RegSetValueEx(hKey,
_T(“AppInit_DLL”),
0、
REG_SZ、
(CONST BYTE*)szReadBuff,
(_tcslen(szReadBuff) + 1)*sizeof(TCHAR));
//2. レジストリを起動して dll をロードします
バイト byEnable[4] = { 0x1 };
nReg = RegSetValueEx(hKey,
_T(“LoadAppInit_DLLs”),
0、
REG_DWORD、
(CONST BYTE*)有効化、
4);
printf(“RegSetValueEx AppInit_DLLs = %s Result=%d\n”, szReadBuff, nReg);
}