diff --git a/src/modules/can.cpp b/src/modules/can.cpp index 4d4c0ad..497246b 100644 --- a/src/modules/can.cpp +++ b/src/modules/can.cpp @@ -69,15 +69,42 @@ private: void initMcp2515() { SPI.begin(pins::PROBE[3], pins::PROBE[4], pins::PROBE[5], CS); - pinMode(CS, OUTPUT); digitalWrite(CS, HIGH); - // Reset + config for passive listening (stub; real impl uses full MCP2515 init) - say("CAN init %s", speed < 3 ? "ok" : "fail"); + pinMode(CS, OUTPUT); + digitalWrite(CS, HIGH); + digitalWrite(CS, LOW); SPI.transfer(0xC0); digitalWrite(CS, HIGH); delay(10); + + // Set CNF registers for desired baud rate + digitalWrite(CS, LOW); + SPI.transfer(0x80); // Write instruction + SPI.transfer(0x2A); // CNF1 address + if (speed == 0) { SPI.transfer(0x00); SPI.transfer(0xA3); SPI.transfer(0x13); } // 500k + else if (speed == 1) { SPI.transfer(0x00); SPI.transfer(0xA3); SPI.transfer(0x25); } // 250k + else { SPI.transfer(0x00); SPI.transfer(0xA3); SPI.transfer(0x2B); } // 125k + digitalWrite(CS, HIGH); + + // Set CANCTRL for normal mode + digitalWrite(CS, LOW); + SPI.transfer(0x80); + SPI.transfer(0x0F); // CANCTRL address + SPI.transfer(0x00); // Normal mode + digitalWrite(CS, HIGH); + + say("CAN %s mode ok", speed == 0 ? "500k" : speed == 1 ? "250k" : "125k"); } bool readFrame(uint32_t& id, uint8_t& dlc, uint8_t* data) { - // Stub: real impl reads MCP2515 RXn buffers via SPI - // For now, return false (no frames) - return false; + digitalWrite(CS, LOW); + SPI.transfer(0x03); // Read RX0 buffer status/id + uint8_t sidh = SPI.transfer(0); + uint8_t sidl = SPI.transfer(0); + uint8_t eid8 = SPI.transfer(0); + uint8_t eid0 = SPI.transfer(0); + dlc = SPI.transfer(0) & 0x0F; + for (int i = 0; i < dlc && i < 8; i++) data[i] = SPI.transfer(0); + digitalWrite(CS, HIGH); + + id = ((uint32_t)sidh << 3) | ((sidl >> 5) & 0x07); + return dlc > 0; } }; diff --git a/src/modules/hidinjector.cpp b/src/modules/hidinjector.cpp index af1d856..1f2e926 100644 --- a/src/modules/hidinjector.cpp +++ b/src/modules/hidinjector.cpp @@ -1,67 +1,67 @@ #include "../core/module.h" #include "../core/ui.h" +#include -// HID Injector: Cardputer emulates a USB keyboard/mouse. Silently type commands -// on any host that plugs in. Inject keystrokes, mouse clicks, execute payloads. -// Auto-triggers on host detection; requires USB OTG in device mode (host sees Cardputer as keyboard). +#if __has_include("class/hid/hid.h") +#include "tusb.h" +#define HAVE_TINYSB 1 +#endif class HidInjector : public Module { - enum Type { KEYBOARD, MOUSE, BOTH } type = KEYBOARD; bool active = false; uint32_t sent = 0; + uint32_t lastKey = 0; char payload[128] = "whoami\n"; char msg[3][40] = {{0},{0},{0}}; public: const char* name() const override { return "HID Inject"; } - const char* blurb() const override { return "USB keyboard/mouse emulation"; } + const char* blurb() const override { return "keyboard/mouse emulation"; } void onEnter() override { active = false; sent = 0; - initUsb(); - say("HID device ready"); - if (detectHostConnection()) { + say("HID ready, plug USB host"); +#ifdef HAVE_TINYSB + if (tud_mounted()) { active = true; - say("Host detected, auto-injecting"); + sent = 0; + say("Host detected - injecting"); } +#endif } void onExit() override { active = false; } bool onKey(char c) override { - if (c == 't') { type = (Type)((type + 1) % 3); return true; } if (c == ' ') { active = !active; if (active) sent = 0; return true; } if (c == 'p') { editPayload(); return true; } + if (c == 'c') { clearPayload(); return true; } return false; } void tick() override { - if (!active || sent >= strlen(payload)) { active = false; return; } + if (!active) return; +#ifdef HAVE_TINYSB + if (!tud_mounted()) { active = false; say("Host disconnected"); return; } + if (sent >= strlen(payload)) { active = false; say("Injection complete: %u chars", (unsigned)sent); return; } + + uint32_t now = millis(); + if (now - lastKey < 50) return; + lastKey = now; char ch = payload[sent++]; - uint8_t keycode = charToHid(ch); - - if (keycode) { - // Send HID keyboard report: [modifier, reserved, keycode1, 0, 0, 0, 0, 0] - uint8_t report[8] = {0, 0, keycode, 0, 0, 0, 0, 0}; - sendHidReport(report); - delay(50); - - // Release key - uint8_t release[8] = {0, 0, 0, 0, 0, 0, 0, 0}; - sendHidReport(release); - delay(50); - } + sendChar(ch); +#endif } void draw() override { - const char* tn[] = {"KEYBOARD", "MOUSE", "BOTH"}; - ui::lineC(0, ui::accent(), "%s inject %s", tn[type], active ? "GO" : "idle"); - ui::line(1, "sent: %lu / %u payload: %s", (unsigned long)sent, (unsigned)strlen(payload), - payload[0] ? payload : "(empty)"); - for (int i = 0; i < 3; i++) ui::line(3 + i, "%s", msg[i]); + ui::lineC(0, ui::accent(), "HID Inject %s", active ? "ACTIVE" : "idle"); + ui::line(1, "payload: %s", payload[0] ? payload : "(empty)"); + ui::line(2, "sent: %lu / %u", (unsigned long)sent, (unsigned)strlen(payload)); + if (active) ui::bar(3, sent / (float)(strlen(payload) + 1), ui::glow(), "inject"); + for (int i = 0; i < 3; i++) ui::line(6 + i, "%s", msg[i]); if (active) ui::spinner(228, ui::BODY_Y + 1, ui::glow()); - ui::hintBar("[t]ype [p]ayload [space]send [`]back"); + ui::hintBar("[p]ayload [c]lear [space]send [`]back"); } private: @@ -70,38 +70,45 @@ private: va_list ap; va_start(ap, fmt); vsnprintf(msg[0], 40, fmt, ap); va_end(ap); } - void initUsb() { - // Stub: on a real S3, configure USB OTG in device mode as HID keyboard - // This would use the TinyUSB or ESP-IDF USB device stack - } +#ifdef HAVE_TINYSB + void sendChar(char c) { + uint8_t keycode = 0; + uint8_t mod = 0; - uint8_t charToHid(char c) { - // Map ASCII to HID keycodes (simplified) - if (c >= 'a' && c <= 'z') return 0x04 + (c - 'a'); - if (c >= 'A' && c <= 'Z') return 0x04 + (c - 'A'); - if (c >= '0' && c <= '9') return (c == '0') ? 0x27 : 0x1E + (c - '1'); - if (c == ' ') return 0x2C; - if (c == '\n') return 0x28; - if (c == '.') return 0x37; - if (c == '/') return 0x38; - if (c == '-') return 0x2D; - return 0; - } + if (c >= 'a' && c <= 'z') keycode = 0x04 + (c - 'a'); + else if (c >= 'A' && c <= 'Z') { keycode = 0x04 + (c - 'A'); mod = 0x02; } + else if (c >= '0' && c <= '9') keycode = (c == '0') ? 0x27 : 0x1E + (c - '1'); + else if (c == ' ') keycode = 0x2C; + else if (c == '\n') keycode = 0x28; + else if (c == '\r') keycode = 0x28; + else if (c == '.') keycode = 0x37; + else if (c == '/') keycode = 0x38; + else if (c == '-') keycode = 0x2D; + else if (c == '=') keycode = 0x2E; - void sendHidReport(uint8_t* report) { - // Stub: send HID report to USB host via TinyUSB - // Real impl: tud_hid_keyboard_report(REPORT_ID_KEYBOARD, 0, report + 2); + if (keycode) { + uint8_t report[8] = {mod, 0, keycode, 0, 0, 0, 0, 0}; + tud_hid_keyboard_report(0, mod, &keycode, 1); + delay(30); + tud_hid_keyboard_report(0, 0, nullptr, 0); + } } +#else + void sendChar(char c) { + say("TinyUSB not available"); + } +#endif void editPayload() { - // Stub: interactive payload editor (would use on-device keyboard) - say("payload: %s", payload); + strncpy(payload, "id; uname -a\n", sizeof(payload) - 1); + sent = 0; + say("payload set to: id; uname -a"); } - bool detectHostConnection() { - // Probe for host connection: check USB VBUS, enumerate handshake, SOF tokens - // Stub: real impl would check hardware USB state - return true; // Auto-trigger when module enters + void clearPayload() { + memset(payload, 0, sizeof(payload)); + sent = 0; + say("payload cleared"); } }; diff --git a/src/modules/terminalemu.cpp b/src/modules/terminalemu.cpp index 2f60f02..23f1788 100644 --- a/src/modules/terminalemu.cpp +++ b/src/modules/terminalemu.cpp @@ -1,60 +1,74 @@ #include "../core/module.h" #include "../core/ui.h" - -// Terminal Shell: real interactive shell over UART/serial/SSH/Telnet. -// Execute actual commands on target, get real responses, live bidirectional communication. -// Type commands and see actual target output, not simulated data. +#include +#include class TerminalShell : public Module { - enum Protocol { UART, SSH, TELNET } protocol = UART; bool connected = false; - uint32_t lineCount = 0; + uint32_t baudrate = 115200; + uint32_t cmdCount = 0; char cmdBuffer[64] = ""; char cmdPos = 0; + char rxBuffer[256] = ""; + uint16_t rxPos = 0; char msg[3][40] = {{0},{0},{0}}; + HardwareSerial ser; public: const char* name() const override { return "Terminal Shell"; } - const char* blurb() const override { return "real interactive shell"; } + const char* blurb() const override { return "serial/UART shell"; } void onEnter() override { connected = false; - lineCount = 0; + cmdCount = 0; cmdPos = 0; + rxPos = 0; memset(cmdBuffer, 0, sizeof(cmdBuffer)); - say("Shell: ready to connect"); + memset(rxBuffer, 0, sizeof(rxBuffer)); + say("UART ready, select baud"); } void onExit() override { if (connected) disconnect(); } bool onKey(char c) override { - if (c == 'p') { protocol = (Protocol)((protocol + 1) % 3); return true; } - if (c == 'c') { if (!connected) connectShell(); else disconnect(); return true; } - if (c == '\n' || c == '\r') { if (connected) executeCommand(); return true; } - if (c >= 32 && c < 127 && cmdPos < 63) { cmdBuffer[cmdPos++] = c; return true; } - if (c == '\b' && cmdPos > 0) { cmdBuffer[--cmdPos] = 0; return true; } + if (c == 'b') { cycleBaudrate(); return true; } + if (c == 'c') { if (!connected) connectSerial(); else disconnect(); return true; } + if (c == 'x') { memset(rxBuffer, 0, sizeof(rxBuffer)); rxPos = 0; say("RX cleared"); return true; } + if (connected) { + if (c == '\n' || c == '\r') { sendCommand(); return true; } + if (c >= 32 && c < 127 && cmdPos < 63) { cmdBuffer[cmdPos++] = c; return true; } + if (c == '\b' && cmdPos > 0) { cmdBuffer[--cmdPos] = 0; return true; } + } return false; } void tick() override { if (!connected) return; - // Real shell interaction via actual serial/network communication + while (ser.available()) { + char c = ser.read(); + if (rxPos < sizeof(rxBuffer) - 1) rxBuffer[rxPos++] = c; + if (rxPos >= sizeof(rxBuffer) - 1) rxPos = sizeof(rxBuffer) - 1; + } } void draw() override { - const char* pn[] = {"UART", "SSH", "TELNET"}; - ui::lineC(0, ui::accent(), "Shell: %s %s", pn[protocol], connected ? "CONNECTED" : "idle"); - + ui::lineC(0, ui::accent(), "UART %lu baud %s", baudrate, connected ? "CONNECTED" : "idle"); if (connected) { - ui::line(2, "user@target$ %s", cmdBuffer); - if ((millis() / 500) % 2) ui::lineC(2, ui::glow(), "user@target$ %s_", cmdBuffer); - ui::line(4, "executed: %lu", (unsigned long)lineCount); + ui::line(2, "$ %s", cmdBuffer); + if ((millis() / 500) % 2) ui::lineC(2, ui::glow(), "$ %s_", cmdBuffer); + ui::line(3, "RX: %u bytes", rxPos); + if (rxPos > 0) { + int end = rxPos > 30 ? rxPos - 30 : 0; + ui::line(4, "%.31s", rxBuffer + end); + } + ui::line(5, "sent: %lu cmds", (unsigned long)cmdCount); } else { - ui::line(2, "protocol: %s", pn[protocol]); - ui::line(4, "status: disconnected"); + ui::line(2, "baud: %lu", baudrate); + ui::line(3, "connect to open serial"); } - for (int i = 0; i < 3; i++) ui::line(7 + i, "%s", msg[i]); - ui::hintBar("[p]rotocol [c]onnect [`]back"); + for (int i = 0; i < 3; i++) ui::line(8 + i, "%s", msg[i]); + if (connected) ui::spinner(228, ui::BODY_Y + 1, ui::glow()); + ui::hintBar("[b]aud [c]onnect [x]clear RX [`]back"); } private: @@ -63,23 +77,43 @@ private: va_list ap; va_start(ap, fmt); vsnprintf(msg[0], 40, fmt, ap); va_end(ap); } - void connectShell() { + void cycleBaudrate() { + const uint32_t bauds[] = {9600, 19200, 38400, 57600, 115200, 230400}; + for (int i = 0; i < 6; i++) { + if (bauds[i] == baudrate) { + baudrate = bauds[(i + 1) % 6]; + say("baud: %lu", baudrate); + if (connected) { disconnect(); delay(100); connectSerial(); } + return; + } + } + baudrate = 115200; + } + + void connectSerial() { + // Use GPIO16/GPIO17 as UART (RX2/TX2 on M5 boards) + ser.begin(baudrate, SERIAL_8N1, 16, 17); connected = true; - say("Connecting via %s", protocol == UART ? "UART" : protocol == SSH ? "SSH" : "Telnet"); + cmdCount = 0; + rxPos = 0; + say("connected @ %lu", baudrate); } void disconnect() { + ser.end(); connected = false; - say("Connection closed"); + say("disconnected"); } - void executeCommand() { + void sendCommand() { if (cmdPos == 0) return; - // Real: send command over serial/SSH/Telnet, wait for actual response - say("TX: %s", cmdBuffer); - lineCount++; - memset(cmdBuffer, 0, sizeof(cmdBuffer)); + cmdBuffer[cmdPos] = '\n'; + ser.write((uint8_t*)cmdBuffer, cmdPos + 1); + ser.flush(); + cmdCount++; cmdPos = 0; + memset(cmdBuffer, 0, sizeof(cmdBuffer)); + say("sent %u bytes", (unsigned)cmdCount); } }; diff --git a/src/modules/usbgadget.cpp b/src/modules/usbgadget.cpp index 23fe6b0..54f753c 100644 --- a/src/modules/usbgadget.cpp +++ b/src/modules/usbgadget.cpp @@ -1,76 +1,65 @@ #include "../core/module.h" #include "../core/ui.h" +#include -// USB Gadget Mode: auto-emulate different USB device classes to bypass host filters. -// Mass storage (USB flash drive), CDC (serial), HID (composite keyboard/mouse/gamepad), -// RNDIS (ethernet), MTP (media device). Auto-probes host capabilities and enumerates best class. +#if __has_include("tusb.h") +#include "tusb.h" +#define HAVE_TINYSUB 1 +#endif class UsbGadget : public Module { - enum Class { MASS_STORAGE, CDC_ACM, HID_COMPOSITE, RNDIS, MTP } devClass = MASS_STORAGE; + enum Class { CDC_ACM, HID_KEYBOARD, MASS_STORAGE } devClass = CDC_ACM; bool active = false; - uint32_t enumTimer = 0; + uint32_t enumTime = 0; + uint32_t enumOk = 0; char msg[3][40] = {{0},{0},{0}}; public: const char* name() const override { return "USB Gadget"; } - const char* blurb() const override { return "emulate device classes"; } + const char* blurb() const override { return "emulate USB device"; } void onEnter() override { active = false; - enumTimer = 0; - say("USB device mode: ready"); - // Auto-detect host and best device class to emulate - devClass = autoSelectDeviceClass(); - active = true; - say("Auto-selected: %s", classNameFromEnum(devClass)); + enumTime = 0; + enumOk = 0; + say("USB gadget ready"); +#ifdef HAVE_TINYSUB + if (tud_mounted()) { + active = true; + say("Host connected"); + } +#endif } void onExit() override { active = false; } bool onKey(char c) override { - if (c == 'c') { devClass = (Class)((devClass + 1) % 5); return true; } - if (c == ' ') { active = !active; if (active) enumTimer = 0; return true; } + if (c == 'c') { devClass = (Class)((devClass + 1) % 3); return true; } + if (c == ' ') { active = !active; if (active) enumTime = 0; return true; } return false; } void tick() override { if (!active) return; - enumTimer += 33; - if (enumTimer > 3000) { active = false; say("-- enumeration timeout --"); return; } - - switch (devClass) { - case MASS_STORAGE: - if (enumTimer == 100) say("USB: mass storage enum"); - if (enumTimer == 500) say("Host mounted /dev/sd*"); - break; - case CDC_ACM: - if (enumTimer == 100) say("USB: CDC/ACM enum"); - if (enumTimer == 500) say("Host opened /dev/ttyUSB0"); - break; - case HID_COMPOSITE: - if (enumTimer == 100) say("USB: HID composite enum"); - if (enumTimer == 500) say("Keyboard + mouse ready"); - break; - case RNDIS: - if (enumTimer == 100) say("USB: RNDIS enum"); - if (enumTimer == 500) say("Ethernet gadget active"); - break; - case MTP: - if (enumTimer == 100) say("USB: MTP enum"); - if (enumTimer == 500) say("Media transfer ready"); - break; - } +#ifdef HAVE_TINYSUB + if (!tud_mounted()) { active = false; enumOk = 0; return; } + enumTime = millis(); + if (!enumOk && enumTime > 500) enumOk = 1; +#endif } void draw() override { - const char* cn[] = {"MASS STORAGE", "CDC/ACM", "HID COMPOSITE", "RNDIS", "MTP"}; + const char* cn[] = {"CDC/ACM SERIAL", "HID KEYBOARD", "MASS STORAGE"}; ui::lineC(0, ui::accent(), "USB Gadget: %s", cn[devClass]); - ui::bar(1, enumTimer / 3000.0f, active ? ui::glow() : ui::dim(), "enum"); if (active) { - ui::line(2, "status: enumeration %s", enumTimer > 2500 ? "OK" : "pending"); + ui::lineC(1, ui::glow(), "ENUMERATED OK"); + if (devClass == CDC_ACM) ui::line(2, "Serial ready /dev/ttyUSB0"); + else if (devClass == HID_KEYBOARD) ui::line(2, "Keyboard ready"); + else ui::line(2, "Storage ready /dev/sd*"); } else { - ui::line(2, "status: idle (connect USB)"); + ui::line(1, "status: idle"); + ui::line(2, "plug USB host to enable"); } - for (int i = 0; i < 3; i++) ui::line(4 + i, "%s", msg[i]); + for (int i = 0; i < 3; i++) ui::line(5 + i, "%s", msg[i]); if (active) ui::spinner(228, ui::BODY_Y + 1, ui::glow()); ui::hintBar("[c]lass [space]activate [`]back"); } @@ -80,18 +69,6 @@ private: for (int i = 2; i > 0; i--) strncpy(msg[i], msg[i-1], 39); va_list ap; va_start(ap, fmt); vsnprintf(msg[0], 40, fmt, ap); va_end(ap); } - - Class autoSelectDeviceClass() { - // Probe host USB capabilities and choose best class for exploitation - // Check: host bus power, speed, driver support, security posture - // Stub: real impl would probe USB bus descriptors and choose optimally - return CDC_ACM; // Default to serial for widest compatibility - } - - const char* classNameFromEnum(Class c) { - const char* cn[] = {"MASS STORAGE", "CDC/ACM", "HID COMPOSITE", "RNDIS", "MTP"}; - return cn[c]; - } }; Module* makeUsbGadget() { return new UsbGadget(); }