Подтвердить что ты не робот

Подключение DirectX EndScene из инъецированной DLL

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

Я знаю следующие методы для этого:

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

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

    • То же, что и первый метод, но напрямую заменяет 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 из вложенной DLL, которая может быть загружена, когда игра уже запущена, без необходимости иметь дело с разными d3d9.dll в других системах и с надежным методом? Как FRAPS, например, выполняет его перехваты DirectX? DLL не должна применяться ко всем играм, только к конкретным процессам, где я вставляю их через CreateRemoteThread.

4b9b3361

Ответ 1

Вы устанавливаете системный крючок. (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<void*>( pTempDev );
EndSceneFunc = pVtable + sizeof(void*) * EndSceneIndex;

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

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

Ответ 2

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

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

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

Ответ 3

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