web-dev-qa-db-ja.com

注入されたDirectX EndSceneをフックするDLL

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

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

  1. 新しいd3d9.dllを作成し、それをゲームパスにコピーします。現在のフォルダーが最初に検索されるため、system32などに進む前に、変更されたDLLが読み込まれ、追加のコードが実行されます。

    欠点:ゲームを開始する前に、そこに配置する必要があります。

    • 最初の方法と同じですが、system32のDLLを直接置き換えます。

    欠点:ゲーム固有のコードを追加できません。 DLLをロードしたくないアプリケーションは除外できません。

    • IDA Pro 4.9 Freeなどのツールを使用してDLLからEndSceneオフセットを直接取得します。DLLはそのままロードされるため、このオフセットをDLLゲームにマップされるときの開始アドレス、実際のオフセットを取得してフックする。

    下側:オフセットはすべてのシステムで同じではありません。

    • フッキングDirect3DCreate9 D3D9を取得するためにフックし、次にD3D9-> CreateDeviceをフックするためにデバイスポインターを取得し、次に仮想を介してDevice-> EndSceneをフックするテーブル。

    ダウンサイド:プロセスがすでに実行されている場合、DLLは注入できません。初期をフックするには、_CREATE_SUSPENDED_フラグでプロセスを開始する必要があります- Direct3DCreate9

    • DLLが挿入されるとすぐに、新しいウィンドウに新しいデバイスを作成します。次に、このデバイスからEndSceneオフセットを取得してフックし、その結果、ゲームで使用されるデバイス。

    ダウンサイド:私が読んだいくつかの情報のように、2つ目のデバイスを作成すると、既存のデバイスに干渉する可能性があり、ウィンドウモードとフルスクリーンモードなどでバグが発生する可能性があります。

    • 3番目の方法と同じです。ただし、EndSceneを取得するためにパターンスキャンを実行します。

    欠点:はそれほど信頼できるようには見えません。

他のシステムで別のd3d9.dllを処理することなく、ゲームがすでに実行されているときに読み込まれる可能性がある、注入されたDLLからEndSceneをフックするにはどうすればよいですか。信頼できる方法ですか?たとえばFRAPSはDirectXフックをどのように実行しますか? DLLはすべてのゲームに当てはまるわけではなく、CreateRemoteThreadを介して注入する特定のプロセスにのみ当てはまります。

31
Etan

システム全体のフックをインストールします。 (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のいずれかで、同時に両方ではありません)。

15
Christopher

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

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

Updatehooking Direct3D 9、10および11 の更新バージョンもまだ使用していますEasyHookおよびSlimDXの代わりにSharpDX

4
Justin Stenning

私はこの質問が古いことを知っていますが、これは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);
}
2
Fredaikis