Files
Carputer-cracker/src/modules/exfil.cpp
Indiana Holmes d1c37b83e0 Add theme engine, animated UI, and Bus Dump / Crypto / Exfil modules
UI:
- Theme engine (5 palettes) persisted to NVS; live Theme switcher module
- Animated shell: boot splash, CRT scanlines, breathing menu cursor,
  wipe-in transitions, spinners, progress bars

New recon modules:
- Bus Dump: SPI-NOR + I2C EEPROM readout to microSD (read-only)
- Crypto Lab: entropy, magic-byte file-ID, XOR brute, AES-128 known-key
- Exfil: SD browser + base64 dump export over USB serial, manifest writer

README updated with the new module table and look-and-feel notes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AAhMHMRAQLQ9hSbBECKNfn
2026-09-11 00:17:57 +00:00

105 lines
3.6 KiB
C++

#include "../core/module.h"
#include "../core/ui.h"
#include <SD.h>
#include "mbedtls/base64.h"
// Exfil / Export — get captured data OFF the Cardputer for analysis on
// your workstation. Browse the SD card and stream a selected dump out
// over USB serial as base64 (paste into `base64 -d > file` on the host).
// Also writes a session manifest of what you pulled. A data-offload
// helper for your own captures — not a network channel.
class Exfil : public Module {
static constexpr int MAXF = 32;
char files[MAXF][32]; uint32_t sizes[MAXF]; int nf = 0, sel = 0;
bool sending = false; File cur; uint32_t sent = 0, total = 0;
char status[2][40] = {{0},{0}};
public:
const char* name() const override { return "Exfil"; }
const char* blurb() const override { return "export dumps->host"; }
void onEnter() override { sending=false; if (!SD.begin()) say("no SD"); else scan(); }
void onExit() override { if (cur) cur.close(); sending=false; }
bool onKey(char c) override {
if (sending) { if (c==' ') stop(); return true; }
int m = nf ? nf : 1;
if (c == ';') { sel = (sel-1+m)%m; return true; }
if (c == '.') { sel = (sel+1)%m; return true; }
if (c == 'r') { scan(); return true; }
if (c == '\r' || c == ' ') { startSend(); return true; }
if (c == 'x') { writeManifest(); return true; }
return false;
}
void tick() override {
if (!sending) return;
uint8_t in[48]; int n = cur.read(in, sizeof(in));
if (n <= 0) { Serial.println(); say("done: %s", files[sel]); stop(); return; }
unsigned char out[80]; size_t olen = 0;
mbedtls_base64_encode(out, sizeof(out), &olen, in, n);
Serial.write(out, olen);
sent += n;
}
void draw() override {
ui::lineC(0, ui::accent(), "SD export %s", sending ? "STREAMING" : "browse");
if (sending) {
ui::bar(1, total ? sent/(float)total : 0, ui::glow(), "b64->usb");
} else {
for (int i = 0; i < nf && i < 5; i++)
ui::lineC(1+i, i==sel ? ui::glow() : ui::fg(), "%c %-16s %luB",
i==sel?char(0x10):' ', files[i], (unsigned long)sizes[i]);
if (!nf) ui::line(1, "no files in /dump");
}
for (int i = 0; i < 2; i++) ui::line(6+i, "%s", status[i]);
ui::hintBar("; . pick enter send [x]manifest [`]back");
}
private:
void say(const char* fmt, ...) {
strncpy(status[1], status[0], 39);
va_list ap; va_start(ap,fmt); vsnprintf(status[0],40,fmt,ap); va_end(ap);
}
void scan() {
nf = 0; sel = 0;
File d = SD.open("/dump");
if (!d) { say("no /dump dir"); return; }
File f;
while ((f = d.openNextFile()) && nf < MAXF) {
if (!f.isDirectory()) {
strncpy(files[nf], f.name(), 31);
sizes[nf] = f.size();
nf++;
}
f.close();
}
d.close();
say("%d file(s)", nf);
}
void startSend() {
if (!nf) { say("nothing to send"); return; }
char path[48]; snprintf(path, sizeof(path), "/dump/%s", files[sel]);
cur = SD.open(path, FILE_READ);
if (!cur) { say("open fail"); return; }
total = sizes[sel]; sent = 0; sending = true;
Serial.printf("\n----- BEGIN %s (base64) -----\n", files[sel]);
say("streaming %s", files[sel]);
}
void stop() {
if (cur) cur.close();
Serial.printf("\n----- END -----\n");
sending = false;
}
void writeManifest() {
File m = SD.open("/dump/MANIFEST.txt", FILE_WRITE);
if (!m) { say("manifest fail"); return; }
m.printf("Card-Crack export manifest\n");
for (int i = 0; i < nf; i++) m.printf("%-20s %lu B\n", files[i], (unsigned long)sizes[i]);
m.close();
say("wrote MANIFEST.txt");
}
};
Module* makeExfil() { return new Exfil(); }