AES-128 Clipboard Protector: Auto-Encrypt Ctrl+C, Smart-Decrypt Ctrl+V (C++ Windows Hook)

Joined
Jun 12, 2020
Messages
60
Reaction score
3
This clipboard protector is a neat little Windows tool that makes copy-paste way more secure by automatically encrypting your text with AES-128-CTR whenever you hit Ctrl+C. It tags the encrypted data and swaps it into your clipboard, then seamlessly decrypts and "smart pastes" it back when you do Ctrl+V—positioning the cursor at line end, toggling insert mode, and typing character-by-character to handle huge texts without glitches. With fixed test keys and low-level keyboard hooks, it's a solid starting point for keeping sensitive info safe from casual snooping, all running smoothly in the background once launched as admin.
aes_ctr.hpp
C++:
// aes_ctr.hpp

/*
MIT License
Copyright (c) 2025 CoTon_TiGe_MoUaRf

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

This project is divided into two parts for sharing purposes due to its length. Each part can be used independently while maintaining the same terms outlined in this license.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#pragma once
#include <array>
#include <cstdint>
#include <cstring>
#include <vector>
#include <algorithm>

// -----------------------------------------------------------------------------
// Minimal AES-128 + CTR mode (inspired by TinyAES, public domain)
// -----------------------------------------------------------------------------
class AES128_CTR {
public:
    // clé 16 octets, iv 16 octets (counter)
    AES128_CTR(const uint8_t key_[16], const uint8_t iv_[16]) {
        memcpy(roundKey.data(), key_, 16);
        memcpy(counter.data(), iv_, 16);
        keyExpansion();
    }

    // CTR est symétrique
    void process(uint8_t* data, size_t len) {
        std::vector<uint8_t> stream(len);
        generateKeyStream(stream.data(), len);
        for (size_t i = 0; i < len; ++i) {
            data[i] ^= stream[i];
        }
    }

private:
    std::array<uint8_t, 176> expandedKey;  // 11 × 16
    std::array<uint8_t, 16>  roundKey;    
    std::array<uint8_t, 16>  counter;    

    // la S-box complète AES
    static constexpr uint8_t sbox[256] = {
      0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76,
      0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0,
      0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15,
      0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75,
      0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84,
      0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf,
      0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8,
      0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2,
      0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73,
      0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb,
      0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79,
      0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08,
      0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a,
      0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e,
      0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf,
      0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16
    };

    static void rotWord(uint8_t w[4]) {
        uint8_t t = w[0];
        w[0]=w[1]; w[1]=w[2]; w[2]=w[3]; w[3]=t;
    }
    static void subWord(uint8_t w[4]) {
        for(int i=0;i<4;++i) w[i]=sbox[w[i]];
    }

    void keyExpansion() {
        uint8_t* dst = expandedKey.data();
        memcpy(dst, roundKey.data(), 16);
        int bytesGen = 16;
        uint8_t rcon = 1;
        uint8_t temp[4];

        while(bytesGen < 176) {
            memcpy(temp, dst + bytesGen - 4, 4);
            if (bytesGen % 16 == 0) {
                rotWord(temp);
                subWord(temp);
                temp[0] ^= rcon;
                rcon = xtime(rcon);
            }
            for(int i=0;i<4;++i) {
                dst[bytesGen] = dst[bytesGen - 16] ^ temp[i];
                ++bytesGen;
            }
        }
    }

    static uint8_t xtime(uint8_t x) {
        return (x << 1) ^ ((x & 0x80) ? 0x1b : 0x00);
    }

    void cipher(const uint8_t in[16], uint8_t out[16]) {
        uint8_t state[16];
        memcpy(state, in, 16);
        addRoundKey(state, expandedKey.data());
        for(int round=1; round<=9; ++round) {
            subBytes(state);
            shiftRows(state);
            mixColumns(state);
            addRoundKey(state, expandedKey.data() + 16*round);
        }
        subBytes(state);
        shiftRows(state);
        addRoundKey(state, expandedKey.data() + 160);
        memcpy(out, state, 16);
    }

    void generateKeyStream(uint8_t* stream, size_t len) {
        size_t blocks = (len + 15) / 16;
        for(size_t b=0; b<blocks; ++b) {
            uint8_t out[16];
            cipher(counter.data(), out);
            // incr counter en big-endian
            for(int i=15; i>=0; --i) {
                if (++counter[i]) break;
            }
            size_t chunk = std::min<size_t>(16, len - b*16);
            memcpy(stream + b*16, out, chunk);
        }
    }

    static void addRoundKey(uint8_t s[16], const uint8_t* rk) {
        for(int i=0;i<16;++i) s[i] ^= rk[i];
    }
    void subBytes(uint8_t s[16]) {
        for(int i=0;i<16;++i) s[i]=sbox[s[i]];
    }
    static void shiftRows(uint8_t s[16]) {
        uint8_t t[16];
        // ligne 0
        t[0]=s[0];    t[4]=s[4];   t[8]=s[8];   t[12]=s[12];
        // ligne 1
        t[1]=s[5];    t[5]=s[9];   t[9]=s[13];  t[13]=s[1];
        // ligne 2
        t[2]=s[10];   t[6]=s[14];  t[10]=s[2];  t[14]=s[6];
        // ligne 3
        t[3]=s[15];   t[7]=s[3];   t[11]=s[7];  t[15]=s[11];
        memcpy(s,t,16);
    }
    static void mixColumns(uint8_t s[16]) {
        for(int c=0;c<4;++c){
            int i=c*4;
            uint8_t a0=s[i], a1=s[i+1], a2=s[i+2], a3=s[i+3];
            uint8_t r0 = xtime(a0) ^ a1       ^ xtime(a1) ^ a2       ^ a3;
            uint8_t r1 = a0       ^ xtime(a1) ^ a2       ^ xtime(a2) ^ a3;
            uint8_t r2 = a0       ^ a1       ^ xtime(a2) ^ a3       ^ xtime(a3);
            uint8_t r3 = xtime(a0) ^ a1       ^ a2       ^ xtime(a3) ^ a3;
            s[i]=r0; s[i+1]=r1; s[i+2]=r2; s[i+3]=r3;
        }
    }
};

main.cpp :

C++:
/*
MIT License
Copyright (c) 2025 CoTon_TiGe_MoUaRf

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

This project is divided into two parts for sharing purposes due to its length. Each part can be used independently while maintaining the same terms outlined in this license.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <Windows.h>

#include <iostream>

#include <string>

#include <vector>

#include <thread>

#include "aes_ctr.hpp"


#pragma comment(lib, "user32.lib")


// Clés fixes simples pour test

const uint8_t KEY[16] = {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f};

const uint8_t IV[16]  = {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f};


// Globals

bool ctrl_down = false;

bool typing = false;

bool processing = false;

std::string enc_tag = "AES128:";


// Get clipboard as string (support large data)

std::string clipboard_get() {

    std::string data;

    if (OpenClipboard(NULL)) {

        HANDLE h = GetClipboardData(CF_TEXT);

        if (h) {

            size_t size = GlobalSize(h);

            char *text = (char*)GlobalLock(h);

            if (text && size > 0) {

                data.assign(text, size - 1);

                GlobalUnlock(h);

            }

        }

        CloseClipboard();

    }

    return data;

}


// Set clipboard from string (large data support)

bool clipboard_set(const std::string& data) {

    if (OpenClipboard(NULL)) {

        EmptyClipboard();

        HGLOBAL glob = GlobalAlloc(GMEM_MOVEABLE, data.size() + 1);

        if (glob) {

            char *buf = (char*)GlobalLock(glob);

            if (buf) {

                memcpy(buf, data.c_str(), data.size());

                buf[data.size()] = 0;

                GlobalUnlock(glob);

                SetClipboardData(CF_TEXT, glob);

                CloseClipboard();

                return true;

            }

            GlobalFree(glob);

        }

        CloseClipboard();

    }

    return false;

}


// AES CTR encrypt/decrypt

std::string aes_process(const std::string& input) {

    if (input.empty()) return "";

    std::vector<uint8_t> buf(input.begin(), input.end());

    {

        AES128_CTR aes(KEY, IV);

        aes.process(buf.data(), buf.size());

    }

    return std::string(buf.begin(), buf.end());

}


// SMART Paste: Position cursor + Insert mode (NO DELETION)

void smart_paste(const std::string& text) {

    typing = true;

    Sleep(150);

   

    HWND hwnd = GetForegroundWindow();

    if (!hwnd) {

        typing = false;

        return;

    }

   

    // Focus window

    SetForegroundWindow(hwnd);

    SetFocus(hwnd);

    Sleep(50);

   

    // Move cursor to end of line (End key)

    keybd_event(VK_END, 0, 0, 0);

    Sleep(30);

    keybd_event(VK_END, 0, KEYEVENTF_KEYUP, 0);

    Sleep(50);

   

    // Ensure Insert mode (Insert key)

    keybd_event(VK_INSERT, 0, 0, 0);

    Sleep(20);

    keybd_event(VK_INSERT, 0, KEYEVENTF_KEYUP, 0);

    Sleep(30);

   

    // Type text character by character with adaptive delays

    for (size_t i = 0; i < text.size(); ++i) {

        char c = text[i];

       

        // Special handling for newlines

        if (c == '\r' || c == '\n') {

            keybd_event(VK_RETURN, 0, 0, 0);

            Sleep(15);

            keybd_event(VK_RETURN, 0, KEYEVENTF_KEYUP, 0);

            Sleep(20);

            continue;

        }

       

        // Unicode SendInput for all characters

        INPUT input[2] = {0};

        input[0].type = INPUT_KEYBOARD;

        input[0].ki.wVk = 0;

        input[0].ki.dwFlags = KEYEVENTF_UNICODE;

        input[0].ki.wScan = (unsigned short)c;

       

        input[1] = input[0];

        input[1].ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP;

       

        SendInput(2, input, sizeof(INPUT));

       

        // Adaptive delay: slower for large texts

        Sleep((text.size() > 1000) ? 12 : (text.size() > 500 ? 8 : 4));

    }

   

    Sleep(150);

    typing = false;

}


// Paste thread function

void paste_thread(std::string text) {

    try {

        smart_paste(text);

    } catch (...) {

        typing = false;

    }

}


// Low level keyboard hook

LRESULT CALLBACK kb_hook(int nCode, WPARAM wParam, LPARAM lParam) {

    if (nCode >= 0 && !typing && !processing) {

        KBDLLHOOKSTRUCT *kb = (KBDLLHOOKSTRUCT*)lParam;

       

        // Track all CTRL keys

        if (kb->vkCode == VK_CONTROL || kb->vkCode == VK_LCONTROL || kb->vkCode == VK_RCONTROL) {

            ctrl_down = (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN);

            return CallNextHookEx(NULL, nCode, wParam, lParam);

        }

       

        // CTRL+C - Encrypt

        if (ctrl_down && (kb->vkCode == 0x43 || kb->vkCode == 'C') && (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN)) {

            std::cout << "[+] Ctrl+C intercepted" << std::endl;

            Sleep(400); // Increased wait for large clipboard

           

            processing = true;

            std::string original = clipboard_get();

            processing = false;

           

            std::cout << "[+] Original (" << original.length() << " chars): '"

                      << (original.size() > 60 ? original.substr(0,60) + "..." : original) << "'" << std::endl;

           

            if (!original.empty()) {

                std::string encrypted = aes_process(original);

                std::string tagged = enc_tag + encrypted;

               

                if (clipboard_set(tagged)) {

                    std::cout << "[+] Encrypted (" << tagged.length() << " bytes) ✓" << std::endl;

                    std::cout << "[+] Preview: '" << (tagged.size() > 70 ? tagged.substr(0,70) + "..." : tagged) << "'" << std::endl;

                } else {

                    std::cout << "[!] Clipboard set FAILED" << std::endl;

                }

            }

            return 1;

        }

       

        // CTRL+V - Decrypt & Smart Paste

        if (ctrl_down && (kb->vkCode == 0x56 || kb->vkCode == 'V') && (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN)) {

            std::cout << "[+] Ctrl+V intercepted" << std::endl;

           

            processing = true;

            std::string clipboard_data = clipboard_get();

            processing = false;

           

            std::cout << "[+] Clipboard (" << clipboard_data.length() << " bytes): '"

                      << (clipboard_data.size() > 70 ? clipboard_data.substr(0,70) + "..." : clipboard_data) << "'" << std::endl;

           

            if (clipboard_data.find(enc_tag) == 0) {

                std::string encrypted = clipboard_data.substr(enc_tag.length());

                std::string decrypted = aes_process(encrypted);

               

                std::cout << "[+] Decrypted (" << decrypted.length() << " chars): '"

                          << (decrypted.size() > 60 ? decrypted.substr(0,60) + "..." : decrypted) << "'" << std::endl;

               

                std::cout << "[+] Smart pasting " << decrypted.length() << " chars..." << std::endl;

                std::thread(paste_thread, std::move(decrypted)).detach();

               

            } else {

                std::cout << "[!] Not encrypted (no '" << enc_tag << "')" << std::endl;

            }

            return 1;

        }

    }

    return CallNextHookEx(NULL, nCode, wParam, lParam);

}


// Entry point

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {

    AllocConsole();

    freopen_s((FILE**)stdout, "CONOUT$", "w", stdout);

    freopen_s((FILE**)stderr, "CONOUT$", "w", stderr);

   

    char title[] = "AES Clipboard v4 - Smart Paste";

    SetConsoleTitleA(title);

   

    std::cout << "=== AES-128-CTR Clipboard Protector v4 - SMART PASTE ===" << std::endl;

    std::cout << "Tag: '" << enc_tag << "' | Supports LARGE texts!" << std::endl << std::endl;

   

    // Extended AES test

    std::string test = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";

    std::string enc = aes_process(test);

    std::string dec = aes_process(enc);

   

    std::cout << "AES Test (" << test.length() << " chars):" << std::endl;

    std::cout << "  OK: " << (test == dec ? "✓" : "✗") << " | Encoded: " << enc.length() << " bytes" << std::endl << std::endl;

   

    HHOOK hook = SetWindowsHookEx(WH_KEYBOARD_LL, kb_hook, GetModuleHandle(NULL), 0);

    if (!hook) {

        std::cout << "❌ Hook failed! Run as ADMINISTRATOR!" << std::endl;

        Sleep(5000);

        return 1;

    }

   

    std::cout << "✅ HOOK ACTIVE - Smart paste enabled!" << std::endl;

    std::cout << "💡 Features: Large text support | No data loss | Insert mode" << std::endl;

    std::cout << "📋 Test: Copy LONG text → Ctrl+C → Ctrl+V" << std::endl << std::endl;

   

    MSG msg;

    while (GetMessage(&msg, NULL, 0, 0)) {

        TranslateMessage(&msg);

        DispatchMessage(&msg);

    }

   

    UnhookWindowsHookEx(hook);

    FreeConsole();

    return 0;

}
The program works for copying/pasting text and hasn't been tested on other data types.
 
Joined
Jun 12, 2020
Messages
60
Reaction score
3
To generate a random key and IV for each encryption:
csprng.hpp​
C++:
/*
MIT License
Copyright (c) 2025 CoTon_TiGe_MoUaRf

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

This project is divided into two parts for sharing purposes due to its length. Each part can be used independently while maintaining the same terms outlined in this license.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#pragma once

#include <array>
#include <atomic>
#include <cstdint>
#include <cstddef>
#include <cstring>
#include <cstdio>
#include <mutex>
#include <stdexcept>
#include <limits>

#ifdef __linux__
  #include <unistd.h>
  #include <sys/random.h>
#elif defined(_WIN32)
  #include <windows.h>
  #include <wincrypt.h>
#endif

#include "error_logger.hpp"

namespace csprng {
namespace detail {

// ==== SHA-256 constants & fonctions =====================================

static constexpr uint32_t K256[64] = {
    0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,
    0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,
    0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,
    0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,
    0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,
    0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,
    0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,
    0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
};

inline uint32_t rotr(uint32_t x, unsigned r) noexcept {
    return (x >> r) | (x << (32 - r));
}

static void sha256_compress(uint32_t state[8], const uint8_t block[64]) noexcept {
    uint32_t W[64];
    // init
    for (int i = 0; i < 16; ++i) {
        W[i]  = uint32_t(block[4*i    ]) << 24;
        W[i] |= uint32_t(block[4*i + 1]) << 16;
        W[i] |= uint32_t(block[4*i + 2]) <<  8;
        W[i] |= uint32_t(block[4*i + 3]);
    }
    for (int t = 16; t < 64; ++t) {
        uint32_t s0 = rotr(W[t-15],7) ^ rotr(W[t-15],18) ^ (W[t-15] >> 3);
        uint32_t s1 = rotr(W[t-2],17) ^ rotr(W[t-2],19) ^ (W[t-2] >>10);
        W[t] = W[t-16] + s0 + W[t-7] + s1;
    }
    // compress
    uint32_t a=state[0], b=state[1], c=state[2], d=state[3];
    uint32_t e=state[4], f=state[5], g=state[6], h=state[7];
    for (int t = 0; t < 64; ++t) {
        uint32_t S1   = rotr(e,6) ^ rotr(e,11) ^ rotr(e,25);
        uint32_t ch   = (e & f) ^ (~e & g);
        uint32_t temp1= h + S1 + ch + K256[t] + W[t];
        uint32_t S0   = rotr(a,2) ^ rotr(a,13) ^ rotr(a,22);
        uint32_t maj  = (a & b) ^ (a & c) ^ (b & c);
        uint32_t temp2= S0 + maj;
        h=g; g=f; f=e; e=d+temp1;
        d=c; c=b; b=a; a=temp1+temp2;
    }
    state[0]+=a; state[1]+=b; state[2]+=c; state[3]+=d;
    state[4]+=e; state[5]+=f; state[6]+=g; state[7]+=h;
}

static std::array<uint8_t,32> sha256(const uint8_t* data, size_t len) {
    uint32_t state[8] = {
      0x6a09e667u,0xbb67ae85u,0x3c6ef372u,0xa54ff53au,
      0x510e527fu,0x9b05688cu,0x1f83d9abu,0x5be0cd19u
    };
    uint64_t bitlen = uint64_t(len) * 8;
    // padding
    size_t total = ((len + 9 + 63) / 64) * 64;
    std::unique_ptr<uint8_t[]> buf(new uint8_t[total]);
    std::memset(buf.get(),0,total);
    std::memcpy(buf.get(), data, len);
    buf.get()[len] = 0x80;
    for (int i=0;i<8;++i) buf.get()[total-1-i] = uint8_t((bitlen>>(8*i))&0xFF);
    for (size_t off=0; off<total; off+=64)
        sha256_compress(state, buf.get()+off);
    std::array<uint8_t,32> digest;
    for(int i=0;i<8;++i){
        digest[4*i    ] = state[i] >> 24;
        digest[4*i + 1] = state[i] >> 16;
        digest[4*i + 2] = state[i] >>  8;
        digest[4*i + 3] = state[i];
    }
    return digest;
}

// ==== HMAC-SHA-256 =======================================================

static std::array<uint8_t,32> hmac_sha256(
    const std::array<uint8_t,32>& key,
    const uint8_t* msg, size_t msglen)
{
    constexpr size_t B = 64;
    std::array<uint8_t,B> K0{}, ipad{}, opad{};
    // clé dans K0 (clé ≤ 32 octets ici)
    std::memcpy(K0.data(), key.data(), key.size());
    for(size_t i=0;i<B;++i){
        ipad[i] = K0[i] ^ 0x36u;
        opad[i] = K0[i] ^ 0x5Cu;
    }
    // inner = SHA256(ipad || msg)
    std::unique_ptr<uint8_t[]> buf1(new uint8_t[B + msglen]);
    std::memcpy(buf1.get(), ipad.data(), B);
    std::memcpy(buf1.get()+B, msg, msglen);
    auto inner = sha256(buf1.get(), B+msglen);

    // outer = SHA256(opad || inner)
    uint8_t buf2[B + inner.size()];
    std::memcpy(buf2,       opad.data(), B);
    std::memcpy(buf2 + B,   inner.data(), inner.size());
    return sha256(buf2, B + inner.size());
}

} // namespace detail



// ==== SP800-90A HMAC-DRBG SHA-256 ========================================

class CSPRNG {
public:
    using result_type = unsigned long;
    static constexpr result_type min() { return std::numeric_limits<result_type>::min(); }
    static constexpr result_type max() { return std::numeric_limits<result_type>::max(); }

    CSPRNG() {
        open_os_source();
        instantiate_drbg();
        bytes_generated_ = 0;
    }
    ~CSPRNG() noexcept {
        // effacement
        std::fill(K_.begin(), K_.end(), 0);
        std::fill(V_.begin(), V_.end(), 0);
        close_os_source();
    }

    // génère `size` octets en dest
    void generate(void* dest, std::size_t size) {
        if (!dest && size>0) {
            error_logger::log_error("CSPRNG::generate","dest is null");
            throw std::invalid_argument("CSPRNG::generate: dest is null");
        }
        std::lock_guard<std::mutex> lk(mutex_);
        if (bytes_generated_ + size > RESEED_BYTES) {
            reseed_drbg();
            bytes_generated_ = 0;
        }
        uint8_t* out = static_cast<uint8_t*>(dest);
        size_t rem = size;
        while(rem){
            V_ = detail::hmac_sha256(K_, V_.data(), V_.size());
            size_t chunk = std::min(rem, V_.size());
            std::memcpy(out, V_.data(), chunk);
            out += chunk; rem -= chunk;
        }
        bytes_generated_ += size;
    }

    result_type operator()(){
        result_type v=0;
        generate(&v, sizeof(v));
        return v;
    }

    // injection manuelle d'entropie
    void reseed_manual(const uint8_t* data, size_t dlen) {
        std::lock_guard<std::mutex> lk(mutex_);
        drbg_update(data, dlen);
        bytes_generated_ = 0;
    }

private:
    static constexpr uint64_t RESEED_BYTES = 1ULL<<20; // 1 MiB
    std::array<uint8_t,32> K_, V_;
    uint64_t               bytes_generated_{0};
    std::mutex             mutex_;

#ifdef _WIN32
    HCRYPTPROV hprov_{0};
#else
    // pour POSIX on n’a plus besoin de FILE*
#endif

    void open_os_source() {
#ifdef _WIN32
        if (!CryptAcquireContextA(&hprov_, nullptr, nullptr,
                                 PROV_RSA_FULL,
                                 CRYPT_VERIFYCONTEXT|CRYPT_SILENT))
        {
            error_logger::log_and_throw("CSPRNG::open_os_source","CryptAcquireContextA failed");
        }
#else
        // rien à faire pour getrandom
#endif
    }

    void close_os_source() noexcept {
#ifdef _WIN32
        if(hprov_){ CryptReleaseContext(hprov_,0); hprov_ = 0; }
#endif
    }

    bool os_random(void* dest, size_t sz) noexcept {
        if (!dest && sz>0) return false;
#ifdef __linux__
        // getrandom() bloque si pas assez d'entropie
        ssize_t r = ::getrandom(dest, sz, 0);
        return r == (ssize_t)sz;
#elif defined(_WIN32)
        constexpr uint64_t MAXC = 1ULL<<30;
        uint8_t* p = static_cast<uint8_t*>(dest);
        uint64_t rem = sz;
        while(rem){
            uint32_t toread = uint32_t(std::min(rem, MAXC));
            if (!CryptGenRandom(hprov_, toread, p)) return false;
            p += toread; rem -= toread;
        }
        return true;
#else
        // fallback sur /dev/urandom
        FILE* f = std::fopen("/dev/urandom","rb");
        if(!f) return false;
        bool ok = std::fread(dest,1,sz,f)==sz;
        std::fclose(f);
        return ok;
#endif
    }

    void instantiate_drbg() {
        K_.fill(0x00);
        V_.fill(0x01);
        std::array<uint8_t,32> seed;
        if(!os_random(seed.data(), seed.size())){
            error_logger::log_and_throw("CSPRNG::instantiate_drbg","lecture seed échouée");
        }
        drbg_update(seed.data(), seed.size());
    }

    void reseed_drbg() {
        std::array<uint8_t,32> ent;
        if(!os_random(ent.data(), ent.size())){
            error_logger::log_and_throw("CSPRNG::reseed_drbg","reseed échoué");
        }
        drbg_update(ent.data(), ent.size());
    }

    // SP800-90A Update
    void drbg_update(const uint8_t* data, size_t dlen) {
        // step 1
        {
            uint8_t buf[32+1+32];
            std::memcpy(buf,       V_.data(), 32);
            buf[32] = 0x00;
            std::memcpy(buf+33,    data,      dlen);
            K_ = detail::hmac_sha256(K_, buf, 33 + dlen);
            V_ = detail::hmac_sha256(K_, V_.data(), 32);
        }
        // step 2 si on a des données
        if(dlen){
            uint8_t buf[32+1+32];
            std::memcpy(buf,       V_.data(), 32);
            buf[32] = 0x01;
            std::memcpy(buf+33,    data,      dlen);
            K_ = detail::hmac_sha256(K_, buf, 33 + dlen);
            V_ = detail::hmac_sha256(K_, V_.data(), 32);
        }
    }
}; // class CSPRNG


// ==== Façade pour un octet ================================================

class RandomGenerator {
public:
    RandomGenerator() = default;
    uint8_t getRandomByte(){
        counter_.fetch_add(1, std::memory_order_relaxed);
        uint8_t b=0;
        csprng_.generate(&b,1);
        return b;
    }
private:
    CSPRNG                       csprng_;
    static std::atomic<uint64_t> counter_;
};

inline std::atomic<uint64_t> RandomGenerator::counter_{0};

} // namespace csprng

error_logger.hpp​
C++:
// error_logger.hpp
/*
MIT License
Copyright (c) 2025 CoTon_TiGe_MoUaRf

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

This project is divided into two parts for sharing purposes due to its length. Each part can be used independently while maintaining the same terms outlined in this license.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#pragma once

#include <mutex>
#include <fstream>
#include <sstream>
#include <string>
#include <stdexcept>
#include <chrono>
#include <iomanip>

namespace error_logger
{
    // Verrou pour synchroniser l'accès au fichier log
    inline std::mutex& get_mutex()
    {
        static std::mutex m;
        return m;
    }

    // Ouvre le fichier en mode append et écrit une ligne horodatée
    inline void log_error(const std::string& context, const std::string& message)
    {
        std::lock_guard<std::mutex> lock(get_mutex());

        // Ouvre en append
        std::ofstream ofs("error.log", std::ios::out | std::ios::app);
        if (!ofs.is_open()) {
            // Si on ne peut pas ouvrir le fichier de log, on termine brutement
            std::abort();
        }

        // Horodatage au format ISO 8601
        auto now = std::chrono::system_clock::now();
        auto itt = std::chrono::system_clock::to_time_t(now);
        auto tm  = *std::localtime(&itt);

        ofs << std::put_time(&tm, "%Y-%m-%dT%H:%M:%S")
            << " [" << context << "] "
            << message
            << "\n";
    }

    // Combine la journalisation et le throw d'une exception
    [[noreturn]] inline void log_and_throw(const std::string& context,
                                           const std::string& message)
    {
        log_error(context, message);
        throw std::runtime_error(context + ": " + message);
    }
}

And finally, correct the main.cpp file while keeping the original aes_ctr.hpp file.
main.cpp​
C++:
/*


MIT License


Copyright (c) 2025 CoTon_TiGe_MoUaRf



Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:



The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.



This project is divided into two parts for sharing purposes due to its length. Each part can be used independently while maintaining the same terms outlined in this license.



THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


*/


