Hello everyone, please notice that this is my first tutorial I have ever wrote so please give me lots of feedback of what I could have done better.
First of all, what is a keyboard hook?
A keyboard hook is a windows hook that will hook the keyboard, a windows hook is something that will 'trap' the windows events.
A windows hook works under the skin of applications.
As soon as a event is sent to the window you hooked, you will recieve the message first, meaning you could modify, log or delete it.
I will show you how to delete the message.
How do a keyboard hook look?
A keyboard hook does most often exist within a DLL (Dynamically Linked Library) but in some cases they don't have to (WH_KEYBOARD_LL).
The keyboard hook we will use is called WH_KEYBOARD and is not global by default(?).
This is how our almighty DLL will look
Code:
#include "dll.h"
#include <windows.h>
extern "C" __declspec (dllexport) LRESULT __stdcall KeyboardProc(int code, WPARAM wParam, LPARAM lParam)
{
if(code == HC_ACTION) //if the wparam/lparam contains any data
{
if(wParam == VK_RETURN) //if wparam (which is a vk) is return
{
return 1; //return 1, which means that the key event wont be processed
}
}
return CallNextHookEx(0, code, wParam, lParam);
}
extern "C" BOOL __declspec (dllexport) __stdcall DllMain (HINSTANCE hInst,
DWORD reason,
LPVOID reserved)
{
switch (reason)
{
case DLL_PROCESS_ATTACH:
MessageBox(HWND_DESKTOP, "Attached!", "Yesh", MB_OK);
break; //let us know we attached the dll.
case DLL_PROCESS_DETACH:
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
}
return TRUE;
}
The only winAPI we use in our DLL is a keyboardproc and it looks like this (taken from msdn):
Code:
LRESULT CALLBACK KeyboardProc(
int code,
WPARAM wParam,
LPARAM lParam
);
Where code, is a code that we use to see if there is any data on wParam or lParam.
The WPARAM specifies the Virtual Key code.
The lParam Specifies the repeat count, scan code, extended-key flag, context code, previous key-state flag, and transition-state flag. we do not have to use this.
Why don't you show us a global hook?
1 reason, I don't want any script-kiddie to copy paste the code and then have his own keylogger which he can use to steal illegal data.
I do not in any way support stealing private people.
The way I show you, will only recieve keyboard events in our specified process.
How do I load the correct DLL into the correct process?
This can be done in a few ways, first I will show you the easiest way.
What we want to do is inject the dll in a thread ID, I will choose tibias thread id.
Of course, this can also be done in a few ways (finding the thread ID) but here is one.
We use the CreateToolHelp32Snapshot API to get a snapshot of all running processes.
after that, we need to find the PID of our application so that we can find the pid in the snapshot and compare, if it matches we got our thread id!
We use GetWindowThreadProcessId API to find the pid address.
It takes 2 parameters first is a HANDLE to the window, the second is a pointer to a variable which recieves the pid (if it finds it).
Then loop through the snapshot using first Thread32First and then Thread32Next, until we find our thread id.
Code:
HANDLE Snap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
THREADENTRY32 te;
te.dwSize = sizeof(te);
unsigned long tid = 0;
DWORD pid;
GetWindowThreadProcessId(::FindWindow("TibiaClient", 0), &pid);
if(Thread32First(Snap, &te))
do
{
if (te.th32OwnerProcessID == pid)
tid = te.th32ThreadID;
}
while (Thread32Next(Snap, &te));
if(tid == 0)
{
cout << "I could not find the thread id :(\n";
exit(1);
}
Now that we have the thread id, we can go ahead loading the library and then installing the hook.
Installing the hook
What we need to do now is to load our library using the LoadLibrary API.
It will look something like, LoadLibrary("ourdll.dll");.
After that, we need to get the address to our keyboardproc and we can do that easily by using the GetProcAddress API.
And then we can just set the hook using the SetWindowsHookEx with our proc and the thread ID we got before.
We also call a MessageBox after we installed our hook so that it will not unhook immediately.
Code:
HINSTANCE hDll = LoadLibrary("Keyboardhooktest.dll");
if(hDll == NULL)
{
cout << "I could not find the DLL :(\n";
exit(1);
}
HOOKPROC KeyboardProc = reinterpret_cast<HOOKPROC>(GetProcAddress(hDll, "KeyboardProc@12"));
if(KeyboardProc == NULL)
{
cout << "I could not find the address of the proc :(\n";
exit(1);
}
HHOOK hook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, hDll, tid);
if(hook == NULL)
{
cout << "I could not set the hook :(\n";
exit(1);
}
MessageBox(HWND_DESKTOP, "H", "g", MB_OK);
UnhookWindowsHookEx(hook);
That is it folks, a non-global keyboard hook made easy.
Have any questions?
Contact me at Henric-Johansson at hotmail.com
full code:
Code:
#include <iostream>
#include <windows.h>
#include <tlhelp32.h>
using namespace std;
int main()
{
HANDLE Snap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
THREADENTRY32 te;
te.dwSize = sizeof(te);
unsigned long tid = 0;
DWORD pid;
GetWindowThreadProcessId(::FindWindow("TibiaClient", 0), &pid);
if(Thread32First(Snap, &te))
do
{
if (te.th32OwnerProcessID == pid)
tid = te.th32ThreadID;
}
while (Thread32Next(Snap, &te));
if(tid == 0)
{
cout << "I could not find the thread id :(\n";
exit(1);
}
HINSTANCE hDll = LoadLibrary("Keyboardhooktest.dll");
if(hDll == NULL)
{
cout << "I could not find the DLL :(\n";
exit(1);
}
HOOKPROC KeyboardProc = reinterpret_cast<HOOKPROC>(GetProcAddress(hDll, "KeyboardProc@12"));
if(KeyboardProc == NULL)
{
cout << "I could not find the address of the proc :(\n";
exit(1);
}
HHOOK hook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, hDll, tid);
if(hook == NULL)
{
cout << "I could not set the hook :(\n";
exit(1);
}
MessageBox(HWND_DESKTOP, "H", "g", MB_OK);
UnhookWindowsHookEx(hook);
}
Please give me feedback, Kekke.