[C++] D3D9 Hooking via Detours
Chapter 2
This is the start of our class. We include stdafx.h because it includes the DirectX9 includes and such that we will need in this file and others soon to come. Next, back in the notepad window of d3d9.h lets copy and paste some lines.
Code:
/*** IUnknown methods ***/
STDMETHOD(QueryInterface)(THIS_ REFIID riid, void** ppvObj) PURE;
STDMETHOD_(ULONG,AddRef)(THIS) PURE;
STDMETHOD_(ULONG,Release)(THIS) PURE;
/*** IDirect3D9 methods ***/
STDMETHOD(RegisterSoftwareDevice)(THIS_ void* pInitializeFunction) PURE;
STDMETHOD_(UINT, GetAdapterCount)(THIS) PURE;
STDMETHOD(GetAdapterIdentifier)(THIS_ UINT Adapter,DWORD Flags,D3DADAPTER_IDENTIFIER9* pIdentifier) PURE;
STDMETHOD_(UINT, GetAdapterModeCount)(THIS_ UINT Adapter,D3DFORMAT Format) PURE;
STDMETHOD(EnumAdapterModes)(THIS_ UINT Adapter,D3DFORMAT Format,UINT Mode,D3DDISPLAYMODE* pMode) PURE;
STDMETHOD(GetAdapterDisplayMode)(THIS_ UINT Adapter,D3DDISPLAYMODE* pMode) PURE;
STDMETHOD(CheckDeviceType)(THIS_ UINT Adapter,D3DDEVTYPE DevType,D3DFORMAT AdapterFormat,D3DFORMAT BackBufferFormat,BOOL bWindowed) PURE;
STDMETHOD(CheckDeviceFormat)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT AdapterFormat,DWORD Usage,D3DRESOURCETYPE RType,D3DFORMAT CheckFormat) PURE;
STDMETHOD(CheckDeviceMultiSampleType)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT SurfaceFormat,BOOL Windowed,D3DMULTISAMPLE_TYPE MultiSampleType,DWORD* pQualityLevels) PURE;
STDMETHOD(CheckDepthStencilMatch)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT AdapterFormat,D3DFORMAT RenderTargetFormat,D3DFORMAT DepthStencilFormat) PURE;
STDMETHOD(CheckDeviceFormatConversion)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT SourceFormat,D3DFORMAT TargetFormat) PURE;
STDMETHOD(GetDeviceCaps)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DCAPS9* pCaps) PURE;
STDMETHOD_(HMONITOR, GetAdapterMonitor)(THIS_ UINT Adapter) PURE;
STDMETHOD(CreateDevice)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,HWND hFocusWindow,DWORD BehaviorFlags,D3DPRESENT_PARAMETERS* pPresentationParameters,IDirect3DDevice9** ppReturnedDeviceInterface) PURE;
And paste it into ID3D9Wrapper.h between the virtual function and the IDirect3D9* variable. If you pasted in the right spot the last thing in the wrapper should be :
Code:
IDirect3D9* Direct3D9; };
The next step we need to do is remove the PURE define from each function. Just delete the word PURE so the last thing on each line is ); An easy way to do this is hit CTRL+H, in Find What put: " PURE;" (without the quotes but with the space in the front) and in Replace With put: ";" (without the quotes) and make sure to select Current Document under Look In. Then hit replace all.
All done, save and close this file.
ID3D9Wrapper.cpp
Now for this file, I am not writing everything out that will be in it here on the forums because its a shit ton of code. You have to rewrite all the functions that we just added to the .h file. But don't worry its real easy. You are simply creating the functions to return the normal values as if it was the real Direct3D instance.
First, lets add our includes and create the class construct/deconstruct.
So inside ID3D9Wrapper.cpp the includes you will want are :
Code:
#include "ID3D9Wrapper.h"
Next we have our construct of the class. We want to store the Direct3D pointer to be used inside the class so we use :
Code:
Direct3D9Wrapper::Direct3D9Wrapper(LPDIRECT3D9 pDirect3D)
{
Direct3D9 = pDirect3D;
}
And under, our deconstruct doesn't need to do anything so :
Direct3D9Wrapper::~Direct3D9Wrapper() {}
Next is the functions. You can simply just walk down through each function and add them yourself easily. I will show you an example of how to do it yourself. (You can also look in the example if you don't understand.)
Our first function in the list was :
STDMETHOD(QueryInterface)(THIS_ REFIID riid, void** ppvObj);
To understand this, STDMETHOD is a macro that states anything inside the ( ) will return HRESULT. As for STDMETHOD_ this is like an extended version of the macro which states the first value inside the ( ) is the return type, and the second is the function name.
So looking at that line, we know QueryInterface returns HRESULT. So our function inside the .cpp file will look like :
Code:
HRESULT Direct3D9Wrapper::QueryInterface(const IID &riid, void **ppvObj)
{
return Direct3D9->QueryInterface(riid, ppvObj);
}
Simple isn't it ?
An example of a function using STDMETHOD_ would be :
Code:
STDMETHOD_(ULONG,AddRef)(THIS);
So our function would be :
Code:
ULONG Direct3D9Wrapper::AddRef()
{
return Direct3D9->AddRef();
}
Once you get to CreateDevice, stop. This is the function we need to alter from its original code. This is because if we don't it will simply create a regular Direct3DDevice9 interface. Instead, we want it to create an instance of our Direct3DDevice9Wrapper class.
So our CreateDevice function will look like :
Code:
HRESULT Direct3D9Wrapper::CreateDevice(UINT Adapter, D3DDEVTYPE DeviceType,HWND hFocusWindow, DWORD BehaviorFlags,D3DPRESENT_PARAMETERS *pPresentationParameters,IDirect3DDevice9 **ppReturnedDeviceInterface)
{
IDirect3DDevice9* pDirect3DDevice9;
HRESULT hRes = Direct3D9->CreateDevice( Adapter, DeviceType, hFocusWindow, BehaviorFlags, pPresentationParameters, &pDirect3DDevice9 );
*ppReturnedDeviceInterface = new Direct3DDevice9Wrapper( pDirect3DDevice9, this, pPresentationParameters );
return hRes;
}
As you can see, instead of returning the regular device used in CreateDevice, we set our wrappers device pointer to the one used in ppReturnedDeviceInterface. This is so that it uses our wrapper instead of the normal Direct3DDevice functions.
And you are done with this file now. So you can save and close it.
ID3D9Wrapper_Device.h
Just like the other .h file for the wrapper, you need to find the functions that are used by the original Direct3DDevice9 interface. To do that, go back to your notepad that has d3d9.h open and scroll down further from the last spot we copied code from. Not much further down from where we just were in that file, you should find :
Code:
DECLARE_INTERFACE_(IDirect3DDevice9, IUnknown)
This is the start the Direct3DDevice9 interface which we will need the code from. As I said above, this is the file that contains the most code in this hook and tutorial. I will not show you everything to copy on here cause it will be way to long, but, I will show you where to start and stop :
Start copying at:
Code:
/*** IUnknown methods ***/
STDMETHOD(QueryInterface)(THIS_ REFIID riid, void** ppvObj) PURE;
STDMETHOD_(ULONG,AddRef)(THIS) PURE;
STDMETHOD_(ULONG,Release)(THIS) PURE;
Copy everything, including what I just showed you above, down to, and including:
Code:
STDMETHOD(DrawTriPatch)(THIS_ UINT Handle,CONST float* pNumSegs,CONST D3DTRIPATCH_INFO* pTriPatchInfo) PURE;
STDMETHOD(DeletePatch)(THIS_ UINT Handle) PURE;
STDMETHOD(CreateQuery)(THIS_ D3DQUERYTYPE Type,IDirect3DQuery9** ppQuery) PURE;
Yea, it's a lot of things, but, it's easy to write them. They are just pass-through functions so you do not need to add any code inside them other then the returns unless you are modifying them for your needs.
Ok that being said, copy and paste that code into ID3D9Wrapper_Device.h then you will need to embrace these, just like the other header, in a class. First, we need our includes :
Code:
#pragma once
#include "stdafx.h"
Next, the class define :
Code:
class Direct3DDevice9Wrapper : public IDirect3DDevice9
{
public:
Next, the construct / deconstruct, to be added after the line that says public :
Code:
Direct3DDevice9Wrapper(IDirect3DDevice9* pDirect3DDevice9, IDirect3D9* pDirect3D9, D3DPRESENT_PARAMETERS *pPresentationParameters);
virtual ~Direct3DDevice9Wrapper();
Then at the end of the file, you will need the closing the of the class. But you will also need 3 variables that will be used inside this class. Which are the variables that hold the pointer to the device and a variable to track strides.
Code:
IDirect3DDevice9* Direct3DDevice9;
IDirect3D9* Direct3D9;
UINT m_Stride;
};
Next, you need to remove the PURE lines again. Just like before, hit CTRL+H, in the top box enter ' PURE;' including the space but not the quotes, and in the bottom, ';' not including the quotes. Then hit Replace All which should replace like 115-120 things. Now you are done with this file for now, so save and close.
ID3D9Wrapper_Device.cpp
Ah, the mother-load file. This file contains the most code as I said, but again as I said, it's just pass through functions at first until you add things yourself. So lets get started..
First, we need our includes for the file :
Code:
#include "ID3D9Wrapper_Device.h"
We only need this because the .h file already includes stdafx.h which contains the other includes that will be needed. Next, we need our construct / deconstruct of this class :
Code:
Direct3DDevice9Wrapper::Direct3DDevice9Wrapper(IDi rect3DDevice9 *pDirect3DDevice9, IDirect3D9 *pDirect3D9, D3DPRESENT_PARAMETERS *pPresentationParameters)
{
Direct3DDevice9 = pDirect3DDevice9;
Direct3D9 = pDirect3D9;
}
Direct3DDevice9Wrapper::~Direct3DDevice9Wrapper(){ }
In the construct, we are grabbing the pointer to the device and storing it to be used in this class, and then altering the returned device pointer with our hooked one so the system uses our wrapped functions.
Next, you need to go through the list of functions and write them out just like you did with the other file. Remember, STDMETHOD means the return type is HRESULT and STDMETHOD_ means the return type is the first variable in the parentheses.
Again heres some examples to help you out with this step :
Code:
HRESULT Direct3DDevice9Wrapper::QueryInterface(const IID &riid, void **ppvObj)
{
return Direct3DDevice9->QueryInterface( riid, ppvObj );
}
ULONG Direct3DDevice9Wrapper::AddRef()
{
return Direct3DDevice9->AddRef();
}
ULONG Direct3DDevice9Wrapper::Release()
{
return Direct3DDevice9->Release();
}
Go to Chapter 3