Question on DirectInput hooking

c++ / delphi package - dll injection and api hooking
Post Reply
Astaelan
Posts: 22
Joined: Wed Sep 22, 2004 7:08 pm

Question on DirectInput hooking

Post by Astaelan »

Hello Madshi, kudo's on this great library you've come up with. I've read a bunch of the documentation and I've been, I dare say, daunted by it's extensive capabilities.

I have recently been trying to find a way to hook the input of a game when it's running. I can give you more details later on the game, but suffice to say for now that I THINK it uses DirectInput. This conclusion is due to trying to inject windows messages using keybd_event and mouse_event API calls with no success. Works great on the desktop and any program with a message pump, but not for the game in question. After a long haul, my research brought me to this madCodeHook, in relation to someone else who tried to hook DINPUT.DLL. I'm pretty new to the whole hooking and DLL injecting, but I don't think what I want to do is all that difficult for someone with your experience. I've looked through some of the demo's and haven't found anything in relation to DirectInput, so I appologize if I missed a perfect example somewhere.

Basically, here's the bottom line. To inject keyboard and mouse data into DirectInput, so the game will process it as if the user had pressed the mouse or keyboard themselves. That said, my thought process leads me to believe if I can hook when this input occurs in the game, could I not also inject the my own input somehow using the hook?

I realize this is kind of a newbish question. I would gladly take the time to sit down and learn the library inside and out if I can just find out it's possible to do what I want to. If a few snippets of code came along, I'd be even happier :)

The one link I found discussing DirectInput hooking with madCodeHook was here:
http://www.experts-exchange.com/Program ... 92502.html

I'd be much obliged if Madshi or anyone could elaborate on this matter a bit more to discuss how you can actually inject keyboard and mouse input, as opposed to just hooking CreateDevice, as I think was the point in that post. If I am correct, I would have to hook CreateDevice as well and keep a pointer to the device created by the game, am I at least on the right track?

Much obliged for any help anyone could send my way, I've been looking at things like writing VxD's and the use of DeviceIoControl for IO port manipulation. I'd love a slightly higher level to work with :)

Thanks again.
madshi
Site Admin
Posts: 10753
Joined: Sun Mar 21, 2004 5:25 pm

Post by madshi »

I can imagine that hooking and manipulating GetDeviceStateMouse/Keyboard could work, though I'm not sure about it. After having a very quick look at the DirectInput documentation I think that games using DirectInput don't get messages about key/mouse events, but instead poll for such events. I could be wrong about that, though. If I'm right, you probably can't directly feed events into the DirectInput queue, instead you'll probably have to manipulate GetDeviceStateMouse/Keyboard.

Writing a little driver might be the cleaner solution, though. But of course that's your decision... :wink:
Astaelan
Posts: 22
Joined: Wed Sep 22, 2004 7:08 pm

Post by Astaelan »

Hey Madshi, thanks a lot for getting back to me so quickly, I'd almost given up on posting in forums for lack of replies. Much obliged for the response.

On the topic now, you are 100 percent correct. Games which feed only off DirectInput typically have their message pump's ignore the WM_ messages generated after DirectInput is done with it.
This is all well and good, except we're talking the wrong direction. What I want to know is not whether I can get the current keyboard state, but rather SET it. More specifically, I want to be able to convince DirectInput that the right mouse button has been clicked.
My thought process lead me to believe that if we could hook the REAL DirectInput calls for keyboard and mouse, could you not "generate" the same events, and call the next hook to pass it on?

Bare with my psuedo example here. Let's say you hook the device creation of the DirectInput devices. This gives you pDIIKeyboard and pDIIMouse (pDII standing for pointer to DirectInput Interface). Now, what I'm wondering from yourself and the other experts, is if there is a way to manipulate the keyboard and mouse state once you have the DirectInput device? My assumption is that you can get the device by hooking CreateDevice (index 3?) of the DINPUT.DLL interface. If this assumption is correct, how would you go about injecting new data into the device?

On the same topic, but a different approach, I was wondering if you happen to know if SetKeyboardState generated WM_ event messages or if it works with the lower level input layer?
I have not found an equivilent to SetKeyboardState for the mouse, but maybe such exists (SetMouseState produced no reasonable results to look at).

The driver solution is by no means easier, even if it's cleaner. I've all but lost myself in the world of assembly trying to figure out how to write a Miniport driver. I'm sure it can do what I want, but it also means a fairly unwieldy approach requiring the installation of virtual drivers to support the injection.
I'd really like to know if anyone has had any success with hooking DINPUT.DLL, and if they have any ideas on how you can inject new input into the device so games will notice it on their next loop checking input (as most DirectInput games do).

