Вопрос

Я хочу в обход EndScene из произвольного приложения DirectX 9 для создания небольшого наложения.В качестве примера можно взять наложение счетчика кадров FRAPS, которое отображается в играх при активации.

Я знаю следующие способы сделать это:

  1. Создание нового d3d9.dll, который затем копируется в путь к играм.Поскольку сначала выполняется поиск в текущей папке, прежде чем переходить к system32 и т. д., моя измененная DLL загружается, выполняя мой дополнительный код.

    Недостаток: Вы должны поместить его туда, прежде чем начать игру.

    • То же, что и первый метод, но с заменой DLL напрямую в system32.

    Недостаток: Вы не можете добавить код конкретной игры.Вы не можете исключить приложения, в которых вы не хотите, чтобы ваша DLL загружалась.

    • Получение смещения EndScene непосредственно из DLL с помощью таких инструментов, как IDA Pro 4.9 Free.Поскольку DLL загружается как есть, вы можете просто добавить это смещение к начальному адресу DLL, когда она отображается в игре, чтобы получить фактическое смещение, а затем подключить его.

    Недостаток: Смещение не одинаково в каждой системе.

    • Зацепка Direct3DCreate9 чтобы получить D3D9, затем подключим D3D9->Создатьустройство чтобы получить указатель устройства, а затем подключиться Устройство->Конечная сцена через виртуальный стол.

    Недостаток: DLL не может быть внедрена, когда процесс уже запущен.Вам необходимо начать процесс с CREATE_SUSPENDED флаг для подключения начального Direct3DCreate9.

    • Создание нового устройства в новом окне, как только будет внедрена DLL.Затем, получив EndScene смещение от этого устройства и его перехват, в результате чего получается перехват для устройства, которое используется игрой.

    Недостаток: Судя по некоторой информации, которую я прочитал, создание второго устройства может помешать работе существующего устройства и может привести к ошибке при работе в оконном режиме или в оконном режиме.полноэкранный режим и т. д.

    • То же, что и третий способ.Однако вы выполните сканирование шаблона, чтобы получить EndScene.

    Недостаток: выглядит не так надежно.

Как я могу зацепить EndScene из внедренной DLL, которую можно загрузить, когда игра уже запущена, без необходимости иметь дело с другими d3d9.dllв других системах и с помощью надежного метода?Как, например, FRAPS выполняет перехваты DirectX?DLL не должна применяться ко всем играм, а только к конкретным процессам, в которые я ее внедряю через CreateRemoteThread.

Это было полезно?

Решение

Вы устанавливаете общесистемный хук.(SetWindowsHookEx) После этого вы сможете загружаться в каждый процесс.

Теперь, когда вызывается перехват, вы ищете загруженный d3d9.dll.

Если он загружен, вы создаете временный объект D3D9 и проходите по виртуальной таблице, чтобы получить адрес метода EndScene.

Затем вы можете исправить вызов EndScene своим собственным методом.(Замените первую инструкцию в EndScene вызовом вашего метода.

Когда вы закончите, вам придется исправить обратный вызов, чтобы вызвать исходный метод EndScene.А потом переустанови патч.

Вот как это делает FRAPS.(Связь)


Адрес функции можно найти в виртуальной таблице интерфейса.

Итак, вы можете сделать следующее (псевдокод):

IDirect3DDevice9* pTempDev = ...;
const int EndSceneIndex = 26 (?);

typedef HRESULT (IDirect3DDevice9::* EndSceneFunc)( void );

BYTE* pVtable = reinterpret_cast<void*>( pTempDev );
EndSceneFunc = pVtable + sizeof(void*) * EndSceneIndex;

EndSceneFunc теперь содержит указатель на саму функцию.Теперь мы можем либо исправить все сайты вызовов, либо исправить саму функцию.

Имейте в виду, что все это зависит от знаний реализации COM-интерфейсов в Windows.Но это работает на всех версиях Windows (32 или 64, а не на обеих одновременно).

Другие советы

Я знаю немного старый вопрос, но если кто-то заинтересован в том, чтобы сделать это с помощью C#, вот мой пример: подключение API Direct3D 9 с помощью C#.При этом используется EasyHook — сборка .NET с открытым исходным кодом, которая позволяет «безопасно» устанавливать перехватчики из управляемого кода в неуправляемые функции.(Примечание:EasyHook берет на себя все проблемы, связанные с внедрением DLL, например.CREATE_SUSPENDED, ACL, 32- и 64-разрядные версии и т. д.)

Я использую аналогичный подход VTable, упомянутый Кристофером, через небольшую вспомогательную dll C++ для динамического определения адреса функций IDirect3DDevice9 для перехвата.Это делается путем создания временного дескриптора окна и создания одноразового IDirect3Device9 внутри внедренной сборки перед последующим подключением нужных функций.Это позволяет вашему приложению перехватить уже запущенную цель (обновление:обратите внимание, что это возможно также полностью в C # - см. комментарии на связанной странице).

Обновлять:также есть обновленная версия подключение Direct3D 9, 10 и 11 все еще использую EasyHook и SharpDX вместо SlimDX

Я знаю, что этот вопрос старый, но он должен работать для любой программы, использующей 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);
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top