显卡驱动入手的构造D3DHook的方法

转载https://bbs.pediy.com/thread-195759.htm
本文要点:
介绍一种从显卡驱动入手的构造D3DHook的方法,DX9,DX9EX,DX10,DX11均试用,无需重启进程,注入即可使用。

以DX9为例。

前置知识:
1. API Hook的基础知识
2. DX绘图知识
3. 关于DX Hook 有很多种方法,传统的方法请自行查找资料。
4. Windows Display Driver 的基础知识

首先Vender驱动导出了一个叫做OpenAdapter的函数。
原型
1
typedef HRESULT APIENTRY _OpenAdapter(D3DDDIARG_OPENADAPTER
*pAdapterData);
通过观察这个函数的参数可以发现这两个结构体
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
typedef struct _D3DDDIARG_OPENADAPTER
{
     HANDLE                         hAdapter;           // in /out :  Runtime handle /out : Driver handle
     UINT                           Interface;          // in :  Interface version
     UINT                           Version;            // in :  Runtime version
     CONST D3DDDI_ADAPTERCALLBACKS* pAdapterCallbacks;  // in :  Pointer to runtime callbacks
     D3DDDI_ADAPTERFUNCS*           pAdapterFuncs;      // out: Driver function table
     UINT                           DriverVersion;      // out: D3D UMD interface version the
                                                        //      driver was compiled with. Use
                                                        //      D3D_UMD_INTERFACE_VERSION.
} D3DDDIARG_OPENADAPTER;
typedef struct _D3DDDI_ADAPTERFUNCS
{
     PFND3DDDI_GETCAPS                       pfnGetCaps;
     PFND3DDDI_CREATEDEVICE                  pfnCreateDevice;
     PFND3DDDI_CLOSEADAPTER                  pfnCloseAdapter;
} D3DDDI_ADAPTERFUNCS;

HookOpenAdapter代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
BOOL DetourOpenAdapter()
{
OpenAdapter = (_OpenAdapter *)GetProcAddress(GetModuleHandleA( "nvd3dum.dll" ), "OpenAdapter" );
if (OpenAdapter == NULL)
{
OpenAdapter = (_OpenAdapter *)GetProcAddress(GetModuleHandleA( "aticfx32.dll" ), "OpenAdapter" );
}
 
if (OpenAdapter)
{
DetourRestoreAfterWith();
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach((void **)&OpenAdapter, WarpOpenAdapter);
DetourTransactionCommit();
}
 
return OpenAdapter != NULL;
}

通过Hook OpenAdapter 可以得到pfnCreateDevice;
代码如下
1
2
3
4
5
6
7
8
9
10
HRESULT APIENTRY WarpOpenAdapter(D3DDDIARG_OPENADAPTER *pAdapterData)
{
     HRESULT ret = OpenAdapter(pAdapterData);
     if (ret == S_OK && pAdapterData->pAdapterFuncs->pfnCreateDevice)
     {
         DdiCreateDevice = pAdapterData->pAdapterFuncs->pfnCreateDevice;
         pAdapterData->pAdapterFuncs->pfnCreateDevice = WarpDdiCreateDevice;
     }
     return ret;
}
pfnCreateDevice是Vender驱动上报给runtime的createDevice API.当APP调用CreateDevice最后会到厂商驱动的pfnCreateDevice; 替换厂商驱动上报给runtime的pfnCreateDevice函数指针,就可以实现Hook pfnCreateDevice。这样下次APP调用CreateDevice就会先进入我们的WarpDdiCreateDevice;
代码如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
HRESULT APIENTRY WarpDdiCreateDevice(
     HANDLE hAdapter,
     D3DDDIARG_CREATEDEVICE *pDeviceData)
{
     // DdiCreateDevice must not be NULL if this path hit
     HRESULT ret = DdiCreateDevice(hAdapter, pDeviceData);
     if (pDeviceData->pDeviceFuncs->pfnPresent && gDetourFuncTable.DdiPresent.isAttatched == FALSE)
     {
         DdiPresent = pDeviceData->pDeviceFuncs->pfnPresent;
         ULONG_PTR realTramponLine = DetourDDiPresent();
         gDetourFuncTable.DdiPresent.isAttatched = TRUE;
         gDetourFuncTable.DdiPresent.preFuncPtr = DdiPresent;
         gDetourFuncTable.DdiPresent.TrampoLinePtr = (PFND3DDDI_PRESENT)realTramponLine;
         gDetourFuncTable.DdiPresent.warpFuncPtr = WarpDdiPresent;
     }
     if (pDeviceData->pDeviceFuncs->pfnPresent1 && gDetourFuncTable.DdiPresent1.isAttatched == FALSE)
     {
         DdiPresent1 = pDeviceData->pDeviceFuncs->pfnPresent1;
         ULONG_PTR realTramponLine = DetourDDiPresent1();
         gDetourFuncTable.DdiPresent1.isAttatched = TRUE;
         gDetourFuncTable.DdiPresent1.preFuncPtr = DdiPresent1;
         gDetourFuncTable.DdiPresent1.TrampoLinePtr = (PFND3DDDI_PRESENT1)realTramponLine;
         gDetourFuncTable.DdiPresent1.warpFuncPtr = WarpDdiPresent1;
     }
     DdiLock = pDeviceData->pDeviceFuncs->pfnLock;
     DdiCreateResource = pDeviceData->pDeviceFuncs->pfnCreateResource;
     DdiCreateResource2 = pDeviceData->pDeviceFuncs->pfnCreateResource2;
     DdiUnlock = pDeviceData->pDeviceFuncs->pfnUnlock;
     DdiBlt = pDeviceData->pDeviceFuncs->pfnBlt;
 
     return ret;
}
 
