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

めいくりぷとのブログ

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

WDDM Hook

DirectX C++

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 Detour_CreateDevice()
{
    typedef HRESULT(APIENTRY *pfnCreateDevice)(
        __in HANDLE hAdapter,
        __inout D3DDDIARG_CREATEDEVICE *pDeviceData);
 
    static pfnCreateDevice _CreateDevice =
        reinterpret_cast<pfnCreateDevice>(D3DManager::GetInstance()->m_pfnCreateDevice);
    pfnCreateDevice CreateDevice_Hook = [](
        __in HANDLE hAdapter,
        __inout D3DDDIARG_CREATEDEVICE *pDeviceData) -> HRESULT
    {
        HRESULT hResult = _CreateDevice(hAdapter, pDeviceData);
 
        if (hResult == S_OK && pDeviceData->pDeviceFuncs)
        {
            DirectX *pDirectX = DirectX::GetInstance();
           
            if (!pDirectX->m_pDeviceFuncs)
            {
                pDirectX->Init(pDeviceData->pDeviceFuncs);
            }
        }
 
        return hResult;
    };
 
    return DetourFunction(TRUE, reinterpret_cast<LPVOID*>(&_CreateDevice), CreateDevice_Hook);
}
 
BOOL Detour_OpenAdapter()
{
    typedef HRESULT(APIENTRY *pfnOpenAdapter)(
        __inout D3DDDIARG_OPENADAPTER *pOpenData);
 
    static pfnOpenAdapter _OpenAdapter =
        reinterpret_cast<pfnOpenAdapter>(GetProcAddress(D3DManager::GetInstance()->GetDispDrv(), "OpenAdapter"));
    pfnOpenAdapter OpenAdapter_Hook = [](
        __inout D3DDDIARG_OPENADAPTER *pOpenData) -> HRESULT
    {
        HRESULT hResult = _OpenAdapter(pOpenData);
 
        if (hResult == S_OK && pOpenData->pAdapterFuncs->pfnCreateDevice)
        {
            D3DManager *pManager = D3DManager::GetInstance();
 
            if (!pManager->m_pfnCreateDevice)
            {
                pManager->m_pfnCreateDevice = pOpenData->pAdapterFuncs->pfnCreateDevice;
 
                if (pManager->m_pfnCreateDevice)
                {
                    Detour_CreateDevice();
                }
            }
        }
 
        return hResult;
    };
 
    return DetourFunction(TRUE, reinterpret_cast<LPVOID*>(&_OpenAdapter), OpenAdapter_Hook);
}


以上。

OpenGL ICD Hook

OpenGL C++

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

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

BOOL ICDManager::GetIcdFileName(
    _In_ HDC hdc, 
    _Inout_ LPWSTR szBuffer, 
    _In_ DWORD nSize)
{
    if (!m_hGdi32)
        return FALSE;

    m_D3DKMTOpenAdapterFromHdc = 
        reinterpret_cast<PFND3DKMT_OPENADAPTERFROMHDC>(GetProcAddress(m_hGdi32, "D3DKMTOpenAdapterFromHdc"));
    m_D3DKMTQueryAdapterInfo = 
        reinterpret_cast<PFND3DKMT_QUERYADAPTERINFO>(GetProcAddress(m_hGdi32, "D3DKMTQueryAdapterInfo"));
    m_D3DKMTCloseAdapter = 
        reinterpret_cast<PFND3DKMT_CLOSEADAPTER>(GetProcAddress(m_hGdi32, "D3DKMTCloseAdapter"));

    if (m_D3DKMTOpenAdapterFromHdc &&
	m_D3DKMTQueryAdapterInfo &&
	m_D3DKMTCloseAdapter) 
    {
        D3DKMT_OPENADAPTERFROMHDC HdcAdapter = { 0 };
        D3DKMT_CLOSEADAPTER CloseAdapter = { 0 };
        D3DKMT_QUERYADAPTERINFO	QueryAdapterInfo = { 0 };
        D3DKMT_OPENGLINFO OpenGLInfo = { 0 };

	HdcAdapter.hDc = hdc;

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

	    if (NT_SUCCESS(m_D3DKMTQueryAdapterInfo(&QueryAdapterInfo))) 
            {
		CloseAdapter.hAdapter = HdcAdapter.hAdapter;
		m_D3DKMTCloseAdapter(&CloseAdapter);

		StringCchPrintf(szBuffer, nSize, TEXT("%s"), QueryAdapterInfo.pPrivateDriverData);
		return TRUE;
	    }
	}
    }

    return FALSE;
}

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

BOOL Detour_DrvSetContext()
{
    typedef PGLCLTPROCTABLE(APIENTRY * pfnDrvSetContext)(
        _In_ HDC hdc, 
        _In_ DHGLRC dhglrc, 
        _In_opt_ PFN_SETPROCTABLE pfnSetProcTable
    );
 
    static pfnDrvSetContext _DrvSetContext =
        reinterpret_cast<pfnDrvSetContext>(GetProcAddress(ICDManager::GetInstance()->GetICD(), "DrvSetContext"));
    pfnDrvSetContext DrvSetContext_Hook = [](
        _In_ HDC hdc,
        _In_ DHGLRC dhglrc,
        _In_opt_ PFN_SETPROCTABLE pfnSetProcTable) -> PGLCLTPROCTABLE
    {
        PGLCLTPROCTABLE pglProcTable = _DrvSetContext(hdc, dhglrc, pfnSetProcTable);
        ICDManager *pManager = ICDManager::GetInstance();
 
        if (!pManager->GetDispatchTable()) {
            if (pManager->SetDispatchTable(pglProcTable)) {
                OpenGL::GetInstance()->Init(); // hooking glDispatchTable's functions
            }
        }
 
        return pglProcTable;
    };
 
    return DetourFunction(TRUE, reinterpret_cast<LPVOID*>(&_DrvSetContext), DrvSetContext_Hook);
}
 
BOOL Initialize()
{
    TCHAR szFilename[MAX_PATH];
    HDC hDC = GetDC(GetDesktopWindow());
 
    ICDManager *pManager = ICDManager::GetInstance();

    if (pManager->GetIcdFileName(hDC, szFilename, MAX_PATH)) {
        if (pManager->InitOpenGlIcd(szFilename)) {
            Detour_DrvSetContext();
            return TRUE;
        }
    }
 
    return FALSE;
}

以上。

シンタックス・ハイライトテスト

ソースコードを色付けして記述する(シンタックス・ハイライト) - はてなダイアリーのヘルプ

class hoge
{
public:
    hoge();
}

int main()
{
    hoge *p = new hoge();

    return 0;
]