読者です 読者をやめる 読者になる 読者になる

めいくりぷとのブログ

技術的なことやゲームのことやら・・・

WDDM Hook

Windows Display Driver Model (WDDM) hook について適当に書きます。

前記事でOpenGL ICD hookについて書きましたが、ほとんど同じです。
今回も大雑把に説明します。


まずは、各ディスプレイドライバのエクスポート関数 "OpenAdapter" をフックしします。

NVIDIA GeForce => nvd3dum.dll
Radeon => aticfx32.dll
Intel Graphics => igdumd32.dll

OpenAdapterの第一引数の D3DDDIARG_OPENADAPTER 構造体から pOpenData->pAdapterFuncs->pfnCreateDevice のアドレスを取得し、そのアドレスを元に pfnCreateDevice をフックします。

pfnCreateDevice(HANDLE hDevice, D3DDDIARG_CREATEDEVICE *pDeviceData);

第二引数の D3DDDIARG_CREATEDEVICE 構造体の D3DDDI_DEVICEFUNS *pDeviceFuncs がvtableへのポインタになりますので、
pfnCreateDevice のフックコード内で取得し、vtable内の各関数をフックします。
以下にサンプルソースを貼っておきます。

BOOL WINAPI Detour_OpenAdapter(__in BOOL fEnable);

HRESULT APIENTRY CreateDevice(
	__in HANDLE hAdapter,
	__inout D3DDDIARG_CREATEDEVICE *pDeviceData)
{
	CDriverManager *pDriver = CDriverManager::GetInstance();
	HRESULT hResult;

	// create driver context
	hResult = pDriver->CreateDevice(hAdapter, pDeviceData);
	if (hResult == S_OK
		&& pDeviceData
		&& pDeviceData->pDeviceFuncs)
	{
		// valid device funcs
		if (pDriver->IsValidDeviceFuncs() == FALSE)
		{
			// copy device funcs
			pDriver->CopyDeviceFuncs(*pDeviceData->pDeviceFuncs);
			if (pDriver->IsValidDeviceFuncs())
			{
				// init graphics context & swap device funcs function table
				CGraphicsContext::GetInstance()->Init(pDeviceData->pDeviceFuncs);

				// detour remove
				Detour_OpenAdapter(FALSE);
			}
		}
	}

	return hResult;
}

BOOL WINAPI Detour_OpenAdapter(__in BOOL fEnable)
{
	typedef HRESULT(APIENTRY *pfnOpenAdapter)(
		__inout D3DDDIARG_OPENADAPTER *pOpenData
		);

	static pfnOpenAdapter _OpenAdapter =
		reinterpret_cast<pfnOpenAdapter>(GetProcAddress(CDriverManager::GetInstance()->GetDriver(), "OpenAdapter"));

	pfnOpenAdapter OpenAdapter_Hook = [](
		__inout D3DDDIARG_OPENADAPTER *pOpenData) -> HRESULT
	{
		CDriverManager *pDriver;
		LPVOID pDevice;
		HRESULT hResult;

		hResult = _OpenAdapter(pOpenData);
		if (hResult == S_OK 
			&& pOpenData 
			&& pOpenData->pAdapterFuncs)
		{
			pDevice = pOpenData->pAdapterFuncs->pfnCreateDevice;
			if (pDevice)
			{
				pDriver = CDriverManager::GetInstance();
				pDriver->SetDevice(reinterpret_cast<PFND3DDDI_CREATEDEVICE>(pDevice));
				if (pDriver->IsValidDevice())
					pOpenData->pAdapterFuncs->pfnCreateDevice = CreateDevice;
			}
		}

		return hResult;
	};

	return DetourFunction(fEnable, reinterpret_cast<LPVOID*>(&_OpenAdapter), OpenAdapter_Hook);
}
#include "stdafx.h"

#include "CDriverManager.hpp"
#include "D3DHeaders.hpp"

#include <ShlObj.h>

CDriverManager::CDriverManager()
{
	this->m_hDriver = NULL;
	this->m_pfnCreateDevice = NULL;
	this->m_pDeviceFuncs = NULL;

	this->m_vDriver.push_back(L"nvd3dum.dll");		// NVIDIA GeForce
	this->m_vDriver.push_back(L"aticfx32.dll");		// Radeon
	this->m_vDriver.push_back(L"igdumd32.dll");		// Intel Graphics
}

CDriverManager::~CDriverManager()
{
	this->m_vDriver.clear();
}

BOOL CDriverManager::Init()
{
	for (int i = 0; i < this->m_vDriver.size(); i++)
	{
		m_hDriver = this->LoadDriver(this->m_vDriver.at(i));
		if (m_hDriver)
			return TRUE;
	}

	return FALSE;
}

BOOL CDriverManager::IsValidDevice()
{
	return (this->m_pfnCreateDevice != NULL);
}

BOOL CDriverManager::IsValidDeviceFuncs()
{
	return (this->m_pDeviceFuncs != NULL);
}

void CDriverManager::SetDevice(__in PFND3DDDI_CREATEDEVICE pDevice)
{
	this->m_pfnCreateDevice = pDevice;
}

D3DDDI_DEVICEFUNCS *CDriverManager::GetDeviceFuncs()
{
	return this->m_pDeviceFuncs;
}

void CDriverManager::CopyDeviceFuncs(__in const D3DDDI_DEVICEFUNCS pDevice)
{
	this->m_pDeviceFuncs = new D3DDDI_DEVICEFUNCS(pDevice);
}

HRESULT CDriverManager::CreateDevice(__in HANDLE hAdapter, __inout D3DDDIARG_CREATEDEVICE *pDeviceData)
{
	return this->m_pfnCreateDevice(hAdapter, pDeviceData);
}

HMODULE CDriverManager::GetDriver()
{
	return this->m_hDriver;
}

HMODULE CDriverManager::LoadDriver(__in LPCWSTR lpFileName)
{
	HMODULE hModule = GetModuleHandle(lpFileName);

	if (!hModule)
	{
		TCHAR szPath[MAX_PATH];

		if (!SUCCEEDED(SHGetFolderPath(NULL, CSIDL_SYSTEMX86, NULL, 0, szPath)))
			return NULL;

		if (!SUCCEEDED(StringCchPrintf(szPath, MAX_PATH, TEXT("%s\\%s"), szPath, lpFileName)))
			return NULL;

		hModule = LoadLibrary(szPath);

		if (!hModule)
			return NULL;
	}

	return hModule;
}

以上。