35

EndScene任意の DirectX 9 アプリケーションから迂回して小さなオーバーレイを作成したい。例として、FRAPS のフレーム カウンター オーバーレイを取り上げることができます。これは、アクティブ化されたときにゲームに表示されます。

これを行うには、次の方法を知っています。

  1. 新しい を作成しd3d9.dll、それをゲーム パスにコピーします。現在のフォルダーが最初に検索されるため、system32 などに移動する前に、変更した DLL が読み込まれ、追加のコードが実行されます。
    欠点:ゲームを開始する前に、そこに置く必要があります。

  2. 最初の方法と同じですが、system32 の DLL を直接置き換えます。
    欠点:ゲーム固有のコードを追加することはできません。DLL をロードしたくないアプリケーションを除外することはできません。

  3. IDA Pro 4.9 Free などのツールを使用して、DLL から EndScene オフセットを直接取得します。DLL はそのままロードされるため、このオフセットを DLL の開始アドレスに追加するだけでゲームにマップされ、実際のオフセットが取得され、フックされます。
    欠点:オフセットはすべてのシステムで同じではありません。

  4. D3D9Direct3DCreate9を取得するためにフックD3D9->CreateDeviceし、デバイス ポインターを取得するためにフックDevice->EndSceneし、仮想テーブルを介してフックします。
    欠点:プロセスが既に実行されている場合、DLL を挿入することはできません。CREATE_SUSPENDEDイニシャルをフックするには、フラグを使用してプロセスを開始する必要がありますDirect3DCreate9

  5. DLL が挿入されるとすぐに、新しいウィンドウで新しいデバイスを作成します。次に、EndSceneこのデバイスからオフセットを取得してフックすると、ゲームで使用されるデバイスのフックになります。
    欠点:私が読んだいくつかの情報によると、2番目のデバイスを作成すると既存のデバイスに干渉する可能性があり、ウィンドウモードとフルスクリーンモードなどでバグが発生する可能性があります.

  6. 3番目の方法と同じです。ただし、取得するにはパターン スキャンを実行しますEndScene
    欠点:それほど信頼できるようには見えません。

他のシステムで異なるd3d9.dllを処理する必要なく、信頼できる方法でEndScene、ゲームが既に実行されているときに読み込まれる可能性のある注入された DLL からフックするにはどうすればよいですか? たとえば、FRAPS は DirectX フックをどのように実行しますか? DLL は、すべてのゲームに適用する必要はありません。CreateRemoteThread

4

3 に答える 3

21

システム全体のフックをインストールします。(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 のいずれか)。

于 2010-01-03T11:43:31.523 に答える
6

私が知っている少し古い質問ですが、誰かが C# でこれを行うことに興味がある場合に備えて、C#を使用して Direct3D 9 API をフックする私の例を次に示します。これは、マネージ コードからアンマネージ関数にフックを「安全に」インストールできるオープン ソース .NET アセンブリである EasyHook を利用します。(注: EasyHook は、DLL インジェクションに関連するすべての問題を処理します。たとえば、CREATE_SUSPENDED、ACL、32 ビット対 64 ビットなど)。

フックする IDirect3DDevice9 関数のアドレスを動的に決定するために、小さな C++ ヘルパー dll を介して Christopher が述べたのと同様の VTable アプローチを使用します。これは、一時的なウィンドウ ハンドルを作成し、挿入されたアセンブリ内に使い捨ての IDirect3Device9 を作成してから、目的の関数をフックすることによって行われます。これにより、アプリケーションは既に実行されているターゲットをフックできます (更新: これは C# 内でも完全に可能であることに注意してください - リンクされたページのコメントを参照してください)。

更新: Direct3D 9、10、および 11 をフックするための更新バージョンもあります。まだ EasyHook を使用し、SlimDX の代わりに SharpDX を使用しています。

于 2010-05-15T01:52:47.223 に答える
6

この質問は古いことは知っていますが、これは 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);
}
于 2015-06-05T10:38:40.047 に答える