Перехват DirectX EndScene из внедренной DLL
-
22-09-2019 - |
Вопрос
Я хочу в обход EndScene
из произвольного приложения DirectX 9 для создания небольшого наложения.В качестве примера можно взять наложение счетчика кадров FRAPS, которое отображается в играх при активации.
Я знаю следующие способы сделать это:
Создание нового 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);
}