
さあ始めましょう
すべての作業は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ライブラリの高度に変更されたバージョンであり、実装されています。
- D3D Windowerプログラムから完全に独立
- 設定はwndmode.iniファイルの[WINDOWMODE]セクションからロードされます
- Genie互換性のために置き換えられたデフォルト設定
- ウィンドウの周囲のフレームをオン/オフする境界線パラメーターを追加しました
- ゲームの解像度がシステムの解像度と等しい場合、フレームは自動的に削除されます
次の設定が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をゲームのあるディレクトリにコピーし、ゲームを開始します。 ドラムロール...