Thanks! Hope to hear from you again soon!
Astaelan
Posts: 22
Joined: Wed Sep 22, 2004 7:08 pm

Post by Astaelan »

Sorry, I'm back again so soon. I just had another thought after I read your post again a little more carefully...

Do you mean I should hook into the method GetDeviceStateMouse/Keyboard, and when the game calls this every loop, it's going to be interrupted by my hook, thereby effectively letting me return whatever I want to be set?

It never occured to me to actually hook the Get method... I can return whatever I want then. Where could I go to look into what methods DirectInput has to hook so I know what to hook with madHookCode? You said you found GetDeviceStateMouse/Keyboard, my next stop is google to search for these... hope you were literal :)

Eagerly waiting your next tidbit of information :)
Astaelan
Posts: 22
Joined: Wed Sep 22, 2004 7:08 pm

Post by Astaelan »

And I'm back yet again! Still waiting on replies, but I went ahead and started playing around with the hooking stuff. I'm a bit confused how it works however. I will paste my code at the end, but I'll explain it now. I start the application, at which point it calls HookAPI on DINPUT.DLL, for GetDeviceState, which I found is defined as follows:

Code: Select all

HRESULT GetDeviceState(      
    DWORD cbData,
    LPVOID lpvData
);
And the parameters are as follows:

Code: Select all

cbData
    Size of the buffer in the lpvData parameter, in bytes. 
lpvData
    Address of a structure that receives the current state of the device. The format of the data is established by a prior call to the IDirectInputDevice8::SetDataFormat method.
So far so good? Seems like this should allow us to hook into all DirectInput device states.

So, what I did was write a program with the following code. What happens however, if I put a break point in the HookProc, it's never called. Am I doing something wrong here?
I don't quite understand how HookAPI is used. Once it's called, it seems to return right away with no known directinput applications open, though it could be possible.
What can be assumed by the return of TRUE by HookAPI?

Furthermore, what exactly is the difference between HookAPI and HookCode, seems like you need to use both, but the HookCode appears in the examples to be called from the HookProc of the HookAPI call. I'm a bit confused here, could you help clear it up a bit? Here's the code.

Code: Select all

#include <stdio.h>
#include <windows.h>
#include "madCHook.h"

FILE *pLog = NULL;

HRESULT (*GetDeviceStateNextHook)(DWORD cbData, LPVOID lpvData);

void Log(const char *pLogOut)
{
	fprintf(pLog, "%s\n", pLogOut);
	fflush(pLog);
}

HRESULT GetDeviceStateHookProc(DWORD cbData, LPVOID lpvData)
{
    HRESULT res = GetDeviceStateNextHook(cbData, lpvData);
    Log("InHook!");
    return res;
}

int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR     lpCmdLine,
                   int       nCmdShow)
{
    if((pLog = fopen("hooker.log", "w")) != NULL)
	{
		Log("Log Opened");
		BOOL bResult = HookAPI("dinput.dll", "GetDeviceState", GetDeviceStateHookProc, (PVOID*)&GetDeviceStateNextHook);
		char sBuf[256];
		sprintf(sBuf, "Hooking %s", (bResult ? "Succeeded" : "Failed"));
		Log(sBuf);
		int iHookCount = 100;
		while(iHookCount > 0)
		{
			Sleep(1000);
			iHookCount--;
		}
        UnhookCode((PVOID*) &GetDeviceStateNextHook);
		Log("Unhooked");
		fclose(pLog);
	}
	return true;
}
Basically, the hookproc never gets called, but HookAPI returns TRUE. Am I doing something wrong? As soon as I run something like, say, EverQuest, should I not immediately start receiving some calls to the hook? Or is my method of sleeping incorrect?

Be my saviour, please. I'd love to get this working, and save my already pain-wrought wrist some hours of clicking. :)

I also see that the examples are as DLL's, probably for the use of injecting them into the program. Is that required in this case, is that where I am going wrong? I'm not quite sure where the line is drawn that you need to inject the DLL as opposed to being able to hook it in an external EXE. Appreciate any help you can provide.
madshi
Site Admin
Posts: 10753
Joined: Sun Mar 21, 2004 5:25 pm

Post by madshi »

Using HookAPI/Code only affects the current process. So you can't hook external processes by putting HookAPI/Code calls in your own exe. That's why you need to create a little dll which does the hooking work and then inject that dll into the target process.

