Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions application.fam
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ App(
sources=[
"src/app.c",
"src/screen.c",
"src/screen_boot.c",
"src/screen_blackout.c",
"src/screen_bluetooth.c",
"src/screen_deauth.c",
Expand Down Expand Up @@ -45,6 +46,7 @@ App(
"src/screen_deauth_client.c",
"src/screen_arp_from_creds.c",
"src/screen_mitm_pcap.c",
"src/screen_nmap.c",
"src/screen_beacon_spam.c",
"src/uart_comm.c",
],
Expand Down
Binary file modified dist/c5lab_dev.fap
Binary file not shown.
Binary file modified dist/debug/c5lab_dev_d.elf
Binary file not shown.
158 changes: 107 additions & 51 deletions src/app.c
Original file line number Diff line number Diff line change
@@ -1,20 +1,77 @@
#include "app.h"
#include "uart_comm.h"
#include "screen_main_menu.h"
#include "screen_boot.h"
#include "screen.h"
#include <furi_hal.h>
#include <furi_hal_power.h>
#include <storage/storage.h>
#include <string.h>

// ============================================================================
// Custom event handler
//
// Boot screen worker thread posts BOOT_EVENT_* to drive the post-boot
// transition. We MUST NOT call screen_pop / push directly from inside the
// boot worker thread - those manipulate the global view stack and the GUI
// dispatcher state, so we route the work through the dispatcher's custom
// event queue which runs callbacks on the GUI thread.
// ============================================================================

static void app_push_main_menu(WiFiApp* app) {
void* main_menu_data = NULL;
View* main_menu = screen_main_menu_create(app, &main_menu_data);
if(!main_menu) {
view_dispatcher_stop(app->view_dispatcher);
return;
}
screen_push_with_cleanup(app, main_menu, main_menu_cleanup_internal, main_menu_data);
}

static bool app_custom_event_handler(void* context, uint32_t event) {
WiFiApp* app = (WiFiApp*)context;
if(!app) return false;

switch(event) {
case BOOT_EVENT_DONE:
// Push main menu first (becomes the active view), THEN remove the
// boot screen from the bottom of the stack. screen_pop is a no-op
// when the stack has only one entry, hence the dedicated remove_first.
app_push_main_menu(app);
screen_remove_first(app);
return true;

case BOOT_EVENT_CONTINUE:
// User chose to continue without a connected board.
app->board_connected = false;
app_push_main_menu(app);
screen_remove_first(app);
return true;

case BOOT_EVENT_FAILED:
// Boot worker is now waiting for user input. Nothing to do here -
// the screen draws its own "OK=continue BACK=exit" footer.
return true;

case BOOT_EVENT_CANCELLED:
// BACK pressed during/after boot - tear down the app.
// screen_pop_all in cleanup releases the boot view; here we just stop.
view_dispatcher_stop(app->view_dispatcher);
return true;

default:
return false;
}
}

int32_t wifi_attacks_app(void* p) {
UNUSED(p);

WiFiApp* app = (WiFiApp*)malloc(sizeof(WiFiApp));
if(!app) return -1;
memset(app, 0, sizeof(WiFiApp));
// Initialize GUI

// GUI plumbing
app->gui = furi_record_open(RECORD_GUI);
if(!app->gui) {
free(app);
Expand All @@ -27,55 +84,44 @@ int32_t wifi_attacks_app(void* p) {
return -1;
}
app->view_stack = view_stack_alloc();

view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);

// Enable 5V power output on GPIO to power the ESP32 board
if(furi_hal_power_is_otg_enabled()) {
// Already enabled, nothing to do
} else {
furi_hal_power_enable_otg();
}

// Initialize UART
uart_comm_init(app);

// Check initial board connection
furi_delay_ms(500); // Give board time to initialize
app->board_connected = uart_check_board_connection(app);

// If board connected, check SD card
if(app->board_connected) {
app->sd_card_ok = uart_check_sd_card(app);
app->sd_card_checked = true;
}

// Initialize app state
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
view_dispatcher_set_custom_event_callback(app->view_dispatcher, app_custom_event_handler);

// CRITICAL: Disable expansion service BEFORE the boot worker acquires USART.
// Expansion service owns USART by default (waiting for an expansion module);
// skipping this triggers furi_check failures inside furi_hal_serial when we
// try to take the port - especially visible on battery power.
app->expansion = furi_record_open(RECORD_EXPANSION);
expansion_disable(app->expansion);

// App state init (5V, UART, board check are deferred to the boot screen
// worker thread so the user sees live progress instead of a frozen UI).
app->networks = NULL;
app->network_count = 0;
memset(app->selected_networks, 0, sizeof(app->selected_networks));
app->selected_count = 0;

// Initialize scanning state

app->scan_results = NULL;
app->scan_result_count = 0;
app->scan_result_capacity = 0;
app->scanning_in_progress = false;
app->scan_bytes_received = 0;
app->last_scan_line = furi_string_alloc();

app->attack_status = furi_string_alloc();
app->attack_log = furi_string_alloc();
app->current_ssid = furi_string_alloc();
app->current_password = furi_string_alloc();
app->attack_in_progress = false;

app->sniffer_packet_count = 0;
app->evil_twin_html_selection = 0;
app->html_files = NULL;
app->html_file_count = 0;
app->evil_twin_password = furi_string_alloc();

// Load red team mode from persistent storage
app->red_team_mode = false;
{
Expand All @@ -91,59 +137,69 @@ int32_t wifi_attacks_app(void* p) {
storage_file_free(file);
furi_record_close(RECORD_STORAGE);
}

// Create and push main menu with cleanup
void* main_menu_data = NULL;
View* main_menu = screen_main_menu_create(app, &main_menu_data);
if(!main_menu) {
uart_comm_deinit(app);

// Push the boot screen as the first view. Its worker thread powers up the
// board, probes UART, and posts BOOT_EVENT_DONE / BOOT_EVENT_FAILED via
// the custom event handler above.
void* boot_data = NULL;
View* boot_view = screen_boot_create(app, &boot_data);
if(!boot_view) {
if(app->expansion) {
expansion_enable(app->expansion);
furi_record_close(RECORD_EXPANSION);
}
view_dispatcher_free(app->view_dispatcher);
view_stack_free(app->view_stack);
furi_record_close(RECORD_GUI);
free(app);
return -1;
}

// Use screen_push_with_cleanup for main menu too
screen_push_with_cleanup(app, main_menu, main_menu_cleanup_internal, main_menu_data);

screen_push_with_cleanup(app, boot_view, screen_boot_cleanup_internal, boot_data);

// Run the ViewDispatcher event loop
view_dispatcher_run(app->view_dispatcher);

// Cleanup - remove all views first
screen_pop_all(app);

// Disable 5V GPIO power output
if(furi_hal_power_is_otg_enabled()) {
furi_hal_power_disable_otg();
}

uart_comm_deinit(app);


// Restore expansion service ownership of USART per SDK contract.
if(app->expansion) {
expansion_enable(app->expansion);
furi_record_close(RECORD_EXPANSION);
app->expansion = NULL;
}

furi_string_free(app->attack_status);
furi_string_free(app->attack_log);
furi_string_free(app->current_ssid);
furi_string_free(app->current_password);
furi_string_free(app->evil_twin_password);
furi_string_free(app->last_scan_line);

if(app->scan_results) free(app->scan_results);

for(uint32_t i = 0; i < app->network_count; i++) {
free(app->networks[i]);
}
if(app->networks) free(app->networks);

for(uint32_t i = 0; i < app->html_file_count; i++) {
free(app->html_files[i]);
}
if(app->html_files) free(app->html_files);

view_dispatcher_free(app->view_dispatcher);
view_stack_free(app->view_stack);
furi_record_close(RECORD_GUI);

free(app);

return 0;
}
21 changes: 21 additions & 0 deletions src/app.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <furi_hal_serial.h>
#include <furi_hal_serial_control.h>
#include <furi/core/stream_buffer.h>
#include <expansion/expansion.h>
#include <stdint.h>
#include <stdbool.h>

Expand All @@ -29,6 +30,14 @@ typedef struct {

#define MAX_SCAN_RESULTS 64

// Cached password entry (populated from `show_pass evil` and from successful captures)
#define MAX_CACHED_PASSWORDS 32

typedef struct {
char ssid[33];
char password[65];
} CachedPassword;

// Screen context structure
typedef struct {
WiFiApp* app;
Expand Down Expand Up @@ -92,6 +101,18 @@ struct WiFiApp {

// WiFi connection status (set by wifi_connect success in ARP/wpasec screens)
bool wifi_connected;

// Cache of known WPA passwords keyed by SSID. Populated lazily from `show_pass evil`
// and also after successful captures (Evil Twin, Portal, Karma, Rogue AP).
// Prevents re-running `show_pass evil` on every attack screen.
CachedPassword password_cache[MAX_CACHED_PASSWORDS];
uint8_t password_cache_count;
bool password_cache_loaded;

// Expansion service handle. We must call expansion_disable() before acquiring
// USART (otherwise the expansion service races us on the same port and
// causes furi_check failures), and expansion_enable() on shutdown.
Expansion* expansion;
};

// App entry point
Expand Down
12 changes: 12 additions & 0 deletions src/screen.c
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,18 @@ void screen_pop(WiFiApp* app) {
}
}

void screen_remove_first(WiFiApp* app) {
if(!app || screen_view_stack_size == 0) return;
ScreenStackEntry first = screen_view_stack[0];
for(uint8_t i = 0; i + 1 < screen_view_stack_size; i++) {
screen_view_stack[i] = screen_view_stack[i + 1];
}
screen_view_stack_size--;
view_dispatcher_remove_view(app->view_dispatcher, first.view_id);
if(first.cleanup) first.cleanup(first.view, first.cleanup_data);
if(first.view) view_free(first.view);
}

void screen_pop_to_main(WiFiApp* app) {
// Pop all views except the first one (main menu)
while(screen_view_stack_size > 1) {
Expand Down
2 changes: 2 additions & 0 deletions src/screen.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ void screen_push_with_cleanup(WiFiApp* app, View* view, void (*cleanup)(View*, v
void screen_pop(WiFiApp* app);
void screen_pop_to_main(WiFiApp* app);
void screen_pop_all(WiFiApp* app);
// Remove the bottom-most entry from the stack (e.g. boot screen after success).
void screen_remove_first(WiFiApp* app);

// Helper drawing functions
void screen_draw_title(Canvas* canvas, const char* title);
Expand Down
Loading
Loading