转载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的函数。
原型
*pAdapterData);
通过观察这个函数的参数可以发现这两个结构体
HookOpenAdapter代码
通过Hook OpenAdapter 可以得到pfnCreateDevice;
代码如下
pfnCreateDevice是Vender驱动上报给runtime的createDevice API.当APP调用CreateDevice最后会到厂商驱动的pfnCreateDevice; 替换厂商驱动上报给runtime的pfnCreateDevice函数指针,就可以实现Hook pfnCreateDevice。这样下次APP调用CreateDevice就会先进入我们的WarpDdiCreateDevice;
代码如下
观察参数D3DDDIARG_CREATEDEVICE *pDeviceData不难发现 pDeviceFuncs中有我们需要的一切绘图API,这里用到的函数如下:
代码如下
到这里我们已经顺利拿到厂商的pfnPresent函数了,是不是有点兴奋呢?因为我们要做的任何绘图工作需要在pfnPresent之前完成。这样present才会把我们画的东西拷贝到屏幕上。
或许你会有一个疑问,APP已经在运行了,不会再调用CreateDevice了,这样我们的Hook不就无效了吗?
答案是这样的,既然APP调用CreateDevice,那我们就自己调用。我们来构造这样一个CreateDevice的条件,创建出来的d3dDevice还能在后面用来作为我们绘图的设备(OnRenderFrame).
拿到pfnPresent之后做inlineHook
然后在pfnPrsent的代理函数中做我们自己的绘图操作。里面涉及的函数下面会有解释
我实现了一个简单的pfnPresent1代理函数,在里面调用真正的pfnPresent1之前做我们的绘图(我是的WIN 8.1,WDDM1.3 所以就对pfnPresent1动手了)
代理函数如下
HRESULT APIENTRY onRenderFrame(__out IDirect3DSurface9 ** pOutSurf, RECT * pOutRC)是我们的绘图函数,在这里实现我们要的东西,画到RenderTarget上,然后输出RenderTarget和它的大小。
当在WarpDdiPresent1得到我们的绘图结果(RenderTarget和大小)之后,我们就能把画好的结果拿出来,然后通过调用厂商提供的pfnBlt函数 贴到present的SrcSurf上。
//用上面拿到的pfnCreateResource2 创建一张surface 用来保存我们的绘图结果。
//把OnRenderFrame输出的RenderTarget的内容拷贝到创建的Surface
//调用厂商提供的pfnBLT把surafce上保存的图画 搬到 present的SrcSurf中,这样 present的时候 我们画的东西就展现出来了。
如有错误之处,还请各位指正。欢迎各位大牛小牛牛牛拍砖了。
本文要点:
介绍一种从显卡驱动入手的构造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 |
通过观察这个函数的参数可以发现这两个结构体
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; } |
代码如下
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; |
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; } |
或许你会有一个疑问,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(); } |
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; } |
我实现了一个简单的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; } |
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; } |
//用上面拿到的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; } |
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; } |
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); } |