Подключение DirectX EndScene из инъецированной DLL
Я хочу EndScene
из произвольного приложения DirectX 9 для создания небольшого наложения. В качестве примера вы можете взять накладку счетчика кадров FRAPS, которая отображается в играх при активации.
Я знаю следующие методы для этого:
-
Создаем новую d3d9.dll , которая затем копируется в игровой путь. Поскольку поиск текущей папки выполняется сначала, перед переходом на system32 и т. Д. Моя модифицированная DLL загружается, выполняя мой дополнительный код.
- Ядро Linux: пример подключения к системному вызову
- Hook / Overlay DirectX-игра?
- Применение привязки git post-commit ко всем текущим и будущим репозиториям
- Может ли панель разблокировки OS X запускать логин, похожий на loginwindow?
- Почему SetWindowsHookEx должен использоваться с очередью сообщений Windows
Недостаток: вы должны положить его туда, прежде чем начать игру.
- То же, что и первый метод, но напрямую заменяя DLL в system32.
Недостаток: вы не можете добавить определенный код игры. Вы не можете исключать приложения, в которых вы не хотите, чтобы ваша DLL была загружена.
- Получение смещения EndScene непосредственно из DLL с помощью таких инструментов, как IDA Pro 4.9 Free. Поскольку DLL загружается как есть, вы можете просто добавить это смещение к стартовому адресу DLL, когда он сопоставляется с игрой, чтобы получить фактическое смещение, а затем подключить его.
Нижняя сторона: смещение не является одинаковым для каждой системы.
- Подключив Direct3DCreate9, чтобы получить D3D9, затем подключите D3D9-> CreateDevice, чтобы получить указатель устройства, а затем подключите Device-> EndScene через виртуальную таблицу.
Недостаток: DLL не может быть введена, когда процесс уже запущен. Вы должны запустить этот процесс с помощью флага
CREATE_SUSPENDED
чтобы подключить начальный Direct3DCreate9 .- Создание нового устройства в новом окне, как только будет загружена DLL. Затем, получив смещение
EndScene
от этого устройства и подключив его, выEndScene
крючок для устройства, которое используется в игре.
Даунсайд: по некоторой информации, которую я прочитал, создание второго устройства может помешать существующему устройству, и оно может быть связано с оконным и полноэкранным режимами и т. Д.
- То же, что и третий метод. Однако вы получите сканирование шаблонов, чтобы получить
EndScene
.
Даунсайд: не выглядит так надежно.
Как я могу подключить EndScene
из инъецированной DLL, которая может быть загружена, когда игра уже запущена, без необходимости иметь дело с различными d3d9.dll в других системах и с надежным методом? Как FRAPS, например, выполняют его DirectX-перехватчики? DLL не должна применяться ко всем играм, а также к конкретным процессам, где я ввожу их через CreateRemoteThread
.
Вы устанавливаете системный крючок. (SetWindowsHookEx). При этом вы можете загрузиться в каждый процесс.
Теперь, когда вызывается hook, вы ищете загруженный файл 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( pTempDev ); EndSceneFunc = pVtable + sizeof(void*) * EndSceneIndex;
EndSceneFunc теперь содержит указатель на функцию. Теперь мы можем либо исправить все call-сайты, либо мы можем исправить эту функцию.
Помните, что все это зависит от знания реализации COM-интерфейсов в Windows. Но это работает во всех версиях Windows (32 или 64, не оба одновременно).
Немного старый вопрос, который я знаю, но если кто-то заинтересован в этом с C #, вот мой пример по подключению Direct3D 9 API с помощью C # . Это использует EasyHook сборку с открытым исходным кодом .NET, которая позволяет «безопасно» устанавливать крючки из управляемого кода в неуправляемые функции. (Примечание: EasyHook заботится обо всех проблемах, связанных с инъекцией DLL – например, CREATE_SUSPENDED, ACL, 32 против 64-битного и так далее)
Я использую аналогичный подход VTable, упомянутый Кристофером через небольшую DLL-помощницу C ++, чтобы динамически определить адрес функций IDirect3DDevice9 для подключения. Это делается путем создания временного дескриптора windows и создания отбрасываемого IDirect3Device9 внутри внедренной сборки, прежде чем подключать нужные функции. Это позволяет вашему приложению подключить уже запущенную цель (Update: обратите внимание, что это возможно и внутри 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); }