The difference between HookAPI and HookCode is that HookAPI can hook exported APIs only. You can use the freeware tool "PEBrowse Pro" from http://www.smidgeonsoft.com/ to see which dll exports which APIs. You can also hook functions which are not exported. For that to work you need to know the function address (entry point). Then you need to feed this into HookCode.

Check out what dinput.dll exports. You'll not find GetDeviceState there. Instead you'll find DirectInputCreateA/W and DirectInputCreateEx. You'll need to hook those (or the one which the target application calls). Going from there your hook callback function will get access to the IDirectInput interface the application asked for. Then you can hook into the interface methods.
Do you mean I should hook into the method GetDeviceStateMouse/Keyboard, and when the game calls this every loop, it's going to be interrupted by my hook, thereby effectively letting me return whatever I want to be set?
Yes.
I don't quite understand how HookAPI is used. Once it's called, it seems to return right away with no known directinput applications open, though it could be possible.
What can be assumed by the return of TRUE by HookAPI?
Calling HookAPI even works if DirectInput.dll is not loaded in the current process yet. As soon as the application loads DirectInput.dll, madCodeHook will then install the requested API hook. But this all affects the current process only (see above).

I recommend using the HookDirect3D demo as the starting point and change it so that it fits your needs. You can see everything there: How to hook the interface creation API (like DirectDrawCreate or DirectInputCreate) and then how to hook the interface methods.
Astaelan
Posts: 22
Joined: Wed Sep 22, 2004 7:08 pm

Post by Astaelan »

Thanks for the information Madshi. After I last posted last night, I went on to look at the documentation and demo's a bit closer. I did in fact use the Direct3D example to further my understanding. I think I understand how HookAPI and HookCode work now.

At first, I thought you should be able to hook GetDeviceState directly, without having to hook into DirectInputCreateA and then CreateDevice (the third index of the interface I think, right?)

Anyway, after some playing around and testing, I ran into some problems. So, back to square one, I will post what I have been attempting to do now.

Okay, so, starting from scratch, I created an MFC EXE. Dialog based application, here is what I put into the OnCreate message handler:

Code: Select all

int CPTHookerDlg::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CDialog::OnCreate(lpCreateStruct) == -1)
		return -1;

	int iReturn = 0;
    if(!InjectLibrary(CURRENT_USER|CURRENT_PROCESS, "PTHooker.dll"))
	{
		MessageBox("Failed to inject PTHooker.dll");
		iReturn = -1;
	}
	return iReturn;
}
Okay, so my thought process here is that when OnCreate is called, it should attempt to inject the PTHooker.dll into every application running in association to the current user (which should include any games about to be run after this program runs). Okay, so here's my first problem. InjectLibrary is always returning false, and thus failing to inject PTHooker.dll. Is there any reason this should be occuring? It is my understanding that when InjectLibrary is called, windows is creating a hook somewhere so that whenever any program is now opened, PTHooker.dll will automatically be loaded like a LoadLibrary call on the first line of the program's source.
So, theoretically, by running this, it should execute immediately, and exit, leaving the PTHooker.dll in memory, waiting to hook into the next program that runs. Am I correct in this assumption?

Ok, so, first problem identified. What am I doing wrong that is failing to hook this PTHooker.dll? Here is the source code for the PTHooker.dll

Code: Select all


#include <windows.h>
#include "../madCHook.h"

HRESULT (WINAPI   *DirectInputCreateANext)(HINSTANCE hInst, DWORD dwVersion, LPVOID pDI, LPVOID pUnknown);


BOOL HookDirectInput(void)
{
  return HookAPI("dinput.dll", "DirectInputCreateA", DirectInputCreateACallback, (PVOID*) &DirectInputCreateANext);
}


BOOL WINAPI DllMain(HANDLE hModule, DWORD fdwReason, LPVOID lpReserved)
{
    if (fdwReason == DLL_PROCESS_ATTACH)
    {
        if(HookDirectInput())
		{
			MessageBox(NULL, "Woohoo!", "", MB_OK);
		}
    }
    else if (fdwReason == DLL_PROCESS_DETACH)
      UnhookDirectInput();

    return true;
}

Alright, the first problem I ran into writing the DLL was that many of the type's used by the prototypes for DirectInput methods require types from DirectInput. Is there a convenient way around this? In my examples I converted most types to generic windows types, hoping that, for example, PVOID (void *) will be sufficient to cast the arguments to, from things like LPDIRECTINPUT or whatever other types are defined. So first question, will the argument list I used for DirectInputCreateA suffice?

