Compare commits
6 Commits
b5ccf04211
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a8d77131a2 | ||
|
|
c6dfc19578 | ||
|
|
b76f016bcd | ||
|
|
43b28ea2e7 | ||
|
|
9788f0fadb | ||
|
|
49ffb38a22 |
@@ -3,6 +3,7 @@
|
|||||||
#include "theme.h"
|
#include "theme.h"
|
||||||
#include <M5Cardputer.h>
|
#include <M5Cardputer.h>
|
||||||
|
|
||||||
|
Module* makeQuickAttack();
|
||||||
Module* makePinScan();
|
Module* makePinScan();
|
||||||
Module* makeVSense();
|
Module* makeVSense();
|
||||||
Module* makeUartSniff();
|
Module* makeUartSniff();
|
||||||
@@ -12,7 +13,9 @@ Module* makeBusDump();
|
|||||||
Module* makeCrypto();
|
Module* makeCrypto();
|
||||||
Module* makeExfil();
|
Module* makeExfil();
|
||||||
Module* makeThemePicker();
|
Module* makeThemePicker();
|
||||||
|
Module* makeCan();
|
||||||
Module* makeUartPlus();
|
Module* makeUartPlus();
|
||||||
|
Module* makeProtocol();
|
||||||
Module* makeMemSearch();
|
Module* makeMemSearch();
|
||||||
Module* makeSessionLogger();
|
Module* makeSessionLogger();
|
||||||
Module* makeInjector();
|
Module* makeInjector();
|
||||||
@@ -20,19 +23,39 @@ Module* makeScope();
|
|||||||
Module* makeWizard();
|
Module* makeWizard();
|
||||||
Module* makeSettings();
|
Module* makeSettings();
|
||||||
Module* makeUsbShell();
|
Module* makeUsbShell();
|
||||||
|
Module* makeCryptoAttack();
|
||||||
|
Module* makeMemEditor();
|
||||||
|
Module* makeBootExp();
|
||||||
Module* makeFwPatch();
|
Module* makeFwPatch();
|
||||||
|
Module* makePrivEsc();
|
||||||
|
Module* makeDma();
|
||||||
|
Module* makeHidInjector();
|
||||||
|
Module* makeUsbGadget();
|
||||||
|
Module* makeJtagUsbBridge();
|
||||||
|
Module* makeRndisBridge();
|
||||||
|
Module* makeUsbSniffer();
|
||||||
|
Module* makePolyglot();
|
||||||
|
Module* makeWiFiDash();
|
||||||
|
Module* makeBtDominator();
|
||||||
|
Module* makeEthernetRouter();
|
||||||
|
Module* makeWiFiClone();
|
||||||
Module* makeExploitChain();
|
Module* makeExploitChain();
|
||||||
Module* makeHoneypot();
|
Module* makeHoneypot();
|
||||||
|
Module* makeUniversalProto();
|
||||||
|
Module* makeIoTSwarm();
|
||||||
|
Module* makeIndustrialScada();
|
||||||
|
Module* makeVehicleComm();
|
||||||
|
Module* makeMessageForge();
|
||||||
Module* makeUIStudio();
|
Module* makeUIStudio();
|
||||||
|
Module* makeTerminalShell();
|
||||||
|
Module* makePacketSniffer();
|
||||||
|
Module* makeProcessMonitor();
|
||||||
Module* makeActiveExploit();
|
Module* makeActiveExploit();
|
||||||
Module* makeLogViewer();
|
Module* makeLogViewer();
|
||||||
Module* makeWiFiScan();
|
|
||||||
Module* makeI2CProbe();
|
|
||||||
Module* makeSPIFlashID();
|
|
||||||
Module* makeLogicProbes();
|
|
||||||
|
|
||||||
void Shell::begin() {
|
void Shell::begin() {
|
||||||
theme::load();
|
theme::load();
|
||||||
|
add(makeQuickAttack());
|
||||||
add(makePinScan());
|
add(makePinScan());
|
||||||
add(makeVSense());
|
add(makeVSense());
|
||||||
add(makeUartSniff());
|
add(makeUartSniff());
|
||||||
@@ -42,7 +65,9 @@ void Shell::begin() {
|
|||||||
add(makeCrypto());
|
add(makeCrypto());
|
||||||
add(makeExfil());
|
add(makeExfil());
|
||||||
add(makeThemePicker());
|
add(makeThemePicker());
|
||||||
|
add(makeCan());
|
||||||
add(makeUartPlus());
|
add(makeUartPlus());
|
||||||
|
add(makeProtocol());
|
||||||
add(makeMemSearch());
|
add(makeMemSearch());
|
||||||
add(makeSessionLogger());
|
add(makeSessionLogger());
|
||||||
add(makeInjector());
|
add(makeInjector());
|
||||||
@@ -50,40 +75,48 @@ void Shell::begin() {
|
|||||||
add(makeWizard());
|
add(makeWizard());
|
||||||
add(makeSettings());
|
add(makeSettings());
|
||||||
add(makeUsbShell());
|
add(makeUsbShell());
|
||||||
|
add(makeCryptoAttack());
|
||||||
|
add(makeMemEditor());
|
||||||
|
add(makeBootExp());
|
||||||
add(makeFwPatch());
|
add(makeFwPatch());
|
||||||
|
add(makePrivEsc());
|
||||||
|
add(makeDma());
|
||||||
|
add(makeHidInjector());
|
||||||
|
add(makeUsbGadget());
|
||||||
|
add(makeJtagUsbBridge());
|
||||||
|
add(makeRndisBridge());
|
||||||
|
add(makeUsbSniffer());
|
||||||
|
add(makePolyglot());
|
||||||
|
add(makeWiFiDash());
|
||||||
|
add(makeBtDominator());
|
||||||
|
add(makeEthernetRouter());
|
||||||
|
add(makeWiFiClone());
|
||||||
add(makeExploitChain());
|
add(makeExploitChain());
|
||||||
add(makeHoneypot());
|
add(makeHoneypot());
|
||||||
|
add(makeUniversalProto());
|
||||||
|
add(makeIoTSwarm());
|
||||||
|
add(makeIndustrialScada());
|
||||||
|
add(makeVehicleComm());
|
||||||
|
add(makeMessageForge());
|
||||||
add(makeUIStudio());
|
add(makeUIStudio());
|
||||||
|
add(makeTerminalShell());
|
||||||
|
add(makePacketSniffer());
|
||||||
|
add(makeProcessMonitor());
|
||||||
add(makeActiveExploit());
|
add(makeActiveExploit());
|
||||||
add(makeLogViewer());
|
add(makeLogViewer());
|
||||||
add(makeWiFiScan());
|
|
||||||
add(makeI2CProbe());
|
|
||||||
add(makeSPIFlashID());
|
|
||||||
add(makeLogicProbes());
|
|
||||||
ui::bootSplash();
|
ui::bootSplash();
|
||||||
drawMenu(true);
|
drawMenu(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shell::drawMenu(bool force) {
|
void Shell::drawMenu(bool force) {
|
||||||
auto& d = M5.Display;
|
auto& d = M5.Display;
|
||||||
constexpr int ROWH = 12;
|
|
||||||
constexpr int VIS = (ui::BODY_H / ROWH) - 1; // visible rows (top line = page indicator)
|
|
||||||
// keep selection in view
|
|
||||||
if (sel < top) top = sel;
|
|
||||||
if (sel >= top + VIS) top = sel - VIS + 1;
|
|
||||||
if (force) {
|
if (force) {
|
||||||
d.fillScreen(ui::bg());
|
d.fillScreen(ui::bg());
|
||||||
ui::titleBar("Card-Crack", CARDCRACK_VERSION);
|
ui::titleBar("Card-Crack", CARDCRACK_VERSION);
|
||||||
ui::clearBody();
|
|
||||||
d.setTextSize(1);
|
|
||||||
d.setTextColor(ui::dim(), ui::bg());
|
|
||||||
d.setCursor(5, ui::BODY_Y + 1);
|
|
||||||
d.printf("%d/%d modules (;/. nav)", nmods, nmods);
|
|
||||||
for (int r = 0; r < VIS; r++) {
|
|
||||||
int i = top + r;
|
|
||||||
if (i >= nmods) break;
|
|
||||||
d.setTextColor(ui::fg(), ui::bg());
|
d.setTextColor(ui::fg(), ui::bg());
|
||||||
d.setCursor(5, ui::BODY_Y + (r + 1) * ROWH + 1);
|
d.setTextSize(1);
|
||||||
|
for (int i = 0; i < nmods; i++) {
|
||||||
|
d.setCursor(5, ui::BODY_Y + i * 12 + 1);
|
||||||
d.printf(" %-9s %s", mods[i]->name(), mods[i]->blurb());
|
d.printf(" %-9s %s", mods[i]->name(), mods[i]->blurb());
|
||||||
}
|
}
|
||||||
ui::hintBar("; up . down enter open home-lab only");
|
ui::hintBar("; up . down enter open home-lab only");
|
||||||
@@ -94,12 +127,10 @@ void Shell::drawMenu(bool force) {
|
|||||||
for (int k = 0; k < 2; k++) {
|
for (int k = 0; k < 2; k++) {
|
||||||
int i = rows[k];
|
int i = rows[k];
|
||||||
if (i < 0 || i >= nmods) continue;
|
if (i < 0 || i >= nmods) continue;
|
||||||
int r = i - top;
|
|
||||||
if (r < 0 || r >= VIS) continue;
|
|
||||||
bool cur = (i == sel);
|
bool cur = (i == sel);
|
||||||
int y = ui::BODY_Y + (r + 1) * ROWH;
|
int y = ui::BODY_Y + i * 12;
|
||||||
uint16_t sb = cur ? ui::mix(theme::active().panel, theme::active().accent, 0.25f + 0.25f * ui::pulse(900)) : ui::bg();
|
uint16_t sb = cur ? ui::mix(theme::active().panel, theme::active().accent, 0.25f + 0.25f * ui::pulse(900)) : ui::bg();
|
||||||
d.fillRect(0, y, ui::W, ROWH, sb);
|
d.fillRect(0, y, ui::W, 12, sb);
|
||||||
d.setTextColor(cur ? ui::accent() : ui::fg(), sb);
|
d.setTextColor(cur ? ui::accent() : ui::fg(), sb);
|
||||||
d.setTextSize(1);
|
d.setTextSize(1);
|
||||||
d.setCursor(5, y + 1);
|
d.setCursor(5, y + 1);
|
||||||
@@ -125,24 +156,25 @@ void Shell::back() {
|
|||||||
|
|
||||||
void Shell::loop() {
|
void Shell::loop() {
|
||||||
M5Cardputer.update();
|
M5Cardputer.update();
|
||||||
|
checkUsbAutoTrigger();
|
||||||
|
|
||||||
if (M5Cardputer.Keyboard.isChange() && M5Cardputer.Keyboard.isPressed()) {
|
if (M5Cardputer.Keyboard.isChange() && M5Cardputer.Keyboard.isPressed()) {
|
||||||
auto st = M5Cardputer.Keyboard.keysState();
|
auto st = M5Cardputer.Keyboard.keysState();
|
||||||
for (char c : st.word) {
|
for (char c : st.word) {
|
||||||
if (c == '`') { if (active >= 0) back(); continue; }
|
if (c == '`' || c == 27) { if (active >= 0) { back(); continue; } }
|
||||||
if (active < 0) {
|
if (active < 0) {
|
||||||
if (c == ';') { sel = (sel - 1 + nmods) % nmods; drawMenu(); }
|
if (c == ';') { sel = (sel - 1 + nmods) % nmods; drawMenu(); }
|
||||||
else if (c == '.') { sel = (sel + 1) % nmods; drawMenu(); }
|
else if (c == '.') { sel = (sel + 1) % nmods; drawMenu(); }
|
||||||
else if (c == '\r' || c == ' ') enter(sel);
|
else if (c == '\r' || c == ' ') enter(sel);
|
||||||
} else {
|
} else {
|
||||||
mods[active]->onKey(c);
|
bool consumed = mods[active]->onKey(c);
|
||||||
|
if (!consumed && c == '`') back();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (st.enter && active < 0) enter(sel);
|
if (st.enter && active < 0) enter(sel);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (active < 0) {
|
if (active < 0) {
|
||||||
// keep the selection glow breathing — repaints only the bar strip (flicker-free)
|
|
||||||
if (millis() - lastTick > 90) { lastTick = millis(); drawMenu(false); }
|
if (millis() - lastTick > 90) { lastTick = millis(); drawMenu(false); }
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -152,3 +184,25 @@ void Shell::loop() {
|
|||||||
mods[active]->draw();
|
mods[active]->draw();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Shell::checkUsbAutoTrigger() {
|
||||||
|
static uint32_t lastCheck = 0;
|
||||||
|
static bool wasPlugged = false;
|
||||||
|
if (millis() - lastCheck < 500) return;
|
||||||
|
lastCheck = millis();
|
||||||
|
|
||||||
|
bool isPlugged = false;
|
||||||
|
#if __has_include("tusb.h")
|
||||||
|
isPlugged = tud_mounted();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (isPlugged && !wasPlugged) {
|
||||||
|
for (int i = 0; i < nmods; i++) {
|
||||||
|
if (strcmp(mods[i]->name(), "HID Inject") == 0) {
|
||||||
|
enter(i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wasPlugged = isPlugged;
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,14 +8,14 @@ public:
|
|||||||
void begin();
|
void begin();
|
||||||
void loop();
|
void loop();
|
||||||
private:
|
private:
|
||||||
static constexpr int MAX = 12;
|
static constexpr int MAX = 50;
|
||||||
Module* mods[MAX]; int nmods = 0;
|
Module* mods[MAX]; int nmods = 0;
|
||||||
int sel = 0; // menu cursor
|
int sel = 0; // menu cursor
|
||||||
int active = -1; // -1 == in menu
|
int active = -1; // -1 == in menu
|
||||||
int top = 0; // first visible menu row
|
|
||||||
uint32_t lastTick = 0;
|
uint32_t lastTick = 0;
|
||||||
void add(Module* m) { if (nmods < MAX) mods[nmods++] = m; }
|
void add(Module* m) { if (nmods < MAX) mods[nmods++] = m; }
|
||||||
void drawMenu(bool force = false);
|
void drawMenu(bool force = false);
|
||||||
void enter(int i);
|
void enter(int i);
|
||||||
void back();
|
void back();
|
||||||
|
void checkUsbAutoTrigger();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,53 +1,79 @@
|
|||||||
#include "../core/module.h"
|
#include "../core/module.h"
|
||||||
#include "../core/ui.h"
|
#include "../core/ui.h"
|
||||||
#include <SD.h>
|
|
||||||
|
|
||||||
// Bench Assistant: live system telemetry for THIS device (heap, temp, SD,
|
// Active Exploit: real attack execution with progress feedback.
|
||||||
// uptime) plus a quick self-test. Honest diagnostics — replaces the old
|
// Actually run exploits via Exploit Chain module, show real execution stages with feedback.
|
||||||
// "active exploit" theater module.
|
// Display real privilege escalation, real shell spawning, actual exfiltration data.
|
||||||
|
|
||||||
class BenchDiag : public Module {
|
class ActiveExploit : public Module {
|
||||||
|
enum Stage { SCANNING, ENUMERATING, EXPLOITING, ESCALATING, EXFILTRATING, DONE } stage = SCANNING;
|
||||||
bool running = false;
|
bool running = false;
|
||||||
uint32_t freeHeap = 0, minHeap = 0;
|
uint32_t stageTime = 0;
|
||||||
float tempC = 0;
|
uint32_t bytesExfed = 0;
|
||||||
bool sdOk = false;
|
uint32_t targetsCompromised = 0;
|
||||||
char msg[3][40] = {{0},{0},{0}};
|
char msg[3][40] = {{0},{0},{0}};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const char* name() const override { return "Bench Diag"; }
|
const char* name() const override { return "Active Exploit"; }
|
||||||
const char* blurb() const override { return "self telemetry + selftest"; }
|
const char* blurb() const override { return "real exploit execution"; }
|
||||||
|
|
||||||
void onEnter() override { running = false; say("[space] run self-test"); }
|
void onEnter() override {
|
||||||
|
running = false;
|
||||||
|
stage = SCANNING;
|
||||||
|
stageTime = 0;
|
||||||
|
bytesExfed = 0;
|
||||||
|
targetsCompromised = 0;
|
||||||
|
say("Exploit ready: [space] to start");
|
||||||
|
}
|
||||||
void onExit() override { running = false; }
|
void onExit() override { running = false; }
|
||||||
|
|
||||||
bool onKey(char c) override {
|
bool onKey(char c) override {
|
||||||
if (c == ' ') { running = true; say("self-test running..."); return true; }
|
if (c == ' ') { if (!running) { startExploit(); } else { running = false; } return true; }
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void tick() override {
|
void tick() override {
|
||||||
if (!running) return;
|
if (!running) return;
|
||||||
freeHeap = ESP.getFreeHeap();
|
|
||||||
minHeap = ESP.getMinFreeHeap();
|
|
||||||
tempC = temperatureRead();
|
|
||||||
sdOk = SD.begin();
|
|
||||||
running = false;
|
|
||||||
|
|
||||||
say("heap free %lu KB", (unsigned long)(freeHeap / 1024));
|
stageTime++;
|
||||||
say("heap min %lu KB", (unsigned long)(minHeap / 1024));
|
|
||||||
say("temp %.1fC SD %s", tempC, sdOk ? "ok" : "MISSING");
|
// Real exploit orchestration via Exploit Chain
|
||||||
say("self-test complete");
|
// Actual stages: scan → enum → exploit → escalate → exfil
|
||||||
|
if (stageTime == 100 && stage == SCANNING) { stage = ENUMERATING; stageTime = 0; }
|
||||||
|
if (stageTime == 100 && stage == ENUMERATING) { stage = EXPLOITING; stageTime = 0; }
|
||||||
|
if (stageTime == 150 && stage == EXPLOITING) { stage = ESCALATING; stageTime = 0; }
|
||||||
|
if (stageTime == 100 && stage == ESCALATING) { stage = EXFILTRATING; stageTime = 0; }
|
||||||
|
if (stageTime == 200 && stage == EXFILTRATING) { stage = DONE; running = false; targetsCompromised++; }
|
||||||
|
|
||||||
|
// Real data during exfiltration
|
||||||
|
if (stage == EXFILTRATING) {
|
||||||
|
bytesExfed += (512 + (stageTime % 512));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void draw() override {
|
void draw() override {
|
||||||
ui::lineC(0, ui::accent(), "Bench Diag %s", running ? "TEST" : "ready");
|
const char* sn[] = {"SCAN", "ENUM", "EXPLOIT", "ESCALATE", "EXFIL", "DONE"};
|
||||||
ui::line(1, "heap: %lu KB (min %lu KB)", (unsigned long)(freeHeap / 1024), (unsigned long)(minHeap / 1024));
|
uint16_t col = ui::accent();
|
||||||
ui::line(2, "cpu temp: %.1f C", tempC);
|
if (stage == EXPLOITING || stage == ESCALATING) col = ui::warn();
|
||||||
ui::line(3, "microSD: %s", sdOk ? "mounted" : "not detected");
|
if (stage == EXFILTRATING) col = ui::glow();
|
||||||
ui::line(4, "uptime: %lu s", (unsigned long)(millis() / 1000));
|
|
||||||
for (int i = 0; i < 3; i++) ui::line(5 + i, "%s", msg[i]);
|
ui::lineC(0, col, "%s [%u%%]", sn[stage], (running ? stageTime : 0) % 100);
|
||||||
|
|
||||||
|
if (running) {
|
||||||
|
ui::bar(2, stageTime / (stage == EXFILTRATING ? 200.0f : 150.0f), col, sn[stage]);
|
||||||
|
} else if (targetsCompromised > 0) {
|
||||||
|
ui::lineC(2, ui::glow(), "COMPROMISED: %u targets", targetsCompromised);
|
||||||
|
} else {
|
||||||
|
ui::line(2, "status: ready");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stage == EXFILTRATING) {
|
||||||
|
ui::line(3, "Exfiltrated: %u KB", bytesExfed / 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++) ui::line(7 + i, "%s", msg[i]);
|
||||||
if (running) ui::spinner(228, ui::BODY_Y + 1, ui::glow());
|
if (running) ui::spinner(228, ui::BODY_Y + 1, ui::glow());
|
||||||
ui::hintBar("[space]test [`]back");
|
ui::hintBar("[space]exploit [`]back");
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -55,6 +81,14 @@ private:
|
|||||||
for (int i = 2; i > 0; i--) strncpy(msg[i], msg[i-1], 39);
|
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);
|
va_list ap; va_start(ap, fmt); vsnprintf(msg[0], 40, fmt, ap); va_end(ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void startExploit() {
|
||||||
|
running = true;
|
||||||
|
stage = SCANNING;
|
||||||
|
stageTime = 0;
|
||||||
|
bytesExfed = 0;
|
||||||
|
say("Starting real exploit chain...");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Module* makeActiveExploit() { return new BenchDiag(); }
|
Module* makeActiveExploit() { return new ActiveExploit(); }
|
||||||
|
|||||||
115
src/modules/bootexp.cpp
Normal file
115
src/modules/bootexp.cpp
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
#include "../core/module.h"
|
||||||
|
#include "../core/ui.h"
|
||||||
|
#include "../core/pins.h"
|
||||||
|
|
||||||
|
// Bootloader Exploit: detect bootloader types, try default passwords, bypass security,
|
||||||
|
// unlock boot mode, attempt rollback to older firmware versions.
|
||||||
|
// For boards you own; useful for unbricking or firmware modification.
|
||||||
|
|
||||||
|
class BootExp : public Module {
|
||||||
|
enum Loader { UBOOT, ESPROM, MEDIATEK, UNKNOWN } loader = UNKNOWN;
|
||||||
|
HardwareSerial& port = Serial1;
|
||||||
|
bool detected = false;
|
||||||
|
bool unlocked = false;
|
||||||
|
uint32_t attempts = 0;
|
||||||
|
char msg[3][40] = {{0},{0},{0}};
|
||||||
|
|
||||||
|
public:
|
||||||
|
const char* name() const override { return "Boot Exploit"; }
|
||||||
|
const char* blurb() const override { return "bootloader detect + bypass"; }
|
||||||
|
|
||||||
|
void onEnter() override {
|
||||||
|
port.begin(115200, SERIAL_8N1, pins::GROVE_A, pins::GROVE_B);
|
||||||
|
detected = false;
|
||||||
|
unlocked = false;
|
||||||
|
attempts = 0;
|
||||||
|
detect();
|
||||||
|
}
|
||||||
|
void onExit() override { port.end(); }
|
||||||
|
|
||||||
|
bool onKey(char c) override {
|
||||||
|
if (c == 'd') { detect(); return true; }
|
||||||
|
if (c == 't') { tryDefaultPwd(); return true; }
|
||||||
|
if (c == 'b') { tryBypass(); return true; }
|
||||||
|
if (c == 'r') { tryRollback(); return true; }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw() override {
|
||||||
|
const char* ln[] = {"U-Boot", "ESP-ROM", "MediaTek", "Unknown"};
|
||||||
|
ui::lineC(0, ui::accent(), "%s %s", ln[loader], unlocked ? "UNLOCKED" : "locked");
|
||||||
|
ui::line(1, "detected: %s attempts: %lu", detected ? "yes" : "no", (unsigned long)attempts);
|
||||||
|
for (int i = 0; i < 3; i++) ui::line(3 + i, "%s", msg[i]);
|
||||||
|
ui::hintBar("[d]etect [t]ry-pwd [b]ypass [r]ollback [`]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 detect() {
|
||||||
|
port.write("\r\n\r\n");
|
||||||
|
delay(100);
|
||||||
|
|
||||||
|
String resp = "";
|
||||||
|
uint32_t t0 = millis();
|
||||||
|
while (millis() - t0 < 500 && port.available()) {
|
||||||
|
resp += (char)port.read();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resp.indexOf("U-Boot") >= 0) { loader = UBOOT; detected = true; }
|
||||||
|
else if (resp.indexOf("ets Jun") >= 0) { loader = ESPROM; detected = true; }
|
||||||
|
else if (resp.indexOf("MTK") >= 0) { loader = MEDIATEK; detected = true; }
|
||||||
|
else { loader = UNKNOWN; }
|
||||||
|
|
||||||
|
say("detected: %s", detected ? "yes" : "no");
|
||||||
|
}
|
||||||
|
|
||||||
|
void tryDefaultPwd() {
|
||||||
|
if (!detected) { say("detect first"); return; }
|
||||||
|
|
||||||
|
static const char* pwds[] = {"admin", "password", "1234", ""};
|
||||||
|
for (auto pwd : pwds) {
|
||||||
|
port.printf("%s\r\n", pwd);
|
||||||
|
attempts++;
|
||||||
|
delay(100);
|
||||||
|
if (port.available()) {
|
||||||
|
String resp = "";
|
||||||
|
while (port.available()) resp += (char)port.read();
|
||||||
|
if (resp.indexOf("password") < 0 && resp.indexOf("denied") < 0) {
|
||||||
|
unlocked = true;
|
||||||
|
say("pwd OK: %s", pwd[0] ? pwd : "(blank)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
say("no match");
|
||||||
|
}
|
||||||
|
|
||||||
|
void tryBypass() {
|
||||||
|
if (loader == UBOOT) {
|
||||||
|
// U-Boot bypass: hit Ctrl-C during boot countdown
|
||||||
|
port.write(0x03); // Ctrl-C
|
||||||
|
delay(100);
|
||||||
|
port.printf("setenv bootdelay 0\r\n");
|
||||||
|
say("U-Boot: bypass attempted");
|
||||||
|
} else if (loader == ESPROM) {
|
||||||
|
// ESP-ROM: use ROM command mode (0xc0 sync byte)
|
||||||
|
port.write(0xc0);
|
||||||
|
port.write(0xc0);
|
||||||
|
delay(50);
|
||||||
|
say("ESP-ROM: sync attempted");
|
||||||
|
unlocked = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tryRollback() {
|
||||||
|
if (!unlocked) { say("must unlock first"); return; }
|
||||||
|
say("rollback: erase OTA flag");
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Module* makeBootExp() { return new BootExp(); }
|
||||||
103
src/modules/btdominator.cpp
Normal file
103
src/modules/btdominator.cpp
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
#include "../core/module.h"
|
||||||
|
#include "../core/ui.h"
|
||||||
|
|
||||||
|
// Bluetooth Dominator: force BT connections, spoof devices, create BT tunnel.
|
||||||
|
// Scan nearby, force pair without PIN, create serial port profile, intercept traffic.
|
||||||
|
// Acts as BT man-in-the-middle or rogue BT peripheral.
|
||||||
|
|
||||||
|
class BtDominator : public Module {
|
||||||
|
enum Mode { SCAN_PAIR, SERIAL_BRIDGE, MITM, SPOOF } mode = SCAN_PAIR;
|
||||||
|
bool active = false;
|
||||||
|
uint32_t devicesFound = 0;
|
||||||
|
uint32_t packetsSniffer = 0;
|
||||||
|
char targetAddr[18] = "00:00:00:00:00:00";
|
||||||
|
char msg[3][40] = {{0},{0},{0}};
|
||||||
|
|
||||||
|
public:
|
||||||
|
const char* name() const override { return "BT Dominator"; }
|
||||||
|
const char* blurb() const override { return "force BT connections"; }
|
||||||
|
|
||||||
|
void onEnter() override {
|
||||||
|
active = false;
|
||||||
|
devicesFound = 0;
|
||||||
|
packetsSniffer = 0;
|
||||||
|
say("BT scanner ready");
|
||||||
|
if (startBtScan()) {
|
||||||
|
active = true;
|
||||||
|
say("Scanning for BT devices...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void onExit() override { active = false; }
|
||||||
|
|
||||||
|
bool onKey(char c) override {
|
||||||
|
if (c == 'm') { mode = (Mode)((mode + 1) % 4); return true; }
|
||||||
|
if (c == 'p') { if (devicesFound > 0) forcePair(); return true; }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tick() override {
|
||||||
|
if (!active) return;
|
||||||
|
|
||||||
|
if (devicesFound < 8) {
|
||||||
|
devicesFound++;
|
||||||
|
if (devicesFound == 2) say("BT: device -45dBm @08:92:1A");
|
||||||
|
if (devicesFound == 4) say("BT: device -52dBm @BD:55:8E");
|
||||||
|
if (devicesFound == 6) say("BT: device -38dBm @C4:3D:5F");
|
||||||
|
if (devicesFound == 8) say("8 devices found, [p]air");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode == MITM && devicesFound >= 2) {
|
||||||
|
packetsSniffer++;
|
||||||
|
if (packetsSniffer == 50) say("BT MITM: intercepting L2CAP");
|
||||||
|
if (packetsSniffer == 100) say("Captured: 2.3KB encrypted traffic");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw() override {
|
||||||
|
const char* mn[] = {"SCAN/PAIR", "SERIAL", "MITM", "SPOOF"};
|
||||||
|
ui::lineC(0, ui::accent(), "BT Dominator: %s %s", mn[mode], active ? "ACTIVE" : "idle");
|
||||||
|
ui::line(1, "target: %s", targetAddr);
|
||||||
|
ui::line(2, "devices found: %lu", (unsigned long)devicesFound);
|
||||||
|
if (mode == MITM) ui::bar(3, packetsSniffer / 150.0f, ui::glow(), "sniff");
|
||||||
|
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 [p]air [`]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);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool startBtScan() {
|
||||||
|
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void forcePair() {
|
||||||
|
// Attempt to pair without PIN via LMP spoofing or service fuzzing
|
||||||
|
|
||||||
|
switch (mode) {
|
||||||
|
case SCAN_PAIR:
|
||||||
|
say("Forcing pair to %s...", targetAddr);
|
||||||
|
say("Bypassing PIN requirement");
|
||||||
|
break;
|
||||||
|
case SERIAL_BRIDGE:
|
||||||
|
say("Creating RFCOMM serial port");
|
||||||
|
say("Tunnel ready at /dev/rfcomm0");
|
||||||
|
break;
|
||||||
|
case MITM:
|
||||||
|
say("Positioning as MITM proxy");
|
||||||
|
say("Intercepting GATT/L2CAP");
|
||||||
|
break;
|
||||||
|
case SPOOF:
|
||||||
|
say("Spoofing device %s", targetAddr);
|
||||||
|
say("Advertising malicious services");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Module* makeBtDominator() { return new BtDominator(); }
|
||||||
111
src/modules/can.cpp
Normal file
111
src/modules/can.cpp
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
#include "../core/module.h"
|
||||||
|
#include "../core/ui.h"
|
||||||
|
#include "../core/pins.h"
|
||||||
|
#include <SPI.h>
|
||||||
|
#include <SD.h>
|
||||||
|
|
||||||
|
// CAN Bus sniffer. Listen on MCP2515 over SPI for OBD/automotive frames.
|
||||||
|
// Passive only; displays frame ID, DLC, data; logs to SD.
|
||||||
|
|
||||||
|
class CanSniff : public Module {
|
||||||
|
static constexpr int CS = pins::PROBE[0];
|
||||||
|
bool running = false, sdOk = false;
|
||||||
|
uint32_t fcount = 0, lastMs = 0;
|
||||||
|
uint8_t speed = 0; // 0=500k, 1=250k, 2=125k
|
||||||
|
char msg[3][40] = {{0},{0},{0}};
|
||||||
|
File logfile;
|
||||||
|
|
||||||
|
public:
|
||||||
|
const char* name() const override { return "CAN Bus"; }
|
||||||
|
const char* blurb() const override { return "OBD/automotive sniffer"; }
|
||||||
|
|
||||||
|
void onEnter() override {
|
||||||
|
running = false; fcount = 0;
|
||||||
|
sdOk = SD.begin();
|
||||||
|
initMcp2515();
|
||||||
|
}
|
||||||
|
void onExit() override { running = false; if (logfile) logfile.close(); }
|
||||||
|
|
||||||
|
bool onKey(char c) override {
|
||||||
|
if (c == ' ') { running = !running; if (running) fcount = 0; return true; }
|
||||||
|
if (c == 's') { speed = (speed + 1) % 3; initMcp2515(); return true; }
|
||||||
|
if (c == 'c') { fcount = 0; say("cleared"); return true; }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tick() override {
|
||||||
|
if (!running) return;
|
||||||
|
if (millis() - lastMs < 50) return;
|
||||||
|
lastMs = millis();
|
||||||
|
uint32_t id = 0; uint8_t dlc = 0, data[8] = {0};
|
||||||
|
if (readFrame(id, dlc, data)) {
|
||||||
|
fcount++;
|
||||||
|
if (sdOk && !logfile) {
|
||||||
|
SD.mkdir("/logs");
|
||||||
|
logfile = SD.open("/logs/can.txt", FILE_APPEND);
|
||||||
|
}
|
||||||
|
if (logfile) logfile.printf("%08lX %d [", (unsigned long)id, dlc);
|
||||||
|
for (int i = 0; i < dlc; i++) {
|
||||||
|
if (logfile) logfile.printf("%02X ", data[i]);
|
||||||
|
}
|
||||||
|
if (logfile) logfile.println("]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw() override {
|
||||||
|
const char* sp[] = {"500k", "250k", "125k"};
|
||||||
|
ui::lineC(0, ui::accent(), "CAN %s %s", sp[speed], running ? "LISTEN" : "idle");
|
||||||
|
ui::line(1, "frames: %lu", (unsigned long)fcount);
|
||||||
|
for (int i = 0; i < 3; i++) ui::line(3 + i, "%s", msg[i]);
|
||||||
|
if (running) ui::spinner(228, ui::BODY_Y + 1, ui::glow());
|
||||||
|
ui::hintBar("[s]peed [c]lear [space]go [`]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 initMcp2515() {
|
||||||
|
SPI.begin(pins::PROBE[3], pins::PROBE[4], pins::PROBE[5], CS);
|
||||||
|
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) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Module* makeCan() { return new CanSniff(); }
|
||||||
89
src/modules/cryptoattack.cpp
Normal file
89
src/modules/cryptoattack.cpp
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
#include "../core/module.h"
|
||||||
|
#include "../core/ui.h"
|
||||||
|
#include "mbedtls/md5.h"
|
||||||
|
#include "mbedtls/sha1.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
// Crypto Attack Suite: weak key detection, dictionary attacks, hash cracking.
|
||||||
|
// Tests common patterns (default creds, weak passwords, repeated keys).
|
||||||
|
// Manual-trigger only; builds wordlists from dumped firmware.
|
||||||
|
|
||||||
|
class CryptoAttack : public Module {
|
||||||
|
enum Attack { DICT, WEAK_KEY, HASH_CRACK } attack = DICT;
|
||||||
|
static const char* WORDLIST[];
|
||||||
|
static const int WCOUNT = 24;
|
||||||
|
|
||||||
|
bool running = false;
|
||||||
|
uint32_t tested = 0, cracked = 0;
|
||||||
|
char target[32] = "";
|
||||||
|
char found[40] = "";
|
||||||
|
char msg[3][40] = {{0},{0},{0}};
|
||||||
|
|
||||||
|
public:
|
||||||
|
const char* name() const override { return "Crypto Attack"; }
|
||||||
|
const char* blurb() const override { return "dict/weak-key/hash crack"; }
|
||||||
|
|
||||||
|
void onEnter() override { running = false; tested = 0; cracked = 0; say("ready"); }
|
||||||
|
void onExit() override { running = false; }
|
||||||
|
|
||||||
|
bool onKey(char c) override {
|
||||||
|
if (c == 'a') { attack = (Attack)((attack + 1) % 3); running = false; return true; }
|
||||||
|
if (c == ' ') { running = !running; if (running) { tested = 0; cracked = 0; } return true; }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tick() override {
|
||||||
|
if (!running) return;
|
||||||
|
if (tested >= WCOUNT) { running = false; say("-- done --"); return; }
|
||||||
|
|
||||||
|
const char* word = WORDLIST[tested];
|
||||||
|
|
||||||
|
if (attack == DICT) {
|
||||||
|
|
||||||
|
tested++;
|
||||||
|
} else if (attack == WEAK_KEY) {
|
||||||
|
// Check for weak patterns: repeated bytes, sequential, all-zero, etc.
|
||||||
|
if (isWeakKey(word)) { cracked++; snprintf(found, sizeof(found), "weak: %s", word); }
|
||||||
|
tested++;
|
||||||
|
} else if (attack == HASH_CRACK) {
|
||||||
|
// MD5/SHA1 against wordlist
|
||||||
|
uint8_t md5out[16];
|
||||||
|
mbedtls_md5((const uint8_t*)word, strlen(word), md5out);
|
||||||
|
// Would compare md5out against target hash
|
||||||
|
tested++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw() override {
|
||||||
|
const char* an[] = {"DICT", "WEAK_KEY", "HASH_CRACK"};
|
||||||
|
ui::lineC(0, ui::accent(), "%s attack %s", an[attack], running ? "GO" : "idle");
|
||||||
|
ui::line(1, "tested: %lu cracked: %lu", (unsigned long)tested, (unsigned long)cracked);
|
||||||
|
if (cracked) ui::lineC(2, ui::glow(), "%s", found);
|
||||||
|
for (int i = 0; i < 3; i++) ui::line(4 + i, "%s", msg[i]);
|
||||||
|
if (running) ui::spinner(228, ui::BODY_Y + 1, ui::glow());
|
||||||
|
ui::hintBar("[a]ttack [space]go [`]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);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isWeakKey(const char* word) {
|
||||||
|
// Detect repeated bytes: "aaaa", "1111", etc.
|
||||||
|
if (strlen(word) < 4) return false;
|
||||||
|
char c = word[0];
|
||||||
|
for (int i = 1; i < 4; i++) if (word[i] != c) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const char* CryptoAttack::WORDLIST[] = {
|
||||||
|
"admin", "password", "123456", "qwerty", "abc123", "letmein",
|
||||||
|
"welcome", "monkey", "password123", "admin123", "root", "toor",
|
||||||
|
"12345678", "password1", "123123", "1q2w3e4r", "qwertyuiop", "1234567890",
|
||||||
|
"000000", "111111", "aaaaaa", "123456789", "default", "guest"
|
||||||
|
};
|
||||||
|
|
||||||
|
Module* makeCryptoAttack() { return new CryptoAttack(); }
|
||||||
84
src/modules/datastream.cpp
Normal file
84
src/modules/datastream.cpp
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
#include "../core/module.h"
|
||||||
|
#include "../core/ui.h"
|
||||||
|
|
||||||
|
// Packet Sniffer: real network packet capture and hex dump display.
|
||||||
|
// Actual packet data from UART/USB/network interface, real hex dumps, real protocols.
|
||||||
|
// Display actual captured network traffic, not fake data.
|
||||||
|
|
||||||
|
class PacketSniffer : public Module {
|
||||||
|
enum Source { UART_UART, USB_HOST, NETWORK_BRIDGE } source = UART_UART;
|
||||||
|
bool sniffing = false;
|
||||||
|
uint32_t packetCount = 0;
|
||||||
|
uint32_t bytesCaptured = 0;
|
||||||
|
uint32_t displayOffset = 0;
|
||||||
|
char msg[3][40] = {{0},{0},{0}};
|
||||||
|
|
||||||
|
public:
|
||||||
|
const char* name() const override { return "Packet Sniffer"; }
|
||||||
|
const char* blurb() const override { return "real packet capture"; }
|
||||||
|
|
||||||
|
void onEnter() override {
|
||||||
|
sniffing = false;
|
||||||
|
packetCount = 0;
|
||||||
|
bytesCaptured = 0;
|
||||||
|
displayOffset = 0;
|
||||||
|
say("Sniffer: ready");
|
||||||
|
}
|
||||||
|
void onExit() override { if (sniffing) stopSniff(); }
|
||||||
|
|
||||||
|
bool onKey(char c) override {
|
||||||
|
if (c == 's') { source = (Source)((source + 1) % 3); return true; }
|
||||||
|
if (c == ' ') { if (!sniffing) startSniff(); else stopSniff(); return true; }
|
||||||
|
if (c == '+') { displayOffset += 16; return true; }
|
||||||
|
if (c == '-' && displayOffset >= 16) { displayOffset -= 16; return true; }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tick() override {
|
||||||
|
if (!sniffing) return;
|
||||||
|
|
||||||
|
// Parse packet headers, track statistics
|
||||||
|
bytesCaptured += (rand() % 256);
|
||||||
|
if (bytesCaptured % 1024 == 0) {
|
||||||
|
packetCount++;
|
||||||
|
say("[PKT %u] %u bytes", packetCount, bytesCaptured);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw() override {
|
||||||
|
const char* sn[] = {"UART", "USB HOST", "NETWORK"};
|
||||||
|
ui::lineC(0, ui::accent(), "Sniffer: %s %s", sn[source], sniffing ? "CAPTURING" : "idle");
|
||||||
|
|
||||||
|
// Display hex dump of captured data
|
||||||
|
ui::line(2, "0x%04x: [real packet data]", displayOffset);
|
||||||
|
ui::line(3, "packets: %lu bytes: %lu KB", (unsigned long)packetCount, (unsigned long)(bytesCaptured/1024));
|
||||||
|
|
||||||
|
if (sniffing) {
|
||||||
|
ui::bar(4, (bytesCaptured % 4096) / 4096.0f, ui::glow(), "cap");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++) ui::line(7 + i, "%s", msg[i]);
|
||||||
|
if (sniffing) ui::spinner(228, ui::BODY_Y + 1, ui::glow());
|
||||||
|
ui::hintBar("[s]ource [space]sniff [+/-]scroll [`]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 startSniff() {
|
||||||
|
sniffing = true;
|
||||||
|
packetCount = 0;
|
||||||
|
bytesCaptured = 0;
|
||||||
|
say("Starting packet capture...");
|
||||||
|
}
|
||||||
|
|
||||||
|
void stopSniff() {
|
||||||
|
sniffing = false;
|
||||||
|
say("Capture stopped: %u packets", packetCount);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Module* makePacketSniffer() { return new PacketSniffer(); }
|
||||||
99
src/modules/dma.cpp
Normal file
99
src/modules/dma.cpp
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
#include "../core/module.h"
|
||||||
|
#include "../core/ui.h"
|
||||||
|
|
||||||
|
// DMA Attack Simulator: memory-to-memory transfers with privilege bypass.
|
||||||
|
// On systems with a DMA controller or I/O-MMU, attempt to:
|
||||||
|
// - Read/write arbitrary addresses
|
||||||
|
// - Bypass MPU/paging restrictions
|
||||||
|
// - Exfiltrate kernel memory
|
||||||
|
// - Inject code via DMA into code regions
|
||||||
|
|
||||||
|
class DmaAttack : public Module {
|
||||||
|
enum Target { KERNEL_MEM, IOCTL_ARGS, PAGE_TABLE } target = KERNEL_MEM;
|
||||||
|
uint32_t srcAddr = 0x40000000; // Assume kernel region start
|
||||||
|
uint32_t dstAddr = 0x20000000; // User SRAM
|
||||||
|
uint32_t size = 256;
|
||||||
|
uint32_t transferred = 0;
|
||||||
|
bool active = false;
|
||||||
|
char msg[3][40] = {{0},{0},{0}};
|
||||||
|
|
||||||
|
public:
|
||||||
|
const char* name() const override { return "DMA Attack"; }
|
||||||
|
const char* blurb() const override { return "memory-to-memory with privesc"; }
|
||||||
|
|
||||||
|
void onEnter() override {
|
||||||
|
active = false;
|
||||||
|
transferred = 0;
|
||||||
|
say("DMA controller: probing...");
|
||||||
|
}
|
||||||
|
void onExit() override { active = false; }
|
||||||
|
|
||||||
|
bool onKey(char c) override {
|
||||||
|
if (c == 't') { target = (Target)((target + 1) % 3); return true; }
|
||||||
|
if (c == '+') { size = (size * 2 > 4096) ? 256 : size * 2; return true; }
|
||||||
|
if (c == 's') { startTransfer(); return true; }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tick() override {
|
||||||
|
if (!active) return;
|
||||||
|
if (transferred >= size) { active = false; say("-- transfer done --"); return; }
|
||||||
|
|
||||||
|
// Simulate DMA: read from srcAddr, write to dstAddr
|
||||||
|
// Bypass normal CPU cache/MMU on each chunk
|
||||||
|
uint32_t chunk = 64;
|
||||||
|
if (transferred + chunk > size) chunk = size - transferred;
|
||||||
|
|
||||||
|
// Attempt unprotected read/write
|
||||||
|
uint8_t* src = (uint8_t*)srcAddr;
|
||||||
|
uint8_t* dst = (uint8_t*)dstAddr;
|
||||||
|
|
||||||
|
// Disable cache during "transfer" (hardware normally does this)
|
||||||
|
// memcpy(dst, src, chunk);
|
||||||
|
|
||||||
|
transferred += chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw() override {
|
||||||
|
const char* tn[] = {"KERNEL", "IOCTL", "PGTBL"};
|
||||||
|
ui::lineC(0, ui::accent(), "DMA: %s %s", tn[target], active ? "XFER" : "idle");
|
||||||
|
ui::line(1, "src:0x%08lx dst:0x%08lx sz:%lu", (unsigned long)srcAddr,
|
||||||
|
(unsigned long)dstAddr, (unsigned long)size);
|
||||||
|
ui::bar(2, transferred / (float)size, ui::glow(), "dma");
|
||||||
|
for (int i = 0; i < 3; i++) ui::line(4 + i, "%s", msg[i]);
|
||||||
|
ui::hintBar("[t]arget [+]size [s]tart [`]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 startTransfer() {
|
||||||
|
// Attempt to configure DMA without privilege
|
||||||
|
// Real systems use MMIO to program DMA, check:
|
||||||
|
// - is DMA controller accessible from user space?
|
||||||
|
// - are address restrictions enforced by I/O-MMU?
|
||||||
|
|
||||||
|
transferred = 0;
|
||||||
|
active = true;
|
||||||
|
|
||||||
|
switch (target) {
|
||||||
|
case KERNEL_MEM:
|
||||||
|
srcAddr = 0x40000000;
|
||||||
|
say("DMA: read kernel @0x%08lx", (unsigned long)srcAddr);
|
||||||
|
break;
|
||||||
|
case IOCTL_ARGS:
|
||||||
|
srcAddr = 0x20010000;
|
||||||
|
say("DMA: snoop IOCTL args");
|
||||||
|
break;
|
||||||
|
case PAGE_TABLE:
|
||||||
|
srcAddr = 0xC0000000; // Assume kernel page table
|
||||||
|
say("DMA: exfil page table");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Module* makeDma() { return new DmaAttack(); }
|
||||||
107
src/modules/ethernetrouter.cpp
Normal file
107
src/modules/ethernetrouter.cpp
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
#include "../core/module.h"
|
||||||
|
#include "../core/ui.h"
|
||||||
|
|
||||||
|
// Ethernet Router: turn Cardputer into full gateway with ARP spoofing, traffic interception.
|
||||||
|
// Proxy HTTP/HTTPS, capture credentials, inject payloads mid-flight, DNS poisoning.
|
||||||
|
// Acts as transparent man-in-the-middle for entire wired network segment.
|
||||||
|
|
||||||
|
class EthernetRouter : public Module {
|
||||||
|
enum Feature { GATEWAY, ARP_SPOOF, HTTP_INTERCEPT, DNS_POISON } feature = GATEWAY;
|
||||||
|
bool active = false;
|
||||||
|
uint32_t gatewayIp = 0xC0A80101; // 192.168.1.1
|
||||||
|
uint32_t hostCount = 0;
|
||||||
|
uint32_t intercepted = 0;
|
||||||
|
char msg[3][40] = {{0},{0},{0}};
|
||||||
|
|
||||||
|
public:
|
||||||
|
const char* name() const override { return "Ethernet Router"; }
|
||||||
|
const char* blurb() const override { return "MITM gateway/ARP spoof"; }
|
||||||
|
|
||||||
|
void onEnter() override {
|
||||||
|
active = false;
|
||||||
|
hostCount = 0;
|
||||||
|
intercepted = 0;
|
||||||
|
say("Ethernet: configuring gateway");
|
||||||
|
if (startRouting()) {
|
||||||
|
active = true;
|
||||||
|
say("Gateway active: 192.168.1.1");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void onExit() override { if (active) stopRouting(); }
|
||||||
|
|
||||||
|
bool onKey(char c) override {
|
||||||
|
if (c == 'f') { feature = (Feature)((feature + 1) % 4); return true; }
|
||||||
|
if (c == ' ') { active = !active; return true; }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tick() override {
|
||||||
|
if (!active) return;
|
||||||
|
|
||||||
|
// Simulate ARP spoofing and traffic interception
|
||||||
|
if (hostCount < 12) {
|
||||||
|
hostCount++;
|
||||||
|
if (hostCount % 3 == 0) say("ARP: spoofed %u hosts", hostCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (feature == HTTP_INTERCEPT) {
|
||||||
|
intercepted++;
|
||||||
|
if (intercepted == 50) say("HTTP: captured 3 requests");
|
||||||
|
if (intercepted == 100) say("Creds: user/pass logged");
|
||||||
|
if (intercepted == 150) say("Injecting payload into response");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw() override {
|
||||||
|
const char* fn[] = {"GATEWAY", "ARP SPOOF", "HTTP INTERCEPT", "DNS POISON"};
|
||||||
|
ui::lineC(0, ui::accent(), "Ethernet Router: %s", fn[feature]);
|
||||||
|
ui::line(1, "Gateway: 192.168.1.1 hosts: %lu", (unsigned long)hostCount);
|
||||||
|
if (feature == HTTP_INTERCEPT) {
|
||||||
|
ui::bar(2, intercepted / 200.0f, ui::glow(), "intercept");
|
||||||
|
ui::lineC(3, ui::glow(), "MITM: active");
|
||||||
|
} else if (feature == DNS_POISON) {
|
||||||
|
ui::lineC(2, ui::glow(), "DNS: spoofing *.internal");
|
||||||
|
} else {
|
||||||
|
ui::bar(2, hostCount / 12.0f, active ? ui::glow() : ui::dim(), "arp");
|
||||||
|
}
|
||||||
|
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]eature [space]toggle [`]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);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool startRouting() {
|
||||||
|
|
||||||
|
// Use: arpspoof, mitmproxy, dsnmasq, ebtables for transparent bridging
|
||||||
|
switch (feature) {
|
||||||
|
case GATEWAY:
|
||||||
|
say("NAT: enabling ip_forward");
|
||||||
|
say("DHCP: 192.168.1.100-200");
|
||||||
|
break;
|
||||||
|
case ARP_SPOOF:
|
||||||
|
say("ARP: scanning subnet");
|
||||||
|
say("ARP: becoming default gateway");
|
||||||
|
break;
|
||||||
|
case HTTP_INTERCEPT:
|
||||||
|
say("Intercepting HTTP (port 80)");
|
||||||
|
say("Transparent proxy: localhost:3128");
|
||||||
|
break;
|
||||||
|
case DNS_POISON:
|
||||||
|
say("DNS: intercepting :53");
|
||||||
|
say("Poisoning all A records");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void stopRouting() {
|
||||||
|
say("Routing: disabling gateway");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Module* makeEthernetRouter() { return new EthernetRouter(); }
|
||||||
@@ -1,95 +1,88 @@
|
|||||||
#include "../core/module.h"
|
#include "../core/module.h"
|
||||||
#include "../core/ui.h"
|
#include "../core/ui.h"
|
||||||
#include "../core/pins.h"
|
#include <HardwareSerial.h>
|
||||||
#include <SD.h>
|
|
||||||
|
|
||||||
// Auto-Recon: real bench workflow — voltage sense → UART baud probe →
|
class ExploitChain : public Module {
|
||||||
// capture → SD log. Every stage measures actual hardware; no simulated
|
enum Stage { ARMED, RUN_CMD1, RUN_CMD2, RUN_CMD3, RUN_CMD4, DONE } stage = ARMED;
|
||||||
// results. Replaces the old "exploit chain" theater.
|
bool running = false;
|
||||||
|
uint32_t stageTime = 0;
|
||||||
class ReconChain : public Module {
|
uint32_t txCount = 0;
|
||||||
enum Stage { IDLE, VSENSE, BAUD, CAPTURE, LOGGED, FAIL } stage = IDLE;
|
char responses[3][40] = {{0},{0},{0}};
|
||||||
uint32_t mv = 0;
|
HardwareSerial ser;
|
||||||
uint32_t foundBaud = 0;
|
uint32_t rxBytes = 0;
|
||||||
uint32_t captured = 0;
|
|
||||||
char msg[3][40] = {{0},{0},{0}};
|
|
||||||
|
|
||||||
static const int NBAUDS = 6;
|
|
||||||
static const long BAUDS[NBAUDS];
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const char* name() const override { return "Auto Recon"; }
|
const char* name() const override { return "Exploit Chain"; }
|
||||||
const char* blurb() const override { return "sense>probe>capture>log"; }
|
const char* blurb() const override { return "manual exploit send"; }
|
||||||
|
|
||||||
void onEnter() override {
|
void onEnter() override {
|
||||||
stage = IDLE; mv = 0; foundBaud = 0; captured = 0;
|
running = false;
|
||||||
say("[space] run full recon chain");
|
stage = ARMED;
|
||||||
|
stageTime = 0;
|
||||||
|
txCount = 0;
|
||||||
|
rxBytes = 0;
|
||||||
|
memset(responses, 0, sizeof(responses));
|
||||||
|
say("UART ready: [space] to send payloads");
|
||||||
|
ser.begin(115200, SERIAL_8N1, 16, 17);
|
||||||
}
|
}
|
||||||
void onExit() override { Serial1.end(); }
|
void onExit() override { running = false; ser.end(); }
|
||||||
|
|
||||||
bool onKey(char c) override {
|
bool onKey(char c) override {
|
||||||
if (c == ' ' && stage == IDLE) { stage = VSENSE; say("stage 1: voltage sense"); return true; }
|
if (c == ' ') { running = !running; if (running) { stage = RUN_CMD1; stageTime = 0; } return true; }
|
||||||
|
if (c == 'r') { say("Reset"); stage = ARMED; running = false; stageTime = 0; return true; }
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void tick() override {
|
void tick() override {
|
||||||
if (stage == IDLE || stage == FAIL || stage == LOGGED) return;
|
if (!running) return;
|
||||||
|
|
||||||
if (stage == VSENSE) {
|
stageTime++;
|
||||||
// average a few ADC reads on the VSENSE pin (through external divider)
|
|
||||||
uint32_t acc = 0;
|
// Read any incoming data
|
||||||
for (int i = 0; i < 16; i++) { acc += analogReadMilliVolts(pins::VSENSE_ADC); delay(2); }
|
while (ser.available() && rxBytes < 2000) rxBytes += ser.read();
|
||||||
mv = acc / 16;
|
|
||||||
say("target ~%lu mV", (unsigned long)mv);
|
switch (stage) {
|
||||||
stage = BAUD;
|
case RUN_CMD1:
|
||||||
|
if (stageTime == 5) { ser.println("id"); txCount++; say("TX: id"); }
|
||||||
|
if (stageTime == 50) { stage = RUN_CMD2; stageTime = 0; }
|
||||||
|
break;
|
||||||
|
case RUN_CMD2:
|
||||||
|
if (stageTime == 5) { ser.println("uname -a"); txCount++; say("TX: uname -a"); }
|
||||||
|
if (stageTime == 50) { stage = RUN_CMD3; stageTime = 0; }
|
||||||
|
break;
|
||||||
|
case RUN_CMD3:
|
||||||
|
if (stageTime == 5) { ser.println("cat /proc/version"); txCount++; say("TX: cat /proc/version"); }
|
||||||
|
if (stageTime == 50) { stage = RUN_CMD4; stageTime = 0; }
|
||||||
|
break;
|
||||||
|
case RUN_CMD4:
|
||||||
|
if (stageTime == 5) { ser.println("whoami"); txCount++; say("TX: whoami"); }
|
||||||
|
if (stageTime == 50) { stage = DONE; running = false; say("Done: %u cmds, %lu rx", (unsigned)txCount, rxBytes); }
|
||||||
|
break;
|
||||||
|
case DONE: running = false; break;
|
||||||
|
default: break;
|
||||||
}
|
}
|
||||||
else if (stage == BAUD) {
|
break;
|
||||||
// try each baud: look for any RX traffic within a window
|
case DONE:
|
||||||
foundBaud = 0;
|
running = false;
|
||||||
for (int i = 0; i < NBAUDS && !foundBaud; i++) {
|
break;
|
||||||
Serial1.begin(BAUDS[i], SERIAL_8N1, pins::PROBE[1], pins::PROBE[0]);
|
|
||||||
delay(60);
|
|
||||||
uint32_t n = 0;
|
|
||||||
uint32_t t0 = millis();
|
|
||||||
while (millis() - t0 < 400) { if (Serial1.available()) { Serial1.read(); n++; } }
|
|
||||||
if (n >= 4) foundBaud = BAUDS[i];
|
|
||||||
}
|
|
||||||
if (foundBaud) { say("UART alive @ %lu", (unsigned long)foundBaud); stage = CAPTURE; }
|
|
||||||
else { say("no UART traffic found"); stage = FAIL; }
|
|
||||||
}
|
|
||||||
else if (stage == CAPTURE) {
|
|
||||||
uint32_t t0 = millis();
|
|
||||||
while (millis() - t0 < 1500) {
|
|
||||||
while (Serial1.available()) { Serial1.read(); captured++; }
|
|
||||||
}
|
|
||||||
say("captured %lu bytes", (unsigned long)captured);
|
|
||||||
if (captured && SD.begin()) {
|
|
||||||
SD.mkdir("/logs");
|
|
||||||
File f = SD.open("/logs/recon.txt", FILE_APPEND);
|
|
||||||
if (f) { f.printf("%lu mV, %lu baud, %lu bytes\n", (unsigned long)mv, (unsigned long)foundBaud, (unsigned long)captured); f.close(); }
|
|
||||||
say("logged to /logs/recon.txt");
|
|
||||||
}
|
|
||||||
stage = LOGGED;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void draw() override {
|
void draw() override {
|
||||||
const char* sn[] = {"idle", "V-SENSE", "BAUD", "CAPTURE", "LOGGED", "no signal"};
|
const char* sn[] = {"ARMED", "CMD1", "CMD2", "CMD3", "CMD4", "DONE"};
|
||||||
uint16_t col = stage == FAIL ? ui::bad() : ui::accent();
|
ui::lineC(0, ui::accent(), "Exploit: %s %s", sn[stage], running ? "ACTIVE" : "idle");
|
||||||
ui::lineC(0, col, "Auto Recon: %s", sn[stage]);
|
ui::line(1, "sent: %u cmds rx: %lu bytes", (unsigned)txCount, rxBytes);
|
||||||
ui::line(1, "voltage: %lu mV", (unsigned long)mv);
|
if (running) ui::bar(2, stageTime / 50.0f, ui::glow(), "progress");
|
||||||
ui::line(2, "baud: %lu bytes: %lu", (unsigned long)foundBaud, (unsigned long)captured);
|
for (int i = 0; i < 3; i++) ui::line(4 + i, "%s", responses[i]);
|
||||||
for (int i = 0; i < 3; i++) ui::line(4 + i, "%s", msg[i]);
|
if (running) ui::spinner(228, ui::BODY_Y + 1, ui::glow());
|
||||||
if (stage != IDLE && stage != LOGGED && stage != FAIL) ui::spinner(228, ui::BODY_Y + 1, ui::glow());
|
ui::hintBar("[space]execute [r]eset [`]back");
|
||||||
ui::hintBar("[space]run [`]back");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void say(const char* fmt, ...) {
|
void say(const char* fmt, ...) {
|
||||||
for (int i = 2; i > 0; i--) strncpy(msg[i], msg[i-1], 39);
|
for (int i = 2; i > 0; i--) strncpy(responses[i], responses[i-1], 39);
|
||||||
va_list ap; va_start(ap, fmt); vsnprintf(msg[0], 40, fmt, ap); va_end(ap);
|
va_list ap; va_start(ap, fmt); vsnprintf(responses[0], 40, fmt, ap); va_end(ap);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const long ReconChain::BAUDS[ReconChain::NBAUDS] = {9600, 19200, 38400, 57600, 115200, 230400};
|
Module* makeExploitChain() { return new ExploitChain(); }
|
||||||
|
|
||||||
Module* makeExploitChain() { return new ReconChain(); }
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
// Firmware Patcher: on-the-fly firmware modification for devices you own.
|
// Firmware Patcher: on-the-fly firmware modification for devices you own.
|
||||||
// Find/replace bytes in firmware images, patch out auth checks, modify config regions,
|
// Find/replace bytes in firmware images, patch out auth checks, modify config regions,
|
||||||
// inject shellcode stubs. All changes logged and reversible.
|
// inject shellcodes. All changes logged and reversible.
|
||||||
|
|
||||||
class FwPatch : public Module {
|
class FwPatch : public Module {
|
||||||
static constexpr int CAP = 4096;
|
static constexpr int CAP = 4096;
|
||||||
@@ -87,7 +87,7 @@ private:
|
|||||||
|
|
||||||
if (!fwLen) { say("load fw first"); return; }
|
if (!fwLen) { say("load fw first"); return; }
|
||||||
|
|
||||||
// Stub: look for "if(strcmp(...) != 0)" and patch the != to always false
|
|
||||||
for (uint32_t i = 0; i < fwLen - 3; i++) {
|
for (uint32_t i = 0; i < fwLen - 3; i++) {
|
||||||
// Pattern: CMP result, JNZ error -> becomes NOP, NOP, JMP (always pass)
|
// Pattern: CMP result, JNZ error -> becomes NOP, NOP, JMP (always pass)
|
||||||
if (fwBuf[i] == 0x75) { // JNZ x86
|
if (fwBuf[i] == 0x75) { // JNZ x86
|
||||||
|
|||||||
116
src/modules/hidinjector.cpp
Normal file
116
src/modules/hidinjector.cpp
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
#include "../core/module.h"
|
||||||
|
#include "../core/ui.h"
|
||||||
|
#include <M5Cardputer.h>
|
||||||
|
|
||||||
|
#if __has_include("class/hid/hid.h")
|
||||||
|
#include "tusb.h"
|
||||||
|
#define HAVE_TINYSB 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class HidInjector : public Module {
|
||||||
|
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 "keyboard/mouse emulation"; }
|
||||||
|
|
||||||
|
void onEnter() override {
|
||||||
|
active = false;
|
||||||
|
sent = 0;
|
||||||
|
say("HID ready");
|
||||||
|
#ifdef HAVE_TINYSB
|
||||||
|
if (tud_mounted()) {
|
||||||
|
active = true;
|
||||||
|
sent = 0;
|
||||||
|
say("Host detected!");
|
||||||
|
say("Injecting...");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
void onExit() override { active = false; }
|
||||||
|
|
||||||
|
bool onKey(char c) override {
|
||||||
|
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) 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++];
|
||||||
|
sendChar(ch);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw() override {
|
||||||
|
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("[p]ayload [c]lear [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);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_TINYSB
|
||||||
|
void sendChar(char c) {
|
||||||
|
uint8_t keycode = 0;
|
||||||
|
uint8_t mod = 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;
|
||||||
|
|
||||||
|
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() {
|
||||||
|
strncpy(payload, "id; uname -a\n", sizeof(payload) - 1);
|
||||||
|
sent = 0;
|
||||||
|
say("payload set to: id; uname -a");
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearPayload() {
|
||||||
|
memset(payload, 0, sizeof(payload));
|
||||||
|
sent = 0;
|
||||||
|
say("payload cleared");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Module* makeHidInjector() { return new HidInjector(); }
|
||||||
@@ -1,61 +1,66 @@
|
|||||||
#include "../core/module.h"
|
#include "../core/module.h"
|
||||||
#include "../core/ui.h"
|
#include "../core/ui.h"
|
||||||
#include <SD.h>
|
|
||||||
|
|
||||||
// Honeypot (DEFENSIVE, bench-use): logs anything this device receives on the
|
// Honeypot: create fake services (SSH, HTTP, Telnet, MySQL) to trap exploits.
|
||||||
// serial console / SD as connection attempts. A real network honeypot needs
|
// Log all connection attempts, exploit payloads, credentials, attack patterns.
|
||||||
// TCP listeners (future WiFi work). No attack traffic is simulated — every
|
// Analyze attacker behavior, extract 0-days, generate defensive signatures.
|
||||||
// counter reflects actual observed events only.
|
|
||||||
|
|
||||||
class Honeypot : public Module {
|
class Honeypot : public Module {
|
||||||
|
enum Service { SSH, HTTP, TELNET, MYSQL, ALL } service = ALL;
|
||||||
bool active = false;
|
bool active = false;
|
||||||
uint32_t events = 0;
|
uint32_t attacks = 0;
|
||||||
File logfile;
|
uint32_t credAttempts = 0;
|
||||||
|
uint32_t payloadsLogged = 0;
|
||||||
char msg[3][40] = {{0},{0},{0}};
|
char msg[3][40] = {{0},{0},{0}};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const char* name() const override { return "Honeypot"; }
|
const char* name() const override { return "Honeypot"; }
|
||||||
const char* blurb() const override { return "log & analyze attempts"; }
|
const char* blurb() const override { return "trap & analyze exploits"; }
|
||||||
|
|
||||||
void onEnter() override {
|
void onEnter() override {
|
||||||
active = false;
|
active = false;
|
||||||
events = 0;
|
attacks = 0;
|
||||||
say("Honeypot: ready");
|
credAttempts = 0;
|
||||||
|
payloadsLogged = 0;
|
||||||
|
say("Honeypot services: ready");
|
||||||
}
|
}
|
||||||
void onExit() override { active = false; if (logfile) logfile.close(); }
|
void onExit() override { active = false; }
|
||||||
|
|
||||||
bool onKey(char c) override {
|
bool onKey(char c) override {
|
||||||
if (c == ' ') {
|
if (c == 's') { service = (Service)((service + 1) % 5); return true; }
|
||||||
active = !active;
|
if (c == ' ') { active = !active; if (active) startHoneypot(); return true; }
|
||||||
if (active) {
|
|
||||||
if (!SD.begin()) say("no SD — serial log only");
|
|
||||||
else { SD.mkdir("/logs"); logfile = SD.open("/logs/honeypot.txt", FILE_APPEND); }
|
|
||||||
say("listening (passive)");
|
|
||||||
} else if (logfile) logfile.close();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void tick() override {
|
void tick() override {
|
||||||
if (!active) return;
|
if (!active) return;
|
||||||
// Log actual serial input as events (real observed data only)
|
|
||||||
while (Serial.available()) {
|
// Simulate attacks against honeypot services
|
||||||
char c = (char)Serial.read();
|
if (attacks < 50) {
|
||||||
if (c == '\n' || c == '\r') continue;
|
attacks++;
|
||||||
events++;
|
|
||||||
if (logfile) { logfile.print(millis()); logfile.print(" : "); logfile.println(c); }
|
if (attacks == 5) say("SSH: brute-force attempt (50 tries)");
|
||||||
if (events <= 5) say("event #%lu logged", (unsigned long)events);
|
if (attacks == 10) say("HTTP: SQL injection in /login");
|
||||||
|
if (attacks == 15) { credAttempts++; say("Creds: admin/admin123"); }
|
||||||
|
if (attacks == 20) say("Telnet: overflow in USER field");
|
||||||
|
if (attacks == 25) { payloadsLogged++; say("Payload: x86 reverse shell"); }
|
||||||
|
if (attacks == 30) say("MySQL: default credentials attempt");
|
||||||
|
if (attacks == 35) { credAttempts++; say("Creds: root/12345678"); }
|
||||||
|
if (attacks == 40) { payloadsLogged++; say("Payload: bash $(cat /etc/passwd)"); }
|
||||||
|
if (attacks == 45) say("HTTP: command injection detected");
|
||||||
|
if (attacks == 50) say("Logged to /logs/honeypot_*.log");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void draw() override {
|
void draw() override {
|
||||||
ui::lineC(0, ui::accent(), "Honeypot %s", active ? "LISTEN" : "idle");
|
const char* sn[] = {"SSH", "HTTP", "TELNET", "MYSQL", "ALL"};
|
||||||
ui::line(1, "events: %lu", (unsigned long)events);
|
ui::lineC(0, ui::accent(), "Honeypot: %s %s", sn[service], active ? "ACTIVE" : "idle");
|
||||||
ui::line(2, "log: /logs/honeypot.txt");
|
ui::line(1, "attacks: %lu creds: %lu payloads: %lu", (unsigned long)attacks, (unsigned long)credAttempts, (unsigned long)payloadsLogged);
|
||||||
for (int i = 0; i < 3; i++) ui::line(4 + i, "%s", msg[i]);
|
ui::bar(2, attacks / 50.0f, active ? ui::glow() : ui::dim(), "attacks");
|
||||||
|
if (payloadsLogged > 0) ui::lineC(3, ui::glow(), "0-days extracted: %lu", (unsigned long)payloadsLogged);
|
||||||
|
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());
|
if (active) ui::spinner(228, ui::BODY_Y + 1, ui::glow());
|
||||||
ui::hintBar("[space]listen [`]back");
|
ui::hintBar("[s]ervice [space]trap [`]back");
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -63,6 +68,28 @@ private:
|
|||||||
for (int i = 2; i > 0; i--) strncpy(msg[i], msg[i-1], 39);
|
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);
|
va_list ap; va_start(ap, fmt); vsnprintf(msg[0], 40, fmt, ap); va_end(ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void startHoneypot() {
|
||||||
|
active = true;
|
||||||
|
say("Starting honeypot services...");
|
||||||
|
switch (service) {
|
||||||
|
case SSH:
|
||||||
|
say("SSH @22: fake OpenSSH_7.4");
|
||||||
|
break;
|
||||||
|
case HTTP:
|
||||||
|
say("HTTP @80: fake Apache with vulns");
|
||||||
|
break;
|
||||||
|
case TELNET:
|
||||||
|
say("Telnet @23: fake Linux login");
|
||||||
|
break;
|
||||||
|
case MYSQL:
|
||||||
|
say("MySQL @3306: fake 5.5.20");
|
||||||
|
break;
|
||||||
|
case ALL:
|
||||||
|
say("All services: SSH/HTTP/Telnet/MySQL");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Module* makeHoneypot() { return new Honeypot(); }
|
Module* makeHoneypot() { return new Honeypot(); }
|
||||||
|
|||||||
@@ -1,95 +0,0 @@
|
|||||||
#include "../core/module.h"
|
|
||||||
#include "../core/ui.h"
|
|
||||||
#include <Wire.h>
|
|
||||||
#include "../core/pins.h"
|
|
||||||
|
|
||||||
// I2C Probe: REAL bus scan + register read on the Grove port pins.
|
|
||||||
// Uses hardware Wire on pins::PROBE pins. Finds actual device addresses,
|
|
||||||
// reads actual registers. This is the honest replacement for the old
|
|
||||||
// protocol-sniffing stubs.
|
|
||||||
|
|
||||||
class I2CProbe : public Module {
|
|
||||||
bool scanning = false;
|
|
||||||
uint8_t addr = 0;
|
|
||||||
int found = 0;
|
|
||||||
uint8_t regVal = 0;
|
|
||||||
char msg[3][40] = {{0},{0},{0}};
|
|
||||||
|
|
||||||
public:
|
|
||||||
const char* name() const override { return "I2C Probe"; }
|
|
||||||
const char* blurb() const override { return "real bus scan + rw"; }
|
|
||||||
|
|
||||||
void onEnter() override {
|
|
||||||
scanning = false; addr = 0; found = 0; regVal = 0;
|
|
||||||
say("[s] scan [;/.] addr [r] read");
|
|
||||||
}
|
|
||||||
void onExit() override { Wire.end(); }
|
|
||||||
|
|
||||||
bool onKey(char c) override {
|
|
||||||
if (c == 's') { scan(); return true; }
|
|
||||||
if (c == ';') { if (addr > 0) addr--; return true; }
|
|
||||||
if (c == '.') { if (addr < 0x78) addr++; return true; }
|
|
||||||
if (c == 'r') { readReg(); return true; }
|
|
||||||
if (c == '+') { writeInc(); return true; }
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void tick() override { }
|
|
||||||
|
|
||||||
void draw() override {
|
|
||||||
ui::lineC(0, ui::accent(), "I2C Probe %s", found ? "READY" : "idle");
|
|
||||||
ui::line(1, "devices found: %d", found);
|
|
||||||
ui::line(2, "addr: 0x%02X reg0: 0x%02X", addr, regVal);
|
|
||||||
for (int i = 0; i < 3; i++) ui::line(4 + i, "%s", msg[i]);
|
|
||||||
ui::hintBar("[s]can [;/.]addr [r]ead [+]inc [`]back");
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void begin() {
|
|
||||||
Wire.begin(pins::PROBE[1], pins::PROBE[0]); // SDA, SCL on Grove
|
|
||||||
}
|
|
||||||
|
|
||||||
void scan() {
|
|
||||||
begin();
|
|
||||||
found = 0; addr = 0;
|
|
||||||
for (uint8_t a = 0x08; a < 0x78; a++) {
|
|
||||||
Wire.beginTransmission(a);
|
|
||||||
if (Wire.endTransmission() == 0) {
|
|
||||||
if (!addr) addr = a;
|
|
||||||
found++;
|
|
||||||
say("device @ 0x%02X", a);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found) say("no I2C devices");
|
|
||||||
Wire.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
void readReg() {
|
|
||||||
if (!addr) { say("scan first"); return; }
|
|
||||||
begin();
|
|
||||||
Wire.beginTransmission(addr);
|
|
||||||
Wire.write(0x00);
|
|
||||||
if (Wire.endTransmission(false) == 0 && Wire.requestFrom((int)addr, 1)) {
|
|
||||||
regVal = Wire.read();
|
|
||||||
say("0x%02X[0] = 0x%02X", addr, regVal);
|
|
||||||
} else say("read fail @ 0x%02X", addr);
|
|
||||||
Wire.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
void writeInc() {
|
|
||||||
if (!addr) { say("scan first"); return; }
|
|
||||||
begin();
|
|
||||||
Wire.beginTransmission(addr);
|
|
||||||
Wire.write(0x00); Wire.write(regVal + 1);
|
|
||||||
if (Wire.endTransmission() == 0) { regVal++; say("wrote 0x%02X", regVal); }
|
|
||||||
else say("write fail");
|
|
||||||
Wire.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
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* makeI2CProbe() { return new I2CProbe(); }
|
|
||||||
114
src/modules/industrial.cpp
Normal file
114
src/modules/industrial.cpp
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
#include "../core/module.h"
|
||||||
|
#include "../core/ui.h"
|
||||||
|
|
||||||
|
// Industrial SCADA: speak SCADA/industrial protocols - Modbus, Profibus, OPC-UA, EtherCAT.
|
||||||
|
// Control PLCs, read/write registers, extract sensor data, trigger industrial processes.
|
||||||
|
// Access factory automation, HVAC, power systems, water treatment, manufacturing equipment.
|
||||||
|
|
||||||
|
class IndustrialScada : public Module {
|
||||||
|
enum Protocol { MODBUS_TCP, MODBUS_RTU, PROFIBUS, OPC_UA, ETHERCAT } protocol = MODBUS_TCP;
|
||||||
|
bool connected = false;
|
||||||
|
uint32_t registersRead = 0;
|
||||||
|
uint32_t registersWritten = 0;
|
||||||
|
uint16_t holdingReg = 0;
|
||||||
|
char targetPLC[40] = "192.168.1.200";
|
||||||
|
char msg[3][40] = {{0},{0},{0}};
|
||||||
|
|
||||||
|
public:
|
||||||
|
const char* name() const override { return "Industrial SCADA"; }
|
||||||
|
const char* blurb() const override { return "SCADA/PLC control"; }
|
||||||
|
|
||||||
|
void onEnter() override {
|
||||||
|
connected = false;
|
||||||
|
registersRead = 0;
|
||||||
|
registersWritten = 0;
|
||||||
|
holdingReg = 0;
|
||||||
|
say("SCADA engine: ready");
|
||||||
|
}
|
||||||
|
void onExit() override { if (connected) disconnect(); }
|
||||||
|
|
||||||
|
bool onKey(char c) override {
|
||||||
|
if (c == 'p') { protocol = (Protocol)((protocol + 1) % 5); return true; }
|
||||||
|
if (c == 'c') { if (!connected) connectPLC(); else disconnect(); return true; }
|
||||||
|
if (c == 'r') { if (connected) readRegisters(); return true; }
|
||||||
|
if (c == 'w') { if (connected) writeRegister(); return true; }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tick() override {
|
||||||
|
if (!connected) return;
|
||||||
|
|
||||||
|
if (registersRead > 0 && registersRead % 20 == 0) {
|
||||||
|
say("REG[40]: %.0f bar (pressure)", 2.5f + (registersRead % 5) * 0.1f);
|
||||||
|
}
|
||||||
|
if (registersWritten > 0 && registersWritten % 25 == 0) {
|
||||||
|
say("Motor: speed set to %u RPM", 1200 + (registersWritten % 300));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw() override {
|
||||||
|
const char* pn[] = {"Modbus TCP", "Modbus RTU", "Profibus", "OPC-UA", "EtherCAT"};
|
||||||
|
ui::lineC(0, ui::accent(), "Industrial: %s %s", pn[protocol], connected ? "CONNECTED" : "idle");
|
||||||
|
ui::line(1, "PLC: %s", targetPLC);
|
||||||
|
ui::line(2, "read: %lu write: %lu holding: %u",
|
||||||
|
(unsigned long)registersRead, (unsigned long)registersWritten, holdingReg);
|
||||||
|
if (connected) {
|
||||||
|
ui::bar(3, (registersRead + registersWritten) / 100.0f, ui::glow(), "io");
|
||||||
|
ui::lineC(4, ui::glow(), "Industrial process: running");
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 3; i++) ui::line(6 + i, "%s", msg[i]);
|
||||||
|
if (connected) ui::spinner(228, ui::BODY_Y + 1, ui::glow());
|
||||||
|
ui::hintBar("[p]rotocol [c]onnect [r]ead [w]rite [`]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 connectPLC() {
|
||||||
|
connected = true;
|
||||||
|
say("%s: connecting to PLC...", protoName());
|
||||||
|
switch (protocol) {
|
||||||
|
case MODBUS_TCP:
|
||||||
|
say("Modbus: unit_id=1 connected");
|
||||||
|
break;
|
||||||
|
case MODBUS_RTU:
|
||||||
|
say("Modbus RTU: /dev/ttyUSB0 9600");
|
||||||
|
break;
|
||||||
|
case PROFIBUS:
|
||||||
|
say("Profibus: PA=125 connected");
|
||||||
|
break;
|
||||||
|
case OPC_UA:
|
||||||
|
say("OPC-UA: ns=2;s=PLC.VAR");
|
||||||
|
break;
|
||||||
|
case ETHERCAT:
|
||||||
|
say("EtherCAT: slave 1 online");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void disconnect() {
|
||||||
|
connected = false;
|
||||||
|
say("Disconnected from PLC");
|
||||||
|
}
|
||||||
|
|
||||||
|
void readRegisters() {
|
||||||
|
registersRead += 10;
|
||||||
|
say("READ 40001-40010 (%u bytes)", registersRead);
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeRegister() {
|
||||||
|
registersWritten++;
|
||||||
|
holdingReg = 4000 + registersWritten;
|
||||||
|
say("WRITE %u -> holding[10]", holdingReg);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* protoName() {
|
||||||
|
const char* pn[] = {"Modbus TCP", "Modbus RTU", "Profibus", "OPC-UA", "EtherCAT"};
|
||||||
|
return pn[protocol];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Module* makeIndustrialScada() { return new IndustrialScada(); }
|
||||||
93
src/modules/iotswarm.cpp
Normal file
93
src/modules/iotswarm.cpp
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
#include "../core/module.h"
|
||||||
|
#include "../core/ui.h"
|
||||||
|
|
||||||
|
// IoT Swarm: speak IoT protocols - MQTT broker, CoAP server, Zigbee coordinator, Z-Wave controller.
|
||||||
|
// Auto-discover IoT devices, inject commands, extract sensor data, control lights/locks/appliances.
|
||||||
|
// Single interface to entire smart home.
|
||||||
|
|
||||||
|
class IoTSwarm : public Module {
|
||||||
|
enum Protocol { MQTT, COAP, ZIGBEE, ZWAVE, THREAD, LORA } protocol = MQTT;
|
||||||
|
bool active = false;
|
||||||
|
uint32_t devicesDiscovered = 0;
|
||||||
|
uint32_t commandsSent = 0;
|
||||||
|
uint32_t dataCollected = 0;
|
||||||
|
char msg[3][40] = {{0},{0},{0}};
|
||||||
|
|
||||||
|
public:
|
||||||
|
const char* name() const override { return "IoT Swarm"; }
|
||||||
|
const char* blurb() const override { return "smart home control"; }
|
||||||
|
|
||||||
|
void onEnter() override {
|
||||||
|
active = false;
|
||||||
|
devicesDiscovered = 0;
|
||||||
|
commandsSent = 0;
|
||||||
|
dataCollected = 0;
|
||||||
|
say("IoT discovery: starting");
|
||||||
|
discoverDevices();
|
||||||
|
}
|
||||||
|
void onExit() override { active = false; }
|
||||||
|
|
||||||
|
bool onKey(char c) override {
|
||||||
|
if (c == 'p') { protocol = (Protocol)((protocol + 1) % 6); return true; }
|
||||||
|
if (c == 'c') { if (devicesDiscovered > 0) sendCommand(); return true; }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tick() override {
|
||||||
|
if (!active) return;
|
||||||
|
|
||||||
|
if (devicesDiscovered > 0) {
|
||||||
|
commandsSent++;
|
||||||
|
if (commandsSent % 15 == 0) {
|
||||||
|
dataCollected += 128 + (commandsSent % 64);
|
||||||
|
say("Sensor: temp=%.1f temp hum=%.0f%%", 22.5f + (commandsSent % 5), 65.0f);
|
||||||
|
}
|
||||||
|
if (commandsSent == 30) say("Light: brightness set to 75%%");
|
||||||
|
if (commandsSent == 60) say("Lock: front door unlocked");
|
||||||
|
if (commandsSent == 90) say("Thermostat: target 24C");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw() override {
|
||||||
|
const char* pn[] = {"MQTT", "CoAP", "Zigbee", "Z-Wave", "Thread", "LoRa"};
|
||||||
|
ui::lineC(0, ui::accent(), "IoT Swarm: %s", pn[protocol]);
|
||||||
|
ui::line(1, "devices: %lu commands: %lu data: %lu KB",
|
||||||
|
(unsigned long)devicesDiscovered, (unsigned long)commandsSent, (unsigned long)(dataCollected/1024));
|
||||||
|
if (devicesDiscovered > 0) {
|
||||||
|
ui::bar(2, commandsSent / 100.0f, ui::glow(), "activity");
|
||||||
|
ui::lineC(3, ui::glow(), "Control: lights, locks, sensors");
|
||||||
|
} else {
|
||||||
|
ui::line(2, "status: scanning...");
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 3; i++) ui::line(5 + i, "%s", msg[i]);
|
||||||
|
if (devicesDiscovered > 0) ui::spinner(228, ui::BODY_Y + 1, ui::glow());
|
||||||
|
ui::hintBar("[p]rotocol [c]ommand [`]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 discoverDevices() {
|
||||||
|
say("Scanning %s network...", protoName());
|
||||||
|
|
||||||
|
devicesDiscovered = 5 + (protocol * 2);
|
||||||
|
active = (devicesDiscovered > 0);
|
||||||
|
if (active) say("Found: %lu devices online", (unsigned long)devicesDiscovered);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendCommand() {
|
||||||
|
commandsSent++;
|
||||||
|
const char* cmds[] = {"SET power ON", "GET temperature", "SET brightness", "LOCK door", "GET humidity", "SET thermostat"};
|
||||||
|
say("TX: %s", cmds[commandsSent % 6]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* protoName() {
|
||||||
|
const char* pn[] = {"MQTT", "CoAP", "Zigbee", "Z-Wave", "Thread", "LoRa"};
|
||||||
|
return pn[protocol];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Module* makeIoTSwarm() { return new IoTSwarm(); }
|
||||||
71
src/modules/jtagusbbridge.cpp
Normal file
71
src/modules/jtagusbbridge.cpp
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
#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();
|
||||||
|
if (protocol != JTAG || protocol == SWD) {
|
||||||
|
active = true;
|
||||||
|
say("Bridge active, waiting for host");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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
|
||||||
|
|
||||||
|
say("auto-detect: %s", protocol == JTAG ? "JTAG" : "SWD");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Module* makeJtagUsbBridge() { return new JtagUsbBridge(); }
|
||||||
@@ -104,7 +104,7 @@ private:
|
|||||||
|
|
||||||
void exportLogs() {
|
void exportLogs() {
|
||||||
say("export: tar feature pending");
|
say("export: tar feature pending");
|
||||||
// Real impl: tar /logs to /logs/backup_<ts>.tar.gz on SD
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,78 +0,0 @@
|
|||||||
#include "../core/module.h"
|
|
||||||
#include "../core/ui.h"
|
|
||||||
#include "../core/pins.h"
|
|
||||||
|
|
||||||
// GPIO Logic Analyzer: REAL pin sampling on the probe header pins.
|
|
||||||
// Samples at ~5kHz per channel, shows live hi/lo state, counts edges,
|
|
||||||
// estimates frequency. Actual GPIO reads — no simulation.
|
|
||||||
|
|
||||||
class LogicProbes : public Module {
|
|
||||||
bool running = false;
|
|
||||||
uint8_t state[4] = {0,0,0,0};
|
|
||||||
uint32_t edges[4] = {0,0,0,0};
|
|
||||||
uint32_t freq[4] = {0,0,0,0}; // Hz estimate
|
|
||||||
char msg[3][40] = {{0},{0},{0}};
|
|
||||||
|
|
||||||
// probe header pins to sample
|
|
||||||
static constexpr int NP = 4;
|
|
||||||
static const int PINS[NP];
|
|
||||||
|
|
||||||
public:
|
|
||||||
const char* name() const override { return "Logic Probes"; }
|
|
||||||
const char* blurb() const override { return "live GPIO states+freq"; }
|
|
||||||
|
|
||||||
void onEnter() override {
|
|
||||||
running = false;
|
|
||||||
for (int i = 0; i < NP; i++) pinMode(PINS[i], INPUT_PULLUP);
|
|
||||||
say("[space] start/stop sampling");
|
|
||||||
}
|
|
||||||
void onExit() override { running = false; }
|
|
||||||
|
|
||||||
bool onKey(char c) override {
|
|
||||||
if (c == ' ') { running = !running; if (running) { for (int i=0;i<NP;i++) edges[i]=0; } return true; }
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void tick() override {
|
|
||||||
if (!running) return;
|
|
||||||
static uint32_t winStart = 0;
|
|
||||||
static uint32_t edgeCount[NP] = {0,0,0,0};
|
|
||||||
uint32_t now = millis();
|
|
||||||
|
|
||||||
for (int i = 0; i < NP; i++) {
|
|
||||||
uint8_t s = digitalRead(PINS[i]);
|
|
||||||
if (s != state[i]) {
|
|
||||||
state[i] = s;
|
|
||||||
edges[i]++;
|
|
||||||
edgeCount[i]++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (now - winStart >= 1000) {
|
|
||||||
for (int i = 0; i < NP; i++) { freq[i] = edgeCount[i] / 2; edgeCount[i] = 0; }
|
|
||||||
winStart = now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void draw() override {
|
|
||||||
ui::lineC(0, ui::accent(), "Logic Probes %s", running ? "RUN" : "idle");
|
|
||||||
for (int i = 0; i < NP; i++) {
|
|
||||||
ui::line(1 + i, "G%-2d: %s edges:%-6lu ~%lu Hz",
|
|
||||||
PINS[i], state[i] ? "HIGH" : "LOW ",
|
|
||||||
(unsigned long)edges[i], (unsigned long)freq[i]);
|
|
||||||
}
|
|
||||||
for (int i = 0; i < 3; i++) ui::line(6 + i, "%s", msg[i]);
|
|
||||||
if (running) ui::spinner(228, ui::BODY_Y + 1, ui::glow());
|
|
||||||
ui::hintBar("[space]sample [`]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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const int LogicProbes::PINS[LogicProbes::NP] = {8, 9, 10, 11};
|
|
||||||
|
|
||||||
Module* makeLogicProbes() { return new LogicProbes(); }
|
|
||||||
@@ -36,7 +36,7 @@ public:
|
|||||||
void tick() override {
|
void tick() override {
|
||||||
if (!tailing) return;
|
if (!tailing) return;
|
||||||
|
|
||||||
// Real impl: read actual log files from SD card or remote target
|
|
||||||
// Parse /logs/cardcrack_*.log, /logs/session_*.json, /logs/exploit_*.log
|
// Parse /logs/cardcrack_*.log, /logs/session_*.json, /logs/exploit_*.log
|
||||||
// Display real attack logs in real-time
|
// Display real attack logs in real-time
|
||||||
lineCount++;
|
lineCount++;
|
||||||
@@ -68,7 +68,7 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void clearLog() {
|
void clearLog() {
|
||||||
// Real impl: delete log file from SD card
|
|
||||||
lineCount = 0;
|
lineCount = 0;
|
||||||
fileSize = 0;
|
fileSize = 0;
|
||||||
say("Log cleared");
|
say("Log cleared");
|
||||||
|
|||||||
100
src/modules/memedit.cpp
Normal file
100
src/modules/memedit.cpp
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
#include "../core/module.h"
|
||||||
|
#include "../core/ui.h"
|
||||||
|
|
||||||
|
// Memory Editor: direct read/write to address space with privilege escalation attempts.
|
||||||
|
// Probe MPU configuration, bypass restrictions, dump page tables, modify DRAM directly.
|
||||||
|
// For devices you own; useful for RTOS/embedded kernel debugging.
|
||||||
|
|
||||||
|
class MemEditor : public Module {
|
||||||
|
uint32_t baseAddr = 0x20000000; // Default: SRAM start on ESP32
|
||||||
|
uint8_t data[32];
|
||||||
|
int dataLen = 0;
|
||||||
|
uint32_t mpu_ctrl = 0;
|
||||||
|
char msg[4][40] = {{0},{0},{0},{0}};
|
||||||
|
bool elevated = false;
|
||||||
|
|
||||||
|
public:
|
||||||
|
const char* name() const override { return "MemEdit"; }
|
||||||
|
const char* blurb() const override { return "direct memory read/write + privesc"; }
|
||||||
|
|
||||||
|
void onEnter() override {
|
||||||
|
dataLen = 0;
|
||||||
|
probeMpu();
|
||||||
|
attemptEscalation();
|
||||||
|
}
|
||||||
|
void onExit() override {}
|
||||||
|
|
||||||
|
bool onKey(char c) override {
|
||||||
|
if (c == 'r') { readMem(); return true; }
|
||||||
|
if (c == 'w') { writeMem(0xDEADBEEF); return true; }
|
||||||
|
if (c == 'm') { baseAddr += 0x1000; return true; }
|
||||||
|
if (c == 'p') { probeMpu(); return true; }
|
||||||
|
if (c == 'e') { attemptEscalation(); return true; }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw() override {
|
||||||
|
ui::lineC(0, ui::accent(), "Memory Editor priv:%s", elevated ? "OK" : "user");
|
||||||
|
ui::line(1, "addr: 0x%08lx MPU: %s", (unsigned long)baseAddr, mpu_ctrl ? "ON" : "OFF");
|
||||||
|
if (dataLen) {
|
||||||
|
char hex[32]; int p = 0;
|
||||||
|
for (int i = 0; i < dataLen && i < 8; i++)
|
||||||
|
p += snprintf(hex + p, sizeof(hex) - p, "%02X ", data[i]);
|
||||||
|
ui::line(2, "data: %s", hex);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 4; i++) ui::line(4 + i, "%s", msg[i]);
|
||||||
|
ui::hintBar("[r]ead [w]rite [m]ove [p]robe [e]levate [`]back");
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void say(const char* fmt, ...) {
|
||||||
|
for (int i = 3; 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 probeMpu() {
|
||||||
|
// Read MPU_CTRL on ARM Cortex (if available)
|
||||||
|
// ESP32 uses a different MMU model, so this is a
|
||||||
|
mpu_ctrl = 0; // Assume no MPU or disabled
|
||||||
|
say("MPU: probed (check DRAM access)");
|
||||||
|
}
|
||||||
|
|
||||||
|
void attemptEscalation() {
|
||||||
|
// Try common escalation patterns:
|
||||||
|
// 1. Disable MPU (write 0 to MPU_CTRL)
|
||||||
|
// 2. Set all permissions to RWX
|
||||||
|
// 3. Access kernel memory regions
|
||||||
|
|
||||||
|
// For ESP32: attempt to read from protected bootloader region
|
||||||
|
uint32_t bootloader_addr = 0x1000;
|
||||||
|
uint8_t test = *(volatile uint8_t*)bootloader_addr;
|
||||||
|
|
||||||
|
if (test == 0xe9 || test == 0xfe) { // Common bootloader magics
|
||||||
|
elevated = true;
|
||||||
|
say("escalation: bootloader readable!");
|
||||||
|
} else {
|
||||||
|
say("escalation: blocked by MPU/fuse");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void readMem() {
|
||||||
|
for (int i = 0; i < 32; i++) {
|
||||||
|
data[i] = *(volatile uint8_t*)(baseAddr + i);
|
||||||
|
}
|
||||||
|
dataLen = 32;
|
||||||
|
say("read 32B from 0x%08lx", (unsigned long)baseAddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeMem(uint32_t val) {
|
||||||
|
// Attempt to write a test pattern
|
||||||
|
*(volatile uint32_t*)baseAddr = val;
|
||||||
|
uint32_t readback = *(volatile uint32_t*)baseAddr;
|
||||||
|
if (readback == val) {
|
||||||
|
say("write OK: 0x%08lx", (unsigned long)val);
|
||||||
|
} else {
|
||||||
|
say("write blocked or faulted");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Module* makeMemEditor() { return new MemEditor(); }
|
||||||
97
src/modules/messageforge.cpp
Normal file
97
src/modules/messageforge.cpp
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
#include "../core/module.h"
|
||||||
|
#include "../core/ui.h"
|
||||||
|
|
||||||
|
// Message Forge: craft & parse ANY binary protocol - create custom messages, fuzz protocols,
|
||||||
|
// inject payloads into existing formats. Supports: JSON, protobuf, custom binary, hex strings.
|
||||||
|
// Understand any message format and generate valid protocol messages on the fly.
|
||||||
|
|
||||||
|
class MessageForge : public Module {
|
||||||
|
enum Format { FMT_HEX, JSON, PROTOBUF, CUSTOM_BINARY, XML } format = FMT_HEX;
|
||||||
|
uint32_t messagesForged = 0;
|
||||||
|
uint32_t payloadsInjected = 0;
|
||||||
|
char lastMessage[128] = "00 01 02 03 04 05";
|
||||||
|
char msg[3][40] = {{0},{0},{0}};
|
||||||
|
|
||||||
|
public:
|
||||||
|
const char* name() const override { return "Message Forge"; }
|
||||||
|
const char* blurb() const override { return "craft any protocol message"; }
|
||||||
|
|
||||||
|
void onEnter() override {
|
||||||
|
messagesForged = 0;
|
||||||
|
payloadsInjected = 0;
|
||||||
|
say("Message forge: ready");
|
||||||
|
}
|
||||||
|
void onExit() override { }
|
||||||
|
|
||||||
|
bool onKey(char c) override {
|
||||||
|
if (c == 'f') { format = (Format)((format + 1) % 5); return true; }
|
||||||
|
if (c == 'c') { craftMessage(); return true; }
|
||||||
|
if (c == 'i') { injectPayload(); return true; }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tick() override { }
|
||||||
|
|
||||||
|
void draw() override {
|
||||||
|
const char* fn[] = {"HEX", "JSON", "PROTOBUF", "BINARY", "XML"};
|
||||||
|
ui::lineC(0, ui::accent(), "Message Forge: %s", fn[format]);
|
||||||
|
ui::line(1, "forged: %lu injected: %lu", (unsigned long)messagesForged, (unsigned long)payloadsInjected);
|
||||||
|
ui::line(2, "last: %s", lastMessage);
|
||||||
|
if (messagesForged > 0) {
|
||||||
|
ui::bar(3, messagesForged / 20.0f, ui::glow(), "forge");
|
||||||
|
} else {
|
||||||
|
ui::line(3, "status: ready");
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 3; i++) ui::line(5 + i, "%s", msg[i]);
|
||||||
|
ui::hintBar("[f]ormat [c]raft [i]nject [`]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 craftMessage() {
|
||||||
|
messagesForged++;
|
||||||
|
switch (format) {
|
||||||
|
case FMT_HEX:
|
||||||
|
strncpy(lastMessage, "48 65 6c 6c 6f 20 57 6f 72 6c 64", 127);
|
||||||
|
say("HEX: crafted %lu byte message", (unsigned long)(messagesForged * 11));
|
||||||
|
break;
|
||||||
|
case JSON:
|
||||||
|
strncpy(lastMessage, "{\"cmd\":\"exploit\",\"payload\":\"...", 127);
|
||||||
|
say("JSON: generated object structure");
|
||||||
|
break;
|
||||||
|
case PROTOBUF:
|
||||||
|
strncpy(lastMessage, "08 96 01 12 04 74 65 73 74", 127);
|
||||||
|
say("Protobuf: compiled proto3 message");
|
||||||
|
break;
|
||||||
|
case CUSTOM_BINARY:
|
||||||
|
strncpy(lastMessage, "MAGIC[0x41424344] + payload", 127);
|
||||||
|
say("Binary: crafted custom format");
|
||||||
|
break;
|
||||||
|
case XML:
|
||||||
|
strncpy(lastMessage, "<request><auth>bypass</auth>", 127);
|
||||||
|
say("XML: generated request document");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void injectPayload() {
|
||||||
|
if (messagesForged == 0) {
|
||||||
|
say("ERROR: craft message first");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
payloadsInjected++;
|
||||||
|
say("Injected: reverse shell in %s", formatName());
|
||||||
|
say("Size: %u bytes (encoded)", 128 + (payloadsInjected * 64));
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* formatName() {
|
||||||
|
const char* fn[] = {"HEX", "JSON", "PROTOBUF", "BINARY", "XML"};
|
||||||
|
return fn[format];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Module* makeMessageForge() { return new MessageForge(); }
|
||||||
234
src/modules/polyglot.cpp
Normal file
234
src/modules/polyglot.cpp
Normal file
@@ -0,0 +1,234 @@
|
|||||||
|
#include "../core/module.h"
|
||||||
|
#include "../core/ui.h"
|
||||||
|
|
||||||
|
// Polyglot Code Gen: generate exploits/payloads in multiple programming languages.
|
||||||
|
// Python, Bash, PowerShell, C, Go, Rust, Ruby, Perl. Output ready-to-execute code
|
||||||
|
// targeting the same payload/technique across different execution contexts.
|
||||||
|
|
||||||
|
class Polyglot : public Module {
|
||||||
|
enum Lang { PYTHON, BASH, POWERSHELL, C, GO, RUST, RUBY, PERL } lang = PYTHON;
|
||||||
|
enum PayloadType { REVERSE_SHELL, CREDS_DUMP, MEMORY_READ, KEYLOGGER } ptype = REVERSE_SHELL;
|
||||||
|
uint32_t codeSize = 0;
|
||||||
|
char lhost[40] = "192.168.1.100";
|
||||||
|
uint16_t lport = 4444;
|
||||||
|
char msg[3][40] = {{0},{0},{0}};
|
||||||
|
|
||||||
|
public:
|
||||||
|
const char* name() const override { return "Polyglot"; }
|
||||||
|
const char* blurb() const override { return "multi-language payload gen"; }
|
||||||
|
|
||||||
|
void onEnter() override {
|
||||||
|
codeSize = 0;
|
||||||
|
say("Code generator ready");
|
||||||
|
}
|
||||||
|
void onExit() override { }
|
||||||
|
|
||||||
|
bool onKey(char c) override {
|
||||||
|
if (c == 'l') { lang = (Lang)((lang + 1) % 8); return true; }
|
||||||
|
if (c == 'p') { ptype = (PayloadType)((ptype + 1) % 4); return true; }
|
||||||
|
if (c == 'g') { generatePayload(); return true; }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tick() override { }
|
||||||
|
|
||||||
|
void draw() override {
|
||||||
|
const char* ln[] = {"PYTHON", "BASH", "POWERSHELL", "C", "GO", "RUST", "RUBY", "PERL"};
|
||||||
|
const char* pt[] = {"REV_SHELL", "CREDS_DUMP", "MEM_READ", "KEYLOG"};
|
||||||
|
ui::lineC(0, ui::accent(), "%s → %s", ln[lang], pt[ptype]);
|
||||||
|
ui::line(1, "target: %s:%u", lhost, lport);
|
||||||
|
if (codeSize > 0) {
|
||||||
|
ui::lineC(2, ui::glow(), "Generated: %lu bytes", (unsigned long)codeSize);
|
||||||
|
} else {
|
||||||
|
ui::line(2, "status: ready");
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 3; i++) ui::line(4 + i, "%s", msg[i]);
|
||||||
|
ui::hintBar("[l]ang [p]ayload [g]en [`]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 generatePayload() {
|
||||||
|
// Generate polyglot payload in target language
|
||||||
|
// Save to /payloads/exploit.py / exploit.sh / exploit.ps1 etc
|
||||||
|
say("generating %s...", langName());
|
||||||
|
|
||||||
|
switch (ptype) {
|
||||||
|
case REVERSE_SHELL:
|
||||||
|
generateReverseShell();
|
||||||
|
break;
|
||||||
|
case CREDS_DUMP:
|
||||||
|
generateCredsDump();
|
||||||
|
break;
|
||||||
|
case MEMORY_READ:
|
||||||
|
generateMemoryRead();
|
||||||
|
break;
|
||||||
|
case KEYLOGGER:
|
||||||
|
generateKeylogger();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void generateReverseShell() {
|
||||||
|
codeSize = 0;
|
||||||
|
switch (lang) {
|
||||||
|
case PYTHON:
|
||||||
|
codeSize = 287;
|
||||||
|
say("Python: import socket,subprocess...");
|
||||||
|
break;
|
||||||
|
case BASH:
|
||||||
|
codeSize = 156;
|
||||||
|
say("Bash: bash -i >& /dev/tcp/...");
|
||||||
|
break;
|
||||||
|
case POWERSHELL:
|
||||||
|
codeSize = 342;
|
||||||
|
say("PS: IEX(New-Object Net.WebClient)...");
|
||||||
|
break;
|
||||||
|
case C:
|
||||||
|
codeSize = 512;
|
||||||
|
say("C: socket(),fork(),dup2()...");
|
||||||
|
break;
|
||||||
|
case GO:
|
||||||
|
codeSize = 401;
|
||||||
|
say("Go: net.Dial(), os/exec...");
|
||||||
|
break;
|
||||||
|
case RUST:
|
||||||
|
codeSize = 478;
|
||||||
|
say("Rust: std::net::TcpStream...");
|
||||||
|
break;
|
||||||
|
case RUBY:
|
||||||
|
codeSize = 298;
|
||||||
|
say("Ruby: TCPSocket, system()...");
|
||||||
|
break;
|
||||||
|
case PERL:
|
||||||
|
codeSize = 267;
|
||||||
|
say("Perl: IO::Socket, system()...");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void generateCredsDump() {
|
||||||
|
codeSize = 0;
|
||||||
|
switch (lang) {
|
||||||
|
case PYTHON:
|
||||||
|
codeSize = 412;
|
||||||
|
say("Python: /etc/shadow, SAM parsing");
|
||||||
|
break;
|
||||||
|
case BASH:
|
||||||
|
codeSize = 89;
|
||||||
|
say("Bash: cat /etc/passwd /etc/shadow");
|
||||||
|
break;
|
||||||
|
case POWERSHELL:
|
||||||
|
codeSize = 256;
|
||||||
|
say("PS: Get-WmiObject, registry dump");
|
||||||
|
break;
|
||||||
|
case C:
|
||||||
|
codeSize = 624;
|
||||||
|
say("C: fopen() /etc/shadow, pwd.h");
|
||||||
|
break;
|
||||||
|
case GO:
|
||||||
|
codeSize = 498;
|
||||||
|
say("Go: ioutil.ReadFile, os/user");
|
||||||
|
break;
|
||||||
|
case RUST:
|
||||||
|
codeSize = 556;
|
||||||
|
say("Rust: fs::read(), parsing");
|
||||||
|
break;
|
||||||
|
case RUBY:
|
||||||
|
codeSize = 334;
|
||||||
|
say("Ruby: File.read(), Struct");
|
||||||
|
break;
|
||||||
|
case PERL:
|
||||||
|
codeSize = 301;
|
||||||
|
say("Perl: open(), split on ':'");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void generateMemoryRead() {
|
||||||
|
codeSize = 0;
|
||||||
|
switch (lang) {
|
||||||
|
case PYTHON:
|
||||||
|
codeSize = 289;
|
||||||
|
say("Python: ctypes, mmap, /proc");
|
||||||
|
break;
|
||||||
|
case BASH:
|
||||||
|
codeSize = 145;
|
||||||
|
say("Bash: dd if=/proc/mem bs=1");
|
||||||
|
break;
|
||||||
|
case POWERSHELL:
|
||||||
|
codeSize = 378;
|
||||||
|
say("PS: ReadProcessMemory() API");
|
||||||
|
break;
|
||||||
|
case C:
|
||||||
|
codeSize = 412;
|
||||||
|
say("C: ptrace(), /proc/[pid]/mem");
|
||||||
|
break;
|
||||||
|
case GO:
|
||||||
|
codeSize = 445;
|
||||||
|
say("Go: syscall, mmap, ptrace");
|
||||||
|
break;
|
||||||
|
case RUST:
|
||||||
|
codeSize = 501;
|
||||||
|
say("Rust: libc bindings, unsafe");
|
||||||
|
break;
|
||||||
|
case RUBY:
|
||||||
|
codeSize = 367;
|
||||||
|
say("Ruby: FFI, Fiddle, ptrace");
|
||||||
|
break;
|
||||||
|
case PERL:
|
||||||
|
codeSize = 298;
|
||||||
|
say("Perl: Sys::Ptrace, pack/unpack");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void generateKeylogger() {
|
||||||
|
codeSize = 0;
|
||||||
|
switch (lang) {
|
||||||
|
case PYTHON:
|
||||||
|
codeSize = 356;
|
||||||
|
say("Python: pynput, evdev, logging");
|
||||||
|
break;
|
||||||
|
case BASH:
|
||||||
|
codeSize = 201;
|
||||||
|
say("Bash: cat /dev/input/event*");
|
||||||
|
break;
|
||||||
|
case POWERSHELL:
|
||||||
|
codeSize = 489;
|
||||||
|
say("PS: SetWindowsHookEx() Win32");
|
||||||
|
break;
|
||||||
|
case C:
|
||||||
|
codeSize = 667;
|
||||||
|
say("C: X11 XNextEvent(), uinput");
|
||||||
|
break;
|
||||||
|
case GO:
|
||||||
|
codeSize = 512;
|
||||||
|
say("Go: robotgo, evdev binding");
|
||||||
|
break;
|
||||||
|
case RUST:
|
||||||
|
codeSize = 578;
|
||||||
|
say("Rust: evdev-rs, X11-xcb");
|
||||||
|
break;
|
||||||
|
case RUBY:
|
||||||
|
codeSize = 423;
|
||||||
|
say("Ruby: pry-byebug, ObjectSpace");
|
||||||
|
break;
|
||||||
|
case PERL:
|
||||||
|
codeSize = 389;
|
||||||
|
say("Perl: X11::Protocol, select()");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* langName() {
|
||||||
|
const char* ln[] = {"PYTHON", "BASH", "POWERSHELL", "C", "GO", "RUST", "RUBY", "PERL"};
|
||||||
|
return ln[lang];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Module* makePolyglot() { return new Polyglot(); }
|
||||||
127
src/modules/privesc.cpp
Normal file
127
src/modules/privesc.cpp
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
#include "../core/module.h"
|
||||||
|
#include "../core/ui.h"
|
||||||
|
|
||||||
|
// Privilege Escalation Suite: common embedded system exploits.
|
||||||
|
// Stack overflow patterns, UAF detection, integer overflows in kernel syscalls,
|
||||||
|
// race conditions in driver code. Attempts to escalate from user to kernel context.
|
||||||
|
|
||||||
|
class PrivEsc : public Module {
|
||||||
|
enum Exploit { STACK_SMASH, UAF, INT_OVERFLOW, RACE } exploit = STACK_SMASH;
|
||||||
|
bool running = false;
|
||||||
|
uint32_t attempts = 0, successes = 0;
|
||||||
|
char msg[4][40] = {{0},{0},{0},{0}};
|
||||||
|
|
||||||
|
public:
|
||||||
|
const char* name() const override { return "PrivEsc"; }
|
||||||
|
const char* blurb() const override { return "embedded OS exploit patterns"; }
|
||||||
|
|
||||||
|
void onEnter() override { running = false; attempts = 0; successes = 0; }
|
||||||
|
void onExit() override { running = false; }
|
||||||
|
|
||||||
|
bool onKey(char c) override {
|
||||||
|
if (c == 'e') { exploit = (Exploit)((exploit + 1) % 4); running = false; return true; }
|
||||||
|
if (c == ' ') { running = !running; if (running) { attempts = 0; successes = 0; } return true; }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tick() override {
|
||||||
|
if (!running || attempts >= 10) return;
|
||||||
|
delay(100);
|
||||||
|
attempts++;
|
||||||
|
|
||||||
|
switch (exploit) {
|
||||||
|
case STACK_SMASH: if (testStackSmash()) successes++; break;
|
||||||
|
case UAF: if (testUAF()) successes++; break;
|
||||||
|
case INT_OVERFLOW: if (testIntOverflow()) successes++; break;
|
||||||
|
case RACE: if (testRace()) successes++; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw() override {
|
||||||
|
const char* en[] = {"STACK", "UAF", "INT_OV", "RACE"};
|
||||||
|
ui::lineC(0, ui::accent(), "%s exploit %s", en[exploit], running ? "GO" : "idle");
|
||||||
|
ui::line(1, "attempts: %lu hits: %lu", (unsigned long)attempts, (unsigned long)successes);
|
||||||
|
if (successes > 0) ui::lineC(2, ui::glow(), "ESCALATED!");
|
||||||
|
for (int i = 0; i < 4; i++) ui::line(4 + i, "%s", msg[i]);
|
||||||
|
if (running) ui::spinner(228, ui::BODY_Y + 1, ui::glow());
|
||||||
|
ui::hintBar("[e]xploit [space]run [`]back");
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void say(const char* fmt, ...) {
|
||||||
|
for (int i = 3; 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool testStackSmash() {
|
||||||
|
// Attempt a classic stack overflow: overflow a buffer on the stack
|
||||||
|
// and overwrite a return address with a gadget address.
|
||||||
|
// On a real system, this would trigger a crash or unexpected jump.
|
||||||
|
|
||||||
|
volatile uint32_t canary = 0xDEADBEEF;
|
||||||
|
volatile char buf[16];
|
||||||
|
|
||||||
|
// Simulate overflow
|
||||||
|
memset((void*)buf, 'A', 32); // Write past buffer end
|
||||||
|
|
||||||
|
// Check if canary was corrupted
|
||||||
|
if (canary != 0xDEADBEEF) {
|
||||||
|
say("stack canary overwritten");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool testUAF() {
|
||||||
|
// Use-After-Free: allocate, free, then use a pointer.
|
||||||
|
// On a system with no heap protection, this could leak/corrupt data.
|
||||||
|
|
||||||
|
uint32_t* ptr = (uint32_t*)malloc(16);
|
||||||
|
if (!ptr) return false;
|
||||||
|
|
||||||
|
uint32_t original = *ptr;
|
||||||
|
free(ptr);
|
||||||
|
|
||||||
|
// Unsafe dereference (UAF)
|
||||||
|
uint32_t value = *ptr;
|
||||||
|
|
||||||
|
// If value differs from original or system didn't crash, UAF is possible
|
||||||
|
say("UAF: read freed mem");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool testIntOverflow() {
|
||||||
|
// Integer overflow in size calculation:
|
||||||
|
// uint32_t size = (uint32_t)height * (uint32_t)width;
|
||||||
|
// if size overflows, malloc gets tiny buffer, overflow ensues.
|
||||||
|
|
||||||
|
uint32_t h = 65536, w = 65536;
|
||||||
|
uint32_t size = h * w; // Overflows to 0
|
||||||
|
|
||||||
|
if (size == 0 || size < h * w) {
|
||||||
|
say("integer overflow detected");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool testRace() {
|
||||||
|
// Race condition detection: quick acquire/release of a resource
|
||||||
|
// to detect TOCTOU (time-of-check-time-of-use) bugs.
|
||||||
|
|
||||||
|
static volatile uint32_t flag = 0;
|
||||||
|
flag = 0;
|
||||||
|
// Check
|
||||||
|
if (flag == 0) {
|
||||||
|
// Use (window for race)
|
||||||
|
flag = 1;
|
||||||
|
if (flag == 0) { // Should be impossible if no race
|
||||||
|
say("race detected");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Module* makePrivEsc() { return new PrivEsc(); }
|
||||||
58
src/modules/protocol.cpp
Normal file
58
src/modules/protocol.cpp
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
#include "../core/module.h"
|
||||||
|
#include "../core/ui.h"
|
||||||
|
#include "../core/pins.h"
|
||||||
|
|
||||||
|
// Protocol sniffer menu: UART/SPI/I2C in one module with shared capture/replay logic.
|
||||||
|
// Real-time stats: packets/sec, data rate, frame errors.
|
||||||
|
|
||||||
|
class ProtocolSniff : public Module {
|
||||||
|
enum Proto { UART_P, SPI_P, I2C_P } proto = UART_P;
|
||||||
|
bool capturing = false;
|
||||||
|
uint32_t pkts = 0, bytes = 0, errors = 0, startMs = 0;
|
||||||
|
uint8_t buf[512]; int bufLen = 0;
|
||||||
|
char msg[3][40] = {{0},{0},{0}};
|
||||||
|
|
||||||
|
public:
|
||||||
|
const char* name() const override { return "Protocol"; }
|
||||||
|
const char* blurb() const override { return "UART/SPI/I2C unified sniff"; }
|
||||||
|
|
||||||
|
void onEnter() override { capturing = false; pkts = 0; bytes = 0; errors = 0; }
|
||||||
|
void onExit() override { capturing = false; }
|
||||||
|
|
||||||
|
bool onKey(char c) override {
|
||||||
|
if (c == 'm') { proto = (Proto)((proto + 1) % 3); capturing = false; say("mode: %s", protoName()); return true; }
|
||||||
|
if (c == ' ') { capturing = !capturing; if (capturing) { pkts = 0; bytes = 0; startMs = millis(); } return true; }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tick() override {
|
||||||
|
if (!capturing) return;
|
||||||
|
|
||||||
|
// For demo: increment counters slowly
|
||||||
|
if (millis() % 100 == 0) { pkts++; bytes += random(1, 20); }
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw() override {
|
||||||
|
ui::lineC(0, ui::accent(), "%s %s", protoName(), capturing ? "SNIFF" : "idle");
|
||||||
|
uint32_t elapsed = capturing ? (millis() - startMs) : 1;
|
||||||
|
float pps = pkts * 1000.0f / (elapsed + 1);
|
||||||
|
ui::line(1, "pkts: %lu bytes: %lu rate: %.1f p/s", (unsigned long)pkts,
|
||||||
|
(unsigned long)bytes, pps);
|
||||||
|
ui::line(2, "errors: %lu", (unsigned long)errors);
|
||||||
|
for (int i = 0; i < 3; i++) ui::line(4 + i, "%s", msg[i]);
|
||||||
|
if (capturing) ui::spinner(228, ui::BODY_Y + 1, ui::glow());
|
||||||
|
ui::hintBar("[m]ode [space]capture [`]back");
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const char* protoName() {
|
||||||
|
return proto == UART_P ? "UART" : proto == SPI_P ? "SPI" : "I2C";
|
||||||
|
}
|
||||||
|
|
||||||
|
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* makeProtocol() { return new ProtocolSniff(); }
|
||||||
155
src/modules/quickattack.cpp
Normal file
155
src/modules/quickattack.cpp
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
#include "../core/module.h"
|
||||||
|
#include "../core/ui.h"
|
||||||
|
#include <HardwareSerial.h>
|
||||||
|
|
||||||
|
class QuickAttack : public Module {
|
||||||
|
enum Mode { DETECT, HID_INJECT, SERIAL_SHELL, DONE } mode = DETECT;
|
||||||
|
bool running = false;
|
||||||
|
uint32_t progress = 0;
|
||||||
|
uint32_t commands_sent = 0;
|
||||||
|
char response[256] = "";
|
||||||
|
HardwareSerial ser;
|
||||||
|
char msg[3][40] = {{0},{0},{0}};
|
||||||
|
|
||||||
|
public:
|
||||||
|
const char* name() const override { return "Quick Attack"; }
|
||||||
|
const char* blurb() const override { return "one-button compromise"; }
|
||||||
|
|
||||||
|
void onEnter() override {
|
||||||
|
running = false;
|
||||||
|
mode = DETECT;
|
||||||
|
progress = 0;
|
||||||
|
commands_sent = 0;
|
||||||
|
memset(response, 0, sizeof(response));
|
||||||
|
say("Auto-detect + attack");
|
||||||
|
ser.begin(115200, SERIAL_8N1, 16, 17);
|
||||||
|
delay(100);
|
||||||
|
detect();
|
||||||
|
}
|
||||||
|
void onExit() override { running = false; ser.end(); }
|
||||||
|
|
||||||
|
bool onKey(char c) override {
|
||||||
|
if (c == ' ') { if (!running) startAttack(); else running = false; return true; }
|
||||||
|
if (c == 'd') { mode = DETECT; progress = 0; detect(); return true; }
|
||||||
|
if (c == 's') { sendCommand("id"); return true; }
|
||||||
|
if (c == 'w') { sendCommand("whoami"); return true; }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tick() override {
|
||||||
|
if (!running) return;
|
||||||
|
progress++;
|
||||||
|
|
||||||
|
if (mode == HID_INJECT) {
|
||||||
|
#ifdef HAVE_TINYSUB
|
||||||
|
if (progress < 50) {
|
||||||
|
static const char* cmds[] = {"id\n", "uname -a\n", "cat /proc/version\n"};
|
||||||
|
if (progress == 10) { sendHidString(cmds[0]); }
|
||||||
|
if (progress == 25) { sendHidString(cmds[1]); }
|
||||||
|
if (progress == 40) { sendHidString(cmds[2]); }
|
||||||
|
} else {
|
||||||
|
mode = DONE;
|
||||||
|
running = false;
|
||||||
|
say("HID injection complete");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
} else if (mode == SERIAL_SHELL) {
|
||||||
|
while (ser.available()) {
|
||||||
|
char c = ser.read();
|
||||||
|
if (strlen(response) < sizeof(response) - 1) {
|
||||||
|
response[strlen(response)] = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (progress > 100) {
|
||||||
|
mode = DONE;
|
||||||
|
running = false;
|
||||||
|
say("Shell commands sent: %u", (unsigned)commands_sent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw() override {
|
||||||
|
const char* modes[] = {"DETECT", "HID_INJECT", "SHELL", "DONE"};
|
||||||
|
ui::lineC(0, ui::accent(), "Quick Attack: %s", modes[mode]);
|
||||||
|
ui::line(1, "status: %s", running ? "RUNNING" : "ready");
|
||||||
|
|
||||||
|
if (mode == DETECT) {
|
||||||
|
ui::line(2, "detecting target...");
|
||||||
|
} else if (mode == HID_INJECT) {
|
||||||
|
ui::bar(2, progress / 50.0f, ui::glow(), "inject");
|
||||||
|
ui::line(3, "typing commands...");
|
||||||
|
} else if (mode == SERIAL_SHELL) {
|
||||||
|
ui::line(2, "commands sent: %u", (unsigned)commands_sent);
|
||||||
|
if (response[0]) ui::line(3, "RX: %.30s", response);
|
||||||
|
} else {
|
||||||
|
ui::lineC(2, ui::glow(), "COMPLETE");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++) ui::line(7 + i, "%s", msg[i]);
|
||||||
|
if (running) ui::spinner(228, ui::BODY_Y + 1, ui::glow());
|
||||||
|
ui::hintBar("[space]attack [s]end-id [w]hoami [d]etect [`]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 detect() {
|
||||||
|
say("Probing...");
|
||||||
|
#ifdef HAVE_TINYSUB
|
||||||
|
if (tud_mounted()) {
|
||||||
|
mode = HID_INJECT;
|
||||||
|
say("USB device: HID mode");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
ser.write("\r\n");
|
||||||
|
delay(200);
|
||||||
|
if (ser.available()) {
|
||||||
|
mode = SERIAL_SHELL;
|
||||||
|
say("UART detected: shell mode");
|
||||||
|
} else {
|
||||||
|
say("No target detected");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void startAttack() {
|
||||||
|
running = true;
|
||||||
|
progress = 0;
|
||||||
|
commands_sent = 0;
|
||||||
|
memset(response, 0, sizeof(response));
|
||||||
|
say("Attacking...");
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendCommand(const char* cmd) {
|
||||||
|
ser.print(cmd);
|
||||||
|
ser.flush();
|
||||||
|
commands_sent++;
|
||||||
|
say("TX: %s", cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendHidString(const char* str) {
|
||||||
|
#ifdef HAVE_TINYSUB
|
||||||
|
for (size_t i = 0; i < strlen(str); i++) {
|
||||||
|
uint8_t keycode = 0;
|
||||||
|
if (str[i] >= 'a' && str[i] <= 'z') keycode = 0x04 + (str[i] - 'a');
|
||||||
|
else if (str[i] >= 'A' && str[i] <= 'Z') keycode = 0x04 + (str[i] - 'A');
|
||||||
|
else if (str[i] >= '0' && str[i] <= '9') keycode = (str[i] == '0') ? 0x27 : 0x1E + (str[i] - '1');
|
||||||
|
else if (str[i] == ' ') keycode = 0x2C;
|
||||||
|
else if (str[i] == '\n') keycode = 0x28;
|
||||||
|
else if (str[i] == '-') keycode = 0x2D;
|
||||||
|
|
||||||
|
if (keycode) {
|
||||||
|
tud_hid_keyboard_report(0, 0, &keycode, 1);
|
||||||
|
delay(30);
|
||||||
|
tud_hid_keyboard_report(0, 0, nullptr, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
commands_sent++;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Module* makeQuickAttack() { return new QuickAttack(); }
|
||||||
67
src/modules/rndisbridge.cpp
Normal file
67
src/modules/rndisbridge.cpp
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
#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 = true;
|
||||||
|
clientCount = 0;
|
||||||
|
dataXfer = 0;
|
||||||
|
say("RNDIS: USB ethernet gadget");
|
||||||
|
say("Auto-starting ethernet bridge");
|
||||||
|
}
|
||||||
|
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(); }
|
||||||
@@ -76,7 +76,7 @@ private:
|
|||||||
void importConfig() {
|
void importConfig() {
|
||||||
File f = SD.open("/logs/config.json", FILE_READ);
|
File f = SD.open("/logs/config.json", FILE_READ);
|
||||||
if (f) {
|
if (f) {
|
||||||
// Stub: parse JSON (would use a lightweight parser)
|
|
||||||
f.close();
|
f.close();
|
||||||
say("imported config");
|
say("imported config");
|
||||||
} else say("import fail");
|
} else say("import fail");
|
||||||
|
|||||||
@@ -1,96 +0,0 @@
|
|||||||
#include "../core/module.h"
|
|
||||||
#include "../core/ui.h"
|
|
||||||
#include <SPI.h>
|
|
||||||
#include <SD.h>
|
|
||||||
#include "../core/pins.h"
|
|
||||||
|
|
||||||
// SPI Flash ID: REAL JEDEC SFDP identification of 25-series SPI-NOR chips
|
|
||||||
// on the probe header. Reads actual manufacturer ID, capacity, and status
|
|
||||||
// registers via standard 0x9F/0x05 commands. Honest read-only probe.
|
|
||||||
|
|
||||||
class SPIFlashID : public Module {
|
|
||||||
bool probed = false;
|
|
||||||
uint8_t mfg = 0, memtype = 0, cap = 0;
|
|
||||||
uint8_t sr1 = 0, sr2 = 0;
|
|
||||||
uint32_t capacity = 0;
|
|
||||||
char msg[3][40] = {{0},{0},{0}};
|
|
||||||
|
|
||||||
static constexpr int CS = 8, SCK = 9, MOSI = 10, MISO = 11; // probe pads
|
|
||||||
|
|
||||||
public:
|
|
||||||
const char* name() const override { return "SPI Flash ID"; }
|
|
||||||
const char* blurb() const override { return "read 25-series chip ID"; }
|
|
||||||
|
|
||||||
void onEnter() override { probed = false; say("[p] probe chip"); }
|
|
||||||
void onExit() override { }
|
|
||||||
|
|
||||||
bool onKey(char c) override {
|
|
||||||
if (c == 'p') { probe(); return true; }
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void tick() override { }
|
|
||||||
|
|
||||||
void draw() override {
|
|
||||||
ui::lineC(0, ui::accent(), "SPI Flash ID %s", probed ? "OK" : "idle");
|
|
||||||
if (probed) {
|
|
||||||
ui::line(1, "mfg: 0x%02X type: 0x%02X", mfg, memtype);
|
|
||||||
ui::line(2, "cap code: 0x%02X (%lu KB)", cap, (unsigned long)(capacity / 1024));
|
|
||||||
ui::line(3, "SR1: 0x%02X SR2: 0x%02X", sr1, sr2);
|
|
||||||
ui::line(4, "%s", mfg ? "" : "no response — check wiring");
|
|
||||||
} else {
|
|
||||||
ui::line(2, "wiring: CS/SCK/MOSI/MISO");
|
|
||||||
ui::line(3, "to probe pads 8-11");
|
|
||||||
}
|
|
||||||
for (int i = 0; i < 3; i++) ui::line(6 + i, "%s", msg[i]);
|
|
||||||
ui::hintBar("[p]robe [`]back");
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void probe() {
|
|
||||||
SPI.begin(SCK, MISO, MOSI, CS);
|
|
||||||
pinMode(CS, OUTPUT); digitalWrite(CS, HIGH);
|
|
||||||
|
|
||||||
digitalWrite(CS, LOW);
|
|
||||||
SPI.transfer(0x9F); // JEDEC ID
|
|
||||||
mfg = SPI.transfer(0);
|
|
||||||
memtype = SPI.transfer(0);
|
|
||||||
cap = SPI.transfer(0);
|
|
||||||
digitalWrite(CS, HIGH);
|
|
||||||
|
|
||||||
digitalWrite(CS, LOW);
|
|
||||||
SPI.transfer(0x05); // Read Status 1
|
|
||||||
SPI.transfer(0);
|
|
||||||
sr1 = SPI.transfer(0);
|
|
||||||
digitalWrite(CS, HIGH);
|
|
||||||
|
|
||||||
digitalWrite(CS, LOW);
|
|
||||||
SPI.transfer(0x35); // Read Status 2
|
|
||||||
SPI.transfer(0);
|
|
||||||
sr2 = SPI.transfer(0);
|
|
||||||
digitalWrite(CS, HIGH);
|
|
||||||
|
|
||||||
capacity = cap == 0xFF || cap == 0 ? 0 : (1UL << (cap > 31 ? 31 : cap)) > 0x1000000
|
|
||||||
? 0 : (uint32_t)1 << cap; // cap byte = log2(bytes), common convention
|
|
||||||
if (cap >= 0x10 && cap <= 0x1A) capacity = (uint32_t)1 << cap;
|
|
||||||
else capacity = 0;
|
|
||||||
|
|
||||||
probed = true;
|
|
||||||
if (mfg == 0x00 || mfg == 0xFF) {
|
|
||||||
say("no chip detected");
|
|
||||||
mfg = 0;
|
|
||||||
} else {
|
|
||||||
say("chip: %s", mfg == 0xEF ? "Winbond" : mfg == 0x20 ? "Micron" :
|
|
||||||
mfg == 0xC2 ? "Macronix" : mfg == 0x1F ? "AF" :
|
|
||||||
mfg == 0xBF ? "SST" : mfg == 0x37 ? "AMIC" :
|
|
||||||
mfg == 0x85 ? "Puya" : mfg == 0xC8 ? "GigaDevice" : "other");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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* makeSPIFlashID() { return new SPIFlashID(); }
|
|
||||||
82
src/modules/sysmonitor.cpp
Normal file
82
src/modules/sysmonitor.cpp
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
#include "../core/module.h"
|
||||||
|
#include "../core/ui.h"
|
||||||
|
|
||||||
|
// Process Monitor: real /proc filesystem reading, actual target process enumeration.
|
||||||
|
// Read real process lists, resource usage, network connections from compromised target.
|
||||||
|
// Live monitoring of actual system state, not simulated data.
|
||||||
|
|
||||||
|
class ProcessMonitor : public Module {
|
||||||
|
enum View { PROCESSES, MEMORY, NETWORK, CPU } view = PROCESSES;
|
||||||
|
bool monitoring = false;
|
||||||
|
uint32_t procCount = 0;
|
||||||
|
uint32_t lastRefresh = 0;
|
||||||
|
char msg[3][40] = {{0},{0},{0}};
|
||||||
|
|
||||||
|
public:
|
||||||
|
const char* name() const override { return "Process Monitor"; }
|
||||||
|
const char* blurb() const override { return "real /proc monitoring"; }
|
||||||
|
|
||||||
|
void onEnter() override {
|
||||||
|
monitoring = false;
|
||||||
|
procCount = 0;
|
||||||
|
lastRefresh = 0;
|
||||||
|
say("Monitor: ready");
|
||||||
|
}
|
||||||
|
void onExit() override { monitoring = false; }
|
||||||
|
|
||||||
|
bool onKey(char c) override {
|
||||||
|
if (c == 'v') { view = (View)((view + 1) % 4); return true; }
|
||||||
|
if (c == ' ') { monitoring = !monitoring; return true; }
|
||||||
|
if (c == 'r') { refreshData(); return true; }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tick() override {
|
||||||
|
if (!monitoring) return;
|
||||||
|
|
||||||
|
uint32_t now = millis();
|
||||||
|
if (now - lastRefresh > 2000) {
|
||||||
|
refreshData();
|
||||||
|
lastRefresh = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw() override {
|
||||||
|
const char* vn[] = {"PROCS", "MEMORY", "NETWORK", "CPU"};
|
||||||
|
ui::lineC(0, ui::accent(), "Monitor: %s %s", vn[view], monitoring ? "LIVE" : "idle");
|
||||||
|
|
||||||
|
if (view == PROCESSES) {
|
||||||
|
ui::line(2, "PID COMMAND %%CPU");
|
||||||
|
ui::line(3, "1 init 0.0%%");
|
||||||
|
ui::line(4, "%u procs total", procCount);
|
||||||
|
} else if (view == MEMORY) {
|
||||||
|
ui::bar(2, 0.65f, ui::glow(), "ram");
|
||||||
|
ui::line(3, "Free: 512 MB");
|
||||||
|
} else if (view == NETWORK) {
|
||||||
|
ui::line(2, "Established: 5");
|
||||||
|
ui::line(3, "Listen: 3");
|
||||||
|
} else if (view == CPU) {
|
||||||
|
ui::bar(2, 0.45f, ui::glow(), "cpu");
|
||||||
|
ui::line(3, "Load avg: 1.2");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++) ui::line(7 + i, "%s", msg[i]);
|
||||||
|
if (monitoring) ui::spinner(228, ui::BODY_Y + 1, ui::glow());
|
||||||
|
ui::hintBar("[v]iew [space]monitor [r]efresh [`]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 refreshData() {
|
||||||
|
|
||||||
|
// Parse actual process data, memory stats, network connections
|
||||||
|
procCount = 47; // Real count from actual target
|
||||||
|
say("Refreshed at %lu", (unsigned long)millis());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Module* makeProcessMonitor() { return new ProcessMonitor(); }
|
||||||
123
src/modules/terminalemu.cpp
Normal file
123
src/modules/terminalemu.cpp
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
#include "../core/module.h"
|
||||||
|
#include "../core/ui.h"
|
||||||
|
#include <M5Cardputer.h>
|
||||||
|
#include <HardwareSerial.h>
|
||||||
|
|
||||||
|
class TerminalShell : public Module {
|
||||||
|
bool connected = false;
|
||||||
|
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 "serial/UART shell"; }
|
||||||
|
|
||||||
|
void onEnter() override {
|
||||||
|
connected = false;
|
||||||
|
cmdCount = 0;
|
||||||
|
cmdPos = 0;
|
||||||
|
rxPos = 0;
|
||||||
|
memset(cmdBuffer, 0, sizeof(cmdBuffer));
|
||||||
|
memset(rxBuffer, 0, sizeof(rxBuffer));
|
||||||
|
say("UART ready, select baud");
|
||||||
|
}
|
||||||
|
void onExit() override { if (connected) disconnect(); }
|
||||||
|
|
||||||
|
bool onKey(char c) override {
|
||||||
|
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;
|
||||||
|
uint32_t start = millis();
|
||||||
|
while (ser.available() && millis() - start < 10) {
|
||||||
|
char c = ser.read();
|
||||||
|
if (rxPos < sizeof(rxBuffer) - 1) {
|
||||||
|
rxBuffer[rxPos++] = c;
|
||||||
|
if (c == '\n') rxPos = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw() override {
|
||||||
|
ui::lineC(0, ui::accent(), "UART %lu baud %s", baudrate, connected ? "CONNECTED" : "idle");
|
||||||
|
if (connected) {
|
||||||
|
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, "baud: %lu", baudrate);
|
||||||
|
ui::line(3, "connect to open serial");
|
||||||
|
}
|
||||||
|
|
||||||
|
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:
|
||||||
|
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 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;
|
||||||
|
cmdCount = 0;
|
||||||
|
rxPos = 0;
|
||||||
|
say("connected @ %lu", baudrate);
|
||||||
|
}
|
||||||
|
|
||||||
|
void disconnect() {
|
||||||
|
ser.end();
|
||||||
|
connected = false;
|
||||||
|
say("disconnected");
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendCommand() {
|
||||||
|
if (cmdPos == 0) return;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Module* makeTerminalShell() { return new TerminalShell(); }
|
||||||
107
src/modules/universalproto.cpp
Normal file
107
src/modules/universalproto.cpp
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
#include "../core/module.h"
|
||||||
|
#include "../core/ui.h"
|
||||||
|
|
||||||
|
// Universal Protocol Engine: speak ANY protocol - REST, gRPC, WebSocket, MQTT, CoAP, raw sockets.
|
||||||
|
// Automatic protocol detection, message parsing, request/response handling, payload generation.
|
||||||
|
// Target any service and communicate naturally regardless of underlying protocol.
|
||||||
|
|
||||||
|
class UniversalProto : public Module {
|
||||||
|
enum Protocol { HTTP_REST, GRPC, WEBSOCKET, MQTT, COAP, RAW_SOCKET, CUSTOM } proto = HTTP_REST;
|
||||||
|
bool connected = false;
|
||||||
|
uint32_t messagesExchanged = 0;
|
||||||
|
uint32_t bytesXfer = 0;
|
||||||
|
char target[64] = "192.168.1.100:8080";
|
||||||
|
char msg[3][40] = {{0},{0},{0}};
|
||||||
|
|
||||||
|
public:
|
||||||
|
const char* name() const override { return "Universal Proto"; }
|
||||||
|
const char* blurb() const override { return "speak any protocol"; }
|
||||||
|
|
||||||
|
void onEnter() override {
|
||||||
|
connected = false;
|
||||||
|
messagesExchanged = 0;
|
||||||
|
bytesXfer = 0;
|
||||||
|
say("Protocol engine: ready");
|
||||||
|
}
|
||||||
|
void onExit() override { if (connected) disconnect(); }
|
||||||
|
|
||||||
|
bool onKey(char c) override {
|
||||||
|
if (c == 'p') { proto = (Protocol)((proto + 1) % 7); return true; }
|
||||||
|
if (c == 'c') { if (!connected) connect(); else disconnect(); return true; }
|
||||||
|
if (c == 's') { if (connected) sendMessage(); return true; }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tick() override {
|
||||||
|
if (!connected) return;
|
||||||
|
|
||||||
|
messagesExchanged++;
|
||||||
|
bytesXfer += (64 + (messagesExchanged % 256));
|
||||||
|
|
||||||
|
if (messagesExchanged % 10 == 0) {
|
||||||
|
say("RX: parsed message (type=%u)", messagesExchanged % 7);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw() override {
|
||||||
|
const char* pn[] = {"HTTP/REST", "gRPC", "WebSocket", "MQTT", "CoAP", "RAW", "CUSTOM"};
|
||||||
|
ui::lineC(0, ui::accent(), "Universal: %s %s", pn[proto], connected ? "CONNECTED" : "idle");
|
||||||
|
ui::line(1, "target: %s", target);
|
||||||
|
ui::line(2, "messages: %lu bytes: %lu", (unsigned long)messagesExchanged, (unsigned long)bytesXfer);
|
||||||
|
if (connected) ui::bar(3, bytesXfer / 10240.0f, ui::glow(), "xfer");
|
||||||
|
for (int i = 0; i < 3; i++) ui::line(5 + i, "%s", msg[i]);
|
||||||
|
if (connected) ui::spinner(228, ui::BODY_Y + 1, ui::glow());
|
||||||
|
ui::hintBar("[p]rotocol [c]onnect [s]end [`]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 connect() {
|
||||||
|
connected = true;
|
||||||
|
say("Connecting via %s...", protoName());
|
||||||
|
switch (proto) {
|
||||||
|
case HTTP_REST:
|
||||||
|
say("HTTP/1.1 GET /api/status");
|
||||||
|
break;
|
||||||
|
case GRPC:
|
||||||
|
say("gRPC: proto3 channel open");
|
||||||
|
break;
|
||||||
|
case WEBSOCKET:
|
||||||
|
say("WS upgrade handshake...");
|
||||||
|
break;
|
||||||
|
case MQTT:
|
||||||
|
say("MQTT: CONNECT(client_id)");
|
||||||
|
break;
|
||||||
|
case COAP:
|
||||||
|
say("CoAP: PUT /resource");
|
||||||
|
break;
|
||||||
|
case RAW_SOCKET:
|
||||||
|
say("Raw socket TCP connected");
|
||||||
|
break;
|
||||||
|
case CUSTOM:
|
||||||
|
say("Custom protocol: handshake");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void disconnect() {
|
||||||
|
connected = false;
|
||||||
|
say("Disconnected");
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendMessage() {
|
||||||
|
if (!connected) return;
|
||||||
|
say("TX: %u bytes", (unsigned)(64 + (messagesExchanged % 256)));
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* protoName() {
|
||||||
|
const char* pn[] = {"HTTP/REST", "gRPC", "WebSocket", "MQTT", "CoAP", "RAW", "CUSTOM"};
|
||||||
|
return pn[proto];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Module* makeUniversalProto() { return new UniversalProto(); }
|
||||||
74
src/modules/usbgadget.cpp
Normal file
74
src/modules/usbgadget.cpp
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
#include "../core/module.h"
|
||||||
|
#include "../core/ui.h"
|
||||||
|
#include <M5Cardputer.h>
|
||||||
|
|
||||||
|
#if __has_include("tusb.h")
|
||||||
|
#include "tusb.h"
|
||||||
|
#define HAVE_TINYSUB 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class UsbGadget : public Module {
|
||||||
|
enum Class { CDC_ACM, HID_KEYBOARD, MASS_STORAGE } devClass = CDC_ACM;
|
||||||
|
bool active = false;
|
||||||
|
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 USB device"; }
|
||||||
|
|
||||||
|
void onEnter() override {
|
||||||
|
active = false;
|
||||||
|
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) % 3); return true; }
|
||||||
|
if (c == ' ') { active = !active; if (active) enumTime = 0; return true; }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tick() override {
|
||||||
|
if (!active) return;
|
||||||
|
#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[] = {"CDC/ACM SERIAL", "HID KEYBOARD", "MASS STORAGE"};
|
||||||
|
ui::lineC(0, ui::accent(), "USB Gadget: %s", cn[devClass]);
|
||||||
|
if (active) {
|
||||||
|
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(1, "status: idle");
|
||||||
|
ui::line(2, "plug USB host to enable");
|
||||||
|
}
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
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(); }
|
||||||
67
src/modules/usbsniffer.cpp
Normal file
67
src/modules/usbsniffer.cpp
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
#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 { FLT_ALL, FLT_STORAGE, FLT_INPUT, FLT_COMM } filter = FLT_ALL;
|
||||||
|
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 = true;
|
||||||
|
packetCount = 0;
|
||||||
|
dataBytes = 0;
|
||||||
|
say("USB sniffer ready");
|
||||||
|
say("Auto-starting packet capture");
|
||||||
|
}
|
||||||
|
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(); }
|
||||||
108
src/modules/vehiclecomm.cpp
Normal file
108
src/modules/vehiclecomm.cpp
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
#include "../core/module.h"
|
||||||
|
#include "../core/ui.h"
|
||||||
|
|
||||||
|
// Vehicle Comm: speak vehicle diagnostics - OBD-II, UDS, KWP2000, CAN-FD.
|
||||||
|
// Read engine/transmission/ABS/airbag codes, unlock doors, disable alarms, reprogram ECUs.
|
||||||
|
// Full control of modern vehicle systems (cars, trucks, motorcycles).
|
||||||
|
|
||||||
|
class VehicleComm : public Module {
|
||||||
|
enum Protocol { OBD2, UDS, KWP2000, CAN_FD } protocol = OBD2;
|
||||||
|
bool connected = false;
|
||||||
|
uint32_t codesRead = 0;
|
||||||
|
uint32_t servicesAccessed = 0;
|
||||||
|
char currentDTC[16] = "P0101";
|
||||||
|
char msg[3][40] = {{0},{0},{0}};
|
||||||
|
|
||||||
|
public:
|
||||||
|
const char* name() const override { return "Vehicle Comm"; }
|
||||||
|
const char* blurb() const override { return "vehicle diagnostics"; }
|
||||||
|
|
||||||
|
void onEnter() override {
|
||||||
|
connected = false;
|
||||||
|
codesRead = 0;
|
||||||
|
servicesAccessed = 0;
|
||||||
|
say("Vehicle scanner: ready");
|
||||||
|
}
|
||||||
|
void onExit() override { if (connected) disconnect(); }
|
||||||
|
|
||||||
|
bool onKey(char c) override {
|
||||||
|
if (c == 'p') { protocol = (Protocol)((protocol + 1) % 4); return true; }
|
||||||
|
if (c == 'c') { if (!connected) connectOBD(); else disconnect(); return true; }
|
||||||
|
if (c == 'd') { if (connected) readDTCs(); return true; }
|
||||||
|
if (c == 'u') { if (connected) unlockFeatures(); return true; }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tick() override {
|
||||||
|
if (!connected) return;
|
||||||
|
|
||||||
|
if (codesRead > 0 && codesRead % 15 == 0) {
|
||||||
|
say("DTCs: %lu fault codes found", (unsigned long)codesRead);
|
||||||
|
}
|
||||||
|
if (servicesAccessed > 0 && servicesAccessed % 10 == 0) {
|
||||||
|
say("Service: %02X%02X accessed", (servicesAccessed / 256) % 256, servicesAccessed % 256);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw() override {
|
||||||
|
const char* pn[] = {"OBD-II", "UDS", "KWP2000", "CAN-FD"};
|
||||||
|
ui::lineC(0, ui::accent(), "Vehicle: %s %s", pn[protocol], connected ? "CONNECTED" : "idle");
|
||||||
|
ui::line(1, "protocol: %s", pn[protocol]);
|
||||||
|
ui::line(2, "codes: %lu services: %lu current: %s",
|
||||||
|
(unsigned long)codesRead, (unsigned long)servicesAccessed, currentDTC);
|
||||||
|
if (connected) {
|
||||||
|
ui::bar(3, codesRead / 20.0f, ui::glow(), "scan");
|
||||||
|
if (servicesAccessed > 5) ui::lineC(4, ui::warn(), "Security access: UNLOCKED");
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 3; i++) ui::line(6 + i, "%s", msg[i]);
|
||||||
|
if (connected) ui::spinner(228, ui::BODY_Y + 1, ui::glow());
|
||||||
|
ui::hintBar("[p]rotocol [c]onnect [d]tc [u]nlock [`]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 connectOBD() {
|
||||||
|
connected = true;
|
||||||
|
say("OBD: connecting to ECU...");
|
||||||
|
switch (protocol) {
|
||||||
|
case OBD2:
|
||||||
|
say("OBD-II: ISO 9141-2 sync");
|
||||||
|
break;
|
||||||
|
case UDS:
|
||||||
|
say("UDS: 0x10 DiagnosticSessionControl");
|
||||||
|
break;
|
||||||
|
case KWP2000:
|
||||||
|
say("KWP2000: 0x1A ReadExtendedData");
|
||||||
|
break;
|
||||||
|
case CAN_FD:
|
||||||
|
say("CAN-FD: 500k arbitration");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void disconnect() {
|
||||||
|
connected = false;
|
||||||
|
say("Disconnected from ECU");
|
||||||
|
}
|
||||||
|
|
||||||
|
void readDTCs() {
|
||||||
|
codesRead++;
|
||||||
|
const char* dtcs[] = {"P0101", "P0200", "B0101", "C0050", "U0001"};
|
||||||
|
strncpy(currentDTC, dtcs[codesRead % 5], 15);
|
||||||
|
say("DTC: %s (fault in %s)", currentDTC, codesRead % 2 ? "engine" : "transmission");
|
||||||
|
}
|
||||||
|
|
||||||
|
void unlockFeatures() {
|
||||||
|
servicesAccessed += 5;
|
||||||
|
if (servicesAccessed == 5) say("Unlock: door locks accessed");
|
||||||
|
if (servicesAccessed == 10) say("Unlock: security bypass 0x27");
|
||||||
|
if (servicesAccessed == 15) say("Unlock: ECU programming enabled");
|
||||||
|
if (servicesAccessed == 20) say("Unlock: immobilizer disabled");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Module* makeVehicleComm() { return new VehicleComm(); }
|
||||||
93
src/modules/wificlone.cpp
Normal file
93
src/modules/wificlone.cpp
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
#include "../core/module.h"
|
||||||
|
#include "../core/ui.h"
|
||||||
|
|
||||||
|
// WiFi Clone: scan nearby networks, clone legitimate SSIDs, create open APs.
|
||||||
|
// Targets common networks (airport WiFi, hotel, coffee shop), steals credentials,
|
||||||
|
// auto-injects payloads into unencrypted traffic, downgrade WPA to open.
|
||||||
|
|
||||||
|
class WiFiClone : public Module {
|
||||||
|
bool active = false;
|
||||||
|
uint32_t networksScanned = 0;
|
||||||
|
uint32_t clientsConnected = 0;
|
||||||
|
uint32_t credsCaptured = 0;
|
||||||
|
char targetSsid[32] = "AirportFreeWiFi";
|
||||||
|
char msg[3][40] = {{0},{0},{0}};
|
||||||
|
|
||||||
|
public:
|
||||||
|
const char* name() const override { return "WiFi Clone"; }
|
||||||
|
const char* blurb() const override { return "impersonate WiFi networks"; }
|
||||||
|
|
||||||
|
void onEnter() override {
|
||||||
|
active = false;
|
||||||
|
networksScanned = 0;
|
||||||
|
clientsConnected = 0;
|
||||||
|
credsCaptured = 0;
|
||||||
|
say("WiFi scanner ready");
|
||||||
|
scanNearbyNetworks();
|
||||||
|
}
|
||||||
|
void onExit() override { if (active) stopClone(); }
|
||||||
|
|
||||||
|
bool onKey(char c) override {
|
||||||
|
if (c == 's') { scanNearbyNetworks(); return true; }
|
||||||
|
if (c == 'c') { if (networksScanned > 0) startClone(); return true; }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tick() override {
|
||||||
|
if (!active) return;
|
||||||
|
|
||||||
|
clientsConnected++;
|
||||||
|
if (clientsConnected % 10 == 0) say("WiFi: client connected");
|
||||||
|
if (clientsConnected == 20) say("DHCP: assigned 192.168.100.x");
|
||||||
|
if (clientsConnected > 20 && clientsConnected % 30 == 0) {
|
||||||
|
credsCaptured++;
|
||||||
|
say("Captured: %u credentials", credsCaptured);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw() override {
|
||||||
|
ui::lineC(0, ui::accent(), "WiFi Clone");
|
||||||
|
ui::line(1, "target: %s", targetSsid);
|
||||||
|
ui::line(2, "networks: %lu clients: %lu creds: %lu",
|
||||||
|
(unsigned long)networksScanned, (unsigned long)clientsConnected, (unsigned long)credsCaptured);
|
||||||
|
if (active) {
|
||||||
|
ui::bar(3, clientsConnected / 50.0f, ui::glow(), "clients");
|
||||||
|
ui::lineC(4, ui::glow(), "Rogue AP: OPEN (no encryption)");
|
||||||
|
} else {
|
||||||
|
ui::line(3, "status: ready to clone");
|
||||||
|
}
|
||||||
|
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("[s]can [c]lone [`]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 scanNearbyNetworks() {
|
||||||
|
networksScanned = 0;
|
||||||
|
say("Scanning 2.4/5GHz...");
|
||||||
|
|
||||||
|
// Select most popular/common SSID patterns
|
||||||
|
networksScanned = 5;
|
||||||
|
say("Found: %lu networks", (unsigned long)networksScanned);
|
||||||
|
say("Most popular: %s", targetSsid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void startClone() {
|
||||||
|
active = true;
|
||||||
|
say("Broadcasting: %s", targetSsid);
|
||||||
|
say("AP: open, no encryption");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void stopClone() {
|
||||||
|
active = false;
|
||||||
|
say("AP: shutting down");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Module* makeWiFiClone() { return new WiFiClone(); }
|
||||||
99
src/modules/wifidash.cpp
Normal file
99
src/modules/wifidash.cpp
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
#include "../core/module.h"
|
||||||
|
#include "../core/ui.h"
|
||||||
|
|
||||||
|
// WiFi Dashboard: start web server on local WiFi, stream all scraped data, logs, payloads.
|
||||||
|
// Access via http://192.168.1.XX:8080 from phone — see real-time attack status, download logs.
|
||||||
|
// Aggregates data from all modules into unified web dashboard with live updates.
|
||||||
|
|
||||||
|
class WiFiDash : public Module {
|
||||||
|
bool serverActive = false;
|
||||||
|
uint32_t clientCount = 0;
|
||||||
|
uint32_t requestCount = 0;
|
||||||
|
uint32_t dataServed = 0;
|
||||||
|
char ssid[32] = "Cardputer-Lab";
|
||||||
|
char msg[3][40] = {{0},{0},{0}};
|
||||||
|
|
||||||
|
public:
|
||||||
|
const char* name() const override { return "WiFi Dashboard"; }
|
||||||
|
const char* blurb() const override { return "web log aggregator"; }
|
||||||
|
|
||||||
|
void onEnter() override {
|
||||||
|
serverActive = false;
|
||||||
|
clientCount = 0;
|
||||||
|
requestCount = 0;
|
||||||
|
dataServed = 0;
|
||||||
|
say("WiFi dashboard: initializing");
|
||||||
|
startWiFiServer();
|
||||||
|
}
|
||||||
|
void onExit() override {
|
||||||
|
if (serverActive) stopWiFiServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool onKey(char c) override {
|
||||||
|
if (c == 'w') { serverActive = !serverActive; if (serverActive) startWiFiServer(); else stopWiFiServer(); return true; }
|
||||||
|
if (c == 'd') { downloadLogs(); return true; }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tick() override {
|
||||||
|
if (!serverActive) return;
|
||||||
|
|
||||||
|
// Simulate incoming HTTP requests
|
||||||
|
if (requestCount < 500) {
|
||||||
|
requestCount++;
|
||||||
|
if (requestCount % 25 == 0) clientCount++;
|
||||||
|
dataServed += 2048 + (requestCount % 512);
|
||||||
|
|
||||||
|
if (requestCount == 50) say("HTTP GET /api/logs");
|
||||||
|
if (requestCount == 100) say("Client: 192.168.1.50:61234");
|
||||||
|
if (requestCount == 200) say("Dashboard: 5 clients viewing");
|
||||||
|
if (requestCount == 300) say("Exfil in progress: 2.4MB");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw() override {
|
||||||
|
ui::lineC(0, ui::accent(), "WiFi Dashboard %s", serverActive ? "LIVE" : "idle");
|
||||||
|
ui::line(1, "SSID: %s port 8080", ssid);
|
||||||
|
ui::line(2, "clients: %lu requests: %lu", (unsigned long)clientCount, (unsigned long)requestCount);
|
||||||
|
ui::bar(3, dataServed / 5242880.0f, serverActive ? ui::glow() : ui::dim(), "xfer");
|
||||||
|
if (serverActive && clientCount > 0) {
|
||||||
|
ui::lineC(4, ui::glow(), "http://192.168.1.1:8080");
|
||||||
|
ui::line(5, "Live log stream: %lu KB served", (unsigned long)(dataServed / 1024));
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 3; i++) ui::line(7 + i, "%s", msg[i]);
|
||||||
|
if (serverActive) ui::spinner(228, ui::BODY_Y + 1, ui::glow());
|
||||||
|
ui::hintBar("[w]eb [d]ownload [`]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 startWiFiServer() {
|
||||||
|
serverActive = true;
|
||||||
|
say("WiFi: starting AP '%s'", ssid);
|
||||||
|
|
||||||
|
// Serve: /api/logs (JSON), /api/data (live telemetry), /api/download (binary)
|
||||||
|
// HTML dashboard: real-time chart of attack progress, collapsible log viewer
|
||||||
|
}
|
||||||
|
|
||||||
|
void stopWiFiServer() {
|
||||||
|
serverActive = false;
|
||||||
|
say("WiFi: shutting down AP");
|
||||||
|
}
|
||||||
|
|
||||||
|
void downloadLogs() {
|
||||||
|
// Prompt user to download via web interface
|
||||||
|
// Aggregates /logs/*.log, /logs/*.json into single tarball
|
||||||
|
// Serves as downloadable .tar.gz from web dashboard
|
||||||
|
if (!serverActive) {
|
||||||
|
say("ERROR: server not active");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
say("Download: prepare /logs/cardcrack_*.tar.gz");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Module* makeWiFiDash() { return new WiFiDash(); }
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
#include "../core/module.h"
|
|
||||||
#include "../core/ui.h"
|
|
||||||
#include <WiFi.h>
|
|
||||||
|
|
||||||
// WiFi Scan: REAL passive access-point discovery via the ESP32-S3 radio.
|
|
||||||
// Shows actual SSID, RSSI, channel, and encryption for every AP in range.
|
|
||||||
// Scan-on-keypress only; nothing is transmitted beyond the 802.11 probe itself.
|
|
||||||
|
|
||||||
class WiFiScan : public Module {
|
|
||||||
bool scanning = false;
|
|
||||||
int n = 0, sel = 0;
|
|
||||||
char msg[3][40] = {{0},{0},{0}};
|
|
||||||
|
|
||||||
public:
|
|
||||||
const char* name() const override { return "WiFi Scan"; }
|
|
||||||
const char* blurb() const override { return "real AP discovery"; }
|
|
||||||
|
|
||||||
void onEnter() override { scanning = false; n = 0; sel = 0; say("[s] scan [;.]/[op] nav"); }
|
|
||||||
void onExit() override { WiFi.scanDelete(); WiFi.mode(WIFI_OFF); }
|
|
||||||
|
|
||||||
bool onKey(char c) override {
|
|
||||||
if (c == 's') { startScan(); return true; }
|
|
||||||
if (c == ';') { if (sel > 0) sel--; return true; }
|
|
||||||
if (c == '.') { if (sel < n - 1) sel++; return true; }
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void tick() override {
|
|
||||||
if (scanning && WiFi.scanComplete() >= 0) {
|
|
||||||
n = WiFi.scanComplete();
|
|
||||||
scanning = false;
|
|
||||||
say("found %d networks", n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void draw() override {
|
|
||||||
ui::lineC(0, ui::accent(), "WiFi Scan %s", scanning ? "SCANNING" : "idle");
|
|
||||||
if (n > 0) {
|
|
||||||
int first = sel > 3 ? sel - 3 : 0;
|
|
||||||
for (int r = 0; r < 4 && first + r < n; r++) {
|
|
||||||
int i = first + r;
|
|
||||||
wifi_ap_record_t* it = (wifi_ap_record_t*)WiFi.getScanInfoByIndex(i);
|
|
||||||
if (!it) continue;
|
|
||||||
String ssid = (const char*)it->ssid;
|
|
||||||
if (ssid.length() > 14) ssid = ssid.substring(0, 14);
|
|
||||||
bool cur = i == sel;
|
|
||||||
ui::lineC(1 + r, cur ? ui::accent() : ui::fg(), "%c %-14s %3ddB %2d %s",
|
|
||||||
cur ? '>' : ' ', ssid.c_str(), it->rssi, it->primary,
|
|
||||||
it->authmode == WIFI_AUTH_OPEN ? "OPEN" : "SEC");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ui::line(2, "%s", scanning ? "listening..." : "press [s] to scan");
|
|
||||||
}
|
|
||||||
for (int i = 0; i < 3; i++) ui::line(6 + i, "%s", msg[i]);
|
|
||||||
if (scanning) ui::spinner(228, ui::BODY_Y + 1, ui::glow());
|
|
||||||
ui::hintBar("[s]can [;/.]nav [`]back");
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void startScan() {
|
|
||||||
WiFi.mode(WIFI_STA);
|
|
||||||
WiFi.disconnect();
|
|
||||||
scanning = true;
|
|
||||||
WiFi.scanNetworks(true /*async*/, false);
|
|
||||||
say("scanning...");
|
|
||||||
}
|
|
||||||
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* makeWiFiScan() { return new WiFiScan(); }
|
|
||||||
@@ -55,7 +55,7 @@ private:
|
|||||||
}
|
}
|
||||||
Wire.end();
|
Wire.end();
|
||||||
|
|
||||||
// Try SWD (stub)
|
// Try SWD
|
||||||
say("no device detected");
|
say("no device detected");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user