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

めいくりぷとのブログ

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

OpenGL ICD Hook

OpenGL ICD hook (グラフィックドライバフック)が完成したので手法を大雑把に説明します。

まずは、これらのAPIからICDを取得します。
GDI32.D3DKMTOpenAdapterFromHdc
GDI32.D3DKMTQueryAdapterInfo
GDI32.D3DKMTCloseAdapter

BOOL CDriverManager::Init()
{
	HDC hDC = GetDC(GetDesktopWindow());
	if (!hDC)
		return FALSE;

	HMODULE hGDI32 = this->LoadDriver(_T("gdi32.dll"), CSIDL_SYSTEM);
	if (hGDI32 == NULL)
		return FALSE;

	this->m_pfnD3DKMTOpenAdapterFromHdc = reinterpret_cast<pfnD3DKMTOpenAdapterFromHdc>(GetProcAddress(hGDI32, "D3DKMTOpenAdapterFromHdc"));
	this->m_pfnD3DKMTQueryAdapterInfo = reinterpret_cast<pfnD3DKMTQueryAdapterInfo>(GetProcAddress(hGDI32, "D3DKMTQueryAdapterInfo"));
	this->m_pfnD3DKMTCloseAdapter = reinterpret_cast<pfnD3DKMTCloseAdapter>(GetProcAddress(hGDI32, "D3DKMTCloseAdapter"));

	if (this->m_pfnD3DKMTOpenAdapterFromHdc == NULL
		|| this->m_pfnD3DKMTQueryAdapterInfo == NULL
		|| this->m_pfnD3DKMTCloseAdapter == NULL)
		return FALSE;

	D3DKMT_OPENADAPTERFROMHDC HdcAdapter		= { 0 };
	D3DKMT_CLOSEADAPTER		  CloseAdapter		= { 0 };
	D3DKMT_QUERYADAPTERINFO	  QueryAdapterInfo  = { 0 };
	D3DKMT_OPENGLINFO		  OpenGLInfo		= { 0 };
	WCHAR wszFilename[MAX_PATH];

	HdcAdapter.hDc = hDC;

	if (NT_SUCCESS(this->m_pfnD3DKMTOpenAdapterFromHdc(&HdcAdapter)))
	{
		QueryAdapterInfo.hAdapter	= HdcAdapter.hAdapter;
		QueryAdapterInfo.Type		= KMTQAITYPE_UMOPENGLINFO;
		QueryAdapterInfo.pPrivateDriverData		= &OpenGLInfo.UmdOpenGlIcdFileName;
		QueryAdapterInfo.PrivateDriverDataSize	= sizeof(D3DKMT_OPENGLINFO);

		if (NT_SUCCESS(this->m_pfnD3DKMTQueryAdapterInfo(&QueryAdapterInfo)))
		{
			CloseAdapter.hAdapter = HdcAdapter.hAdapter;

			this->m_pfnD3DKMTCloseAdapter(&CloseAdapter);

			StringCchPrintf(wszFilename, MAX_PATH, _T("%s"), QueryAdapterInfo.pPrivateDriverData);

			this->m_hDriver = this->LoadDriver(wszFilename, CSIDL_SYSTEMX86);
			if (this->m_hDriver)
				return TRUE;
		}
	}

	return FALSE;
}

取得したICD名から、GetModuleHandleまたはLoadLibraryでモジュールハンドルを取得します。
ICDのモジュールハンドルを元に DrvSetContext をフックし、glDispatchTable を取得します。その後にglDispatchTableの各関数をフックします。

VOID Detour_DrvSetContext(__in BOOL bEnable);

typedef PGLCLTPROCTABLE(GLAPIENTRY *pfnDrvSetContext)(_In_ HDC hdc, _In_ DHGLRC dhglrc, _In_ PFN_SETPROCTABLE pfnSetProcTable);
static pfnDrvSetContext _DrvSetContext = NULL;
static pfnDrvSetContext DrvSetContext_Hook = [](
	_In_ HDC hdc, 
	_In_ DHGLRC dhglrc, 
	_In_ PFN_SETPROCTABLE pfnSetProcTable) -> PGLCLTPROCTABLE
{
	CGraphicsContext *pGraphicsContext;
	GLCLTPROCTABLE *pProcTable;
	
	pProcTable = _DrvSetContext(hdc, dhglrc, pfnSetProcTable);
	if (pProcTable != NULL)
	{
		pGraphicsContext = CGraphicsContext::GetInstance();
		if (pGraphicsContext->IsValidDispatchTable() == FALSE)
		{
			pGraphicsContext->SetDispatchTable(pProcTable->glDispatchTable);
			if (pGraphicsContext->Init(pProcTable))
			{
				// detour remove
				Detour_DrvSetContext(FALSE);
			}
		}
	}

	return pProcTable;
};

VOID Detour_DrvSetContext(__in BOOL bEnable)
{
	if (!_DrvSetContext)
		_DrvSetContext = reinterpret_cast<pfnDrvSetContext>(
			GetProcAddress(CDriverManager::GetInstance()->GetDriver(), "DrvSetContext"));

	DetourFunction(bEnable, reinterpret_cast<LPVOID*>(&_DrvSetContext), DrvSetContext_Hook);
}
 
VOID Initialize(__in HINSTANCE hInstance)
{
	DisableThreadLibraryCalls(hInstance);

	if (CDriverManager::GetInstance()->Init())
	{
		Detour_DrvSetContext(TRUE);

		int args = 0;
		QApplication app(args, NULL);
		CMainWindow mainWindow;
		mainWindow.show();
		app.exec();
	}

	FreeLibraryAndExitThread(hInstance, 0);
}