<-- Home

对D3D9和DINPUT8进行HOOK

现在有很多Windows游戏使用DirectX9及其更新版本来渲染图形,使用Dinput8来处理用户的按键输入。 使用Detours库对D3D9和Dinput8进行Hook,关键代码如下:


IDirectInputDeviceW*  dinput8_device;
IDirect3DDevice9* d3d9_device;

// 用Detours必需声明一堆麻烦的函数指针类型
typedef HRESULT(WINAPI *DirectInput8Create_t)(HINSTANCE hinst, DWORD dwVersion, REFIID riidltf, LPVOID * ppvOut, LPUNKNOWN punkOuter);
typedef HRESULT(WINAPI *DINPUT8_CreateDevice_t)(IDirectInputW* dInput8, REFGUID rguid, LPDIRECTINPUTDEVICE * lplpDirectInputDevice, LPUNKNOWN pUnkOuter);
typedef HRESULT(WINAPI *GetDeviceState_t)(IDirectInputDeviceW* device, DWORD cbData, LPVOID lpvData);
typedef IDirect3D9* (WINAPI *Direct3DCreate9_t)(UINT SDKVersion);
typedef HRESULT (WINAPI *D3D9_CreateDevice_t)(IDirect3D9 * pD3D9, UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS * pPresentationParameters, IDirect3DDevice9 ** ppReturnedDeviceInterface);
typedef HRESULT (WINAPI *EndScene_t)(IDirect3DDevice9* pDevice);

// 原函数指针
Direct3DCreate9_t Direct3DCreate9_Original;
D3D9_CreateDevice_t D3D9_CreateDevice_Original;
EndScene_t	EndScene_Original;
DirectInput8Create_t DirectInput8Create_Original;
DINPUT8_CreateDevice_t DINPUT8_CreateDevice_Original;
GetDeviceState_t GetDeviceState_Original;

// Hook函数
HRESULT WINAPI DirectInput8Create_Hook(HINSTANCE hinst, DWORD dwVersion, REFIID riidltf, LPVOID * ppvOut, LPUNKNOWN punkOuter);
HRESULT WINAPI DINPUT8_CreateDevice_Hook(IDirectInputW* dInput8, REFGUID rguid, LPDIRECTINPUTDEVICE * lplpDirectInputDevice, LPUNKNOWN pUnkOuter);
HRESULT WINAPI GetDeviceState_Hook(IDirectInputDeviceW* device, DWORD cbData, LPVOID lpvData);
IDirect3D9* WINAPI Direct3DCreate9_Hook(UINT sdkVers);
HRESULT WINAPI D3D9_CreateDevice_Hook(IDirect3D9* pD3D9, UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS * pPresentationParameters, IDirect3DDevice9 ** ppReturnedDeviceInterface);
HRESULT WINAPI EndScene_Hook(IDirect3DDevice9* pDevice);

void Hook_DINPUT8_DirectInput8Create();
void Hook_DINPUT8_CreateDevice();
void Hook_DINPUT8_GetDeviceState();
void Hook_D3D9_Direct3DCreate9();
void Hook_D3D9_CreateDevice();
void Hook_D3D9_EndScene();

void logfs(const char* fmt, ...)
{
  char buf[256];
  va_list args;
  va_start(args, fmt);
  vsprintf_s(buf,256, fmt, args);
  OutputDebugStringA(buf);
  va_end(args);
}

bool DetourHookFunction(PVOID *ppPointer, PVOID pDetour)
{
  DetourTransactionBegin();
  DetourUpdateThread(GetCurrentThread());
  if (DetourAttach(ppPointer, pDetour))
    return false;
  if (DetourTransactionCommit() != NO_ERROR)
    return false;
  return true;
}

// Hook DINPUT8 DirectInput8Create
void Hook_DINPUT8_DirectInput8Create()
{
  HMODULE hDIN8 = GetModuleHandleA("dinput8.dll");
  if (hDIN8)
  {
    PBYTE pDirectInput8Create = (PBYTE)GetProcAddress(hDIN8, "DirectInput8Create");
    if (pDirectInput8Create)
    {
      logfs("Found DirectInput8Create at addr 0x%X", (int)pDirectInput8Create);
      DirectInput8Create_Original = (DirectInput8Create_t)(pDirectInput8Create);
      if (DetourHookFunction(&(PVOID&)DirectInput8Create_Original, DirectInput8Create_Hook))
      {
        logfs("Hooked DirectInput8Create");
        return;
      }
    }
  }
  logfs("Failed to hook DirectInput8Create");

}
HRESULT WINAPI DirectInput8Create_Hook(HINSTANCE hinst, DWORD dwVersion, REFIID riidltf, LPVOID * ppvOut, LPUNKNOWN punkOuter) 
{
  logfs("Feed a fake DINPUT8 interface");
  HRESULT hres = DirectInput8Create_Original(hinst, dwVersion, riidltf, ppvOut, punkOuter);
  int* pVTable = (int*)(*(int*)*ppvOut);
  DINPUT8_CreateDevice_Original = (DINPUT8_CreateDevice_t)pVTable[3];
  Hook_DINPUT8_CreateDevice();
  return hres;
}