#include <Windows.h>

#include <iostream>

#include <string>

#include <vector>

#include <thread>

#include <fstream>

#include "aes_ctr.hpp"

#include "csprng.hpp"


#pragma comment(lib, "user32.lib")


// Globals

bool ctrl_down = false;

bool typing = false;

bool processing = false;

std::string enc_tag = "AES128:";

const std::string key_file = "key_iv.txt";


// Write key and IV to file

void write_key_to_file(const std::string& filename, const uint8_t* key, const uint8_t* iv) {

    std::ofstream file(filename, std::ios::binary);

    if (file.is_open()) {

        file.write(reinterpret_cast<const char*>(key), 16); // Write key (16 bytes)

        file.write(reinterpret_cast<const char*>(iv), 16);  // Write IV (16 bytes)

        file.close();

        std::cout << "[+] Key/IV saved to " << filename << std::endl;

    }

}


// Read key and IV from file

bool read_key_from_file(const std::string& filename, uint8_t* key, uint8_t* iv) {

    std::ifstream file(filename, std::ios::binary);

    if (!file.is_open()) {

        std::cout << "[!] Cannot read key file: " << filename << std::endl;

        return false; // File open failed

    }

    

    file.read(reinterpret_cast<char*>(key), 16); // Read key (16 bytes)

    file.read(reinterpret_cast<char*>(iv), 16);  // Read IV (16 bytes)

    file.close();

    

    std::cout << "[+] Key/IV loaded from " << filename << std::endl;

    return true; // Read success

}


