ウィンドウでDirectDrawゲームを実行するためのプロキシDLLの作成

既製プログラムの機能拡張するというトピックの続きで、実行可能ファイル自体に変更を加える必要のない、既にコンパイルされたプログラムのロジックを変更する別の方法についてお話したいと思います。 これは、実行可能ファイルへの直接的な干渉が強く非難されている米国で修正を配布するときに役立ちます。 サンプルのddraw.dllを使用して、アプリケーションで使用されるライブラリを置き換えるために、小さなプロキシdll(約4キロバイトのみ)を作成します。

さあ始めましょう


すべての作業はVisual Studio 2010で実行されます。まず、エクスポートされた関数のターゲットライブラリを調べます。 これを行うには、dumpbinユーティリティを使用します(VS2010 \ VC \ binディレクトリにあります):
vcvars32 dumpbin /EXPORTS c:\windows\system32\ddraw.dll 
その結果、以下が得られます。
  ordinal hint RVA name 1 0 00002E69 AcquireDDThreadLock 2 1 000327FA CompleteCreateSysmemSurface 3 2 00032FAE D3DParseUnknownCommand 4 3 00033EEF DDGetAttachedSurfaceLcl 5 4 000325D7 DDInternalLock 6 5 0003258C DDInternalUnlock 7 6 000363FC DSoundHelp 8 7 0000859D DirectDrawCreate 9 8 00037851 DirectDrawCreateClipper 10 9 0000EBC6 DirectDrawCreateEx 11 A 000338C9 DirectDrawEnumerateA 12 B 00033368 DirectDrawEnumerateExA 13 C 00032CB2 DirectDrawEnumerateExW 14 D 0003333B DirectDrawEnumerateW 15 E 000387C1 DllCanUnloadNow 16 F 00038607 DllGetClassObject 17 10 00032675 GetDDSurfaceLocal 18 11 0003A5F9 GetOLEThunkData 19 12 0000E927 GetSurfaceFromDC 20 13 00027CC4 RegisterSpecialCase 21 14 00002EA8 ReleaseDDThreadLock 22 15 000421A6 SetAppCompatData 

これに基づいて、独自のプロキシdllを既に作成できます。 WinAPI以外は必要ないため、したがって、新しいwin32ライブラリプロジェクトでは、最初に行うことは、 /NODEFAULTLIBをオンにしてエントリポイントDllMainを指定することにより、RTLをオフにすることです。 これはボリュームで大いに勝ちます。 次に、DEFファイルで、エクスポートされた関数を次の形式で指定します。
 LIBRARY "ddraw" EXPORTS AcquireDDThreadLock = FakeAcquireDDThreadLock @1 CheckFullscreen = FakeCheckFullscreen @2 CompleteCreateSysmemSurface = FakeCompleteCreateSysmemSurface @3 D3DParseUnknownCommand = FakeD3DParseUnknownCommand @4 ... 

「偽の」関数が単に制御を元の関数に移すようにする必要があるため、C ++では、各関数の宣言は次のようになります。
 __declspec(naked) void FakeAcquireDDThreadLock() { _asm { jmp [ddraw.AcquireDDThreadLock] } } 
__declspec(naked)を使用して、コンパイラー__declspec(naked)スタックに__declspec(naked)する標準のプロローグおよびエピローグコードを生成しないように強制__declspec(naked)ます。 その結果、スタック上のデータは既に適切な形式で格納されており、スタックにパラメーターを繰り返し渡すことなく、単一のjmpコマンドで元の関数の制御を簡単に転送できます。