Now, according to my educated guess, when InjectLibrary occurs, windows creates this hook for during the creation of processes, allowing it to inject the DLL into future processes just before/while they are starting. Okay, so what's wrong with my DLL that I cannot inject it into anything? I've run the PEBrowse Pro, and you're entirely correct. Only 3 potential exports are of interest:

DirectInputCreateA, DirectInputCreateW, and DirectInputCreateEx, what exactly would the differences be between these?

Assuming I hook one, or all of them, I should be able to obtain the pointer to the generic DirectInput device once the game starts up and DINPUT.DLL calls DirectInputCreate* right?

My first goal is simply just to get this DLL injected into processes, so that once I run the game, the callback will be called and I can obtain the DirectInput pointer. Once I have that, I'll consider it a small first successful step.

Thanks for the tip on the PEBrowse, I wasn't familiar with it. Once I have the directinput device, how did you find out what order the interface methods come in? That is, how do you know Query comes first, AddRef second, and so on? I found a post that said CreateDevice was index "3", the fourth method (0 inclusive). My guess was that I would then do something like this:

Code: Select all

HRESULT (WINAPI   *CreateDeviceNext) (REFGUID guid, LPVOID pDI, LPVOID pUnknown);


  HRESULT result = DirectInputCreateANext(hInst, dwVersion, pDI, pUnknown);
  HookCode(GetInterfaceMethod(pDI, 3), CreateDeviceCallback, (PVOID*) &CreateDeviceNext);
  return result;

Of course, I cannot tell if this code is going to work because the problem still exists way earlier in that I cannot seem to inject the DLL successfully. Is it because I am using CURRENT_USER|CURRENT_PROCESS for the injectlibrary call? Is that an incorrect combination of flags, should I just pass to CURRENT_SESSION? My thought was that I didn't want my DLL hooked into EVERY process, just ones run by the user that have DINPUT.DLL initialized, and not the process injecting the DLL either. So those flags seemed appropriate to my wants.


By the way, I want to take a minute to stop here and say thank you for helping me Madshi. Your support of your product is outstanding. And I will state for the record that if I can get this problem resolved, I will happily purchase a commercial liscense just to support the product, even though I have no commercial intentions.

Primarily, I am thinking, with your permission of course, if this works then it could be incorporated into the OpenSource project called AutoHotKey, at www.autohotkey.com ... Check it out, it's actually a very promising product. I've been in contact with the developer of it. I've passed on my research, pointed him here, and he's informed me that his current to-do list is about 4 weeks or so of work, but he might be able to look into hooking DirectInput then, and perhaps find more uses since madCodeHook is so flexible to hook virtually anything. If I can get some working C++ code to show it's worth the diversion of time, he may be willing to divert to incorporating it sooner.

I thank you again for you help, you've been outstanding with pointing me in the right direction and not giving me the outright answers. I appreciate that, it's a great learning process. I hope you can keep pointing me in the right directions :)
madshi
Site Admin
Posts: 10753
Joined: Sun Mar 21, 2004 5:25 pm

Post by madshi »

Astaelan wrote:Okay, so my thought process here is that when OnCreate is called, it should attempt to inject the PTHooker.dll into every application running in association to the current user (which should include any games about to be run after this program runs). Okay, so here's my first problem. InjectLibrary is always returning false, and thus failing to inject PTHooker.dll. Is there any reason this should be occuring? It is my understanding that when InjectLibrary is called, windows is creating a hook somewhere so that whenever any program is now opened, PTHooker.dll will automatically be loaded like a LoadLibrary call on the first line of the program's source.
So, theoretically, by running this, it should execute immediately, and exit, leaving the PTHooker.dll in memory, waiting to hook into the next program that runs. Am I correct in this assumption?
Probably it doesn't work, because you've linked to the dynamic lib file and didn't copy madCHook.dll to the system folder. When linking to the madCodeHook dynamic lib file, your hook dll links to madCHook.dll. That means nobody can load your dll without also being able to locate and load madCHook.dll. But all the other processes (current and future) don't know where madCHook.dll is located. So that's probably why InjectLibrary failed. Just copy that madCHook.dll to the system folder, then InjectLibrary should work.
Astaelan wrote:

Code: Select all

    else if (fdwReason == DLL_PROCESS_DETACH)
      UnhookDirectInput();
