小さなオーバーレイを作成するために、任意のDirectX 9アプリケーションからEndScene
を迂回したい。例として、アクティブにするとゲームに表示されるFRAPSのフレームカウンターオーバーレイを使用できます。
これを行うには、次の方法を知っています。
新しいd3d9.dllを作成し、それをゲームパスにコピーします。現在のフォルダーが最初に検索されるため、system32などに進む前に、変更されたDLLが読み込まれ、追加のコードが実行されます。
欠点:ゲームを開始する前に、そこに配置する必要があります。
欠点:ゲーム固有のコードを追加できません。 DLLをロードしたくないアプリケーションは除外できません。
下側:オフセットはすべてのシステムで同じではありません。
ダウンサイド:プロセスがすでに実行されている場合、DLLは注入できません。初期をフックするには、_CREATE_SUSPENDED
_フラグでプロセスを開始する必要があります- Direct3DCreate9。
EndScene
オフセットを取得してフックし、その結果、ゲームで使用されるデバイス。ダウンサイド:私が読んだいくつかの情報のように、2つ目のデバイスを作成すると、既存のデバイスに干渉する可能性があり、ウィンドウモードとフルスクリーンモードなどでバグが発生する可能性があります。
EndScene
を取得するためにパターンスキャンを実行します。欠点:はそれほど信頼できるようには見えません。
他のシステムで別のd3d9.dllを処理することなく、ゲームがすでに実行されているときに読み込まれる可能性がある、注入されたDLLからEndScene
をフックするにはどうすればよいですか。信頼できる方法ですか?たとえばFRAPSはDirectXフックをどのように実行しますか? DLLはすべてのゲームに当てはまるわけではなく、CreateRemoteThread
を介して注入する特定のプロセスにのみ当てはまります。
システム全体のフックをインストールします。 (SetWindowsHookEx)これが完了すると、すべてのプロセスにロードされます。
フックが呼び出されると、ロードされたd3d9.dllを探します。
ロードされている場合は、一時的なD3D9オブジェクトを作成し、vtableをウォークしてEndSceneメソッドのアドレスを取得します。
次に、独自のメソッドを使用して、EndScene呼び出しにパッチを適用できます。 (EndSceneの最初の命令をメソッドの呼び出しに置き換えます。
完了したら、元のEndSceneメソッドを呼び出すために、コールバックにパッチを適用する必要があります。次に、パッチを再インストールします。
これはFRAPSが行う方法です。 ( リンク )
インターフェイスのvtableから関数アドレスを見つけることができます。
したがって、以下を実行できます(疑似コード):
IDirect3DDevice9* pTempDev = ...;
const int EndSceneIndex = 26 (?);
typedef HRESULT (IDirect3DDevice9::* EndSceneFunc)( void );
BYTE* pVtable = reinterpret_cast<void*>( pTempDev );
EndSceneFunc = pVtable + sizeof(void*) * EndSceneIndex;
EndSceneFuncには、関数自体へのポインターが含まれています。これで、すべての呼び出しサイトにパッチを適用するか、関数自体にパッチを適用できます。
これはすべて、WindowsでのCOMインターフェースの実装に関する知識に依存することに注意してください。しかし、これはすべてのWindowsバージョンで動作します(32または64のいずれかで、同時に両方ではありません)。
私が知っている少し古い質問ですが、C#でこれを行うことに興味がある場合は、 C# を使用してDirect3D 9 APIをフックする例を次に示します。これは、オープンソースの.NETアセンブリであるEasyHookを利用して、マネージコードからアンマネージ関数にフックを「安全に」インストールできるようにします。 (注:EasyHookは、DLLインジェクション-CREATE_SUSPENDED、ACL、32ビットと64ビットなど)に関連するすべての問題を処理します)
クリストファーが小さなC++ヘルパーDLLを介して言及したのと同様のVTableアプローチを使用して、フックするIDirect3DDevice9関数のアドレスを動的に決定します。これは、一時的なウィンドウハンドルを作成し、注入されたアセンブリ内に使い捨てのIDirect3Device9を作成してから、目的の関数をフックすることによって行われます。これにより、アプリケーションは既に実行されているターゲットをフックできます(更新:これは完全にC#内でも可能です-リンクされたページのコメントを参照してください)。
Update: hooking Direct3D 9、10および11 の更新バージョンもまだ使用していますEasyHookおよびSlimDXの代わりにSharpDX
私はこの質問が古いことを知っていますが、これはDirectX9を使用するすべてのプログラムで機能するはずです。基本的に独自のインスタンスを作成し、VTableへのポインターを取得してから、フックします。迂回3.Xが必要です。
//Just some typedefs:
typedef HRESULT (WINAPI* oEndScene) (LPDIRECT3DDEVICE9 D3DDevice);
static oEndScene EndScene;
//Do this in a function or whatever
HMODULE hDLL=GetModuleHandleA("d3d9");
LPDIRECT3D9(__stdcall*pDirect3DCreate9)(UINT) = (LPDIRECT3D9(__stdcall*)(UINT))GetProcAddress( hDLL, "Direct3DCreate9");
LPDIRECT3D9 pD3D = pDirect3DCreate9(D3D_SDK_VERSION);
D3DDISPLAYMODE d3ddm;
HRESULT hRes = pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm );
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory( &d3dpp, sizeof(d3dpp));
d3dpp.Windowed = true;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = d3ddm.Format;
WNDCLASSEX wc = { sizeof(WNDCLASSEX),CS_CLASSDC,TempWndProc,0L,0L,GetModuleHandle(NULL),NULL,NULL,NULL,NULL,("1"),NULL};
RegisterClassEx(&wc);
HWND hWnd = CreateWindow(("1"),NULL,WS_OVERLAPPEDWINDOW,100,100,300,300,GetDesktopWindow(),NULL,wc.hInstance,NULL);
hRes = pD3D->CreateDevice(
D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_DISABLE_DRIVER_MANAGEMENT,
&d3dpp, &ppReturnedDeviceInterface);
pD3D->Release();
DestroyWindow(hWnd);
if(pD3D == NULL){
//printf ("WARNING: D3D FAILED");
return false;
}
pInterface = (unsigned long*)*((unsigned long*)ppReturnedDeviceInterface);
EndScene = (oEndScene) (DWORD) pInterface[42];
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach(&(PVOID&)EndScene, newEndScene);
DetourTransactionCommit();
そしてあなたの機能:
HRESULT WINAPI D3D9Hook::newEndScene(LPDIRECT3DDEVICE9 pDevice)
{
//Do your stuff here
//Call the original (if you want)
return EndScene(pDevice);
}