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

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

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

  1. Создание нового файла 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,

3 ответа

Решение

Вы устанавливаете общесистемный хук. (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 теперь содержит указатель на саму функцию. Теперь мы можем либо исправить все call-сайты, либо мы можем исправить саму функцию.

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

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