// Get clipboard as string (support large data)

std::string clipboard_get() {

    std::string data;

    if (OpenClipboard(NULL)) {

        HANDLE h = GetClipboardData(CF_TEXT);

        if (h) {

            size_t size = GlobalSize(h);

            char *text = (char*)GlobalLock(h);

            if (text && size > 0) {

                data.assign(text, size - 1);

                GlobalUnlock(h);

            }

        }

        CloseClipboard();

    }

    return data;

}


// Set clipboard from string (large data support)

bool clipboard_set(const std::string& data) {

    if (OpenClipboard(NULL)) {

        EmptyClipboard();

        HGLOBAL glob = GlobalAlloc(GMEM_MOVEABLE, data.size() + 1);

        if (glob) {

            char *buf = (char*)GlobalLock(glob);

            if (buf) {

                memcpy(buf, data.c_str(), data.size());

                buf[data.size()] = 0;

                GlobalUnlock(glob);

                SetClipboardData(CF_TEXT, glob);

                CloseClipboard();

                return true;

            }

            GlobalFree(glob);

        }

        CloseClipboard();

    }

    return false;

}


// AES CTR encrypt/decrypt with file-based key + IV handling

std::string aes_process(const std::string& input, bool is_encrypting = true) {
    if (input.empty()) return "";

    std::vector<uint8_t> buf(input.begin(), input.end());
    uint8_t key[16];
    uint8_t IV[16];

    if (is_encrypting) {
        // Générer une clé et un IV aléatoires
        csprng::CSPRNG randomGenerator;
        randomGenerator.generate(key, sizeof(key));
        randomGenerator.generate(IV, sizeof(IV));

        // Chiffrement
        AES128_CTR aes(key, IV);
        aes.process(buf.data(), buf.size());

        // Sauvegarder la clé et l'IV dans un fichier
        write_key_to_file(key_file, key, IV);

        // Retourner uniquement les données chiffrées
        return std::string(buf.begin(), buf.end());
        
    } else {
        // Pour le déchiffrement, essayer de lire la clé et l'IV depuis le fichier
        if (!read_key_from_file(key_file, key, IV)) {
            return ""; // Échec de la lecture du fichier de clé
        }

        // Déchiffrement
        AES128_CTR aes(key, IV);
        aes.process(buf.data(), buf.size());

        return std::string(buf.begin(), buf.end()); // Retourner les données déchiffrées
    }
}



// SMART Paste: Position cursor + Insert mode (NO DELETION)

void smart_paste(const std::string& text) {

    typing = true;

    Sleep(150);

    

    HWND hwnd = GetForegroundWindow();

    if (!hwnd) {

        typing = false;

        return;

    }

    

    // Focus window

    SetForegroundWindow(hwnd);

    SetFocus(hwnd);

    Sleep(50);

    

    // Move cursor to end of line (End key)

    keybd_event(VK_END, 0, 0, 0);

    Sleep(30);

    keybd_event(VK_END, 0, KEYEVENTF_KEYUP, 0);

    Sleep(50);

    

    // Ensure Insert mode (Insert key)

    keybd_event(VK_INSERT, 0, 0, 0);

    Sleep(20);

    keybd_event(VK_INSERT, 0, KEYEVENTF_KEYUP, 0);

    Sleep(30);

    

    // Type text character by character with adaptive delays

    for (size_t i = 0; i < text.size(); ++i) {

        char c = text[i];

        

        // Special handling for newlines

        if (c == '\r' || c == '\n') {

            keybd_event(VK_RETURN, 0, 0, 0);

            Sleep(15);

            keybd_event(VK_RETURN, 0, KEYEVENTF_KEYUP, 0);

            Sleep(20);

            continue;

        }

        

        // Unicode SendInput for all characters

        INPUT input[2] = {0};

        input[0].type = INPUT_KEYBOARD;

        input[0].ki.wVk = 0;

        input[0].ki.dwFlags = KEYEVENTF_UNICODE;

        input[0].ki.wScan = (unsigned short)c;

        

        input[1] = input[0];

        input[1].ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP;

        

        SendInput(2, input, sizeof(INPUT));

        

        // Adaptive delay: slower for large texts

        Sleep((text.size() > 1000) ? 12 : (text.size() > 500 ? 8 : 4));

    }

    

    Sleep(150);

    typing = false;

}


// Paste thread function

void paste_thread(std::string text) {

    try {

        smart_paste(text);

    } catch (...) {

        typing = false;

    }

}


// Low level keyboard hook

LRESULT CALLBACK kb_hook(int nCode, WPARAM wParam, LPARAM lParam) {

    if (nCode >= 0 && !typing && !processing) {

        KBDLLHOOKSTRUCT *kb = (KBDLLHOOKSTRUCT*)lParam;

        

        // Track all CTRL keys

        if (kb->vkCode == VK_CONTROL || kb->vkCode == VK_LCONTROL || kb->vkCode == VK_RCONTROL) {

            ctrl_down = (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN);

            return CallNextHookEx(NULL, nCode, wParam, lParam);

        }

        

        // CTRL+C - Encrypt

        if (ctrl_down && (kb->vkCode == 0x43 || kb->vkCode == 'C') && (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN)) {

            std::cout << "[+] Ctrl+C intercepted" << std::endl;

            Sleep(400); // Increased wait for large clipboard

            

            processing = true;

            std::string original = clipboard_get();

            processing = false;

            

            std::cout << "[+] Original (" << original.length() << " chars): '" 

                      << (original.size() > 60 ? original.substr(0,60) + "..." : original) << "'" << std::endl;

            

            if (!original.empty()) {

                std::string encrypted = aes_process(original, true);

                std::string tagged = enc_tag + encrypted;

                

                if (clipboard_set(tagged)) {

                    std::cout << "[+] Encrypted (" << tagged.length() << " bytes) ✓" << std::endl;

                    std::cout << "[+] Preview: '" << (tagged.size() > 70 ? tagged.substr(0,70) + "..." : tagged) << "'" << std::endl;

                } else {

                    std::cout << "[!] Clipboard set FAILED" << std::endl;

                }

            }

            return 1;

        }

        

        // CTRL+V - Decrypt & Smart Paste

        if (ctrl_down && (kb->vkCode == 0x56 || kb->vkCode == 'V') && (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN)) {

            std::cout << "[+] Ctrl+V intercepted" << std::endl;

            

            processing = true;

            std::string clipboard_data = clipboard_get();

            processing = false;

            

            std::cout << "[+] Clipboard (" << clipboard_data.length() << " bytes): '" 

                      << (clipboard_data.size() > 70 ? clipboard_data.substr(0,70) + "..." : clipboard_data) << "'" << std::endl;

            

            if (clipboard_data.find(enc_tag) == 0) {

                std::string encrypted = clipboard_data.substr(enc_tag.length());

                std::string decrypted = aes_process(encrypted, false);

                

                std::cout << "[+] Decrypted (" << decrypted.length() << " chars): '" 

                          << (decrypted.size() > 60 ? decrypted.substr(0,60) + "..." : decrypted) << "'" << std::endl;

                

                std::cout << "[+] Smart pasting " << decrypted.length() << " chars..." << std::endl;

                std::thread(paste_thread, std::move(decrypted)).detach();

                

            } else {

                std::cout << "[!] Not encrypted (no '" << enc_tag << "')" << std::endl;

            }

            return 1;

        }

    }

    return CallNextHookEx(NULL, nCode, wParam, lParam);

}


// Entry point

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {

    AllocConsole();

    freopen_s((FILE**)stdout, "CONOUT$", "w", stdout);

    freopen_s((FILE**)stderr, "CONOUT$", "w", stderr);

    

    char title[] = "AES Clipboard v6 - File Key";

    SetConsoleTitleA(title);

    

    std::cout << "=== AES-128-CTR Clipboard Protector v6 - FILE KEY ===" << std::endl;

    std::cout << "Tag: '" << enc_tag << "' | Key file: '" << key_file << "' | Supports LARGE texts!" << std::endl << std::endl;

    

    // Extended AES test

    std::string test = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";

    std::string enc = aes_process(test, true);

    std::string dec = aes_process(enc, false);

    

    std::cout << "AES Test (" << test.length() << " chars):" << std::endl;

    std::cout << "  OK: " << (test == dec ? "✓" : "✗") << " | Encoded: " << enc.length() << " bytes (Key+IV+Data)" << std::endl << std::endl;

    

    HHOOK hook = SetWindowsHookEx(WH_KEYBOARD_LL, kb_hook, GetModuleHandle(NULL), 0);

    if (!hook) {

        std::cout << "❌ Hook failed! Run as ADMINISTRATOR!" << std::endl;

        Sleep(5000);

        return 1;

    }

    

    std::cout << "✅ HOOK ACTIVE - Smart paste enabled!" << std::endl;

    std::cout << "💡 Features: FILE-BASED KEY+IV | Large text support | No data loss | Insert mode" << std::endl;

    std::cout << "📋 Test: Copy LONG text → Ctrl+C → Ctrl+V" << std::endl;

    std::cout << "🔑 Key file '" << key_file << "' will be created/updated on encrypt" << std::endl << std::endl;

    

    MSG msg;

    while (GetMessage(&msg, NULL, 0, 0)) {

        TranslateMessage(&msg);

        DispatchMessage(&msg);

    }

    

    UnhookWindowsHookEx(hook);

    FreeConsole();

    return 0;

}
 