typedef struct _D3DDDIARG_CREATEDEVICE
{
     HANDLE                          hDevice;                // in :  Runtime handle /out : Driver handle
     UINT                            Interface;              // in :  Interface version
     UINT                            Version;                // in :  Runtime Version
     CONST D3DDDI_DEVICECALLBACKS*   pCallbacks;             // in :  Pointer to runtime callbacks
     VOID*                           pCommandBuffer;         // in :  Pointer to the first command buffer to use.
     UINT                            CommandBufferSize;      // in :  Size of the first command buffer to use.
     D3DDDI_ALLOCATIONLIST*          pAllocationList;        // out: Pointer to the first allocation list to use.
     UINT                            AllocationListSize;     // in :  Size of the allocation list that will be available
                                                             //      when the first command buffer is submitted.
     D3DDDI_PATCHLOCATIONLIST*       pPatchLocationList;     // out: Pointer to the first patch location list to use.
     UINT                            PatchLocationListSize;  // in :  Size of the patch location list that will be available
                                                             //      when the first command buffer is submitted.
     D3DDDI_DEVICEFUNCS*             pDeviceFuncs;           // out: Driver function table
     D3DDDI_CREATEDEVICEFLAGS        Flags;                  // in :  Flags
#if (D3D_UMD_INTERFACE_VERSION >= D3D_UMD_INTERFACE_VERSION_WIN7)
     D3DGPU_VIRTUAL_ADDRESS          CommandBuffer;          // out: GPU Virtual address to the command buffer to use. _ADVSCH_
#endif // D3D_UMD_INTERFACE_VERSION
} D3DDDIARG_CREATEDEVICE;
观察参数D3DDDIARG_CREATEDEVICE *pDeviceData不难发现 pDeviceFuncs中有我们需要的一切绘图API,这里用到的函数如下:
1
2
3
4
5
6
7
8
PFND3DDDI_CREATEDEVICE DdiCreateDevice = NULL;
PFND3DDDI_PRESENT DdiPresent = NULL;
PFND3DDDI_PRESENT1 DdiPresent1= NULL;
PFND3DDDI_LOCK DdiLock = NULL;
PFND3DDDI_CREATERESOURCE DdiCreateResource = NULL;
PFND3DDDI_CREATERESOURCE2 DdiCreateResource2 = NULL;
PFND3DDDI_UNLOCK DdiUnlock = NULL;
PFND3DDDI_BLT DdiBlt = NULL;
代码如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
HRESULT APIENTRY WarpDdiCreateDevice(
     HANDLE hAdapter,
     D3DDDIARG_CREATEDEVICE *pDeviceData)
{
     // DdiCreateDevice must not be NULL if this path hit
     HRESULT ret = DdiCreateDevice(hAdapter, pDeviceData);
     if (pDeviceData->pDeviceFuncs->pfnPresent && gDetourFuncTable.DdiPresent.isAttatched == FALSE)
     {
         DdiPresent = pDeviceData->pDeviceFuncs->pfnPresent;
         ULONG_PTR realTramponLine = DetourDDiPresent();
         gDetourFuncTable.DdiPresent.isAttatched = TRUE;
         gDetourFuncTable.DdiPresent.preFuncPtr = DdiPresent;
         gDetourFuncTable.DdiPresent.TrampoLinePtr = (PFND3DDDI_PRESENT)realTramponLine;
         gDetourFuncTable.DdiPresent.warpFuncPtr = WarpDdiPresent;
     }
     if (pDeviceData->pDeviceFuncs->pfnPresent1 && gDetourFuncTable.DdiPresent1.isAttatched == FALSE)
     {
         DdiPresent1 = pDeviceData->pDeviceFuncs->pfnPresent1;
         ULONG_PTR realTramponLine = DetourDDiPresent1();
         gDetourFuncTable.DdiPresent1.isAttatched = TRUE;
         gDetourFuncTable.DdiPresent1.preFuncPtr = DdiPresent1;
         gDetourFuncTable.DdiPresent1.TrampoLinePtr = (PFND3DDDI_PRESENT1)realTramponLine;
         gDetourFuncTable.DdiPresent1.warpFuncPtr = WarpDdiPresent1;
     }
     DdiLock = pDeviceData->pDeviceFuncs->pfnLock;
     DdiCreateResource = pDeviceData->pDeviceFuncs->pfnCreateResource;
     DdiCreateResource2 = pDeviceData->pDeviceFuncs->pfnCreateResource2;
     DdiUnlock = pDeviceData->pDeviceFuncs->pfnUnlock;
     DdiBlt = pDeviceData->pDeviceFuncs->pfnBlt;
 
     return ret;
}
到这里我们已经顺利拿到厂商的pfnPresent函数了,是不是有点兴奋呢?因为我们要做的任何绘图工作需要在pfnPresent之前完成。这样present才会把我们画的东西拷贝到屏幕上。
或许你会有一个疑问,APP已经在运行了,不会再调用CreateDevice了,这样我们的Hook不就无效了吗?
答案是这样的,既然APP调用CreateDevice,那我们就自己调用。我们来构造这样一个CreateDevice的条件,创建出来的d3dDevice还能在后面用来作为我们绘图的设备(OnRenderFrame).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
BOOL TryInitD3D(HWND hWnd)
{
 
     D3DPRESENT_PARAMETERS d3dpp;
     HRESULT hResult = D3DERR_INVALIDCALL;
 
     ZeroMemory(&d3dpp, sizeof(d3dpp));
     d3dpp.Windowed = TRUE;
     d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
     d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
 
     pd3dInternal = Direct3DCreate9(D3D_SDK_VERSION);
     if (pd3dInternal != NULL)
     {
         hResult = pd3dInternal->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
             D3DCREATE_SOFTWARE_VERTEXPROCESSING,
             &d3dpp, &pd3dDevInternal);
     }
 
     return hResult == S_OK && pd3dDevInternal != NULL && InitDx();
}
拿到pfnPresent之后做inlineHook
1
2
3
4
5
6
7
8
9
10
11
12
13
14
ULONG_PTR DetourDDiPresent1()
{
     ULONG_PTR realTrampoLine = NULL;
 
     DetourRestoreAfterWith();
     DetourTransactionBegin();
     DetourUpdateThread(GetCurrentThread());
     if (DdiPresent1)
     {
         DetourAttachEx((void **)&DdiPresent1, WarpDdiPresent1, (PDETOUR_TRAMPOLINE *)&realTrampoLine, NULL, NULL);
     }
     DetourTransactionCommit();
     return realTrampoLine;
}
然后在pfnPrsent的代理函数中做我们自己的绘图操作。里面涉及的函数下面会有解释
我实现了一个简单的pfnPresent1代理函数,在里面调用真正的pfnPresent1之前做我们的绘图(我是的WIN 8.1,WDDM1.3 所以就对pfnPresent1动手了)
代理函数如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
HRESULT APIENTRY WarpDdiPresent1(
     HANDLE hDevice,
     CONST D3DDDIARG_PRESENT1 *pPresent1)
{
     static HANDLE wrapDevice = NULL;
     static HANDLE hDestSurf = NULL;
     static UINT cachedWidth = 0;
     static UINT cachedHeight = 0;
     RECT rc = { 0 };
     RECT destRC = { 0 };
     RECT srcRC = { 0 };
     IDirect3DSurface9 * pSrcSurf = NULL;
     HRESULT hRet = S_OK;
 
         // 如果是运行中的app画图,就进入下面的流程进行我们的画图
         // 反之是我们的画图操作,直接放过,避免重入
     if (hDevice == wrapDevice || wrapDevice == NULL)
     {
         wrapDevice = hDevice;
         if (S_OK == onRenderFrame(&pSrcSurf, &rc))
         {
             if (rc.right - rc.left != cachedWidth ||
                 rc.bottom - rc. top != cachedHeight)
             {
                 cachedWidth = rc.right - rc.left;
                 cachedHeight = rc.bottom - rc. top ;
 
                 //TODO : destroy hDestSurf
                 hDestSurf = CreateSurfaceforWDDM1_3(hDevice, cachedWidth, cachedHeight);
             }
 
             if (hDestSurf != NULL)
             {
                 destRC = srcRC = rc;
                 hRet = SurfaceBltInternal(hDevice, hDestSurf, destRC, pSrcSurf, srcRC);
             }
 
             if (hRet == S_OK)
             {
                 BltToRTWDDM1_3(hDevice, hDestSurf, pPresent1,rc);
             }
         }
 
     }
     hRet = gDetourFuncTable.DdiPresent1.TrampoLinePtr(hDevice, pPresent1);
     return hRet;
}
HRESULT APIENTRY onRenderFrame(__out IDirect3DSurface9 ** pOutSurf, RECT * pOutRC)是我们的绘图函数,在这里实现我们要的东西,画到RenderTarget上,然后输出RenderTarget和它的大小。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
HRESULT APIENTRY onRenderFrame(__out IDirect3DSurface9 ** pOutSurf, RECT * pOutRC)
{
     HRESULT hr;
     RECT rc;
 
     pd3dDev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_RGBA(0, 0, 0, 0), 1.0f, 0);
     if (S_OK == pd3dDev->BeginScene())
     {
         hr = pd3dDev->SetRenderTarget(NULL, pRTSurf);
         // 在这里画你需要的任何东西,没错 就是这么任性。
         pd3dDev->EndScene();
     }
 
     if (hr == S_OK)
     {
         *pOutSurf = pRTSurf;
         *pOutRC = rc;
     }
     else
     {
         *pOutSurf = NULL;
         pOutRC-> top = 0;
         pOutRC->left = 0;
         pOutRC->bottom = 0;
         pOutRC->right = 0;
     }
 
     return hr;
}
当在WarpDdiPresent1得到我们的绘图结果(RenderTarget和大小)之后,我们就能把画好的结果拿出来,然后通过调用厂商提供的pfnBlt函数 贴到present的SrcSurf上。