// Hook DINPUT8 CreateDevice
void Hook_DINPUT8_CreateDevice()
{
  if (DetourHookFunction(&(PVOID&)DINPUT8_CreateDevice_Original, DINPUT8_CreateDevice_Hook))
  {
    logfs("Hooked DINPUT8_CreateDevice");
  }
  else
  {
    logfs("Failed to hook DINPUT8_CreateDevice");
  }
}
HRESULT WINAPI DINPUT8_CreateDevice_Hook(IDirectInputW* dInput8, REFGUID rguid, LPDIRECTINPUTDEVICE * lplpDirectInputDevice, LPUNKNOWN pUnkOuter)
{
  HRESULT hres = DINPUT8_CreateDevice_Original(dInput8, rguid, lplpDirectInputDevice, pUnkOuter);
  dinput8_device = *lplpDirectInputDevice;
  logfs("Got a DINPUT8 DeviceInterface");
  int* pVTable = (int*)(*(int*)dinput8_device);
  GetDeviceState_Original = (GetDeviceState_t)pVTable[9];
  Hook_DINPUT8_GetDeviceState();
  return hres;
}

// Hook DINPUT8 GetDeviceState
void Hook_DINPUT8_GetDeviceState()
{
  if (DetourHookFunction(&(PVOID&)GetDeviceState_Original, GetDeviceState_Hook))
  {
    logfs("Hooked DINPUT8 GetDeviceState");
  }
  else
  {
    logfs("Failed to hook DINPUT8 GetDeviceState");
  }
}
HRESULT WINAPI GetDeviceState_Hook(IDirectInputDeviceW* device, DWORD cbData, LPVOID lpvData)
{
  HRESULT hres = GetDeviceState_Original(device, cbData, LastKeys);
  return hres;
}

// Hook D3D9 Direct3DCreate9
void Hook_D3D9_Direct3DCreate9()
{
  HMODULE hD3D9 = GetModuleHandleA("d3d9.dll");
  if (hD3D9)
  {
    PBYTE pDirect3DCreate9 = (PBYTE)GetProcAddress(hD3D9, "Direct3DCreate9");
    if (pDirect3DCreate9)
    {
      logfs("Found Direct3DCreate9 at addr 0x%X", (int)pDirect3DCreate9);
      Direct3DCreate9_Original = (Direct3DCreate9_t)(pDirect3DCreate9);
      if (DetourHookFunction(&(PVOID&)Direct3DCreate9_Original, Direct3DCreate9_Hook))
      {
        logfs("Hooked Direct3DCreate9");
        return;
      }
    }
  }
  logfs("Failed to hook Direct3DCreate9");
}
IDirect3D9* WINAPI Direct3DCreate9_Hook(UINT sdkVers)
{
  logfs("Feed a fake IDirect3D9(Version:%d)", sdkVers);
  IDirect3D9 *legit = Direct3DCreate9_Original(sdkVers);
  int* pVTable = (int*)(*(int*)legit);
  D3D9_CreateDevice_Original = (D3D9_CreateDevice_t)pVTable[16];
  Hook_D3D9_CreateDevice();
  return legit;
}

// Hook D3D9 CreateDevice
void Hook_D3D9_CreateDevice()
{
  if (DetourHookFunction(&(PVOID&)D3D9_CreateDevice_Original, D3D9_CreateDevice_Hook))
  {
    logfs("Hooked D3D9_CreateDevice");
  }
  else
  {
    logfs("Failed to hook D3D9_CreateDevice");
  }
}
HRESULT WINAPI D3D9_CreateDevice_Hook(IDirect3D9* pD3D9, UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS * pPresentationParameters, IDirect3DDevice9 ** ppReturnedDeviceInterface)
{
  HRESULT hres = D3D9_CreateDevice_Original(pD3D9, Adapter, DeviceType, hFocusWindow, BehaviorFlags, pPresentationParameters, ppReturnedDeviceInterface);
  d3d9_device = *ppReturnedDeviceInterface;
  logfs("Got a D3D9 DeviceInterface");
  int* pVTable = (int*)(*(int*)d3d9_device);
  EndScene_Original = (EndScene_t)pVTable[42];
  Hook_D3D9_EndScene();
  return hres;
}

// Hook D3D9 EndScene
void Hook_D3D9_EndScene()
{
  if (DetourHookFunction(&(PVOID&)EndScene_Original, EndScene_Hook))
  {
    logfs("Hooked EndScene");
  }
  else
  {
    logfs("Failed to hook EndScene");
  }
}
HRESULT WINAPI EndScene_Hook(IDirect3DDevice9* pDevice)
{
  HRESULT hres = EndScene_Original(pDevice);
  return hres;
}

调用Hook_DINPUT8_DirectInput8Create()Hook_D3D9_Direct3DCreate9(),经过一系列连环Hook,可以得到D3D9和DINPUT8的设备指针d3d9_devicedinput8_device,绘图和输入处理的关键函数也都Hook了,然后就可以拦截或设置游戏输入、在原有图形上绘制新图形,为所欲为。

Hook时找原函数指针时要搞清楚C++的虚表,这个坑了我好几次。

下面是把东方风神录的D3D9函数HOOK后在子弹上面绘制其判定点大小的示例:

这也是某种外挂(比如PUBG的透视)的原理。