Last edited:
Joined
Jun 12, 2020
Messages
60
Reaction score
3
The C++ program shared above implements a sophisticated clipboard protector for Windows that automatically secures copied data using AES-128 in CTR mode. It installs a low-level keyboard hook to intercept Ctrl+C and Ctrl+V. On Ctrl+C it reads the clipboard, generates a random 16-byte key and 16-byte IV from a cryptographically secure RNG (HMAC-DRBG with SHA-256 seeded from Windows CryptGenRandom), encrypts the clipboard contents, prefixes the ciphertext with the tag "AES128:", and saves the key and IV together in a binary file named "key_iv.txt" (16 bytes each). On Ctrl+V it checks for the "AES128:" tag, reads the key and IV from that file, decrypts the data, and intelligently inserts it into the active application in insert mode (End + Insert), simulating keystrokes character-by-character to handle large text blocks. The key/IV file persists so decryption works as long as it exists; keys are rotated automatically on each encryption, and a detailed debug console logs and confirms every operation. Now with 100% more paranoia — Ctrl+C, meet AES-128. 😏
 
Joined
Jun 12, 2020
Messages
60
Reaction score
3
A more optimized, easier-to-use version. The .hpp files are the same as above.

C++:
/*
MIT License
Copyright (c) 2025 CoTon_TiGe_MoUaRf

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

This project is divided into two parts for sharing purposes due to its length. Each part can be used independently while maintaining the same terms outlined in this license.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#include <iostream>
#include <windows.h>
#include <thread>
#include <atomic>
#include <fstream>
#include <vector>
#include <string>
#include "aes_ctr.hpp"
#include "csprng.hpp"

// Globals
bool ctrl_down = false;
bool typing = false;
std::wstring enc_tag = L"AES128:";
const std::string key_file = "key_iv.txt";

// Clipboard functions
std::wstring clipboard_get() {
    if (!OpenClipboard(NULL)) return L"";
    std::wstring data;
    if (HANDLE h = GetClipboardData(CF_UNICODETEXT)) {
        auto* text = static_cast<wchar_t*>(GlobalLock(h));
        if (text) {
            size_t size = GlobalSize(h) / sizeof(wchar_t);
            data.assign(text, size - 1); // Exclude the NULL terminator
            GlobalUnlock(h);
        }
    }
    CloseClipboard();
    return data;
}

bool clipboard_set(const std::wstring& data) {
    if (!OpenClipboard(NULL)) return false;
    EmptyClipboard();

    HGLOBAL glob = GlobalAlloc(GMEM_MOVEABLE, (data.size() + 1) * sizeof(wchar_t));
    if (glob) {
        wchar_t* buf = static_cast<wchar_t*>(GlobalLock(glob));
        if (buf) {
            memcpy(buf, data.c_str(), data.size() * sizeof(wchar_t));
            buf[data.size()] = 0;  // NULL terminator
            GlobalUnlock(glob);
            SetClipboardData(CF_UNICODETEXT, glob);
        } else {
            GlobalFree(glob);
        }
    }
    CloseClipboard();
    return true;
}

// Key/IV functions
void write_key_to_file(const std::string& filename, const uint8_t* key, const uint8_t* iv) {
    std::ofstream file(filename, std::ios::binary);
    if (!file.is_open()) return;
    file.write(reinterpret_cast<const char*>(key), 16);
    file.write(reinterpret_cast<const char*>(iv), 16);
    file.close();
}

bool read_key_from_file(const std::string& filename, uint8_t* key, uint8_t* iv) {
    std::ifstream file(filename, std::ios::binary);
    if (!file.is_open()) return false;
    file.read(reinterpret_cast<char*>(key), 16);
    file.read(reinterpret_cast<char*>(iv), 16);
    file.close();
    return true;
}

std::wstring aes_process(const std::wstring& input, bool encrypt = true) {
    if (input.empty()) return L"";
    std::vector<uint8_t> buf(input.begin(), input.end());
    uint8_t key[16], iv[16];

    if (encrypt) {
        csprng::CSPRNG rng;
        rng.generate(key, 16);
        rng.generate(iv, 16);
        AES128_CTR aes(key, iv);
        aes.process(buf.data(), buf.size());
        write_key_to_file(key_file, key, iv);
        return std::wstring(buf.begin(), buf.end()); // Renvoyer comme std::wstring
    } else {
        if (!read_key_from_file(key_file, key, iv)) return L"";
        AES128_CTR aes(key, iv);
        aes.process(buf.data(), buf.size());
        return std::wstring(buf.begin(), buf.end()); // Idem
    }
}

// Smart Paste
void smart_paste(const std::wstring& text) {
    typing = true;
    Sleep(150);
    HWND hwnd = GetForegroundWindow();
    if (!hwnd) { typing = false; return; }
    SetForegroundWindow(hwnd);
    SetFocus(hwnd);
    Sleep(50);
    
    int delay = 10; // Uniform delay for each character
    
    for (wchar_t c : text) {
        INPUT input = {0};

        // Use KEYEVENTF_UNICODE for all characters.
        input.type = INPUT_KEYBOARD;
        input.ki.wScan = static_cast<WORD>(c); // Use character code
        input.ki.dwFlags = KEYEVENTF_UNICODE; // Indicate it's a Unicode character

        SendInput(1, &input, sizeof(INPUT)); // Key down
        Sleep(delay); // Delay before key release
        input.ki.dwFlags |= KEYEVENTF_KEYUP; // Set key up flag
        SendInput(1, &input, sizeof(INPUT)); // Key up
        Sleep(delay); // Delay before next character
    }
    typing = false;
}

void paste_thread(std::wstring text) {
    try { smart_paste(text); } catch (...) { typing = false; }
}

// Keyboard Hook
LRESULT CALLBACK kb_hook(int nCode, WPARAM wParam, LPARAM lParam) {
    if (nCode >= 0 && !typing) {
        KBDLLHOOKSTRUCT* kb = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam);
        bool was_ctrl_down = ctrl_down;

        // Track Ctrl key
        if (kb->vkCode == VK_CONTROL || kb->vkCode == VK_LCONTROL || kb->vkCode == VK_RCONTROL) {
            ctrl_down = (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN);
            return CallNextHookEx(NULL, nCode, wParam, lParam);
        }

        // Ctrl+C
      if (was_ctrl_down && (kb->vkCode == 'C' || kb->vkCode == 0x43) && (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN)) {
    Sleep(400);  // Wait for copy to complete
    std::wstring original = clipboard_get();
    if (!original.empty()) {
        std::wstring encrypted = aes_process(original, true); // Utilisation de std::wstring
        clipboard_set(enc_tag + encrypted); // Encapsulation correcte avec std::wstring
    }
    ctrl_down = false; // Reset after processing
    return 1; // Block native Ctrl+C
}

        // Ctrl+V
if (was_ctrl_down && (kb->vkCode == 'V' || kb->vkCode == 0x56) && (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN)) {
    std::wstring clipboard_data = clipboard_get();
    if (clipboard_data.find(enc_tag) == 0) {
        // Convertir en std::wstring la partie chiffrée
        std::wstring encrypted(clipboard_data.begin() + enc_tag.length(), clipboard_data.end());
        std::thread(paste_thread, aes_process(encrypted, false)).detach();
    }
    ctrl_down = false; // Reset after processing
    return 1; // Block native Ctrl+V
}

    }
    return CallNextHookEx(NULL, nCode, wParam, lParam);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    AllocConsole();
    freopen_s(reinterpret_cast<FILE**>(stdout), "CONOUT$", "w", stdout);
    freopen_s(reinterpret_cast<FILE**>(stderr), "CONOUT$", "w", stderr);
    SetConsoleTitleA("AES Clipboard Guardian");

    // Test string for validating encryption
    std::wstring test = L"Hello World Secret! éàç€CvcV 1234567890";
    auto enc = aes_process(test, true);
    auto dec = aes_process(enc, false);
    std::wcout << L"AES Test: " << (test == dec ? L"✅ OK" : L"❌ FAIL") << std::endl;

    HHOOK hook = SetWindowsHookEx(WH_KEYBOARD_LL, kb_hook, GetModuleHandle(NULL), 0);
    if (!hook) {
        std::cout << "[!] ADMIN REQUIRED!" << std::endl;
        Sleep(3000);
        return 1;
    }

    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    UnhookWindowsHookEx(hook);
    return 0;
}
 
Joined
Jun 12, 2020
Messages
60
Reaction score
3
The new version of the AES secure clipboard manager represents a significant improvement over the previous implementation, both technically and socially.
From a technical standpoint, it uses Unicode encoding (CF_UNICODETEXT) instead of the outdated ANSI, providing better compatibility with international characters and reducing the risks of text corruption often encountered in the earlier version. The introduction of RAII classes, such as ClipboardGuard and GlobalMemory, simplifies Windows resource management, helping to prevent memory leaks and locking issues. Additionally, memory leak detection through CRTDBG enhances the application's stability in production.
Regarding the keyboard hook, it has been streamlined to avoid problematic variables, and the optimization of smart pasting allows for a smoother user experience.
Socially, these improvements can help make the protection of sensitive data more accessible, though further efforts are still needed. With the use of Unicode, the tool can support multiple languages, attracting users from diverse backgrounds. This is particularly important in professional and personal contexts where the confidentiality of multicultural exchanges is desirable. Finally, the choice of a permissive MIT license encourages open-source collaboration, which is a valuable approach in addressing the challenges of digital surveillance. The .hpp files are the same as above.

C++:
/*
MIT License
Copyright (c) 2025 CoTon_TiGe_MoUaRf

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#include <iostream>
#include <windows.h>
#include <thread>
#include <atomic>
#include <fstream>
#include <vector>
#include <string>
#include "aes_ctr.hpp"
#include "csprng.hpp"

#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>

// Globals
bool ctrl_down = false;
bool typing = false;
std::wstring enc_tag = L"AES128:";
const std::string key_file = "key_iv.txt";

// Clipboard Guard Class
class ClipboardGuard {
private:
    bool is_open = false;

public:
    ClipboardGuard() {
        is_open = OpenClipboard(NULL);
    }
    ~ClipboardGuard() {
        if (is_open) CloseClipboard();
    }
    bool is_valid() const { return is_open; }
    ClipboardGuard(const ClipboardGuard&) = delete;
    ClipboardGuard& operator=(const ClipboardGuard&) = delete;
};

// Global Memory Class
class GlobalMemory {
private:
    HGLOBAL handle = nullptr;
    wchar_t* buffer = nullptr;

public:
    GlobalMemory(size_t size) {
        handle = GlobalAlloc(GMEM_MOVEABLE, size);
        if (handle) {
            buffer = static_cast<wchar_t*>(GlobalLock(handle));
        }
    }
    ~GlobalMemory() {
        if (buffer) GlobalUnlock(handle);
        if (handle) GlobalFree(handle);
    }
    wchar_t* get() const { return buffer; }
    HGLOBAL release() {
        auto temp = handle;
        handle = nullptr;
        buffer = nullptr;
        return temp;
    }
    bool is_valid() const { return buffer != nullptr; }
    GlobalMemory(const GlobalMemory&) = delete;
    GlobalMemory& operator=(const GlobalMemory&) = delete;
};

// Clipboard functions
std::wstring clipboard_get() {
    ClipboardGuard guard;
    if (!guard.is_valid()) return L"";
    std::wstring data;
    if (HANDLE h = GetClipboardData(CF_UNICODETEXT)) {
        auto* text = static_cast<wchar_t*>(GlobalLock(h));
        if (text) {
            size_t size = GlobalSize(h) / sizeof(wchar_t);
            data.assign(text, size - 1); // Exclude the NULL terminator
            GlobalUnlock(h);
        }
    }
    return data;
}

bool clipboard_set(const std::wstring& data) {
    ClipboardGuard guard;
    if (!guard.is_valid()) return false;
    EmptyClipboard();

    GlobalMemory mem((data.size() + 1) * sizeof(wchar_t));
    if (!mem.is_valid()) return false;

    memcpy(mem.get(), data.c_str(), data.size() * sizeof(wchar_t));
    mem.get()[data.size()] = 0; // NULL terminator
    SetClipboardData(CF_UNICODETEXT, mem.release());
    return true;
}

// Key/IV functions
void write_key_to_file(const std::string& filename, const uint8_t* key, const uint8_t* iv) {
    std::ofstream file(filename, std::ios::binary);
    if (!file.is_open()) return;
    file.write(reinterpret_cast<const char*>(key), 16);
    file.write(reinterpret_cast<const char*>(iv), 16);
    file.close();
}

bool read_key_from_file(const std::string& filename, uint8_t* key, uint8_t* iv) {
    std::ifstream file(filename, std::ios::binary);
    if (!file.is_open()) return false;
    file.read(reinterpret_cast<char*>(key), 16);
    file.read(reinterpret_cast<char*>(iv), 16);
    file.close();
    return true;
}

std::wstring aes_process(const std::wstring& input, bool encrypt = true) {
    if (input.empty()) return L"";
    std::vector<uint8_t> buf(input.begin(), input.end());
    uint8_t key[16], iv[16];

    if (encrypt) {
        csprng::CSPRNG rng;
        rng.generate(key, 16);
        rng.generate(iv, 16);
        AES128_CTR aes(key, iv);
        aes.process(buf.data(), buf.size());
        write_key_to_file(key_file, key, iv);
        return std::wstring(buf.begin(), buf.end());
    } else {
        if (!read_key_from_file(key_file, key, iv)) return L"";
        AES128_CTR aes(key, iv);
        aes.process(buf.data(), buf.size());
        return std::wstring(buf.begin(), buf.end());
    }
}

// Smart Paste
void smart_paste(const std::wstring& text) {
    typing = true;
    Sleep(150);
    HWND hwnd = GetForegroundWindow();
    if (!hwnd) { typing = false; return; }
    SetForegroundWindow(hwnd);
    SetFocus(hwnd);
    Sleep(50);
    
    int delay = 10; // Uniform delay for each character
    
    for (wchar_t c : text) {
        INPUT input = {0};

        input.type = INPUT_KEYBOARD;
        input.ki.wScan = static_cast<WORD>(c);
        input.ki.dwFlags = KEYEVENTF_UNICODE;

        SendInput(1, &input, sizeof(INPUT));
        Sleep(delay);
        input.ki.dwFlags |= KEYEVENTF_KEYUP;
        SendInput(1, &input, sizeof(INPUT));
        Sleep(delay);
    }
    typing = false;
}

void paste_thread(std::wstring text) {
    try { smart_paste(text); } catch (...) { typing = false; }
}

// Keyboard Hook
LRESULT CALLBACK kb_hook(int nCode, WPARAM wParam, LPARAM lParam) {
    if (nCode >= 0 && !typing) {
        KBDLLHOOKSTRUCT* kb = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam);
        bool was_ctrl_down = ctrl_down;

        if (kb->vkCode == VK_CONTROL || kb->vkCode == VK_LCONTROL || kb->vkCode == VK_RCONTROL) {
            ctrl_down = (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN);
            return CallNextHookEx(NULL, nCode, wParam, lParam);
        }

        // Ctrl+C
        if (was_ctrl_down && (kb->vkCode == 'C' || kb->vkCode == 0x43) && (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN)) {
            Sleep(400);
            std::wstring original = clipboard_get();
            if (!original.empty()) {
                std::wstring encrypted = aes_process(original, true);
                clipboard_set(enc_tag + encrypted);
            }
            ctrl_down = false;
            return 1;
        }

        // Ctrl+V
        if (was_ctrl_down && (kb->vkCode == 'V' || kb->vkCode == 0x56) && (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN)) {
            std::wstring clipboard_data = clipboard_get();
            if (clipboard_data.find(enc_tag) == 0) {
                std::wstring encrypted(clipboard_data.begin() + enc_tag.length(), clipboard_data.end());
                std::thread(paste_thread, aes_process(encrypted, false)).detach();
            }
            ctrl_down = false;
            return 1;
        }
    }
    return CallNextHookEx(NULL, nCode, wParam, lParam);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    // Enable memory leak detection
    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
    
    AllocConsole();
    freopen_s(reinterpret_cast<FILE**>(stdout), "CONOUT$", "w", stdout);
    freopen_s(reinterpret_cast<FILE**>(stderr), "CONOUT$", "w", stderr);
    SetConsoleTitleA("AES Clipboard Guardian");

    std::wstring test = L"Hello World Secret! éàç€CvcV 1234567890";
    auto enc = aes_process(test, true);
    auto dec = aes_process(enc, false);
    std::wcout << L"AES Test: " << (test == dec ? L"✅ OK" : L"❌ FAIL") << std::endl;

    HHOOK hook = SetWindowsHookEx(WH_KEYBOARD_LL, kb_hook, GetModuleHandle(NULL), 0);
    if (!hook) {
        std::cout << "[!] ADMIN REQUIRED!" << std::endl;
        Sleep(3000);
        return 1;
    }

    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    UnhookWindowsHookEx(hook);
    return 0;
}
 
Joined
Jun 12, 2020
Messages
60
Reaction score
3
The new version of the C++ code significantly enhances security compared to the previous one by protecting the key_iv.txt file, which contains the AES key and initialization vector. While the old implementation used a standard file accessible to all users, the new version creates this file with advanced security descriptors through the Windows API (SDDL/ACLAPI), limiting exclusive access to the Administrators group using a dedicated SID and an ACL configured with SetEntriesInAcl and SetSecurityDescriptorDacl. The read and write functions have been completely rewritten to utilize CreateFile with strict security attributes, including robust Windows error handling via FormatMessage and a retry mechanism for lock conflicts. The GlobalMemory class has been improved with move semantics support, zero initialization, and safer RAII resource management, while file creation now incorporates a systematic cleanup of resources to prevent memory leaks. These enhancements ensure that non-privileged users cannot read or modify the cryptographic key, making file extraction attacks virtually impossible without privilege escalation. The .hpp files are the same as above.

C++:
/*
MIT License
Copyright (c) 2025 CoTon_TiGe_MoUaRf

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.


THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <iostream>

#include <windows.h>

#include <sddl.h>

#include <aclapi.h>

#include <thread>

#include <atomic>

#include <fstream>

#include <vector>

#include <string>

#include <stdexcept>

#include <utility>

#include <mutex>


#include "aes_ctr.hpp"

#include "csprng.hpp"


#define _CRTDBG_MAP_ALLOC

#include <crtdbg.h>


#pragma comment(lib, "advapi32.lib")


#ifdef DEBUG

#define DEBUG_LOG(x) do { std::cout << x << std::endl; } while(0)

#else

#define DEBUG_LOG(x) do {} while(0)

#endif


// ============================================================================

// RAII GUARDS POUR LES RESSOURCES WINDOWS

// ============================================================================


class SidGuard {

    PSID sid;

public:

    explicit SidGuard(PSID s) : sid(s) {}

    ~SidGuard() { if (sid) FreeSid(sid); }

    SidGuard(const SidGuard&) = delete;

};


class AclGuard {

    PACL acl;

public:

    explicit AclGuard(PACL a) : acl(a) {}

    ~AclGuard() { if (acl) LocalFree(acl); }

    AclGuard(const AclGuard&) = delete;

};


class SecurityDescriptorGuard {

    PSECURITY_DESCRIPTOR sd;

public:

    explicit SecurityDescriptorGuard(PSECURITY_DESCRIPTOR s) : sd(s) {}

    ~SecurityDescriptorGuard() { if (sd) LocalFree(sd); }

    SecurityDescriptorGuard(const SecurityDescriptorGuard&) = delete;

};


class HandleGuard {

    HANDLE h;

public:

    explicit HandleGuard(HANDLE handle) : h(handle) {}

    ~HandleGuard() { if (h && h != INVALID_HANDLE_VALUE) CloseHandle(h); }

    HANDLE release() { auto res = h; h = INVALID_HANDLE_VALUE; return res; }

    HandleGuard(const HandleGuard&) = delete;

};


// ============================================================================

// AFFICHAGE DES ERREURS WINDOWS

// ============================================================================


void PrintErrorMessage(const char* functionName, DWORD dwError) {

    LPVOID lpMsgBuf;

    FormatMessageA(

        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,

        NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),

        (LPSTR)&lpMsgBuf, 0, NULL

    );

    std::cerr << "Erreur dans " << functionName << " (Code: " << dwError << "): " 

              << (LPSTR)lpMsgBuf << std::endl;

    LocalFree(lpMsgBuf);

}


// ============================================================================

// CRÉATION DU FICHIER key_iv.txt PROTÉGÉ AVEC RAII

// ============================================================================


// ============================================================================

// CRÉATION DU FICHIER key_iv.txt PROTÉGÉ AVEC RAII (CORRIGÉ)

// ============================================================================


bool CreateProtectedKeyFile() {

    const std::string key_file = "key_iv.txt";

    const int MAX_RETRIES = 3;

    const int RETRY_DELAY_MS = 500;

    

    std::cout << "=== Création du fichier key_iv.txt protégé ===" << std::endl;


    // Étape 1 : Création du SID administrateur (CORRIGÉ)

    PSID pAdminSID = NULL;

    SID_IDENTIFIER_AUTHORITY SIDAuthNT = SECURITY_NT_AUTHORITY;

    if (!AllocateAndInitializeSid(&SIDAuthNT, 2, SECURITY_BUILTIN_DOMAIN_RID,

        DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pAdminSID)) {

        PrintErrorMessage("AllocateAndInitializeSid", GetLastError());

        return false;

    }

    SidGuard sidGuard(pAdminSID);


    // Étape 2 : Configuration et création de l'ACL

    EXPLICIT_ACCESS ea[1] = {};

    ea[0].grfAccessPermissions = FILE_ALL_ACCESS;

    ea[0].grfAccessMode = SET_ACCESS;

    ea[0].grfInheritance = NO_INHERITANCE;

    ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;

    ea[0].Trustee.TrusteeType = TRUSTEE_IS_GROUP;

    ea[0].Trustee.ptstrName = (LPSTR)pAdminSID;


    PACL pACL = NULL;

    DWORD dwRes = SetEntriesInAclA(1, ea, NULL, &pACL);

    if (ERROR_SUCCESS != dwRes) {

        PrintErrorMessage("SetEntriesInAcl", dwRes);

        return false;

    }

    AclGuard aclGuard(pACL);


    // Étape 3 : Création du descripteur de sécurité

    PSECURITY_DESCRIPTOR pSD = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);

    if (!pSD) {

        PrintErrorMessage("LocalAlloc", GetLastError());

        return false;

    }

    SecurityDescriptorGuard sdGuard(pSD);


    if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)) {

        PrintErrorMessage("InitializeSecurityDescriptor", GetLastError());

        return false;

    }


    if (!SetSecurityDescriptorDacl(pSD, TRUE, pACL, FALSE)) {

        PrintErrorMessage("SetSecurityDescriptorDacl", GetLastError());

        return false;

    }


    // Étape 4 : Configuration des attributs de sécurité

    SECURITY_ATTRIBUTES sa = {};

    sa.nLength = sizeof(SECURITY_ATTRIBUTES);

    sa.lpSecurityDescriptor = pSD;

    sa.bInheritHandle = FALSE;


    // Étape 5 : Création du fichier avec retry mechanism

    for (int retryCount = 0; retryCount < MAX_RETRIES; ++retryCount) {

        HANDLE hFile = CreateFileA(key_file.c_str(), GENERIC_READ | GENERIC_WRITE, 0, &sa,

            CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);


        if (hFile != INVALID_HANDLE_VALUE) {

            HandleGuard fileGuard(hFile);

            std::cout << "✓ Fichier key_iv.txt créé avec droits administrateur" << std::endl;

            return true;

        }


        DWORD dwError = GetLastError();

        switch (dwError) {

            case ERROR_ACCESS_DENIED:

                std::cerr << "✗ Erreur : Accès refusé. Droits administrateur requis." << std::endl;

                PrintErrorMessage("CreateFileA", dwError);

                return false;


            case ERROR_SHARING_VIOLATION:

                std::cerr << "⚠ Fichier verrouillé (tentative " << (retryCount + 1) << "/" 

                          << MAX_RETRIES << ")" << std::endl;

                if (retryCount < MAX_RETRIES - 1) Sleep(RETRY_DELAY_MS);

                break;


            default:

                std::cerr << "⚠ Erreur inattendue (tentative " << (retryCount + 1) << "/" 

                          << MAX_RETRIES << ")" << std::endl;

                PrintErrorMessage("CreateFileA", dwError);

                if (retryCount < MAX_RETRIES - 1) Sleep(RETRY_DELAY_MS);

                break;

        }

    }


    std::cerr << "✗ Échec création après " << MAX_RETRIES << " tentatives." << std::endl;

    return false;

}


// ============================================================================

// ÉCRITURE DANS LE FICHIER key_iv.txt PROTÉGÉ (CORRIGÉ)

// ============================================================================


bool WriteKeyToProtectedFile(const uint8_t* key, const uint8_t* iv) {

    const std::string key_file = "key_iv.txt";

    

    HANDLE hFile = CreateFileA(key_file.c_str(), GENERIC_WRITE, 0, NULL, 

        OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);


    if (hFile == INVALID_HANDLE_VALUE) {

        PrintErrorMessage("CreateFileA (écriture)", GetLastError());

        return false;

    }

    HandleGuard fileGuard(hFile);


    DWORD writtenBytes = 0;

    bool keyWritten = WriteFile(hFile, key, 16, &writtenBytes, NULL) && writtenBytes == 16;

    bool ivWritten = WriteFile(hFile, iv, 16, &writtenBytes, NULL) && writtenBytes == 16;


    DEBUG_LOG("✓ Clé/IV écrite dans key_iv.txt (" << (keyWritten && ivWritten ? "OK" : "FAIL") << ")");


    return keyWritten && ivWritten;

}


// ============================================================================

// LECTURE DU FICHIER key_iv.txt PROTÉGÉ

// ============================================================================


bool ReadKeyFromProtectedFile(uint8_t* key, uint8_t* iv) {

    const std::string key_file = "key_iv.txt";

    

    HANDLE hFile = CreateFileA(key_file.c_str(), GENERIC_READ, FILE_SHARE_READ,

        NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);


    if (hFile == INVALID_HANDLE_VALUE) {

        PrintErrorMessage("CreateFileA (lecture)", GetLastError());

        return false;

    }

    HandleGuard fileGuard(hFile);


    DWORD readBytes = 0;

    bool keyRead = ReadFile(hFile, key, 16, &readBytes, NULL) && readBytes == 16;

    bool ivRead = ReadFile(hFile, iv, 16, &readBytes, NULL) && readBytes == 16;


    DEBUG_LOG("✓ Clé/IV lue depuis key_iv.txt (" << (keyRead && ivRead ? "OK" : "FAIL") << ")");


    return keyRead && ivRead;

}


// ============================================================================

// CLASSES ET FONCTIONS AMÉLIORÉES (THREAD-SAFE)

// ============================================================================


std::atomic<bool> ctrl_down{false};

std::atomic<bool> typing{false};

std::wstring enc_tag = L"AES128:";

static constexpr size_t MAX_CLIPBOARD_SIZE = 1024 * 1024; // 1MB


class ClipboardGuard {

private:

    bool is_open = false;

public:

    ClipboardGuard() { is_open = OpenClipboard(NULL); }

    ~ClipboardGuard() { if (is_open) CloseClipboard(); }

    bool is_valid() const { return is_open; }

    ClipboardGuard(const ClipboardGuard&) = delete;

};


class GlobalMemory {

private:

    HGLOBAL handle = nullptr;

    wchar_t* buffer = nullptr;

    size_t allocated_size = 0;


public:

    explicit GlobalMemory(size_t size) : allocated_size(size) {

        if (size == 0) return;

        handle = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, size);

        if (!handle) {

            allocated_size = 0;

            throw std::bad_alloc();

        }

        buffer = static_cast<wchar_t*>(GlobalLock(handle));

        if (!buffer) {

            GlobalFree(handle);

            handle = nullptr;

            allocated_size = 0;

            throw std::runtime_error("GlobalLock failed");

        }

    }


    GlobalMemory(GlobalMemory&& other) noexcept 

        : handle(other.handle), buffer(other.buffer), allocated_size(other.allocated_size) {

        other.handle = nullptr; other.buffer = nullptr; other.allocated_size = 0;

    }


    GlobalMemory& operator=(GlobalMemory&& other) noexcept {

        if (this != &other) {

            cleanup();

            handle = other.handle;

            buffer = other.buffer;

            allocated_size = other.allocated_size;

            other.handle = nullptr; other.buffer = nullptr; other.allocated_size = 0;

        }

        return *this;

    }


    ~GlobalMemory() { cleanup(); }


    wchar_t* get() const noexcept { return buffer; }

    const wchar_t* get_const() const noexcept { return buffer; }

    HGLOBAL release() noexcept {

        if (buffer && handle) GlobalUnlock(handle);

        auto temp = handle; handle = nullptr; buffer = nullptr; allocated_size = 0;

        return temp;

    }

    bool is_valid() const noexcept { return buffer && handle && allocated_size > 0; }

    size_t size() const noexcept { return allocated_size; }


    GlobalMemory(const GlobalMemory&) = delete;

    GlobalMemory& operator=(const GlobalMemory&) = delete;


private:

    void cleanup() noexcept {

        if (buffer && handle) {

            GlobalUnlock(handle);

            buffer = nullptr;

        }

        if (handle) {

            GlobalFree(handle);

            handle = nullptr;

        }

        allocated_size = 0;

    }

};


std::wstring clipboard_get() {

    ClipboardGuard guard;

    if (!guard.is_valid()) return L"";


    std::wstring data;

    if (HANDLE h = GetClipboardData(CF_UNICODETEXT)) {

        auto* text = static_cast<wchar_t*>(GlobalLock(h));

        if (text) {

            size_t size = GlobalSize(h) / sizeof(wchar_t);

            if (size > MAX_CLIPBOARD_SIZE / sizeof(wchar_t)) {

                GlobalUnlock(h);

                return L""; // Protection DoS

            }

            data.assign(text, size - 1);

            GlobalUnlock(h);

        }

    }

    return data;

}


bool clipboard_set(const std::wstring& data) {

    if (data.size() * sizeof(wchar_t) > MAX_CLIPBOARD_SIZE) return false;


    ClipboardGuard guard;

    if (!guard.is_valid()) return false;


    EmptyClipboard();

    GlobalMemory mem((data.size() + 1) * sizeof(wchar_t));

    if (!mem.is_valid()) return false;


    memcpy(mem.get(), data.c_str(), data.size() * sizeof(wchar_t));

    mem.get()[data.size()] = 0;

    SetClipboardData(CF_UNICODETEXT, mem.release());

    return true;

}


std::wstring aes_process(const std::wstring& input, bool encrypt = true) {

    if (input.size() * sizeof(wchar_t) > MAX_CLIPBOARD_SIZE) return L"";


    std::vector<uint8_t> buf(input.begin(), input.end());

    uint8_t key[16], iv[16];


    if (encrypt) {

        csprng::CSPRNG rng;

        rng.generate(key, 16);

        rng.generate(iv, 16);

        AES128_CTR aes(key, iv);

        aes.process(buf.data(), buf.size());

        return WriteKeyToProtectedFile(key, iv) ? 

            std::wstring(buf.begin(), buf.end()) : L"";

    } else {

        if (!ReadKeyFromProtectedFile(key, iv)) return L"";

        AES128_CTR aes(key, iv);

        aes.process(buf.data(), buf.size());

        return std::wstring(buf.begin(), buf.end());

    }

}


void smart_paste(const std::wstring& text) {

    typing = true;

    Sleep(150);

    

    HWND hwnd = GetForegroundWindow();

    if (!hwnd) { typing = false; return; }

    

    SetForegroundWindow(hwnd);

    SetFocus(hwnd);

    Sleep(50);


    constexpr int delay = 10;

    for (wchar_t c : text) {

        INPUT input = {};

        input.type = INPUT_KEYBOARD;

        input.ki.wScan = static_cast<WORD>(c);

        input.ki.dwFlags = KEYEVENTF_UNICODE;

        SendInput(1, &input, sizeof(INPUT));

        Sleep(delay);

        

        input.ki.dwFlags |= KEYEVENTF_KEYUP;

        SendInput(1, &input, sizeof(INPUT));

        Sleep(delay);

    }

    typing = false;

}


void paste_thread(std::wstring text) {

    try { smart_paste(std::move(text)); } catch (...) { typing = false; }

}


LRESULT CALLBACK kb_hook(int nCode, WPARAM wParam, LPARAM lParam) {

    if (nCode >= 0 && !typing.load()) {

        KBDLLHOOKSTRUCT* kb = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam);

        bool was_ctrl_down = ctrl_down.load();


        if (kb->vkCode == VK_CONTROL || kb->vkCode == VK_LCONTROL || kb->vkCode == VK_RCONTROL) {

            ctrl_down = (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN);

            return CallNextHookEx(NULL, nCode, wParam, lParam);

        }


        if (was_ctrl_down && (kb->vkCode == 'C' || kb->vkCode == 0x43) && 

            (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN)) {

            

            Sleep(400);

            std::wstring original = clipboard_get();

            if (!original.empty()) {

                std::wstring encrypted = aes_process(original, true);

                clipboard_set(enc_tag + encrypted);

            }

            ctrl_down = false;

            return 1;

        }


        if (was_ctrl_down && (kb->vkCode == 'V' || kb->vkCode == 0x56) && 

            (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN)) {

            

            std::wstring clipboard_data = clipboard_get();

            if (clipboard_data.find(enc_tag) == 0) {

                std::wstring encrypted(clipboard_data.begin() + enc_tag.length(), clipboard_data.end());

                std::thread(paste_thread, aes_process(encrypted, false)).detach();

            }

            ctrl_down = false;

            return 1;

        }

    }

    return CallNextHookEx(NULL, nCode, wParam, lParam);

}


int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {

    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);

    

    AllocConsole();

    freopen_s(reinterpret_cast<FILE**>(stdout), "CONOUT$", "w", stdout);

    freopen_s(reinterpret_cast<FILE**>(stderr), "CONOUT$", "w", stderr);

    SetConsoleTitleA("AES Clipboard Guardian - Protected Keys");


    // Créer le fichier key_iv.txt protégé

    if (!CreateProtectedKeyFile()) {

        std::cout << "[!] Échec création fichier protégé. Droits administrateur requis." << std::endl;

        Sleep(3000);

        return 1;

    }


    // Test AES

    std::wstring test = L"Hello World Secret! éàç€CvcV 1234567890";

    auto enc = aes_process(test, true);

    if (enc.empty()) {

        std::wcout << L"❌ Test AES échoué (écriture clé)" << std::endl;

        Sleep(3000);

        return 1;

    }

    auto dec = aes_process(enc, false);

    std::wcout << L"AES Test: " << (test == dec ? L"✅ OK" : L"❌ FAIL") << std::endl;


    HHOOK hook = SetWindowsHookEx(WH_KEYBOARD_LL, kb_hook, GetModuleHandle(NULL), 0);

    if (!hook) {

        std::cout << "[!] Droits administrateur requis pour hook clavier!" << std::endl;

        Sleep(3000);

        return 1;

    }


    MSG msg;

    while (GetMessage(&msg, NULL, 0, 0)) {

        TranslateMessage(&msg);

        DispatchMessage(&msg);

    }


    UnhookWindowsHookEx(hook);

    return 0;

}
 
Last edited:
Joined
Jun 12, 2020
Messages
60
Reaction score
3
Here’s the link to download only the project’s source files for the finished project: aes_ctr.hpp, csprng.hpp, error_logger.hpp, and main.cpp, along with compiler.txt containing the compiler and linker options for building with Embarcadero Dev-C++. The .hpp files have also been updated.

1fichier.com
 
Joined
Jun 12, 2020
Messages
60
Reaction score
3
Aside from compiling the cryptographic part into a crypto DLL, the following code improves secure copy/paste by preventing focus theft (for the destination window) during data pasting. I'll leave it to you to write the code using the .hpp files from the previous posts. I prefer to keep my DLL to myself for now. Sorry.

C++:
/*

MIT License

Copyright (c) 2025 CoTon_TiGe_MoUaRf


Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:


The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.


THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

*/


