2010-01-03 5 views
28

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

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

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

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

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

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

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

    Даунсайд: Смещение не является одинаковым для каждой системы.

    • Hooking Direct3DCreate9 получить D3D9, а затем закреплять D3D9-> CreateDevice получить указатель устройства, а затем закреплять Device-> EndScene через виртуальную таблицу.

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

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

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

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

    Даунсайд: не выглядит надежно.

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

ответ

15

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

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

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

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

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

Это как FRAPS делает это (Link)


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

Таким образом, вы можете сделать следующее (псевдокод):..

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

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

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

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

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

+1

проверить это для более подробной информации: Windows, Via C/C++, Глава 22 - DLL Injection и API Hooking, парентеральное введение DLL с помощью Windows Крючков – whunmr

+0

Я хочу вставить DLL через 'CreateRemoteThread'. Я не хочу глобальный крючок, только для определенных игр. – Etan

+0

Затем просто отбросьте первый шаг. – Christopher

4

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

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

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

2

Я знаю, этот вопрос старый, но это должно работать для любой программы с помощью 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); 
} 
+0

Почему я получаю приложение «crash», когда возвращается оригинальный EndScene? – Duracell

+0

@ Duracell это действительно зависит от того, как вы его используете. Вы подключаетесь к основной теме? Возможно, ваш указатель на исходный EndScene имеет значение NULL. Мне нужно больше информации, чтобы помочь вам. – Fredaikis

+0

Я ввожу этот код http://pastebin.com/ui0ezFyW в приложение (используйте d3d9), и я получаю сообщение в консоли «printf_s (« Hooked! »);« Из функции «HRESULT WINAPI newEndScene (LPDIRECT3DDEVICE9 pDevice)» , но затем, когда я называю «return EndScene (pDevice)»; - получите приложение для краха, проверьте мою ошибку. – Duracell