You don't need to unhook anything. madCodeHook automatically does that for you - and it does it in the best possible moment.
Astaelan wrote:Alright, the first problem I ran into writing the DLL was that many of the type's used by the prototypes for DirectInput methods require types from DirectInput. Is there a convenient way around this? In my examples I converted most types to generic windows types, hoping that, for example, PVOID (void *) will be sufficient to cast the arguments to, from things like LPDIRECTINPUT or whatever other types are defined. So first question, will the argument list I used for DirectInputCreateA suffice?
Without having checked the documentation I'd guess that it should probably work that way.
Astaelan wrote:Now, according to my educated guess, when InjectLibrary occurs, windows creates this hook for during the creation of processes, allowing it to inject the DLL into future processes just before/while they are starting.
madCodeHook does all the work, not Windows. But apart from that you're right.
Astaelan wrote:DirectInputCreateA, DirectInputCreateW, and DirectInputCreateEx, what exactly would the differences be between these?
One is ansi, one is wide and one offers extended functionality. But I'm no expert in DirectInput, so I can't help much with any DirectInput related questions.
Astaelan wrote:Assuming I hook one, or all of them, I should be able to obtain the pointer to the generic DirectInput device once the game starts up and DINPUT.DLL calls DirectInputCreate* right?
Right.
Astaelan wrote:how did you find out what order the interface methods come in? That is, how do you know Query comes first, AddRef second, and so on?
The methods get their index simply in the order in which they are listed in the headers. The base interface class is IUnknown which only implements QueryInterface, AddRef and Release. So those get the indexes 0-2.
Astaelan wrote:

Code: Select all

HRESULT (WINAPI   *CreateDeviceNext) (REFGUID guid, LPVOID pDI, LPVOID pUnknown);


  HRESULT result = DirectInputCreateANext(hInst, dwVersion, pDI, pUnknown);
  HookCode(GetInterfaceMethod(pDI, 3), CreateDeviceCallback, (PVOID*) &CreateDeviceNext);
  return result;
If CreateDevice is the first method of IDirectInput, then index 3 is the right one. However, methods have an additional self parameter. So you can't use CreateDeviceCallback(REFGUID guid, ...), but instead you have to use CreateDeviceCallback(LPVOID Self, REFGUID guid, ...). Same with CreateDeviceNext.
Astaelan wrote:By the way, I want to take a minute to stop here and say thank you for helping me Madshi. Your support of your product is outstanding. And I will state for the record that if I can get this problem resolved, I will happily purchase a commercial liscense just to support the product, even though I have no commercial intentions.
Great - thanks!
Astaelan wrote:Primarily, I am thinking, with your permission of course, if this works then it could be incorporated into the OpenSource project called AutoHotKey, at www.autohotkey.com ... Check it out, it's actually a very promising product. I've been in contact with the developer of it. I've passed on my research, pointed him here, and he's informed me that his current to-do list is about 4 weeks or so of work, but he might be able to look into hooking DirectInput then, and perhaps find more uses since madCodeHook is so flexible to hook virtually anything. If I can get some working C++ code to show it's worth the diversion of time, he may be willing to divert to incorporating it sooner.
Well, if the integration doesn't violate my license agreement, I'd be happy about it. Of course it must be clear to anyone using AutoHotKey that madCodeHook is only free for non-commercial usage. If someone uses AutoHotKey for commercial purpose he'd have to buy a license to madCodeHook. That must be clear to everyone using AutoHotKey.

Perhaps the simplest solution would be to not distribute the madCodeHook files with AutoHotKey, but instead just add a download link to my madCollection installer.
Astaelan
Posts: 22
Joined: Wed Sep 22, 2004 7:08 pm

Post by Astaelan »

Okay, thanks again for the quick reply. I've been bouncing around forums for the last couple days like a madman, I really think we might get this figured out, you're a saviour :)

Okay, it was stupid of me to overlook the InjectLibrary issue. I copied the madCHook.dll into the system32 directory, however I'm still not having any luck with the injection.
Just to confirm, I removed the madCHook.dll from the EXE/DLL directory and the EXE still ran without a DLL missing error when calling InjectLibrary, so I assume the DLL is found, but InjectLibrary is still returning FALSE. I'm going to RTFM regarding whether InjectLibrary sets an error number when it returns false, but perhaps you could quickly suggest a way to get a reason why InjectLibrary is failing?

As for UnhookDirectInput, thanks for the tip, didn't know madHookCode was that magical, heh. Kudo's once again, another piece of code removed. However, I copied from the demo's this piece of DllMain, is it correct?