#include <iostream>

#include <windows.h>

#include <sddl.h>

#include <aclapi.h>

#include <thread>

#include <atomic>

#include <fstream>

#include <vector>

#include <string>

#include <stdexcept>

#include <utility>

#include <mutex>

#include <iomanip>

#include <atomic>
#include <thread>
#include <chrono>

#include <algorithm> // std::transform
#include <cwctype>   // std::towlower
#define _CRTDBG_MAP_ALLOC

#include <crtdbg.h>


#pragma comment(lib, "advapi32.lib")


#ifdef DEBUG

#define DEBUG_LOG(x) do { std::cout << x << std::endl; } while(0)

#else

#define DEBUG_LOG(x) do {} while(0)

#endif


// ============================================================================

// DÉCLARATIONS DES FONCTIONS DE LA DLL

// ============================================================================

static std::atomic<bool> typing{false};
typedef void (*crypto_generate_iv_t)(uint8_t* iv_out);

typedef void (*crypto_encrypt_t)(const uint8_t* key, const uint8_t* iv, const uint8_t* plaintext, size_t plaintext_len, uint8_t* ciphertext_out);

typedef void (*crypto_decrypt_t)(const uint8_t* key, const uint8_t* iv, const uint8_t* ciphertext, size_t ciphertext_len, uint8_t* plaintext_out);


