I'm doing some tutorials about hooking endScene and I don't understand why there are three ***
instead of one. What are the second and the third ones representing:
memcpy(pTable, *(void***)(pDummyDevice), size);
I can't understand why do I put three '*' instead of just a one if someone can explain to me why there are three I would very appreciate it
bool GetD3D9Device(void** pTable, size_t size)
{
if (!pTable)
return false;
IDirect3D9* pD3D = Direct3DCreate9(D3D_SDK_VERSION);
if (!pD3D) {
return false;
}
IDirect3DDevice9* pDummyDevice = nullptr;
D3DPRESENT_PARAMETERS d3dpp = {};
d3dpp.Windowed = false;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.hDeviceWindow = GetProcessWindow();
HRESULT dummyDevCreated = pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, d3dpp.hDeviceWindow,D3DCREATE_SOFTWARE_VERTEXPROCESSING,&d3dpp,&pDummyDevice);
if (dummyDevCreated != S_OK) {
d3dpp.Windowed = !d3dpp.Windowed;
HRESULT dummyDevCreated = pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, d3dpp.hDeviceWindow, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &pDummyDevice);
if (dummyDevCreated != S_OK)
{
pD3D->Release();
return false;
}
}
memcpy(pTable, *(void***)(pDummyDevice), size);
pDummyDevice->Release();
pD3D->Release();
}
void* d3d9Device[119]; // this is the var
The short version of the story is that this code attempts to extract the direct function pointer table of a IDirect3DDevice9. Presumably in order to hook specific function calls by either replacing the vtable entry with a hooked variant or by placing a redirection at the start of the code of individual functions.
Conceptually, you can think of a IDirect3DDevice9
object as having the following structure:
struct IDirect3DDevice9 {
IDirect3DDevice9_vtbl *vtable;
// private data members of this object
};
struct IDirect3DDevice9_vtbl {
IUnknown_vtbl * unk;
FNPTR TestCooperativeLevel;
FNPTR GetAvailableTextureMem;
// about 100 more function pointers, including `Render`, `Present`, `BeginScene`, `EndScene`, etc.
FNPTR DeletePatch;
FNPTR CreateQuery;
}
Somewhere in the code of the game you are hooking, there will be a call to dev->EndScene()
, which translates to the following instructions:
IDirect3DDevice9_vtbl& vtbl = *dev->vtable;
Since vtable
is the first member, this compiles to the the same instruction as the expression *(void *)dev
.FNPTR IDirect3DDevice9_vtbl_EndScene = vtbl.EndScene
. Endscene is at offset 40 of the vtable, so you could also say ((FNPTR *)&vtbl)[40]
or even *((FNPTR *)&vtbl + 40)
.this
bound to dev
.If you ignore the types for a second, you have a pointer (one star) to a vtable, which contains an array of function pointers (two stars). Because all these function pointers have a different type, your code sample has chosen to treat them as void pointers (there's your third star).
All of this hinges on an implementation detail of the C++ compiler being used, namely that every object with virtual functions has a pointer to a vtable at the start, that all instances of a class share the same vtable, and that all functions are laid out sequentially inside the vtable. Of course the Direct3D API has changed over time, but this was always done by appending new functions to the end of the vtable, which makes existing code keep working.