Code: Select all

BOOL WINAPI DllMain(HANDLE hModule, DWORD fdwReason, LPVOID lpReserved)
{
    if (fdwReason == DLL_PROCESS_ATTACH)
    {
        if(HookDirectInput())
		{
			MessageBox(NULL, "Woohoo!", "", MB_OK);
		}
    }

    return true;
}
As that seems to be the only reason to fail, none the less, DllMain always returns true... So does that maybe have anything to do with InjectLibrary always failing?

I also went ahead and added the self argument to the CreateDevice method, incase for some reason that was the problem. Again, no effect, InjectLibrary is still failing, but thank you for pointing that out, another dumb one I missed.


As far as AutoHotKey goes, I just checked again and it's under the GNU GPL, which is controversal as far as commercial products go, so I'm not sure if that conflicts with your liscense or not. Either way, I have my own non-commercial personal uses for madCodeHook. If it cannot be integrated into AutoHotKey due to liscense issues, no problem. I know AutoHotKey itself isn't sold, but whether people use AutoHotKey in commercial settings and write scripts they sell, I cannot say.

Anyway, back to this InjectLibrary issue, still not having any luck with it.
I am using Visual Studio .NET, and thus MSVC7 and an MFC EXE, and a standard windows DLL, and I noticed 1 piece of code is giving me a warning.

Code: Select all

PVOID GetInterfaceMethod(PVOID intf, DWORD methodIndex)
{
  return *(PVOID*)(*(DWORD*)intf + methodIndex * 4);
}
Gives me a warning:

Code: Select all

warning C4312: 'type cast' : conversion from 'unsigned long' to 'PVOID * ' of greater size
I don't think this is a big deal, but I figured it was worth mentioning.

As you said, all interfaces have 0-2 reserved, DirectInput's interface itself, was indeed index 3 for CreateDevice, which renders a DirectInputDevice interface. Checking the header, I found the following:

DirectInputDevice
[3] GetCapabilities
[4] EnumObjects
[5] GetProperty
[6] SetProperty
[7] Aquire
[8] Unaquire
[9] GetDeviceState **
[10] GetDeviceData
[11] SetDataFormat *
[12] SetEventNotification
[13] SetCooperationLevel
[14] GetObjectInfo
[15] GetDeviceInfo
[16] RunControlPanel
[17] Initialize

Okay, so of these methods. I think that 2 will be important to hook, perhaps only 1, I'm not sure yet. From my research, SetDataFormat is called before GetDeviceState, so that GetDeviceState returns a specific type of data, be it mouse, keyboard, or joystick. Would this be important you think? I think it's the only way to know what the next call to GetDeviceData is supposed to populate the parameters with... So I think I have to hook that, as well as GetDeviceState... Which means HookCode to both 9 and 11.

This seems theoretically easy enough now that you've cleared up a few questions, but the inability for InjectLibrary to work is stumping me. I have tried with the madCHook.dll in both system and system32 directory with no difference. I should perhaps mention I am running XP, and that this will only run under 2k/XP machines, so 9x compatibility isn't an issue.

Okay, so before I finish this post, I'll post a complete, up to date version of my code to look over. Once I get this running, and get my first Woohoo!, I'm going to move the MessageBox into the CreateDeviceCallback, and see if that works.

EXE

Code: Select all

int CPTHookerDlg::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CDialog::OnCreate(lpCreateStruct) == -1)
		return -1;

	int iReturn = 0;
    if(!InjectLibrary(CURRENT_USER|CURRENT_PROCESS, "PTHooker.dll"))
	{
		MessageBox("Failed to inject PTHooker.dll");
		iReturn = -1;
	}
	return iReturn;
}

void CPTHookerDlg::OnClose()
{
    UninjectLibrary(CURRENT_USER|CURRENT_PROCESS, "PTHookerLib.dll");
	CDialog::OnClose();
}
DLL

Code: Select all

#include <windows.h>
#include "../madCHook.h"

HRESULT (WINAPI   *DirectInputCreateANext)(HINSTANCE hInst, DWORD dwVersion, LPVOID pDI, LPVOID pUnknown);
HRESULT (WINAPI   *CreateDeviceNext) (LPVOID self, REFGUID guid, LPVOID pDI, LPVOID pUnknown);

PVOID GetInterfaceMethod(PVOID intf, DWORD methodIndex)
{
	return *(PVOID*)(*(DWORD*)intf + methodIndex * 4);
}

