Flipper Zero/Rogue AP Detector/detector.c
From charlesreid1
Main article: Flipper Zero/Rogue AP Detector
Contents
Requirements
- Define a Detector structure (config, public functions) that provides an interface for the main loop to interact with results
- Implement logic for filtering alters - consult the history log before flagging a new threat. (Ensures persistent rogue AP only triggers user notification once, instead of on every single scan.)
- Detector should be stateful - maintain a history of what it has seen over time
- Two-stage filtering - incoming analysis should be filtered first by score (is the suspicion above the alert threshold), and by history (is the BSSID already in history log)
- Separate list of new detections (critical link to UI - UI polls this list after a scan, and generates alerts from any items that show up in this list)
detector.h
#ifndef DETECTOR_H #define DETECTOR_H #include "analyzer.h" // Depends on the AnalysisResult structure #include <furi.h> #define DETECTOR_MAX_HISTORY 100 // Max number of threats to remember #define DETECTOR_MAX_NEW_ALERTS 10 // Max new alerts per scan cycle /** * @brief User-configurable settings for the detector. */ typedef struct { int alert_threshold; // Suspicion score (0-100) above which an alert is triggered. uint32_t history_timeout_seconds; // How long an acknowledged alert stays in history (e.g., 24 hours). } DetectorConfig; /** * @brief An entry in the detection history log. * Used to prevent spamming alerts for the same device repeatedly. */ typedef struct { uint8_t bssid[6]; // The BSSID of the detected AP. uint32_t last_seen_ts; // Timestamp of the last time it was seen. ThreatType threat_type; // The original threat classification. bool acknowledged; // True if the user has dismissed the alert for this AP. } DetectionHistoryEntry; /** * @brief Main detector state structure. */ typedef struct { DetectorConfig config; // Current configuration. DetectionHistoryEntry* history; // Array of previously detected threats. uint16_t history_count; uint16_t history_capacity; AnalysisResult* new_detections; // Array of threats detected in the CURRENT scan cycle. uint16_t new_detection_count; uint16_t new_detection_capacity; FuriMutex* mutex; // Mutex for thread-safe access. } Detector; /** * @brief Allocates and initializes a new Detector instance. * @return Pointer to the newly created Detector. */ Detector* detector_alloc(); /** * @brief Frees all resources associated with a Detector instance. * @param detector Pointer to the Detector instance to free. */ void detector_free(Detector* detector); /** * @brief Updates the detector's configuration. * @param detector Pointer to the Detector instance. * @param config Pointer to the new configuration settings. */ void detector_set_config(Detector* detector, const DetectorConfig* config); /** * @brief Processes a single analysis result to determine if it's a new, alert-worthy threat. * @param detector Pointer to the Detector instance. * @param result The result from the analyzer module. */ void detector_process_analysis(Detector* detector, const AnalysisResult* result); /** * @brief Clears the list of new detections. Should be called at the start of each scan cycle. * @param detector Pointer to the Detector instance. */ void detector_clear_new_detections(Detector* detector); /** * @brief Gets the list of newly detected threats from the last processing cycle. * @param detector Pointer to the Detector instance. * @param buffer A user-provided buffer to copy the results into. * @param buffer_size The number of elements the buffer can hold. * @return The number of new detections copied to the buffer. */ uint16_t detector_get_new_detections(Detector* detector, AnalysisResult* buffer, uint16_t buffer_size); /** * @brief Marks a specific threat (by BSSID) in the history as acknowledged by the user. * @param detector Pointer to the Detector instance. * @param bssid The BSSID of the threat to acknowledge. */ void detector_acknowledge_threat(Detector* detector, const uint8_t bssid[6]); #endif // DETECTOR_H
detector.c
#include "detector.h" #include <furi_hal_rtc.h> #include <string.h> // --- Public Function Implementations --- Detector* detector_alloc() { Detector* detector = malloc(sizeof(Detector)); furi_check(detector != NULL); // Default configuration detector->config.alert_threshold = 70; // Alert on scores 70 and above detector->config.history_timeout_seconds = 86400; // 24 hours // Allocate history buffer detector->history_capacity = DETECTOR_MAX_HISTORY; detector->history = malloc(sizeof(DetectionHistoryEntry) * detector->history_capacity); furi_check(detector->history != NULL); detector->history_count = 0; // Allocate new detections buffer detector->new_detection_capacity = DETECTOR_MAX_NEW_ALERTS; detector->new_detections = malloc(sizeof(AnalysisResult) * detector->new_detection_capacity); furi_check(detector->new_detections != NULL); detector->new_detection_count = 0; detector->mutex = furi_mutex_alloc(FuriMutexTypeRecursive); return detector; } void detector_free(Detector* detector) { if(!detector) return; furi_mutex_free(detector->mutex); free(detector->history); free(detector->new_detections); free(detector); } void detector_set_config(Detector* detector, const DetectorConfig* config) { furi_check(detector && config); if(furi_mutex_acquire(detector->mutex, FuriWaitForever) != FuriStatusOk) return; memcpy(&detector->config, config, sizeof(DetectorConfig)); furi_mutex_release(detector->mutex); } void detector_clear_new_detections(Detector* detector) { furi_check(detector); if(furi_mutex_acquire(detector->mutex, FuriWaitForever) != FuriStatusOk) return; detector->new_detection_count = 0; furi_mutex_release(detector->mutex); } void detector_process_analysis(Detector* detector, const AnalysisResult* result) { furi_check(detector && result); // --- Filter 1: Is the suspicion score high enough to be considered a threat? --- if(result->suspicion_score < detector->config.alert_threshold) { return; // Not suspicious enough, ignore it. } if(furi_mutex_acquire(detector->mutex, FuriWaitForever) != FuriStatusOk) return; // --- Filter 2: Have we already logged this BSSID in our history? --- bool found_in_history = false; for(uint16_t i = 0; i < detector->history_count; i++) { if(memcmp(detector->history[i].bssid, result->scanned_ap.bssid, 6) == 0) { // We've seen it. Just update its timestamp. detector->history[i].last_seen_ts = furi_hal_rtc_get_timestamp(); found_in_history = true; break; } } // If it's a known threat, we don't need to generate a *new* alert. if(found_in_history) { furi_mutex_release(detector->mutex); return; } // --- It's a NEW threat. Add it to the history and the new detections list. --- // Add to history if(detector->history_count < detector->history_capacity) { DetectionHistoryEntry* new_entry = &detector->history[detector->history_count]; memcpy(new_entry->bssid, result->scanned_ap.bssid, 6); new_entry->last_seen_ts = furi_hal_rtc_get_timestamp(); new_entry->threat_type = result->threat_type; new_entry->acknowledged = false; // New threats are never acknowledged yet. detector->history_count++; } // Add to this cycle's list of new detections for the UI to display if(detector->new_detection_count < detector->new_detection_capacity) { memcpy(&detector->new_detections[detector->new_detection_count], result, sizeof(AnalysisResult)); detector->new_detection_count++; } furi_mutex_release(detector->mutex); } uint16_t detector_get_new_detections(Detector* detector, AnalysisResult* buffer, uint16_t buffer_size) { furi_check(detector && buffer); if(furi_mutex_acquire(detector->mutex, FuriWaitForever) != FuriStatusOk) return 0; uint16_t count_to_copy = (detector->new_detection_count < buffer_size) ? detector->new_detection_count : buffer_size; if(count_to_copy > 0) { memcpy(buffer, detector->new_detections, count_to_copy * sizeof(AnalysisResult)); } furi_mutex_release(detector->mutex); return count_to_copy; } void detector_acknowledge_threat(Detector* detector, const uint8_t bssid[6]) { furi_check(detector && bssid); if(furi_mutex_acquire(detector->mutex, FuriWaitForever) != FuriStatusOk) return; for(uint16_t i = 0; i < detector->history_count; i++) { if(memcmp(detector->history[i].bssid, bssid, 6) == 0) { detector->history[i].acknowledged = true; break; } } furi_mutex_release(detector->mutex); }