遷移のアドレスは、次のようなddraw構造から取得されます。
 struct ddraw_dll { HMODULE dll; FARPROC AcquireDDThreadLock; FARPROC CheckFullscreen; FARPROC CompleteCreateSysmemSurface; FARPROC D3DParseUnknownCommand; // ...     ... } ddraw; 
この構造は、プロキシdllをロードするときにDllMainに入力されます。 最初に元のライブラリをロードし、次にエクスポートされたすべての関数のアドレスを取得します。 コードは次のようになります。
 BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { char path[MAX_PATH]; switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: CopyMemory(path+GetSystemDirectory(path,MAX_PATH-10), "\\ddraw.dll",11); ddraw.dll = LoadLibrary(path); if (ddraw.dll == false) { MessageBox(0, "Cannot load original ddraw.dll library", APP_NAME, MB_ICONERROR); ExitProcess(0); } ddraw.AcquireDDThreadLock = GetProcAddress(ddraw.dll, "AcquireDDThreadLock"); ddraw.CheckFullscreen = GetProcAddress(ddraw.dll, "CheckFullscreen"); ddraw.CompleteCreateSysmemSurface = GetProcAddress(ddraw.dll, "CompleteCreateSysmemSurface"); ddraw.D3DParseUnknownCommand = GetProcAddress(ddraw.dll, "D3DParseUnknownCommand"); // ...       ... break; case DLL_PROCESS_DETACH: FreeLibrary(ddraw.dll); break; } return TRUE; } 

その結果、プロセスをまったく妨げることなく、すべての呼び出しをそれ自体に単純に渡すコンパクトなdllを取得しました。 自動ソリューションを使用しても同様の結果を得ることができますが、コードはそれほど美しくありません。

DirectDrawゲームのウィンドウモード


ターゲットアプリケーションで正常に動作するクリーンなプロキシdllを取得したら、必要な変更を進めることができます。 ライブラリをロードするときに、すでにメモリにあるコードの必要なセクションにパッチを当て、他のライブラリからの呼び出しのフックを設定し、dllでプロキシする関数のロジックを変更することもできます。 この場合、 DirectDrawCreate関数を変更して、最終目標を達成するために動作を変更したいメソッドのスプーフィングされたアドレスを持つIDirectDraw構造体を返すようにするのが最も論理的です。

ただし、これはデモ作業には多すぎるため、前の記事のwndmode.dllライブラリのサービスを使用します。これは必要なものをすべて実装しており、プロセスのアドレススペースにロードするだけです。

DllMainでそれを正しく行い、 case DLL_PROCESS_ATTACH 1行追加します。
 LoadLibrary("wndmode.dll"); 
あなたの好みに対する他の変更:)

Wndmode.dllライブラリ


このライブラリは、すべてのDirectDraw呼び出しをインターセプトし、プログラムがウィンドウ内で実行されるようにパラメーターを変更し、その後で制御を元のDirectDraw関数に転送する役割を担います。

Wndmode.dll自体は、d3dhook.dllライブラリの高度に変更されたバージョンであり、実装されています。
つまり、wndmode.dllを使用して、ほぼすべてのDirectDrawゲームをウィンドウゲームにすることができます。

次の設定がwndmode.iniに含まれている場合があります。
 [WINDOWMODE] UseWindowMode=1 UseGDI=0 UseDirect3D=0 UseDirectInput=0 UseDirectDraw=1 UseDDrawColorEmulate=1 UseDDrawFlipBlt=0 UseDDrawColorConvert=1 UseDDrawPrimaryBlt=1 UseDDrawAutoBlt=0 UseDDrawEmulate=0 UseDDrawPrimaryLost=0 UseCursorMsg=0 UseCursorSet=0 UseCursorGet=0 UseSpeedHack=0 SpeedHackMultiple=10 UseBackgroundResize=0 UseForegroundControl=0 UseFGCGetActiveWindow=0 UseFGCGetForegroundWindow=0 UseFGCFixedWindowPosition=0 EnableExtraKey=0 ShowFps=0 UseCursorClip=0 UseBackgroundPriority=0 DDrawBltWait=-1 Border=1 

ほとんどのオプションはD3D Windowerと同じです。 HeightおよびWidthパラメーターが削除され、ウィンドウサイズが修正され、代わりにBorderパラメーターが追加され(ウィンドウフレームを表示するかどうか)、ゲームの解像度がシステムの解像度と一致する場合に自動フレーム非表示が実装されました。 各ゲームの適切な設定は異なるため、手動で選択する必要があります。

ダウンロードする


ソースコード: bitbucket.org
バイナリ: wndmode.zip (340 kb)

デモ:Age of Empires:The Rise of Rome


ddraw.dll、wndmode.dll、wndmode.iniをゲームのあるディレクトリにコピーし、ゲームを開始します。 ドラムロール...

画像

Source: https://habr.com/ru/post/J133956/


All Articles