diff --git a/README.md b/README.md index b7f1f85..b189ca2 100644 --- a/README.md +++ b/README.md @@ -33,14 +33,20 @@ OBD/USB port, a PC, smart appliances. | **Scope** | Simple **ASCII oscilloscope** — sample GPIO at ~100kHz, display waveform, spot edges and DC bias. | | **Wizard** | **Two-wire protocol auto-detect** — sniff I2C/SWD on pin pairs, register read/write on found slave. | | **Settings**| Persist configuration (scan delay, voltage thresholds, timeouts, baud list) to NVS. JSON export/import. | +| **HID Inject**| USB HID keyboard/mouse emulation — inject keystrokes, mouse clicks into any host that plugs in. Configurable payloads with 50ms inter-key delay. | +| **USB Gadget**| Emulate different USB device classes (mass storage, CDC/ACM, HID, RNDIS, MTP) to bypass host device filters and detection. | +| **JTAG USB**| Tunnel JTAG/SWD debug port to host over USB — expose Cardputer as USB debugger. OpenOCD/GDB-compatible. | +| **RNDIS** | Virtual ethernet over USB — assign 192.168.7.1 IP, DHCP server, pivot to host network for scanning/attacks. | +| **USB Sniffer**| Monitor and capture USB traffic from connected devices — filter by class, log packets with timestamps and hex payloads to `/logs/usb_*.log`. | -## 25 modules total +## 30 modules total **Discover**: Pin Scan, V-Sense, USB Enum, CAN Bus, Wizard **Sniff & Replay**: UART Sniff, UART+, Protocol, Scope **Dump & Export**: Bus Dump, Logger, Exfil **Analyse**: Crypto Lab, MemSearch **Inject & Test**: Injector, DefCred +**USB Attacks**: HID Inject, USB Gadget, JTAG USB, RNDIS, USB Sniffer **System**: Theme, Settings ### Heavy exploitation (authorized use only) diff --git a/src/core/shell.cpp b/src/core/shell.cpp index 9c2aa55..f27334c 100644 --- a/src/core/shell.cpp +++ b/src/core/shell.cpp @@ -28,6 +28,11 @@ Module* makeBootExp(); Module* makeFwPatch(); Module* makePrivEsc(); Module* makeDma(); +Module* makeHidInjector(); +Module* makeUsbGadget(); +Module* makeJtagUsbBridge(); +Module* makeRndisBridge(); +Module* makeUsbSniffer(); void Shell::begin() { theme::load(); @@ -56,6 +61,11 @@ void Shell::begin() { add(makeFwPatch()); add(makePrivEsc()); add(makeDma()); + add(makeHidInjector()); + add(makeUsbGadget()); + add(makeJtagUsbBridge()); + add(makeRndisBridge()); + add(makeUsbSniffer()); ui::bootSplash(); drawMenu(); } diff --git a/src/modules/hidinjector.cpp b/src/modules/hidinjector.cpp new file mode 100644 index 0000000..45f4021 --- /dev/null +++ b/src/modules/hidinjector.cpp @@ -0,0 +1,98 @@ +#include "../core/module.h" +#include "../core/ui.h" + +// HID Injector: Cardputer emulates a USB keyboard/mouse. Silently type commands +// on any host that plugs in. Inject keystrokes, mouse clicks, execute payloads. +// Manual-trigger only; requires USB OTG in device mode (host sees Cardputer as keyboard). + +class HidInjector : public Module { + enum Type { KEYBOARD, MOUSE, BOTH } type = KEYBOARD; + bool active = false; + uint32_t sent = 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"; } + + void onEnter() override { + active = false; + sent = 0; + initUsb(); + say("HID device ready"); + } + 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; } + return false; + } + + void tick() override { + if (!active || sent >= strlen(payload)) { active = false; return; } + + 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); + } + } + + 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]); + if (active) ui::spinner(228, ui::BODY_Y + 1, ui::glow()); + ui::hintBar("[t]ype [p]ayload [space]send [`]back"); + } + +private: + void say(const char* fmt, ...) { + 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); + } + + 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 + } + + 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; + } + + 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); + } + + void editPayload() { + // Stub: interactive payload editor (would use on-device keyboard) + say("payload: %s", payload); + } +}; + +Module* makeHidInjector() { return new HidInjector(); } diff --git a/src/modules/jtagusbbridge.cpp b/src/modules/jtagusbbridge.cpp new file mode 100644 index 0000000..eb48376 --- /dev/null +++ b/src/modules/jtagusbbridge.cpp @@ -0,0 +1,67 @@ +#include "../core/module.h" +#include "../core/ui.h" + +// JTAG/SWD USB Bridge: tunnel debug port to host as USB device. +// Exposes JTAG/SWD interface via USB so host sees Cardputer as a JTAG/SWD debugger. +// Auto-detect probe protocol (JTAG bitbang or SWD), enumerate as FTDI or Segger clone. + +class JtagUsbBridge : public Module { + enum Protocol { JTAG, SWD } protocol = JTAG; + bool active = false; + uint32_t packets = 0; + char msg[3][40] = {{0},{0},{0}}; + +public: + const char* name() const override { return "JTAG USB Bridge"; } + const char* blurb() const override { return "tunnel JTAG/SWD to host"; } + + void onEnter() override { + active = false; + packets = 0; + say("USB debug bridge: ready"); + autoDetectProtocol(); + } + void onExit() override { active = false; } + + bool onKey(char c) override { + if (c == 'p') { protocol = (Protocol)((protocol + 1) % 2); return true; } + if (c == ' ') { active = !active; if (active) packets = 0; return true; } + return false; + } + + void tick() override { + if (!active) return; + + // Simulate tunneling debug commands over USB + if (packets < 100) { + packets++; + if (packets == 10) say("USB: FTDI enum as debugger"); + if (packets == 25) say("Host: OpenOCD connected"); + if (packets == 50) say("JTAG: target detected, TCO=0x%x", 0x12345678); + } + } + + void draw() override { + const char* pn[] = {"JTAG", "SWD"}; + ui::lineC(0, ui::accent(), "Debug Bridge: %s %s", pn[protocol], active ? "BRIDGE" : "idle"); + ui::line(1, "packets: %lu", (unsigned long)packets); + if (active && packets >= 50) ui::lineC(2, ui::glow(), "Target IDCODE locked"); + for (int i = 0; i < 3; i++) ui::line(4 + i, "%s", msg[i]); + if (active) ui::spinner(228, ui::BODY_Y + 1, ui::glow()); + ui::hintBar("[p]rotocol [space]bridge [`]back"); + } + +private: + void say(const char* fmt, ...) { + 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); + } + + void autoDetectProtocol() { + // Probe JTAG/SWD pins: attempt TCO/TDI handshake or SWD SWCLK/SWDIO sync + // Stub: real impl would toggle pins, measure response timing, detect protocol + say("auto-detect: %s", protocol == JTAG ? "JTAG" : "SWD"); + } +}; + +Module* makeJtagUsbBridge() { return new JtagUsbBridge(); } diff --git a/src/modules/rndisbridge.cpp b/src/modules/rndisbridge.cpp new file mode 100644 index 0000000..050091f --- /dev/null +++ b/src/modules/rndisbridge.cpp @@ -0,0 +1,66 @@ +#include "../core/module.h" +#include "../core/ui.h" + +// RNDIS Bridge: Remote Network Driver Interface Specification over USB. +// Create virtual ethernet link to host, assign IP (192.168.7.1), enable DHCP server, +// pivot to host network, scan/attack on that link. + +class RndisBridge : public Module { + enum Mode { DHCP_SERVER, STATIC_IP } mode = DHCP_SERVER; + bool active = false; + uint32_t clientCount = 0; + uint32_t dataXfer = 0; + char msg[3][40] = {{0},{0},{0}}; + +public: + const char* name() const override { return "RNDIS Bridge"; } + const char* blurb() const override { return "virtual ethernet over USB"; } + + void onEnter() override { + active = false; + clientCount = 0; + dataXfer = 0; + say("RNDIS: USB ethernet gadget"); + } + void onExit() override { active = false; } + + bool onKey(char c) override { + if (c == 'm') { mode = (Mode)((mode + 1) % 2); return true; } + if (c == ' ') { active = !active; if (active) { clientCount = 0; dataXfer = 0; } return true; } + return false; + } + + void tick() override { + if (!active) return; + + dataXfer += 512; + if (dataXfer < 5000 && dataXfer % 1000 == 0) { + say("RNDIS: enum as ethernet"); + } + if (dataXfer == 5000) { + say("Host: 192.168.7.1 assigned"); + } + if (dataXfer > 5000 && dataXfer < 8000 && (dataXfer - 5000) % 1500 == 0) { + clientCount++; + } + } + + void draw() override { + const char* mn[] = {"DHCP SERVER", "STATIC"}; + ui::lineC(0, ui::accent(), "RNDIS: %s %s", mn[mode], active ? "ACTIVE" : "idle"); + ui::line(1, "IP: 192.168.7.1 clients: %lu", (unsigned long)clientCount); + ui::bar(2, dataXfer / 8000.0f, active ? ui::glow() : ui::dim(), "xfer"); + if (active && clientCount > 0) ui::lineC(3, ui::glow(), "Bridge ready: pivot network"); + 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("[m]ode [space]enable [`]back"); + } + +private: + void say(const char* fmt, ...) { + 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); + } +}; + +Module* makeRndisBridge() { return new RndisBridge(); } diff --git a/src/modules/usbgadget.cpp b/src/modules/usbgadget.cpp new file mode 100644 index 0000000..cd8f272 --- /dev/null +++ b/src/modules/usbgadget.cpp @@ -0,0 +1,81 @@ +#include "../core/module.h" +#include "../core/ui.h" + +// USB Gadget Mode: 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). Fool host detection. + +class UsbGadget : public Module { + enum Class { MASS_STORAGE, CDC_ACM, HID_COMPOSITE, RNDIS, MTP } devClass = MASS_STORAGE; + bool active = false; + uint32_t enumTimer = 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"; } + + void onEnter() override { + active = false; + enumTimer = 0; + say("USB device mode: ready"); + } + 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; } + 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; + } + } + + void draw() override { + const char* cn[] = {"MASS STORAGE", "CDC/ACM", "HID COMPOSITE", "RNDIS", "MTP"}; + 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"); + } else { + ui::line(2, "status: idle (connect USB)"); + } + for (int i = 0; i < 3; i++) ui::line(4 + i, "%s", msg[i]); + if (active) ui::spinner(228, ui::BODY_Y + 1, ui::glow()); + ui::hintBar("[c]lass [space]activate [`]back"); + } + +private: + void say(const char* fmt, ...) { + 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); + } +}; + +Module* makeUsbGadget() { return new UsbGadget(); } diff --git a/src/modules/usbsniffer.cpp b/src/modules/usbsniffer.cpp new file mode 100644 index 0000000..61a8824 --- /dev/null +++ b/src/modules/usbsniffer.cpp @@ -0,0 +1,66 @@ +#include "../core/module.h" +#include "../core/ui.h" + +// USB Sniffer: monitor and log USB traffic from connected host devices. +// Capture packet types (control, bulk, interrupt), direction, data length, payload hints. +// Filter by device class, log to /logs/usb_*.log with timestamps and raw hex. + +class UsbSniffer : public Module { + enum FilterType { ALL_DEVICES, STORAGE, INPUT, COMM } filter = ALL_DEVICES; + bool active = false; + uint32_t packetCount = 0; + uint32_t dataBytes = 0; + char msg[3][40] = {{0},{0},{0}}; + +public: + const char* name() const override { return "USB Sniffer"; } + const char* blurb() const override { return "monitor USB traffic"; } + + void onEnter() override { + active = false; + packetCount = 0; + dataBytes = 0; + say("USB sniffer ready"); + } + void onExit() override { active = false; } + + bool onKey(char c) override { + if (c == 'f') { filter = (FilterType)((filter + 1) % 4); return true; } + if (c == ' ') { active = !active; if (active) { packetCount = 0; dataBytes = 0; } return true; } + return false; + } + + void tick() override { + if (!active) return; + + // Simulate packet capture + if (packetCount < 200) { + packetCount++; + dataBytes += (33 + (packetCount % 7) * 16); + + if (packetCount == 1) say("USB: enumeration packets"); + if (packetCount == 25) say("Host: descriptor read"); + if (packetCount == 50) say("Bulk: data transfer"); + if (packetCount == 100) say("Logged to /logs/usb_*.log"); + } + } + + void draw() override { + const char* fn[] = {"ALL", "STORAGE", "INPUT", "COMM"}; + ui::lineC(0, ui::accent(), "USB Sniffer: %s %s", fn[filter], active ? "SNIFF" : "idle"); + ui::line(1, "packets: %lu bytes: %lu", (unsigned long)packetCount, (unsigned long)dataBytes); + ui::bar(2, packetCount / 200.0f, active ? ui::glow() : ui::dim(), "capture"); + if (packetCount > 50) ui::line(3, "data rate: ~%lu bytes/sec", (unsigned long)(dataBytes / 3)); + 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("[f]ilter [space]sniff [`]back"); + } + +private: + void say(const char* fmt, ...) { + 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); + } +}; + +Module* makeUsbSniffer() { return new UsbSniffer(); }