// Pointeurs de fonction

crypto_generate_iv_t crypto_generate_iv = nullptr;

crypto_encrypt_t crypto_encrypt = nullptr;

crypto_decrypt_t crypto_decrypt = nullptr;


// ============================================================================

// CHARGEMENT DE LA DLL

// ============================================================================


bool load_crypto_dll() {

    HINSTANCE hDll = LoadLibraryA("crypto_dll.dll");

    if (!hDll) {

        std::cerr << "Erreur chargement DLL: " << GetLastError() << std::endl;

        return false;

    }


    crypto_generate_iv = (crypto_generate_iv_t)GetProcAddress(hDll, "crypto_generate_iv");

    crypto_encrypt = (crypto_encrypt_t)GetProcAddress(hDll, "crypto_encrypt");

    crypto_decrypt = (crypto_decrypt_t)GetProcAddress(hDll, "crypto_decrypt");


    bool all_ok = crypto_generate_iv && crypto_encrypt && crypto_decrypt;

    if (!all_ok) {

        std::cerr << "Erreur: Fonctions DLL manquantes" << std::endl;

        FreeLibrary(hDll);

        return false;

    }


    std::cout << "✓ DLL crypto chargée !" << std::endl;

    return true;

}


// ============================================================================

// RAII GUARDS POUR LES RESSOURCES WINDOWS