HRESULT CreateDeviceCallback(LPVOID self, GUID guid, LPVOID pDI, LPVOID pUnknown)
{
	HRESULT result = CreateDeviceNext(self, guid, pDI, pUnknown);
	return result;
}

HRESULT WINAPI DirectInputCreateACallback(HINSTANCE hInst, DWORD dwVersion, LPVOID pDI, LPVOID pUnknown)
{
	HRESULT result = DirectInputCreateANext(hInst, dwVersion, pDI, pUnknown);
	HookCode(GetInterfaceMethod(pDI, 3), CreateDeviceCallback, (PVOID*) &CreateDeviceNext);
	return result;
}

BOOL HookDirectInput(void)
{
	return HookAPI("dinput.dll", "DirectInputCreateA", DirectInputCreateACallback, (PVOID*) &DirectInputCreateANext);
}

BOOL WINAPI DllMain(HANDLE hModule, DWORD fdwReason, LPVOID lpReserved)
{
	if (fdwReason == DLL_PROCESS_ATTACH)
		if(HookDirectInput())
			MessageBox(NULL, "Woohoo!", "", MB_OK);

	return true;
}
Slowly but surely, I hope we can figure this out. I'd be happy to contribute some additional demo's and tutorials once I get this figured out, to give a little something back for your time helping me :)
madshi
Site Admin
Posts: 10753
Joined: Sun Mar 21, 2004 5:25 pm

Post by madshi »

I think the only thing missing right now is why InjectLibrary fails. Is that PTHooker.dll in the same folder as your exe? It must be (or in the system32 folder), otherwise InjectLibrary won't find the dll.

Also please do not build your projects in debug mode, build them in release mode instead. Debug mode is known to sometimes makes problems with hook dlls.
Astaelan
Posts: 22
Joined: Wed Sep 22, 2004 7:08 pm

Post by Astaelan »

I am such an idiot. Okay, well, I got it working now. At least, my computer spammed up with messagebox's and Woohoo's that won't stop, heh. I closed the program, but the hook doesn't seem to have been uninjected. I just screwed up on the message handler and it never uninjected, it's almost funny... but I fixed it now, no more popups.

Much thanks for all this help, I was happy when I realized what my problem was. Was 2 things actually. 1, I forgot to copy the latest compiled DLL with the latest changes to the EXE directory. The second issue was I missed the Lib part of PTHookerLib.dll in the InjectLibrary string. Subtle, simple, but had me running in circles.

On to the next piece of the puzzle, I'm overwhelmed that this works, and can't wait to actually see about the hook into the game :)

Once again, thank you SO much for your help and this product. Outstanding job, I will be back when I hit another roadblock :)
Astaelan
Posts: 22
Joined: Wed Sep 22, 2004 7:08 pm

Post by Astaelan »

Okay, I was playing around a bit. I hooked DirectInputCreateA, W, and Ex. No problems. I moved a MessageBox into the callback and neither Everquest nor Priston Tale were popping a message box on the callback.

This prompted me to check the game.exe for Priston Tale with PEBrowse, again, thanks for the tip. Upon checking it, I found that the game.exe only imports DDRAW.DLL and DSOUND.DLL as far as DirectX goes... Does this suggest that it's not using DirectInput then? And if not, how could it be getting it's input if not from window messages? (I've written a program to make mouse_event API calls, but the program seems to ignore them, the same way it ignores things like AutoHotKey.
When I opened up EQGAME.EXE, I see that it imports DINPUT8.DLL. It's possible that this DLL might be the one I need to hook for Priston Tale, but the game.exe for Priston Tale doesn't list ANY DINPUT anything...

What other possible methods are there that a game can obtain input? Is there any way I can use PEBrowse or some other tool to figure this out?

The list of DLL's that are imported as as follows for game.exe:

ADVAPI32.DLL, DDRAW.DLL, DSOUND.DLL, GDI32.DLL, HANDES.DLL (this is a DLL that comes with it, contains 2 exported functions, an EncryptFunc and DecryptFunc), IMM32.DLL, KERNEL32.DLL, OLEAUT32.DLL, SHELL32.DLL, SHLWAPI.DLL, USER32.DLL, WININET.DLL, WINMM.DLL, and WSOCK32.DLL...
I notice that in Kernel32.dll, it's importing LoadLibraryA, is it possible that DINPUT.DLL isn't contained in the imports, and is somehow loaded dynamically so we cannot see it in this list?
I'd like to know any ideas on where this game is feeding it's input from... Even if it means to hook a lower level API function if possible... Perhaps GetAsyncKeyState? Not sure how that would work...

I feel like I hit a new roadblock now, I'm not even sure how I can figure out where the input is being grabbed from now... Any ideas would be much appreciated.
Astaelan
Posts: 22
Joined: Wed Sep 22, 2004 7:08 pm

Post by Astaelan »

Woohoo, I'm back again, haha.

Okay, well, I hooked DirectInput8Create, and it triggered when everquest started, but as I somewhat suspected, it never happened for Priston Tale.

So does anyone know, from the list of DLL's I posted, how it is getting it's input? I'd be willing to post an entire dump from PEBrowse for the game.exe and related DLL's if that would help track it down?

What other ways could a windows game get input other than DirectInput or Windows Messages? This is really frustrating, I finally figured it out, could probably write a DirectInput injector now, but the game doesn't use it! :(

Help, anyone? :)
Astaelan
Posts: 22
Joined: Wed Sep 22, 2004 7:08 pm