//用上面拿到的pfnCreateResource2 创建一张surface 用来保存我们的绘图结果。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
HANDLE CreateSurfaceforWDDM1_3(HANDLE hDevice, UINT width, UINT height)
{
     D3DDDIARG_CREATERESOURCE2 createResourceData2;
     D3DDDI_SURFACEINFO surfList[1];
 
     memset(surfList, 0, sizeof(surfList));
     memset(&createResourceData2, 0, sizeof(createResourceData2));
 
     surfList[0].Height = width;
     surfList[0].Width = height;
     surfList[0].Depth = 1;
     surfList[0].pSysMem = NULL;
     surfList[0].SysMemPitch = 0;
     surfList[0].SysMemSlicePitch = 0;
 
     createResourceData2.Format = D3DDDIFMT_A8R8G8B8;
     createResourceData2.Pool = D3DDDIPOOL_NONLOCALVIDMEM;
     createResourceData2.MultisampleType = D3DDDIMULTISAMPLE_NONE;
     createResourceData2.MultisampleQuality = 0;
     createResourceData2.pSurfList = surfList;
     createResourceData2.SurfCount = 1;
     createResourceData2.MipLevels = 0;
     createResourceData2.Fvf = 0;
     createResourceData2.VidPnSourceId = 0;
     createResourceData2.RefreshRate.Denominator = 0;
     createResourceData2.RefreshRate.Numerator = 0;
     // use a particular handle that runtime do not use
     createResourceData2.hResource = (HANDLE)0xfff00000;
     createResourceData2.Flags.Value = 0;
     createResourceData2.Rotation = D3DDDI_ROTATION_IDENTITY;
 
     DdiCreateResource2(hDevice, &createResourceData2);
 
     return createResourceData2.hResource;
}
//把OnRenderFrame输出的RenderTarget的内容拷贝到创建的Surface
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
HRESULT SurfaceBltInternal(HANDLE hDevice, HANDLE hDestSurf, RECT destSurfRC, IDirect3DSurface9 * pSrcSurf, RECT srcSurfRC)
{
     D3DDDIARG_LOCK lockdata;
     D3DDDIARG_UNLOCK unlockData;
     D3DLOCKED_RECT lockedRC;
     HRESULT hr;
 
     if (destSurfRC.right - destSurfRC.left < srcSurfRC.right - srcSurfRC.left ||
         destSurfRC.bottom - destSurfRC. top < srcSurfRC.bottom - srcSurfRC. top )
     {
         hr = S_FALSE;
     }
     else
     {
         memset(&lockdata, 0, sizeof(lockdata));
         memset(&unlockData, 0, sizeof(unlockData));
 
         lockdata.hResource = hDestSurf;
         lockdata.SubResourceIndex = 0;
         lockdata.Area.left = 0;
         lockdata.Area.right = destSurfRC.right - destSurfRC.left;
         lockdata.Area. top = 0;
         lockdata.Area.bottom = destSurfRC.bottom - destSurfRC. top ;
 
         hr = DdiLock(hDevice, &lockdata);
         if (hr == S_OK)
         {
             hr = pSrcSurf->LockRect(&lockedRC, &srcSurfRC, NULL);
             if (S_OK == hr)
             {
                 CHAR *pDest = (CHAR*)lockdata.pSurfData;
                 CHAR *pSrc = (CHAR*)lockedRC.pBits;
                 for (UINT i = 0; i < destSurfRC.bottom - destSurfRC. top ; i++)
                 {
 
                     CopyMemory(pDest, pSrc, lockedRC.Pitch < lockdata.Pitch ? lockedRC.Pitch : lockdata.Pitch);
                     pDest += lockdata.Pitch;
                     pSrc += lockedRC.Pitch;
                 }
 
             }
             pSrcSurf->UnlockRect();
             memset(&unlockData, 0, sizeof(unlockData));
             unlockData.hResource = hDestSurf;
             unlockData.SubResourceIndex = 0;
             DdiUnlock(hDevice, &unlockData);
         }
     }
     return hr;
}
//调用厂商提供的pfnBLT把surafce上保存的图画 搬到 present的SrcSurf中,这样 present的时候 我们画的东西就展现出来了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
HRESULT BltToRTWDDM1_1(HANDLE hDevice, HANDLE hRes, CONST D3DDDIARG_PRESENT *pPresent)
{
     D3DDDIARG_BLT bltData;
     RECT srcRC;
     RECT destRC;
 
     memset(&bltData, 0, sizeof(bltData));
     memset(&srcRC, 0, sizeof(srcRC));
     memset(&destRC, 0, sizeof(destRC));
     
     srcRC.left = 0;
     srcRC. top = 0;
     srcRC.right = 200;
     srcRC.bottom = 200;
 
     destRC.left = 0;
     destRC. top = 0;
     destRC.right = 200;
     destRC.bottom = 200;
 
     bltData.hDstResource = pPresent->hSrcResource;
     bltData.DstSubResourceIndex = pPresent->SrcSubResourceIndex;
     bltData.hSrcResource = hRes;
     bltData.SrcSubResourceIndex = 0;
     bltData.SrcRect = srcRC;
     bltData.DstRect = destRC;
 
     return DdiBlt(hDevice, &bltData);
}
如有错误之处,还请各位指正。欢迎各位大牛小牛牛牛拍砖了。

你可能感兴趣的:(显卡驱动入手的构造D3DHook的方法)