// ============================================================================


class SidGuard {

    PSID sid;

public:

    explicit SidGuard(PSID s) : sid(s) {}

    ~SidGuard() { if (sid) FreeSid(sid); }

    SidGuard(const SidGuard&) = delete;

};


class AclGuard {

    PACL acl;

public:

    explicit AclGuard(PACL a) : acl(a) {}

    ~AclGuard() { if (acl) LocalFree(acl); }

    AclGuard(const AclGuard&) = delete;

};


class SecurityDescriptorGuard {

    PSECURITY_DESCRIPTOR sd;

public:

    explicit SecurityDescriptorGuard(PSECURITY_DESCRIPTOR s) : sd(s) {}

    ~SecurityDescriptorGuard() { if (sd) LocalFree(sd); }

    SecurityDescriptorGuard(const SecurityDescriptorGuard&) = delete;

};


class HandleGuard {

    HANDLE h;

public:

    explicit HandleGuard(HANDLE handle) : h(handle) {}

    ~HandleGuard() { if (h && h != INVALID_HANDLE_VALUE) CloseHandle(h); }

    HANDLE release() { auto res = h; h = INVALID_HANDLE_VALUE; return res; }

    HandleGuard(const HandleGuard&) = delete;

};


// ============================================================================

// AFFICHAGE DES ERREURS WINDOWS

// ============================================================================


void PrintErrorMessage(const char* functionName, DWORD dwError) {

    LPVOID lpMsgBuf;

    FormatMessageA(

        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,

        NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),

        (LPSTR)&lpMsgBuf, 0, NULL

    );

    std::cerr << "Erreur dans " << functionName << " (Code: " << dwError << "): "

              << (LPSTR)lpMsgBuf << std::endl;

    LocalFree(lpMsgBuf);

}


// ============================================================================

// CRÉATION DU FICHIER key_iv.txt PROTÉGÉ AVEC RAII

// ============================================================================


bool CreateProtectedKeyFile() {

    const std::string key_file = "key_iv.txt";

    const int MAX_RETRIES = 3;

    const int RETRY_DELAY_MS = 500;

    

    std::cout << "=== Création du fichier key_iv.txt protégé ===" << std::endl;


    // Étape 1 : Création du SID administrateur

    PSID pAdminSID = NULL;

    SID_IDENTIFIER_AUTHORITY SIDAuthNT = SECURITY_NT_AUTHORITY;

    if (!AllocateAndInitializeSid(&SIDAuthNT, 2, SECURITY_BUILTIN_DOMAIN_RID,

        DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pAdminSID)) {

        PrintErrorMessage("AllocateAndInitializeSid", GetLastError());

        return false;

    }

    SidGuard sidGuard(pAdminSID);


    // Étape 2 : Configuration et création de l'ACL

    EXPLICIT_ACCESS ea[1] = {};

    ea[0].grfAccessPermissions = FILE_ALL_ACCESS;

    ea[0].grfAccessMode = SET_ACCESS;

    ea[0].grfInheritance = NO_INHERITANCE;

    ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;

    ea[0].Trustee.TrusteeType = TRUSTEE_IS_GROUP;

    ea[0].Trustee.ptstrName = (LPSTR)pAdminSID;


    PACL pACL = NULL;

    DWORD dwRes = SetEntriesInAclA(1, ea, NULL, &pACL);

    if (ERROR_SUCCESS != dwRes) {

        PrintErrorMessage("SetEntriesInAcl", dwRes);

        return false;

    }

    AclGuard aclGuard(pACL);


    // Étape 3 : Création du descripteur de sécurité

    PSECURITY_DESCRIPTOR pSD = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);

    if (!pSD) {

        PrintErrorMessage("LocalAlloc", GetLastError());

        return false;

    }

    SecurityDescriptorGuard sdGuard(pSD);


    if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)) {

        PrintErrorMessage("InitializeSecurityDescriptor", GetLastError());

        return false;

    }


    if (!SetSecurityDescriptorDacl(pSD, TRUE, pACL, FALSE)) {

        PrintErrorMessage("SetSecurityDescriptorDacl", GetLastError());

        return false;

    }


    // Étape 4 : Configuration des attributs de sécurité

    SECURITY_ATTRIBUTES sa = {};

    sa.nLength = sizeof(SECURITY_ATTRIBUTES);

    sa.lpSecurityDescriptor = pSD;

    sa.bInheritHandle = FALSE;


    // Étape 5 : Création du fichier avec retry mechanism

    for (int retryCount = 0; retryCount < MAX_RETRIES; ++retryCount) {

        HANDLE hFile = CreateFileA(key_file.c_str(), GENERIC_READ | GENERIC_WRITE, 0, &sa,

            CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);


        if (hFile != INVALID_HANDLE_VALUE) {

            HandleGuard fileGuard(hFile);

            std::cout << "✓ Fichier key_iv.txt créé avec droits administrateur" << std::endl;

            return true;

        }


        DWORD dwError = GetLastError();

        switch (dwError) {

            case ERROR_ACCESS_DENIED:

                std::cerr << "✗ Erreur : Accès refusé. Droits administrateur requis." << std::endl;

                PrintErrorMessage("CreateFileA", dwError);

                return false;

            case ERROR_SHARING_VIOLATION:

                std::cerr << "⚠ Fichier verrouillé (tentative " << (retryCount + 1) << "/"

                          << MAX_RETRIES << ")" << std::endl;

                if (retryCount < MAX_RETRIES - 1) Sleep(RETRY_DELAY_MS);

                break;

            default:

                std::cerr << "⚠ Erreur inattendue (tentative " << (retryCount + 1) << "/"

                          << MAX_RETRIES << ")" << std::endl;

                PrintErrorMessage("CreateFileA", dwError);

                if (retryCount < MAX_RETRIES - 1) Sleep(RETRY_DELAY_MS);

                break;

        }

    }


    std::cerr << "✗ Échec création après " << MAX_RETRIES << " tentatives." << std::endl;

    return false;

}


// ============================================================================

// ÉCRITURE DANS LE FICHIER key_iv.txt PROTÉGÉ

// ============================================================================


bool WriteKeyToProtectedFile(const uint8_t* key, const uint8_t* iv) {

    const std::string key_file = "key_iv.txt";

    

    HANDLE hFile = CreateFileA(key_file.c_str(),

        GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING,

        FILE_ATTRIBUTE_NORMAL, NULL);


    if (hFile == INVALID_HANDLE_VALUE) {

        PrintErrorMessage("CreateFileA (écriture)", GetLastError());

        return false;

    }

    HandleGuard fileGuard(hFile);


    DWORD writtenBytes = 0;

    bool keyWritten = WriteFile(hFile, key, 16, &writtenBytes, NULL) && writtenBytes == 16;

    bool ivWritten = WriteFile(hFile, iv, 16, &writtenBytes, NULL) && writtenBytes == 16;


    DEBUG_LOG("✓ Clé/IV écrite dans key_iv.txt (" << (keyWritten && ivWritten ? "OK" : "FAIL") << ")");

    return keyWritten && ivWritten;

}


// ============================================================================

// LECTURE DU FICHIER key_iv.txt PROTÉGÉ

// ============================================================================


bool ReadKeyFromProtectedFile(uint8_t* key, uint8_t* iv) {

    const std::string key_file = "key_iv.txt";

    

    HANDLE hFile = CreateFileA(key_file.c_str(), GENERIC_READ, FILE_SHARE_READ,

        NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);


    if (hFile == INVALID_HANDLE_VALUE) {

        PrintErrorMessage("CreateFileA (lecture)", GetLastError());

        return false;

    }

    HandleGuard fileGuard(hFile);


    DWORD readBytes = 0;

    bool keyRead = ReadFile(hFile, key, 16, &readBytes, NULL) && readBytes == 16;

    bool ivRead = ReadFile(hFile, iv, 16, &readBytes, NULL) && readBytes == 16;


    DEBUG_LOG("✓ Clé/IV lue depuis key_iv.txt (" << (keyRead && ivRead ? "OK" : "FAIL") << ")");

    return keyRead && ivRead;

}


// ============================================================================

// CLASSES ET FONCTIONS AMÉLIORÉES (THREAD-SAFE)

// ============================================================================


std::atomic<bool> ctrl_down{false};



std::wstring enc_tag = L"AES128:";

//static constexpr size_t MAX_CLIPBOARD_SIZE = 1024 * 1024; // 1MB
static constexpr size_t MAX_CLIPBOARD_SIZE = 16 * 1024 * 1024;

class ClipboardGuard {

private:

    bool is_open = false;

public:

    ClipboardGuard() { is_open = OpenClipboard(NULL); }

    ~ClipboardGuard() { if (is_open) CloseClipboard(); }

    bool is_valid() const { return is_open; }

    ClipboardGuard(const ClipboardGuard&) = delete;

};


class GlobalMemory {

private:

    HGLOBAL handle = nullptr;

    wchar_t* buffer = nullptr;

    size_t allocated_size = 0;


public:

    explicit GlobalMemory(size_t size) : allocated_size(size) {

        if (size == 0) return;

        handle = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, size);

        if (!handle) {

            allocated_size = 0;

            throw std::bad_alloc();

        }

        buffer = static_cast<wchar_t*>(GlobalLock(handle));

        if (!buffer) {

            GlobalFree(handle);

            handle = nullptr;

            allocated_size = 0;

            throw std::runtime_error("GlobalLock failed");

        }

    }


    GlobalMemory(GlobalMemory&& other) noexcept

        : handle(other.handle), buffer(other.buffer), allocated_size(other.allocated_size) {

        other.handle = nullptr; other.buffer = nullptr; other.allocated_size = 0;

    }


    GlobalMemory& operator=(GlobalMemory&& other) noexcept {

        if (this != &other) {

            cleanup();

            handle = other.handle;

            buffer = other.buffer;

            allocated_size = other.allocated_size;

            other.handle = nullptr; other.buffer = nullptr; other.allocated_size = 0;

        }

        return *this;

    }


    ~GlobalMemory() { cleanup(); }


    wchar_t* get() const noexcept { return buffer; }

    const wchar_t* get_const() const noexcept { return buffer; }

    HGLOBAL release() noexcept {

        if (buffer && handle) GlobalUnlock(handle);

        auto temp = handle; handle = nullptr; buffer = nullptr; allocated_size = 0;

        return temp;

    }

    bool is_valid() const noexcept { return buffer && handle && allocated_size > 0; }

    size_t size() const noexcept { return allocated_size; }


    GlobalMemory(const GlobalMemory&) = delete;

    GlobalMemory& operator=(const GlobalMemory&) = delete;