Post by Astaelan »

Okay, and now for some hopefully good news. I was playing around with PEBrowse, what a wonderful toy, thanks again for that.

I decided to start opening any file I thought might be a DLL, even renamed. Though, after looking through them all, the one of major interest I found was the HanDes.dll.

What I found about this DLL was that it Imports a LOT of different methods from various DLL's. The one of interest to me, was user32.dll. I noticed in here, amung a horde of others, that GetKeyState is imported. This suggests, along with other things like SetWindowsHookEx and CallNextHookEx, that they must be hooking GetKeyState, or some other method from one of these DLL's. Furthermore, the only 2 methods that this DLL Exports, are a DecryptFunc and an EncryptFunc. These don't sound too pleasant...

I am curious, so I'm going to try and hook GetKeyState from user32.dll, and see what happens after I start the game... Oh boy, here comes a world of spam :)

Well, wish me luck. I could use any advice anyone has, so I'm going to post the user32.dll imports list in case anyone notices something other than GetKeyState that I should look into.

Code: Select all

HANDES.DLL - PEBrowse Professional
Imports for USER32.DLL

AdjustWindowRectEx
CallNextHookEx
CallWindowProcA
CharUpperA
CheckMenuItem
ClientToScreen
CopyRect
CreateWindowExA
DefWindowProcA
DestroyMenu
DestroyWindow
DispatchMessageA
DrawTextA
EnableMenuItem
EnableWindow
GetCapture
GetClassInfoA
GetClassLongA
GetClassNameA
GetClientRect
GetDC
GetDlgCtrlID
GetDlgItem
GetFocus
GetForegroundWindow
GetKeyState
GetLastActivePopup
GetMenu
GetMenuCheckMarkDimensions
GetMenuItemCount
GetMenuItemID
GetMenuState
GetMessagePos
GetMessageTime
GetNextDlgTabItem
GetParent
GetPropA
GetSubMenu
GetSysColor
GetSysColorBrush
GetSystemMetrics
GetTopWindow
GetWindow
GetWindowLongA
GetWindowPlacement
GetWindowRect
GetWindowTextA
GrayStringA
IsIconic
IsWindowEnabled
LoadBitmapA
LoadCursorA
LoadIconA
LoadStringA
MapWindowPoints
MessageBoxA
ModifyMenuA
PeekMessageA
PostMessageA
PostQuitMessage
PtInRect
RegisterClassA
RegisterWindowMessageA
ReleaseDC
RemovePropA
SendMessageA
SetFocus
SetForegroundWindow
SetMenuItemBitmaps
SetPropA
SetWindowLongA
SetWindowPos
SetWindowsHookExA
SetWindowTextA
SystemParametersInfoA
TabbedTextOutA
UnhookWindowsHookEx
UnregisterClassA
WinHelpA
Seems to me from this list of imports that whatever magic is happening inside of this HanDes.dll, it's the culprit for hooking and preventing things like mouse_event from getting in. Seems the game must still use normal windows messages, just this DLL is causing some weird protection. Any ideas?
Can't thank you enough for all your help, I hope to get this figured out soon.
madshi
Site Admin
Posts: 10753
Joined: Sun Mar 21, 2004 5:25 pm

Post by madshi »

Well, you can use the madCodeHook demo "HookLoadLibrary" to see which dlls the games load dynamically. Maybe that helps. Apart from that it's hard to say for me which APIs the game uses to get its input information.
Post Reply