Подключение 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
,
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);
}