Need help with DirectInput hooking
Need help with DirectInput hooking
Hi there,
I've read the several other posts about DirectInput hooking, and they cleared
it up a bit already.
I'm hooking a game which uses dinput8.dll and therefore uses DirectInput8Create, which I have successfully hooked and it gets executed when the game starts.
However, from this point I'm completely lost, I want to catch keyboard events and mouse clicks, and also send them.
Thanks for your help
I've read the several other posts about DirectInput hooking, and they cleared
it up a bit already.
I'm hooking a game which uses dinput8.dll and therefore uses DirectInput8Create, which I have successfully hooked and it gets executed when the game starts.
However, from this point I'm completely lost, I want to catch keyboard events and mouse clicks, and also send them.
Thanks for your help
I did this using madCodeHook with DirectInput 7, it's the same for 8.
Anyhow, if you look up "GetDeviceState()" in the DirectX docs you will see that it reads in up to the entire array of 256 byte key codes at the time.
So all you need to do is fake a particular key code by setting it's flag so that the key is down, then in a future read (might need to fake it down for a few polling cycles) fake the key up.
Follow the examples posted elsewhere and add the "GetDeviceState()" method to it.
Now it gets a bit more complicated if the application uses DirectInput for both keyboard and mouse inputs.
In your "CreateDeviceEx()" you will want to track the the decives (GUID_SysKeyboard, GUID_SysMouse, etc.) as they are created, because later you will need to compare the interface pointer to the THIS pointer to determin what the "GetDeviceState()" (and other data related calls too) is being polled for. That's the way I did it anyhow.
Possibly you can just observe if cbData == 256 then you know it's intended for a keyboard poll.
Depending on what you are doing, if you need a more general hooking system then you will probably need a fair amount of logic to figure out how the application is using the inputs at run time.
If you can get away with a more specific hook then you can do a bit more reversing of the application and code something more simple.
Hope that helps.
Anyhow, if you look up "GetDeviceState()" in the DirectX docs you will see that it reads in up to the entire array of 256 byte key codes at the time.
So all you need to do is fake a particular key code by setting it's flag so that the key is down, then in a future read (might need to fake it down for a few polling cycles) fake the key up.
Follow the examples posted elsewhere and add the "GetDeviceState()" method to it.
Now it gets a bit more complicated if the application uses DirectInput for both keyboard and mouse inputs.
In your "CreateDeviceEx()" you will want to track the the decives (GUID_SysKeyboard, GUID_SysMouse, etc.) as they are created, because later you will need to compare the interface pointer to the THIS pointer to determin what the "GetDeviceState()" (and other data related calls too) is being polled for. That's the way I did it anyhow.
Possibly you can just observe if cbData == 256 then you know it's intended for a keyboard poll.
Depending on what you are doing, if you need a more general hooking system then you will probably need a fair amount of logic to figure out how the application is using the inputs at run time.
If you can get away with a more specific hook then you can do a bit more reversing of the application and code something more simple.
Hope that helps.
Thanks for the comment, Sirmabus. Is there any chance that you could create a very little DirectInput hooking demo? It seems that quite a lot of people try to do DirectInput hooking recently. They'd surely benefit quite greatly. Of course you don't need to. Just if you like. No problem if you don't.
Ok here is what I have.
When working with DirectInput hooks I realized an ideal setup would be a real robust hook system that could hook up to IDirectInput8 (current as of DirectX 9).
Hooking LoadLibrary()/LoadLibraryEx() and looking at the “DirectInputCreate()”, “dwVersion” parameter to determine what version the application is using.
Then track “SetEventNotification()”, etc., to determine if the application is using polled or event driven input, etc.
Would be nice, but would take some time to develop!
This is just a start and I tested the keyboard hooking a little but just needed to fake left mouse clicks for now.
Note: my C++ code here is not heavily object oriented but I like to use a lot of enums, namespace, etc., to organize and keep things clean.
First a typical “Trace” function I use to output to a debug console from a DLL.
Pick up “Debugview” from http://www.sysinternals.com/ntw2k/freew ... view.shtml
Header to add the DirectInput hook support:
DirectInput7Hook.cpp:
Also, in your “stdafx.h” add the necessary includes in there:
Made enums to the interfaces from “dinput.h”. Easier to read and less error prone then using hard coded numbers for them.
The access function “InjectHook()” just needs to hook “DirectInputCreateEx()” so we can grab the method “CreateDeviceEx()”. Again a more robust system needs also DirectInputCreate() and more methods like “CreateDevice()”.
It turns out my “test” application just uses these..
Just a function to help output the action:
Here we’ve hooked “CreateDeviceEx()” to intercept the interfaces for the basic mouse and keyboard.
These work, but are commented out for now. One might want to hook “SetCooperativeLevel()” to force a more “cooperative” mode, etc.
A quick hack to fake left mouse clicks:
In the particular application I was “testing” on, it was polling the inputs.
Most games appear to poll the inputs easy enough.
This hook, “GetDeviceState()”, is where it all really happens, the previous stuff was mostly setup.
Now the game is polling inputs every game loop cycle or so, and this hook will fire off regularly.
To add filtering of key presses; by looking at the “IDirectInputDevice7::GetDeviceState()” documents you can see all you need to do is for a given key in the array to 0 (clearing the high bit).
To fake a key press set the high bit of the key index for as long as you need it down.
I.E:
Hope this helps.
EDIT: Link with "dxguid.lib" for the GUID defines.
When working with DirectInput hooks I realized an ideal setup would be a real robust hook system that could hook up to IDirectInput8 (current as of DirectX 9).
Hooking LoadLibrary()/LoadLibraryEx() and looking at the “DirectInputCreate()”, “dwVersion” parameter to determine what version the application is using.
Then track “SetEventNotification()”, etc., to determine if the application is using polled or event driven input, etc.
Would be nice, but would take some time to develop!
This is just a start and I tested the keyboard hooking a little but just needed to fake left mouse clicks for now.
Note: my C++ code here is not heavily object oriented but I like to use a lot of enums, namespace, etc., to organize and keep things clean.
First a typical “Trace” function I use to output to a debug console from a DLL.
Pick up “Debugview” from http://www.sysinternals.com/ntw2k/freew ... view.shtml
Code: Select all
// ****************************************************************************
// Func: Trace()
// Desc: Output text to debugger console
//
// ****************************************************************************
void Trace(const char *format, ...)
{
if(format)
{
// Format string
va_list vl;
char str[256];
va_start(vl, format);
_vsntprintf(str, sizeof(str), format, vl);
va_end(vl);
// Output it to debugger
OutputDebugString(str);
}
}
Header to add the DirectInput hook support:
Code: Select all
// ****************************************************************************
// File: DirectInput7Hook.h
// Desc: DirectX DirectInput capture module
//
// ****************************************************************************
namespace DINPUT7HOOK
{
BOOL InjectHook(void);
void FakeLeftClick(void);
};
DirectInput7Hook.cpp:
Code: Select all
// ****************************************************************************
// File: DirectInputCapture.cpp
// Desc: DirectX DirectInput capture module using MadCodeHook
// Currently only supports "IID_IDirectInput7A" and standard input
// interfaces: "keyboard" and "mouse".
//
// ****************************************************************************
// In "stdafx.h" include <dinput.h> for direct input defines
#include "stdafx.h"
#include "DirectInput7Hook.h"
// IDirectInput7 interface method indexes
enum eIDI7METHOD
{
eIDI7M_QueryInterface,
eIDI7M_AddRef,
eIDI7M_Release,
eIDI7M_CreateDevice,
eIDI7M_EnumDevices,
eIDI7M_GetDeviceStatus,
eIDI7M_RunControlPanel,
eIDI7M_Initialize,
eIDI7M_FindDevice,
eIDI7M_CreateDeviceEx
};
Also, in your “stdafx.h” add the necessary includes in there:
Code: Select all
#include <dinput.h>
namespace MADCODEHOOK
{
#include "..\..\madCodeHook\madCHook.h"
};
Made enums to the interfaces from “dinput.h”. Easier to read and less error prone then using hard coded numbers for them.
Code: Select all
// Method hook indexes
enum eIDI7MHOOK
{
eMH_CreateDeviceEx,
// ** Add more method ID’s here, and fill in aMethodHook[] down below **
eMH_COUNT
};
// IDirectInput7 method hook container
struct tIDI7METHODHOOK
{
eIDI7METHOD eMethod;
PVOID pHook;
PVOID pNext;
};
// DirectInput device interface methods
// Note: The interface functions/addresses are the same for both keyboard and mouse
enum eDIDMETHOD
{
eDIDM_QueryInterface,
eDIDM_AddRef,
eDIDM_Release,
eDIDM_GetCapabilities,
eDIDM_EnumObjects,
eDIDM_GetProperty,
eDIDM_SetProperty,
eDIDM_Acquire,
eDIDM_Unacquire,
eDIDM_GetDeviceState,
eDIDM_GetDeviceData,
eDIDM_SetDataFormat,
eDIDM_SetEventNotification,
eDIDM_SetCooperativeLevel,
eDIDM_GetObjectInfo,
eDIDM_GetDeviceInfo,
eDIDM_RunControlPanel,
eDIDM_Initialize,
eDIDM_COUNT
};
// Direct input devices
enum eDEVICE
{
eDID_KEYBOARD,
eDID_MOUSE,
eDID_COUNT
};
// Method hook indexes
enum eDIDMHOOK
{
//eDIDMH_SetCooperativeLevel,
//eDIDMH_Acquire,
eDIDMH_GetDeviceState,
eDIDMH_COUNT
};
// DirectInput device interface hook container
struct tDIDMETHODHOOK
{
eDIDMETHOD eMethod;
PVOID pHook;
PVOID pNext;
};
// **** Function prototypes ****
// IDirectInput7 method hooks
HRESULT WINAPI CreateDevice_Hook(LPVOID pSelf, REFGUID rguid, LPDIRECTINPUTDEVICE *lplpDirectInputDevice, LPUNKNOWN pUnkOuter);
HRESULT WINAPI CreateDeviceEx_Hook(LPVOID pSelf, REFGUID rguid, REFIID riid, LPVOID *pvOut, LPUNKNOWN pUnkOuter);
// Device method hooks
HRESULT WINAPI SetCooperativeLevel_Hook(LPVOID pSelf, HWND hwnd, DWORD dwFlags);
HRESULT WINAPI Acquire_Hook(LPVOID pSelf);
HRESULT WINAPI GetDeviceState_Hook(LPVOID pSelf, DWORD cbData, LPVOID lpvData);
// **** Data ****
HRESULT (WINAPI *DirectInputCreateEx_Next)(HINSTANCE hinst, DWORD dwVersion, REFIID riidltf, LPVOID *ppvOut, LPUNKNOWN punkOuter) = NULL;
LPDIRECTINPUTDEVICE7 apDeviceInterface[eDID_COUNT] = {NULL};
// IDirectInput7 method hooks
tIDI7METHODHOOK aMethodHook[eMH_COUNT] =
{
{eIDI7M_CreateDeviceEx, CreateDeviceEx_Hook, NULL},
};
// Device method hooks.
tDIDMETHODHOOK aDeviceMethodHook[eDIDMH_COUNT] =
{
//{eDIDM_SetCooperativeLevel, SetCooperativeLevel_Hook, NULL},
//{eDIDM_Acquire, Acquire_Hook, NULL},
{eDIDM_GetDeviceState, GetDeviceState_Hook, NULL}
};
// Get interface method for index
#define GETINTERFACEMETHOD(pInterface, uIndex) ((PVOID) ((DWORD *) *((DWORD * ) (pInterface)))[uIndex])
The access function “InjectHook()” just needs to hook “DirectInputCreateEx()” so we can grab the method “CreateDeviceEx()”. Again a more robust system needs also DirectInputCreate() and more methods like “CreateDevice()”.
It turns out my “test” application just uses these..
Code: Select all
// The DirectInputCreateEx() API hook
static HRESULT WINAPI DirectInputCreateEx_Hook(HINSTANCE hinst, DWORD dwVersion, REFIID riidltf, LPVOID *ppvOut, LPUNKNOWN punkOuter)
{
HRESULT hResult = DirectInputCreateEx_Next(hinst, dwVersion, riidltf, ppvOut, punkOuter);
if(hResult == DI_OK)
{
// Hook methods
for(int i = 0; i < eMH_COUNT; i++)
{
if(!aMethodHook[i].pNext)
{
//Trace("DINPUT7HOOK: ADDR[%d]: 0x%X\n", i, GETINTERFACEMETHOD(*ppvOut, i));
if(!MADCODEHOOK::HookCode(GETINTERFACEMETHOD(*ppvOut, aMethodHook[i].eMethod), aMethodHook[i].pHook, &aMethodHook[i].pNext))
{
Trace("DINPUT7HOOK: HookCode() failed for method: %d!\n", aMethodHook[i].eMethod);
}
}
}
}
Trace("DINPUT7HOOK: DirectInputCreateEx() Result: 0x%X\n", hResult);
return(hResult);
}
// ****************************************************************************
// Func: InjectHook()
// Desc: Inject DirectInput hook into current application
//
// ****************************************************************************
BOOL DINPUT7HOOK::InjectHook(void)
{
BOOL bResult = MADCODEHOOK::HookAPI("DINPUT.DLL", "DirectInputCreateEx", DirectInputCreateEx_Hook, (PVOID *) &DirectInputCreateEx_Next);
Trace("DINPUT7HOOK: InjectHook() Result: %d\n", (bResult & 1));
return(bResult);
}
Code: Select all
// ****************************************************************************
// Func: GetInputDeviceName()
// Desc: Return a string name for a input device GUID
//
// ****************************************************************************
const char *GetInputDeviceName(REFGUID rguid)
{
struct tGUID_NAME
{
const GUID *pGuid;
const char *szName;
} aInputGuids[] =
{
{&GUID_SysKeyboard, "GUID_SysKeyboard"},
{&GUID_SysMouse, "GUID_SysMouse"},
{&GUID_Joystick, "GUID_Joystick"},
{&GUID_SysMouseEm, "GUID_SysMouseEm"},
{&GUID_SysMouseEm2, "GUID_SysMouseEm2"},
{&GUID_SysKeyboardEm, "GUID_SysKeyboardEm"},
{&GUID_SysKeyboardEm2, "GUID_SysKeyboardEm2"}
};
for(int i = 0; i < (sizeof(aInputGuids) / sizeof(tGUID_NAME)); i++)
{
if(memcmp(&rguid, aInputGuids[i].pGuid, sizeof(GUID)) == 0)
{
return(aInputGuids[i].szName);
}
}
return("Unknown");
}
Code: Select all
// ====== Method Hooks ======
HRESULT WINAPI CreateDeviceEx_Hook(LPVOID pSelf, REFGUID rguid, REFIID riid, LPVOID *pvOut, LPUNKNOWN pUnkOuter)
{
HRESULT hResult = ((HRESULT (WINAPI *)(LPVOID, REFGUID, REFIID, LPVOID *, LPUNKNOWN)) aMethodHook[eMH_CreateDeviceEx].pNext)(pSelf, rguid, riid, pvOut, pUnkOuter);
if(hResult == DI_OK)
{
// We only support the 7A interface
if(memcmp(&riid, &IID_IDirectInputDevice7, sizeof(GUID)) == 0)
{
Trace("DINPUT7HOOK: CreateDeviceEx() D: %s, IA: 0x%X\n", GetInputDeviceName(rguid), *pvOut);
// Keyboard
if(memcmp(&rguid, &GUID_SysKeyboard, sizeof(GUID)) == 0)
{
if(!apDeviceInterface[eDID_KEYBOARD])
{
apDeviceInterface[eDID_KEYBOARD] = (LPDIRECTINPUTDEVICE7) *pvOut;
if(!apDeviceInterface[eDID_MOUSE])
{
for(int i = 0; i < eDIDMH_COUNT; i++)
{
if(!aDeviceMethodHook[i].pNext)
{
Trace("DK: 0x%X\n", GETINTERFACEMETHOD(*pvOut, aDeviceMethodHook[i].eMethod));
if(!MADCODEHOOK::HookCode(GETINTERFACEMETHOD(*pvOut, aDeviceMethodHook[i].eMethod), aDeviceMethodHook[i].pHook, &aDeviceMethodHook[i].pNext))
{
Trace("DINPUT7HOOK: CreateDeviceEx() failed for method: %d!\n", aDeviceMethodHook[i].eMethod);
}
}
}
}
}
}
// Mouse?
if(memcmp(&rguid, &GUID_SysMouse, sizeof(GUID)) == 0)
{
if(!apDeviceInterface[eDID_MOUSE])
{
apDeviceInterface[eDID_MOUSE] = (LPDIRECTINPUTDEVICE7) *pvOut;
if(!apDeviceInterface[eDID_KEYBOARD])
{
for(int i = 0; i < eDIDMH_COUNT; i++)
{
if(!aDeviceMethodHook[i].pNext)
{
Trace("DK: 0x%X\n", GETINTERFACEMETHOD(*pvOut, aDeviceMethodHook[i].eMethod));
if(!MADCODEHOOK::HookCode(GETINTERFACEMETHOD(*pvOut, aDeviceMethodHook[i].eMethod), aDeviceMethodHook[i].pHook, &aDeviceMethodHook[i].pNext))
{
Trace("DINPUT7HOOK: CreateDeviceEx() failed for method: %d!\n", aDeviceMethodHook[i].eMethod);
}
}
}
}
}
}
}
else
Trace("DINPUT7HOOK: CreateDeviceEx() unsupported interface!\n");
}
return(hResult);
}
Code: Select all
#if 0
HRESULT WINAPI SetCooperativeLevel_Hook(LPVOID pSelf, HWND hwnd, DWORD dwFlags)
{
HRESULT hResult = ((HRESULT (WINAPI *)(LPVOID, HWND, DWORD)) aDeviceMethodHook[eDIDMH_SetCooperativeLevel].pNext)(pSelf, hwnd, dwFlags);
Trace("DINPUT7HOOK: SetCooperativeLevel() F: 0x%X, SA: 0x%X\n", dwFlags, pSelf);
return(hResult);
}
HRESULT WINAPI Acquire_Hook(LPVOID pSelf)
{
HRESULT hResult = ((HRESULT (WINAPI *)(LPVOID)) aDeviceMethodHook[eDIDMH_Acquire].pNext)(pSelf);
Trace("DINPUT7HOOK: Acquire() SA: 0x%X\n", pSelf);
return(hResult);
}
#endif
A quick hack to fake left mouse clicks:
Code: Select all
int iFakeLeftClicks = 0;
void DINPUT7HOOK::FakeLeftClick(void)
{
++iFakeLeftClicks;
}
Most games appear to poll the inputs easy enough.
This hook, “GetDeviceState()”, is where it all really happens, the previous stuff was mostly setup.
Now the game is polling inputs every game loop cycle or so, and this hook will fire off regularly.
Code: Select all
HRESULT WINAPI GetDeviceState_Hook(LPVOID pSelf, DWORD cbData, LPVOID lpvData)
{
HRESULT hResult = ((HRESULT (WINAPI *)(LPVOID, DWORD, LPVOID)) aDeviceMethodHook[eDIDMH_GetDeviceState].pNext)(pSelf, cbData, lpvData);
if(pSelf == apDeviceInterface[eDID_MOUSE])
{
//Trace("DINPUT7HOOK: GetDeviceState() MOUSE tSize: %d\n", cbData);
if(iFakeLeftClicks > 0)
{
Trace("DINPUT7HOOK: GetDeviceState() MOUSE fake left click\n");
((LPDIMOUSESTATE) lpvData)->rgbButtons[0] = 0x80;
--iFakeLeftClicks;
}
}
else
if(pSelf == apDeviceInterface[eDID_KEYBOARD])
{
//Trace("DINPUT7HOOK: GetDeviceState() KEYBOARD Buffer: 0x%X, Count: %d\n", lpvData, cbData);
}
else
Trace("DINPUT7HOOK: GetDeviceState() unknown! SA: 0x%X\n", pSelf);
return(hResult);
}
To fake a key press set the high bit of the key index for as long as you need it down.
I.E:
Code: Select all
((BYTE *) lpvData)[DIK_A] = 0x80; // Fake press the ‘A’ key
Hope this helps.
EDIT: Link with "dxguid.lib" for the GUID defines.