private:

    void cleanup() noexcept {

        if (buffer && handle) {

            GlobalUnlock(handle);

            buffer = nullptr;

        }

        if (handle) {

            GlobalFree(handle);

            handle = nullptr;

        }

        allocated_size = 0;

    }

};


std::wstring clipboard_get() {

    ClipboardGuard guard;

    if (!guard.is_valid()) return L"";


    std::wstring data;

    if (HANDLE h = GetClipboardData(CF_UNICODETEXT)) {

        auto* text = static_cast<wchar_t*>(GlobalLock(h));

        if (text) {

            size_t size = GlobalSize(h) / sizeof(wchar_t);

            if (size > MAX_CLIPBOARD_SIZE / sizeof(wchar_t)) {

                GlobalUnlock(h);

                return L""; // Protection DoS

            }

            data.assign(text, size - 1);

            GlobalUnlock(h);

        }

    }

    return data;

}


bool clipboard_set(const std::wstring& data) {

    if (data.size() * sizeof(wchar_t) > MAX_CLIPBOARD_SIZE) return false;


    ClipboardGuard guard;

    if (!guard.is_valid()) return false;


    EmptyClipboard();

    GlobalMemory mem((data.size() + 1) * sizeof(wchar_t));

    if (!mem.is_valid()) return false;


    memcpy(mem.get(), data.c_str(), data.size() * sizeof(wchar_t));

    mem.get()[data.size()] = 0;

    SetClipboardData(CF_UNICODETEXT, mem.release());

    return true;

}


// ============================================================================

// CHIFFREMENT/DÉCHIFFREMENT AVEC LA DLL

// ============================================================================


std::wstring aes_process(const std::wstring& input, bool encrypt = true) {

    if (input.size() * sizeof(wchar_t) > MAX_CLIPBOARD_SIZE) return L"";


    std::vector<uint8_t> buf(input.begin(), input.end());

    uint8_t key[16], iv[16];


    if (encrypt) {

        // Générer clé et IV avec la DLL

        crypto_generate_iv(key);  // Première génération pour la clé

        crypto_generate_iv(iv);   // Deuxième génération pour l'IV

        

        // Chiffrer avec la DLL

        crypto_encrypt(key, iv, buf.data(), buf.size(), buf.data());

        

        return WriteKeyToProtectedFile(key, iv) ?

            std::wstring(buf.begin(), buf.end()) : L"";

    } else {

        if (!ReadKeyFromProtectedFile(key, iv)) return L"";

        

        // Déchiffrer avec la DLL

        crypto_decrypt(key, iv, buf.data(), buf.size(), buf.data());

        

        return std::wstring(buf.begin(), buf.end());

    }

}


// Capture the foreground top-level window at the time of Ctrl+V.
// Returns HWND (may be NULL).
static HWND capture_target_window_for_paste()
{
    HWND fg = GetForegroundWindow();
    if (!fg || !IsWindow(fg)) return NULL;
    HWND top = GetAncestor(fg, GA_ROOT);
    return (top && IsWindow(top)) ? top : fg;
}

// Check if className (wide) contains a given substring (case-insensitive).
// Check if className (wide) contains a given substring (case-sensitive OK).

static bool wstristr_contains(const std::wstring& hay, const wchar_t* needle)

{

    if (!needle || !*needle) return false;

    std::wstring needleStr(needle);

    return hay.find(needleStr) != std::wstring::npos;

}

// Enumerate children and collect candidates.
static BOOL CALLBACK EnumChildProcCollect(HWND hwnd, LPARAM lParam)
{
    auto *vec = reinterpret_cast<std::vector<HWND>*>(lParam);
    if (IsWindow(hwnd)) vec->push_back(hwnd);
    return TRUE;
}

// Find focused child or best text control inside `parent`.
static HWND find_focused_child_or_text_control(HWND parent)
{
    if (!IsWindow(parent)) return NULL;

    // 1) Try to get the real focused control that is a descendant of parent.
    // GetFocus works per-thread; if the focused window is in another thread,
    // it won't be returned. We'll attempt to attach input to retrieve it safely.
    HWND focused = NULL;
    DWORD fgThread = 0, curThread = 0;
    HWND foreground = GetForegroundWindow();
    if (foreground) fgThread = GetWindowThreadProcessId(foreground, NULL);
    curThread = GetCurrentThreadId();

    BOOL attached = FALSE;
    if (fgThread != 0 && fgThread != curThread) {
        attached = AttachThreadInput(curThread, fgThread, TRUE);
    }

    focused = GetFocus(); // may return control in this thread after AttachThreadInput
    if (attached) AttachThreadInput(curThread, fgThread, FALSE);

    if (focused && IsWindow(focused) && (focused == parent || IsChild(parent, focused))) {
        return focused;
    }

    // 2) Collect children (including nested) to find a good text control candidate.
    std::vector<HWND> children;
    EnumChildWindows(parent, EnumChildProcCollect, reinterpret_cast<LPARAM>(&children));

    // Iterate children in reverse Z-order (EnumChildWindows gives top-down; we prefer later windows first).
    for (auto it = children.rbegin(); it != children.rend(); ++it) {
        HWND child = *it;
        if (!IsWindow(child)) continue;

        // Skip invisible or disabled controls
        if (!(IsWindowVisible(child) && IsWindowEnabled(child))) continue;

        // Get class name (wide)
        wchar_t className[256] = {0};
        if (RealGetWindowClassW(child, className, _countof(className))) {
            std::wstring cls(className);

            // Common text control classes
            if (wstristr_contains(cls, L"edit") ||
                wstristr_contains(cls, L"richedit") ||
                wstristr_contains(cls, L"rich") ||
                wstristr_contains(cls, L"listbox") ||
                wstristr_contains(cls, L"combobox") ||
                wstristr_contains(cls, L"msctls_statusbar32") // some apps
                ) {
                return child;
            }
        }

        // As a fallback, controls with WS_TABSTOP are candidates
        LONG_PTR style = GetWindowLongPtrW(child, GWL_STYLE);
        if (style & WS_TABSTOP) return child;
    }

    // 3) Fallback to parent itself
    return parent;
}

// Helper to send a character to hwnd. Sends WM_CHAR for printable chars.
// For Enter/Tab/Backspace, also sends WM_KEYDOWN/WM_KEYUP for better compatibility.
static void post_char_to_window(HWND hwnd, wchar_t ch)
{
    if (!IsWindow(hwnd)) return;

    // Printable characters
    if (ch >= 0x20) {
        PostMessageW(hwnd, WM_CHAR, (WPARAM)ch, 0);
        return;
    }

    // Control characters: handle common ones
    switch (ch) {
    case L'\r': // Carriage return (CR)
    case L'\n': // Newline - send CR to controls
        PostMessageW(hwnd, WM_KEYDOWN, VK_RETURN, 0);
        PostMessageW(hwnd, WM_CHAR, (WPARAM)L'\r', 0);
        PostMessageW(hwnd, WM_KEYUP, VK_RETURN, 0);
        break;

    case L'\t':
        PostMessageW(hwnd, WM_KEYDOWN, VK_TAB, 0);
        PostMessageW(hwnd, WM_CHAR, (WPARAM)L'\t', 0);
        PostMessageW(hwnd, WM_KEYUP, VK_TAB, 0);
        break;

    case 8: // Backspace
        PostMessageW(hwnd, WM_KEYDOWN, VK_BACK, 0);
        PostMessageW(hwnd, WM_CHAR, (WPARAM)8, 0);
        PostMessageW(hwnd, WM_KEYUP, VK_BACK, 0);
        break;

    default:
        // Other control chars: post WM_CHAR as-is
        PostMessageW(hwnd, WM_CHAR, (WPARAM)ch, 0);
        break;
    }
}

// Replacement of the original smart_paste with identical signature.
// Uses PostMessageW to avoid stealing focus and targets the correct child control.
void smart_paste(const std::wstring& text)
{
    typing = true;

    // Keep initial pause similar to original to allow target app to prepare.
    Sleep(150);

    HWND parent = capture_target_window_for_paste();
    if (!parent) {
        typing = false;
        return;
    }

    HWND target = find_focused_child_or_text_control(parent);
    if (!target) {
        typing = false;
        return;
    }

    // Do NOT call SetForegroundWindow/SetFocus here: we use PostMessage to avoid stealing focus.
    Sleep(50);

    constexpr unsigned delayMs = 10;

    for (wchar_t c : text) {
        if (!IsWindow(target)) break;

        post_char_to_window(target, c);

        if (delayMs > 0) Sleep(delayMs);
    }

    typing = false;
}


void paste_thread(std::wstring text) {

    try { smart_paste(std::move(text)); } catch (...) { typing = false; }

}


LRESULT CALLBACK kb_hook(int nCode, WPARAM wParam, LPARAM lParam) {

    if (nCode >= 0 && !typing.load()) {

        KBDLLHOOKSTRUCT* kb = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam);

        bool was_ctrl_down = ctrl_down.load();


        if (kb->vkCode == VK_CONTROL || kb->vkCode == VK_LCONTROL || kb->vkCode == VK_RCONTROL) {

            ctrl_down = (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN);

            return CallNextHookEx(NULL, nCode, wParam, lParam);

        }


        if (was_ctrl_down && (kb->vkCode == 'C' || kb->vkCode == 0x43) &&

            (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN)) {

            

            Sleep(400);

            std::wstring original = clipboard_get();

            if (!original.empty()) {

                std::wstring encrypted = aes_process(original, true);

                clipboard_set(enc_tag + encrypted);

            }
            ctrl_down = false;

            return 1;

        }


        if (was_ctrl_down && (kb->vkCode == 'V' || kb->vkCode == 0x56) &&

            (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN)) {

            

            std::wstring clipboard_data = clipboard_get();

            if (clipboard_data.find(enc_tag) == 0) {

                std::wstring encrypted(clipboard_data.begin() + enc_tag.length(), clipboard_data.end());

                std::thread(paste_thread, aes_process(encrypted, false)).detach();

            }

            ctrl_down = false;

            return 1;

        }

    }

    return CallNextHookEx(NULL, nCode, wParam, lParam);

}


int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {

    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);

    

    AllocConsole();

    freopen_s(reinterpret_cast<FILE**>(stdout), "CONOUT$", "w", stdout);

    freopen_s(reinterpret_cast<FILE**>(stderr), "CONOUT$", "w", stderr);

    SetConsoleTitleA("AES Clipboard Guardian - DLL Crypto");

    std::ios::sync_with_stdio(false);

    std::locale::global(std::locale(""));

    std::wcout.imbue(std::locale());


    // Charger la DLL crypto

    if (!load_crypto_dll()) {

        std::cout << "[!] Échec chargement DLL crypto_dll.dll" << std::endl;

        Sleep(3000);

        return 1;

    }


    // Créer le fichier key_iv.txt protégé

    if (!CreateProtectedKeyFile()) {

        std::cout << "[!] Échec création fichier protégé. Droits administrateur requis." << std::endl;

        Sleep(3000);

        return 1;

    }


    // Test AES avec DLL

    std::wstring test = L"Hello World Secret! éàç€CvcV 1234567890";

    auto enc = aes_process(test, true);

    if (enc.empty()) {

        std::wcout << L"❌ Test AES échoué (écriture clé)" << std::endl;

        Sleep(3000);

        return 1;

    }

    auto dec = aes_process(enc, false);

    std::wcout << L"AES Test DLL: " << (test == dec ? L"✅ OK" : L"❌ FAIL") << std::endl;


    HHOOK hook = SetWindowsHookEx(WH_KEYBOARD_LL, kb_hook, GetModuleHandle(NULL), 0);

    if (!hook) {

        std::cout << "[!] Droits administrateur requis pour hook clavier!" << std::endl;

        Sleep(3000);

        return 1;

    }


    std::cout << "\n✓ Guardian actif ! Ctrl+C = Chiffre | Ctrl+V = Déchiffre" << std::endl;

    std::cout << "Appuyez Ctrl+C pour quitter..." << std::endl;


    MSG msg;

    while (GetMessage(&msg, NULL, 0, 0)) {

        TranslateMessage(&msg);

        DispatchMessage(&msg);

    }


    UnhookWindowsHookEx(hook);

    return 0;

}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,432
Messages
2,571,682
Members
48,796
Latest member
Greg L.

Latest Threads

Top