diff --git a/applications/plugins/subbrute/application.fam b/applications/plugins/subbrute/application.fam index 752b0e0e6..1afdc539f 100644 --- a/applications/plugins/subbrute/application.fam +++ b/applications/plugins/subbrute/application.fam @@ -2,7 +2,7 @@ App( appid="subbrute", name="Sub-GHz Bruteforcer", apptype=FlipperAppType.EXTERNAL, - entry_point="subbrute_start", + entry_point="subbrute_app", cdefines=["APP_SUB_BRUTE"], requires=["gui","dialogs"], stack_size=2 * 1024, diff --git a/applications/plugins/subbrute/helpers/subbrute_worker.c b/applications/plugins/subbrute/helpers/subbrute_worker.c new file mode 100644 index 000000000..22e0e7ec4 --- /dev/null +++ b/applications/plugins/subbrute/helpers/subbrute_worker.c @@ -0,0 +1,370 @@ +#include "subbrute_worker.h" + +#include +#include +#include + +#define TAG "SubBruteWorker" + +struct SubBruteWorker { + FuriThread* thread; + volatile bool worker_running; + volatile bool worker_manual_mode; + bool is_manual_init; + + SubGhzEnvironment* environment; + SubGhzTransmitter* transmitter; + FlipperFormat* flipper_format; + + uint32_t last_time_tx_data; + + // Preset and frequency needed + FuriHalSubGhzPreset preset; + uint32_t frequency; + string_t protocol_name; + + //SubBruteWorkerCallback callback; + //void* context; +}; + +/** Taken from subghz_tx_rx_worker.c */ +#define SUBBRUTE_TXRX_WORKER_BUF_SIZE 2048 +#define SUBBRUTE_TXRX_WORKER_MAX_TXRX_SIZE 60 +#define SUBBRUTE_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF 40 +#define SUBBRUTE_TX_TIMEOUT 50 +#define SUBBRUTE_SEND_DELAY 260 + +/** + * Entrypoint for worker + * + * @param context SubBruteWorker* + * @return 0 if ok + */ +int32_t subbrute_worker_thread(void* context) { + furi_assert(context); + SubBruteWorker* instance = (SubBruteWorker*)context; + + if(!instance->worker_running) { + FURI_LOG_W(TAG, "Worker is not set to running state!"); + return -1; + } +#ifdef FURI_DEBUG + FURI_LOG_I(TAG, "Worker start"); +#endif + + instance->environment = subghz_environment_alloc(); + instance->transmitter = subghz_transmitter_alloc_init( + instance->environment, string_get_cstr(instance->protocol_name)); + + furi_hal_subghz_reset(); + furi_hal_subghz_load_preset(instance->preset); + instance->frequency = furi_hal_subghz_set_frequency_and_path(instance->frequency); + + furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_write(&gpio_cc1101_g0, true); + + // Set ready to transmit value + instance->last_time_tx_data = furi_get_tick() - SUBBRUTE_SEND_DELAY; + + while(instance->worker_running) { + // Transmit + if(!furi_hal_subghz_tx()) { + FURI_LOG_E(TAG, "Cannot transmit!"); + break; + } + furi_delay_ms(SUBBRUTE_TX_TIMEOUT); + } + + furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate); + furi_hal_subghz_sleep(); + + subghz_transmitter_free(instance->transmitter); + instance->transmitter = NULL; + subghz_environment_free(instance->environment); + instance->environment = NULL; + +#ifdef FURI_DEBUG + FURI_LOG_I(TAG, "Worker stop"); +#endif + return 0; +} + +SubBruteWorker* subbrute_worker_alloc() { + SubBruteWorker* instance = malloc(sizeof(SubBruteWorker)); + + instance->thread = furi_thread_alloc(); + furi_thread_set_name(instance->thread, "SubBruteAttackWorker"); + furi_thread_set_stack_size(instance->thread, 2048); + furi_thread_set_context(instance->thread, instance); + furi_thread_set_callback(instance->thread, subbrute_worker_thread); + + //instance->status = SubBruteWorkerStatusIDLE; + instance->worker_running = false; + instance->worker_manual_mode = false; + + instance->flipper_format = flipper_format_string_alloc(); + string_init(instance->protocol_name); + + return instance; +} + +void subbrute_worker_free(SubBruteWorker* instance) { + furi_assert(instance); + furi_assert(!instance->worker_running); + + if(instance->transmitter != NULL) { + subghz_transmitter_free(instance->transmitter); + instance->transmitter = NULL; + } + + if(instance->environment != NULL) { + subghz_environment_free(instance->environment); + instance->environment = NULL; + } + + furi_thread_free(instance->thread); + flipper_format_free(instance->flipper_format); + + string_clear(instance->protocol_name); + + free(instance); +} + +bool subbrute_worker_start( + SubBruteWorker* instance, + uint32_t frequency, + FuriHalSubGhzPreset preset, + const char* protocol_name) { + furi_assert(instance); + + if(instance->worker_manual_mode) { + return false; + } + + instance->frequency = frequency; + instance->preset = preset; + + string_clear(instance->protocol_name); + string_init_printf(instance->protocol_name, "%s", protocol_name); + + bool res = false; + + furi_hal_subghz_reset(); + furi_hal_subghz_idle(); + furi_hal_subghz_load_preset(instance->preset); + + furi_hal_subghz_set_frequency_and_path(instance->frequency); + furi_hal_subghz_flush_rx(); + + if(furi_hal_subghz_is_tx_allowed(frequency)) { + instance->frequency = frequency; + res = true; + } + instance->worker_running = res; + +#ifdef FURI_DEBUG + FURI_LOG_I(TAG, "Frequency: %d", frequency); +#endif + instance->preset = preset; + + furi_thread_start(instance->thread); + + return res; +} + +void subbrute_worker_stop(SubBruteWorker* instance) { + furi_assert(instance); + + instance->worker_running = false; + + furi_thread_join(instance->thread); + + furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate); + furi_hal_subghz_sleep(); +} + +bool subbrute_worker_is_running(SubBruteWorker* instance) { + furi_assert(instance); + + return instance->worker_running; +} + +bool subbrute_worker_can_transmit(SubBruteWorker* instance) { + furi_assert(instance); + + return (furi_get_tick() - instance->last_time_tx_data) > SUBBRUTE_SEND_DELAY; +} + +bool subbrute_worker_transmit(SubBruteWorker* instance, const char* payload) { + furi_assert(instance); + furi_assert(instance->worker_running); + + if(!subbrute_worker_can_transmit(instance)) { + FURI_LOG_E(TAG, "Too early to transmit"); + + return false; + } + instance->last_time_tx_data = furi_get_tick(); + +#ifdef FURI_DEBUG + //FURI_LOG_D(TAG, "payload: %s", payload); +#endif + + Stream* stream = flipper_format_get_raw_stream(instance->flipper_format); + stream_clean(stream); + stream_write_cstring(stream, payload); + subghz_transmitter_deserialize(instance->transmitter, instance->flipper_format); + + return true; +} + +bool subbrute_worker_init_manual_transmit( + SubBruteWorker* instance, + uint32_t frequency, + FuriHalSubGhzPreset preset, + const char* protocol_name) { +#ifdef FURI_DEBUG + FURI_LOG_D( + TAG, + "subbrute_worker_init_manual_transmit. frequency: %d, protocol: %s", + frequency, + protocol_name); +#endif + if(instance->worker_manual_mode || !subbrute_worker_can_transmit(instance)) { +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "cannot transmit"); +#endif + return false; + } + if(instance->worker_running) { +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "subbrute_worker_stop"); +#endif + subbrute_worker_stop(instance); + } + + // Not transmit at this period + instance->worker_manual_mode = true; + + if(instance->is_manual_init) { + FURI_LOG_E(TAG, "Trying to setup without normally shutdown prev transmit session!"); + subbrute_worker_manual_transmit_stop(instance); + } + + instance->preset = preset; + instance->frequency = frequency; + + string_clear(instance->protocol_name); + string_init_printf(instance->protocol_name, "%s", protocol_name); + + furi_hal_subghz_reset(); + furi_hal_subghz_idle(); + furi_hal_subghz_load_preset(instance->preset); + + furi_hal_subghz_set_frequency_and_path(instance->frequency); + furi_hal_subghz_flush_rx(); + + if(!furi_hal_subghz_is_tx_allowed(frequency)) { + FURI_LOG_E(TAG, "Frequency: %d invalid!", frequency); + + instance->frequency = frequency; + instance->worker_manual_mode = false; + return false; + } + +#ifdef FURI_DEBUG + FURI_LOG_I(TAG, "Frequency: %d", frequency); +#endif + + instance->environment = subghz_environment_alloc(); + instance->transmitter = subghz_transmitter_alloc_init( + instance->environment, string_get_cstr(instance->protocol_name)); + + furi_hal_subghz_reset(); + furi_hal_subghz_load_preset(instance->preset); + instance->frequency = furi_hal_subghz_set_frequency_and_path(frequency); + + furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate); + furi_hal_subghz_sleep(); + subghz_transmitter_free(instance->transmitter); + instance->transmitter = NULL; + + instance->worker_manual_mode = false; + instance->is_manual_init = true; + + return true; +} + +void subbrute_worker_manual_transmit_stop(SubBruteWorker* instance) { +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "subbrute_worker_manual_transmit_stop"); +#endif + if(!instance->is_manual_init) { + return; + } + + furi_hal_subghz_idle(); + furi_hal_subghz_sleep(); + + if(instance->transmitter != NULL) { + subghz_transmitter_free(instance->transmitter); + instance->transmitter = NULL; + } + subghz_environment_free(instance->environment); + instance->environment = NULL; + + instance->is_manual_init = false; +} + +bool subbrute_worker_manual_transmit(SubBruteWorker* instance, const char* payload) { + furi_assert(instance); + + if(instance->worker_manual_mode || !subbrute_worker_can_transmit(instance)) { +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "cannot transmit"); +#endif + return false; + } + if(instance->worker_running) { +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "subbrute_worker_stop"); +#endif + subbrute_worker_stop(instance); + } + if(!instance->is_manual_init) { + FURI_LOG_E(TAG, "Manually transmit doesn't set!"); + return false; + } + + instance->last_time_tx_data = furi_get_tick(); + instance->worker_manual_mode = true; + + Stream* stream = flipper_format_get_raw_stream(instance->flipper_format); + stream_clean(stream); + stream_write_cstring(stream, payload); + + instance->transmitter = subghz_transmitter_alloc_init( + instance->environment, string_get_cstr(instance->protocol_name)); + subghz_transmitter_deserialize(instance->transmitter, instance->flipper_format); + furi_hal_subghz_reset(); + furi_hal_subghz_load_preset(instance->preset); + instance->frequency = furi_hal_subghz_set_frequency_and_path(instance->frequency); + + furi_hal_subghz_start_async_tx(subghz_transmitter_yield, instance->transmitter); + + while(!furi_hal_subghz_is_async_tx_complete()) { + furi_delay_ms(SUBBRUTE_TX_TIMEOUT); + } + furi_hal_subghz_stop_async_tx(); + + furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate); + furi_hal_subghz_sleep(); + subghz_transmitter_free(instance->transmitter); + instance->transmitter = NULL; + + stream_clean(stream); + + instance->worker_manual_mode = false; + + return true; +} \ No newline at end of file diff --git a/applications/plugins/subbrute/helpers/subbrute_worker.h b/applications/plugins/subbrute/helpers/subbrute_worker.h new file mode 100644 index 000000000..37875fa12 --- /dev/null +++ b/applications/plugins/subbrute/helpers/subbrute_worker.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +typedef struct SubBruteWorker SubBruteWorker; +/** + * Same like SubGhzTxRxWorkerStatus in subghz_tx_rx_worker.h + * using just to not include that file + +typedef enum { + SubBruteWorkerStatusIDLE, + SubBruteWorkerStatusTx, + // SubBruteWorkerStatusRx, +} SubBruteWorkerStatus; + +//typedef void (*SubBruteWorkerCallback)(SubBruteWorkerStatus event, void* context); +*/ +SubBruteWorker* subbrute_worker_alloc(); +void subbrute_worker_free(SubBruteWorker* instance); +bool subbrute_worker_start( + SubBruteWorker* instance, + uint32_t frequency, + FuriHalSubGhzPreset preset, + const char* protocol_name); +void subbrute_worker_stop(SubBruteWorker* instance); +//bool subbrute_worker_write(SubBruteWorker* instance, uint8_t* data, size_t size); +bool subbrute_worker_is_running(SubBruteWorker* instance); +bool subbrute_worker_can_transmit(SubBruteWorker* instance); +bool subbrute_worker_transmit(SubBruteWorker* instance, const char* payload); +bool subbrute_worker_init_manual_transmit(SubBruteWorker* instance, + uint32_t frequency, + FuriHalSubGhzPreset preset, + const char* protocol_name); +bool subbrute_worker_manual_transmit(SubBruteWorker* instance, const char* payload); +void subbrute_worker_manual_transmit_stop(SubBruteWorker* instance); \ No newline at end of file diff --git a/applications/plugins/subbrute/scene/subbrute_scene_entrypoint.c b/applications/plugins/subbrute/scene/subbrute_scene_entrypoint.c deleted file mode 100644 index 6f8497b9f..000000000 --- a/applications/plugins/subbrute/scene/subbrute_scene_entrypoint.c +++ /dev/null @@ -1,197 +0,0 @@ -#include "subbrute_scene_entrypoint.h" -#include "../subbrute_utils.h" - -string_t subbrute_menu_items[10]; - -void subbrute_scene_entrypoint_menu_callback(SubBruteState* context, uint32_t index) { - string_set_str(context->preset, "FuriHalSubGhzPresetOok650Async"); - string_set_str(context->protocol, "RAW"); - context->repeat = 5; - context->te = 0; - context->attack = index; - switch(index) { - case SubBruteAttackLoadFile: - context->current_scene = SceneSelectFile; - break; - case SubBruteAttackCAME12bit307: - case SubBruteAttackCAME12bit433: - case SubBruteAttackCAME12bit868: - if(index == SubBruteAttackCAME12bit307) { - context->frequency = 307800000; - } else if(index == SubBruteAttackCAME12bit433) { - context->frequency = 433920000; - } else if(index == SubBruteAttackCAME12bit868) { - context->frequency = 868350000; - } - context->bit = 12; - string_set_str(context->protocol, "CAME"); - string_set_str(context->preset, "FuriHalSubGhzPresetOok650Async"); - if(!subbrute_is_frequency_allowed(context)) { - return; - } - context->current_scene = SceneAttack; - break; - case SubBruteAttackChamberlain9bit315: - context->frequency = 315000000; - context->bit = 9; - string_set_str(context->protocol, "Cham_Code"); - string_set_str(context->preset, "FuriHalSubGhzPresetOok650Async"); - - if(!subbrute_is_frequency_allowed(context)) { - return; - } - context->current_scene = SceneAttack; - break; - case SubBruteAttackChamberlain9bit390: - context->frequency = 390000000; - context->bit = 9; - string_set_str(context->protocol, "Cham_Code"); - string_set_str(context->preset, "FuriHalSubGhzPresetOok650Async"); - - if(!subbrute_is_frequency_allowed(context)) { - return; - } - context->current_scene = SceneAttack; - break; - case SubBruteAttackLinear10bit300: - context->frequency = 300000000; - context->bit = 10; - string_set_str(context->protocol, "Linear"); - string_set_str(context->preset, "FuriHalSubGhzPresetOok650Async"); - if(!subbrute_is_frequency_allowed(context)) { - return; - } - context->current_scene = SceneAttack; - break; - case SubBruteAttackLinear10bit310: - context->frequency = 310000000; - context->bit = 10; - string_set_str(context->protocol, "Linear"); - string_set_str(context->preset, "FuriHalSubGhzPresetOok650Async"); - if(!subbrute_is_frequency_allowed(context)) { - return; - } - context->current_scene = SceneAttack; - break; - case SubBruteAttackNICE12bit433: - context->frequency = 433920000; - context->bit = 12; - string_set_str(context->protocol, "Nice FLO"); - string_set_str(context->preset, "FuriHalSubGhzPresetOok650Async"); - if(!subbrute_is_frequency_allowed(context)) { - return; - } - context->current_scene = SceneAttack; - break; - case SubBruteAttackNICE12bit868: - context->frequency = 868350000; - context->bit = 12; - string_set_str(context->protocol, "Nice FLO"); - string_set_str(context->preset, "FuriHalSubGhzPresetOok650Async"); - if(!subbrute_is_frequency_allowed(context)) { - return; - } - context->current_scene = SceneAttack; - break; - default: - break; - } -} - -void subbrute_scene_entrypoint_on_enter(SubBruteState* context) { - // Clear the previous payload - context->menu_index = 0; - for(uint32_t i = 0; i < 10; i++) { - string_init(subbrute_menu_items[i]); - } - - string_set(subbrute_menu_items[0], "BF existing dump"); - string_set(subbrute_menu_items[1], "CAME 12bit 307mhz"); - string_set(subbrute_menu_items[2], "CAME 12bit 433mhz"); - string_set(subbrute_menu_items[3], "CAME 12bit 868mhz"); - string_set(subbrute_menu_items[4], "Chamberlain 9bit 315mhz"); - string_set(subbrute_menu_items[5], "Chamberlain 9bit 390mhz"); - string_set(subbrute_menu_items[6], "Linear 10bit 300mhz"); - string_set(subbrute_menu_items[7], "Linear 10bit 310mhz"); - string_set(subbrute_menu_items[8], "NICE 12bit 433mhz"); - string_set(subbrute_menu_items[9], "NICE 12bit 868mhz"); -} - -void subbrute_scene_entrypoint_on_exit(SubBruteState* context) { - UNUSED(context); - for(uint32_t i = 0; i < 10; i++) { - string_clear(subbrute_menu_items[i]); - } -} - -void subbrute_scene_entrypoint_on_tick(SubBruteState* context) { - UNUSED(context); -} - -void subbrute_scene_entrypoint_on_event(SubBruteEvent event, SubBruteState* context) { - if(event.evt_type == EventTypeKey) { - if(event.input_type == InputTypeShort) { - switch(event.key) { - case InputKeyDown: - if(context->menu_index < SubBruteAttackNICE12bit868) { - context->menu_index++; - } - break; - case InputKeyUp: - if(context->menu_index > SubBruteAttackLoadFile) { - context->menu_index--; - } - break; - case InputKeyLeft: - case InputKeyRight: - break; - case InputKeyOk: - subbrute_scene_entrypoint_menu_callback(context, context->menu_index); - break; - case InputKeyBack: - context->is_running = false; - break; - } - } - } -} - -void subbrute_scene_entrypoint_on_draw(Canvas* canvas, SubBruteState* context) { - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - - // Title - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 64, 6, AlignCenter, AlignTop, "Sub-GHz Bruteforcer"); - - if(context->menu_index > SubBruteAttackLoadFile) { - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, - 64, - 24, - AlignCenter, - AlignTop, - string_get_cstr(subbrute_menu_items[context->menu_index - 1])); - } - - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned( - canvas, - 64, - 36, - AlignCenter, - AlignTop, - string_get_cstr(subbrute_menu_items[context->menu_index])); - - if(context->menu_index < SubBruteAttackNICE12bit868) { - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, - 64, - 48, - AlignCenter, - AlignTop, - string_get_cstr(subbrute_menu_items[context->menu_index + 1])); - } -} \ No newline at end of file diff --git a/applications/plugins/subbrute/scene/subbrute_scene_entrypoint.h b/applications/plugins/subbrute/scene/subbrute_scene_entrypoint.h deleted file mode 100644 index af6b3bc49..000000000 --- a/applications/plugins/subbrute/scene/subbrute_scene_entrypoint.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once -#include "../subbrute.h" - -void subbrute_scene_entrypoint_on_enter(SubBruteState* context); -void subbrute_scene_entrypoint_on_exit(SubBruteState* context); -void subbrute_scene_entrypoint_on_tick(SubBruteState* context); -void subbrute_scene_entrypoint_on_event(SubBruteEvent event, SubBruteState* context); -void subbrute_scene_entrypoint_on_draw(Canvas* canvas, SubBruteState* context); \ No newline at end of file diff --git a/applications/plugins/subbrute/scene/subbrute_scene_load_file.c b/applications/plugins/subbrute/scene/subbrute_scene_load_file.c deleted file mode 100644 index e2c99c9cd..000000000 --- a/applications/plugins/subbrute/scene/subbrute_scene_load_file.c +++ /dev/null @@ -1,222 +0,0 @@ -#include "subbrute_scene_load_file.h" -#include "subbrute_scene_entrypoint.h" -#include "../subbrute_utils.h" -#include - -#define SUBGHZ_APP_PATH_FOLDER "/ext/subghz" - -bool subbrute_load(SubBruteState* context, const char* file_path) { - bool result = false; - - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); - - string_t temp_str; - string_init(temp_str); - uint32_t temp_data32; - - do { - if(!flipper_format_file_open_existing(fff_data_file, file_path)) { - FURI_LOG_E(TAG, "Error open file %s", file_path); - string_reset(context->notification_msg); - string_set_str(context->notification_msg, "Error open file"); - break; - } - if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) { - FURI_LOG_E(TAG, "Missing or incorrect header"); - string_reset(context->notification_msg); - string_set_str(context->notification_msg, "Missing or incorrect header"); - break; - } - // Frequency - if(flipper_format_read_uint32(fff_data_file, "Frequency", &temp_data32, 1)) { - FURI_LOG_I(TAG, "Frequency: %d", temp_data32); - context->frequency = temp_data32; - if(!subbrute_is_frequency_allowed(context)) { - break; - } - } else { - FURI_LOG_E(TAG, "Missing or incorrect Frequency"); - string_reset(context->notification_msg); - string_set_str(context->notification_msg, "Missing or incorrect Frequency"); - break; - } - // Preset - if(!flipper_format_read_string(fff_data_file, "Preset", context->preset)) { - FURI_LOG_E(TAG, "Preset FAIL"); - string_reset(context->notification_msg); - string_set_str(context->notification_msg, "Preset FAIL"); - } - // Protocol - if(!flipper_format_read_string(fff_data_file, "Protocol", context->protocol)) { - FURI_LOG_E(TAG, "Missing Protocol"); - string_reset(context->notification_msg); - string_set_str(context->notification_msg, "Missing Protocol"); - break; - } else { - FURI_LOG_I(TAG, "Protocol: %s", string_get_cstr(context->protocol)); - } - - if(strcmp(string_get_cstr(context->protocol), "RAW") == 0) { - FURI_LOG_E(TAG, "RAW unsupported"); - string_reset(context->notification_msg); - string_set_str(context->notification_msg, "RAW unsupported"); - break; - } - - const SubGhzProtocol* registry = - subghz_protocol_registry_get_by_name(string_get_cstr(context->protocol)); - - if(registry && registry->type == SubGhzProtocolTypeDynamic) { - FURI_LOG_D(TAG, "Protocol is dynamic - not supported"); - string_reset(context->notification_msg); - string_set_str(context->notification_msg, "Dynamic protocol unsupported"); - break; - } - - context->decoder_result = subghz_receiver_search_decoder_base_by_name( - context->receiver, string_get_cstr(context->protocol)); - - if(context->decoder_result) { - FURI_LOG_I(TAG, "Found decoder"); - } else { - FURI_LOG_E(TAG, "Protocol not found"); - string_reset(context->notification_msg); - string_set_str(context->notification_msg, "Protocol not found"); - break; - } - - // Bit - if(!flipper_format_read_uint32(fff_data_file, "Bit", &temp_data32, 1)) { - FURI_LOG_E(TAG, "Missing or incorrect Bit"); - string_reset(context->notification_msg); - string_set_str(context->notification_msg, "Missing or incorrect Bit"); - break; - } else { - FURI_LOG_I(TAG, "Bit: %d", temp_data32); - context->bit = temp_data32; - } - - // Key - if(!flipper_format_read_string(fff_data_file, "Key", temp_str)) { - FURI_LOG_E(TAG, "Missing or incorrect Key"); - string_reset(context->notification_msg); - string_set_str(context->notification_msg, "Missing or incorrect Key"); - break; - } else { - FURI_LOG_I(TAG, "Key: %s", string_get_cstr(temp_str)); - string_set(context->key, string_get_cstr(temp_str)); - } - - // TE - if(!flipper_format_read_uint32(fff_data_file, "TE", &temp_data32, 1)) { - FURI_LOG_E(TAG, "Missing or incorrect TE"); - //string_reset(context->notification_msg); - //string_set_str(context->notification_msg, "Missing or incorrect TE"); - //break; - } else { - FURI_LOG_I(TAG, "TE: %d", temp_data32); - context->te = temp_data32; - } - - // Repeat - if(flipper_format_read_uint32(fff_data_file, "Repeat", &temp_data32, 1)) { - FURI_LOG_I(TAG, "Repeat: %d", temp_data32); - context->repeat = temp_data32; - } else { - FURI_LOG_I(TAG, "Repeat: 3 (default)"); - context->repeat = 3; - } - - result = true; - } while(0); - - string_clear(temp_str); - flipper_format_file_close(fff_data_file); - flipper_format_free(fff_data_file); - furi_record_close(RECORD_STORAGE); - if(result) { - FURI_LOG_I(TAG, "Loaded successfully"); - string_reset(context->notification_msg); - string_set_str(context->notification_msg, "File looks ok."); - } - - return result; -} - -void subbrute_scene_load_file_on_enter(SubBruteState* context) { - if(subbrute_load_protocol_from_file(context)) { - context->current_scene = SceneSelectField; - } else { - subbrute_scene_entrypoint_on_enter(context); - context->current_scene = SceneEntryPoint; - } -} - -void subbrute_scene_load_file_on_exit(SubBruteState* context) { - UNUSED(context); -} - -void subbrute_scene_load_file_on_tick(SubBruteState* context) { - UNUSED(context); -} - -void subbrute_scene_load_file_on_event(SubBruteEvent event, SubBruteState* context) { - UNUSED(context); - if(event.evt_type == EventTypeKey) { - if(event.input_type == InputTypeShort) { - switch(event.key) { - case InputKeyDown: - case InputKeyUp: - case InputKeyLeft: - case InputKeyRight: - case InputKeyOk: - break; - case InputKeyBack: - context->current_scene = SceneEntryPoint; - break; - } - } - } -} - -void subbrute_scene_load_file_on_draw(Canvas* canvas, SubBruteState* context) { - UNUSED(context); - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - - // Frame - //canvas_draw_frame(canvas, 0, 0, 128, 64); - - // Title - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 64, 16, AlignCenter, AlignTop, "SubGHz Fuzzer"); - canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignTop, "Error: Press back"); -} - -bool subbrute_load_protocol_from_file(SubBruteState* context) { - string_t file_path; - string_init(file_path); - string_set_str(file_path, SUBGHZ_APP_PATH_FOLDER); - context->environment = subghz_environment_alloc(); - context->receiver = subghz_receiver_alloc_init(context->environment); - subghz_receiver_set_filter(context->receiver, SubGhzProtocolFlag_Decodable); - - // Input events and views are managed by file_select - - DialogsFileBrowserOptions browser_options; - dialog_file_browser_set_basic_options(&browser_options, SUBGHZ_APP_EXTENSION, &I_sub1_10px); - - bool res = dialog_file_browser_show(context->dialogs, file_path, file_path, &browser_options); - - if(res) { - res = subbrute_load(context, string_get_cstr(file_path)); - } - - subghz_environment_free(context->environment); - subghz_receiver_free(context->receiver); - - string_clear(file_path); - - return res; -} \ No newline at end of file diff --git a/applications/plugins/subbrute/scene/subbrute_scene_load_file.h b/applications/plugins/subbrute/scene/subbrute_scene_load_file.h deleted file mode 100644 index 9e186b1c9..000000000 --- a/applications/plugins/subbrute/scene/subbrute_scene_load_file.h +++ /dev/null @@ -1,8 +0,0 @@ -#include "../subbrute.h" - -void subbrute_scene_load_file_on_enter(SubBruteState* context); -void subbrute_scene_load_file_on_exit(SubBruteState* context); -void subbrute_scene_load_file_on_tick(SubBruteState* context); -void subbrute_scene_load_file_on_event(SubBruteEvent event, SubBruteState* context); -void subbrute_scene_load_file_on_draw(Canvas* canvas, SubBruteState* context); -bool subbrute_load_protocol_from_file(SubBruteState* context); \ No newline at end of file diff --git a/applications/plugins/subbrute/scene/subbrute_scene_run_attack.c b/applications/plugins/subbrute/scene/subbrute_scene_run_attack.c deleted file mode 100644 index 4b4507882..000000000 --- a/applications/plugins/subbrute/scene/subbrute_scene_run_attack.c +++ /dev/null @@ -1,384 +0,0 @@ -#include "subbrute_scene_run_attack.h" -#include -#include - -//uint64_t subbrute_counter = 0; -uint64_t max_value; -bool locked = false; -bool toSave = false; -char subbrute_payload_byte[4]; -#define SUBBRUTE_DELAY 1 - -FuriHalSubGhzPreset str_to_preset(string_t preset) { - if(string_cmp_str(preset, "FuriHalSubGhzPresetOok270Async") == 0) { - return FuriHalSubGhzPresetOok270Async; - } - if(string_cmp_str(preset, "FuriHalSubGhzPresetOok650Async") == 0) { - return FuriHalSubGhzPresetOok650Async; - } - if(string_cmp_str(preset, "FuriHalSubGhzPreset2FSKDev238Async") == 0) { - return FuriHalSubGhzPreset2FSKDev238Async; - } - if(string_cmp_str(preset, "FuriHalSubGhzPreset2FSKDev476Async") == 0) { - return FuriHalSubGhzPreset2FSKDev476Async; - } - if(string_cmp_str(preset, "FuriHalSubGhzPresetMSK99_97KbAsync") == 0) { - return FuriHalSubGhzPresetMSK99_97KbAsync; - } - if(string_cmp_str(preset, "FuriHalSubGhzPresetMSK99_97KbAsync") == 0) { - return FuriHalSubGhzPresetMSK99_97KbAsync; - } - return FuriHalSubGhzPresetCustom; -} - -void subbrute_emit(SubBruteState* context) { - //FURI_LOG_D(TAG, string_get_cstr(context->flipper_format_string)); - - context->transmitter = - subghz_transmitter_alloc_init(context->environment, string_get_cstr(context->protocol)); - - subghz_transmitter_deserialize(context->transmitter, context->flipper_format); - furi_hal_subghz_reset(); - furi_hal_subghz_load_preset(str_to_preset(context->preset)); - - context->frequency_cal = furi_hal_subghz_set_frequency_and_path(context->frequency); - - furi_hal_subghz_start_async_tx(subghz_transmitter_yield, context->transmitter); - while(!(furi_hal_subghz_is_async_tx_complete())) { - furi_delay_ms(1); - } - - furi_hal_subghz_stop_async_tx(); - subghz_transmitter_stop(context->transmitter); - furi_hal_subghz_idle(); - subghz_transmitter_free(context->transmitter); -} - -void prepare_emit(SubBruteState* context) { - UNUSED(context); - - furi_hal_subghz_init(); -} - -void clear_emit(SubBruteState* context) { - UNUSED(context); - - //furi_hal_subghz_stop_async_tx(); - //furi_hal_subghz_idle(); - furi_hal_subghz_sleep(); -} -/* -void subbrute_send_raw_packet(SubBruteState* context) { - string_reset(context->candidate); - - // Payload to padded binary string - int* binaryNum = (int*)malloc(sizeof(int) * context->bit); - uint32_t i = 0; - for(i = 0; i < context->bit; i++) { - binaryNum[i] = 0; - } - i = 0; - uint64_t counter = context->payload; - while(counter > 0) { - binaryNum[i] = counter % 2; - counter = counter / 2; - i++; - } - - // printing binary array in reverse order and build raw payload - for(uint32_t loop = 0; loop < context->repeat; loop++) { - for(int j = (int)context->bit - 1; j >= 0; j--) { - if(binaryNum[j] == 1) { - string_cat(context->candidate, context->subbrute_raw_one); - } else { - string_cat(context->candidate, context->subbrute_raw_zero); - } - } - string_cat(context->candidate, context->subbrute_raw_stop); - } - - free(binaryNum); - - string_init_printf( - context->flipper_format_string, - "Filetype: Flipper SubGhz RAW File\n" - "Version: 1\n" - "Frequency: %d\n" - "Preset: %s\n" - "Protocol: RAW\n" - "RAW_Data: %s", - context->frequency, - string_get_cstr(context->preset), - string_get_cstr(context->candidate)); - - subbrute_emit(context); -} -*/ -void subbrute_send_packet_parsed(SubBruteState* context) { - if(context->attack == SubBruteAttackLoadFile) { - snprintf(subbrute_payload_byte, 4, "%02X ", (uint8_t)context->payload); - string_replace_at(context->candidate, context->str_index, 3, subbrute_payload_byte); - } else { - string_t buffer; - string_init(buffer); - string_init_printf(buffer, "%16X", context->payload); - int j = 0; - string_set_str(context->candidate, " "); - for(uint8_t i = 0; i < 16; i++) { - if(string_get_char(buffer, i) != ' ') { - string_set_char(context->candidate, i + j, string_get_char(buffer, i)); - } else { - string_set_char(context->candidate, i + j, '0'); - } - if(i % 2 != 0) { - j++; - } - } - string_clear(buffer); - } - if(strcmp(string_get_cstr(context->protocol), "Princeton") == 0) { - string_init_printf( - context->flipper_format_string, - "Filetype: Flipper SubGhz Key File\n" - "Version: 1\n" - "Frequency: %u\n" - "Preset: %s\n" - "Protocol: %s\n" - "Bit: %d\n" - "Key: %s\n" - "TE: %d\n", - context->frequency, - string_get_cstr(context->preset), - string_get_cstr(context->protocol), - context->bit, - string_get_cstr(context->candidate), - context->te); - } else { - string_init_printf( - context->flipper_format_string, - "Filetype: Flipper SubGhz Key File\n" - "Version: 1\n" - "Frequency: %u\n" - "Preset: %s\n" - "Protocol: %s\n" - "Bit: %d\n" - "Key: %s\n", - context->frequency, - string_get_cstr(context->preset), - string_get_cstr(context->protocol), - context->bit, - string_get_cstr(context->candidate)); - } - - stream_clean(context->stream); - stream_write_string(context->stream, context->flipper_format_string); -} - -void subbrute_send_packet(SubBruteState* context) { - ///if(string_cmp_str(context->protocol, "RAW") == 0) { - // subbrute_send_raw_packet(context); - //} else { - subbrute_send_packet_parsed(context); - subbrute_emit(context); - //} - string_clear(context->flipper_format_string); -} - -void subbrute_scene_run_attack_on_exit(SubBruteState* context) { - if(!toSave) { - clear_emit(context); - furi_thread_free(context->bruthread); - flipper_format_free(context->flipper_format); - subghz_receiver_free(context->receiver); - subghz_environment_free(context->environment); - } -} - -void subbrute_scene_run_attack_on_tick(SubBruteState* context) { - if(!context->is_attacking || locked) { - return; - } - //if(0 != subbrute_counter) { - locked = true; - subbrute_send_packet(context); - - if(context->payload == max_value) { - //context->payload = 0x00; - //subbrute_counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - } else { - context->payload++; - } - locked = false; - //} - /*if(subbrute_counter > SUBBRUTE_DELAY) { - subbrute_counter = 0; - } else { - subbrute_counter++; - }*/ -} -void subbrute_run_timer(SubBruteState* context) { - while(true) { - if(!context->is_attacking) { - context->is_thread_running = false; - break; - } - //furi_delay_ms(10); - subbrute_scene_run_attack_on_tick(context); - } -} - -// entrypoint for worker -static int32_t subbrute_worker_thread(void* ctx) { - SubBruteState* app = ctx; - subbrute_run_timer(app); - return 0; -} - -void start_bruthread(SubBruteState* app) { - if(!app->is_thread_running) { - furi_thread_start(app->bruthread); - app->is_thread_running = true; - } -} - -void subbrute_scene_run_attack_on_enter(SubBruteState* context) { - if(!toSave) { - if(context->attack == SubBruteAttackLoadFile) { - max_value = 0xFF; - } else { - string_t max_value_s; - string_init(max_value_s); - for(uint8_t i = 0; i < context->bit; i++) { - string_cat_printf(max_value_s, "1"); - } - max_value = (uint64_t)strtol(string_get_cstr(max_value_s), NULL, 2); - string_clear(max_value_s); - } - context->str_index = (context->key_index * 3); - string_init_set(context->candidate, context->key); - context->flipper_format = flipper_format_string_alloc(); - context->stream = flipper_format_get_raw_stream(context->flipper_format); - context->environment = subghz_environment_alloc(); - context->receiver = subghz_receiver_alloc_init(context->environment); - subghz_receiver_set_filter(context->receiver, SubGhzProtocolFlag_Decodable); - - prepare_emit(context); - context->bruthread = furi_thread_alloc(); - furi_thread_set_name(context->bruthread, "SubBrute Worker"); - furi_thread_set_stack_size(context->bruthread, 2048); - furi_thread_set_context(context->bruthread, context); - furi_thread_set_callback(context->bruthread, subbrute_worker_thread); - } else { - toSave = false; - } -} - -void subbrute_scene_run_attack_on_event(SubBruteEvent event, SubBruteState* context) { - if(event.evt_type == EventTypeKey) { - if(event.input_type == InputTypeShort) { - switch(event.key) { - case InputKeyDown: - break; - case InputKeyUp: - if(!context->is_attacking) { - subbrute_send_packet_parsed(context); - string_clear(context->flipper_format_string); - toSave = true; - context->current_scene = SceneSaveName; - } - break; - case InputKeyLeft: - if(!context->is_attacking && context->payload > 0x00) { - context->payload--; - subbrute_send_packet(context); - notification_message(context->notify, &sequence_blink_blue_10); - } else if(!context->is_attacking && context->payload == 0x00) { - context->payload = max_value; - subbrute_send_packet(context); - notification_message(context->notify, &sequence_blink_blue_10); - } - break; - case InputKeyRight: - if(!context->is_attacking && context->payload < max_value) { - context->payload++; - subbrute_send_packet(context); - notification_message(context->notify, &sequence_blink_blue_10); - } else if(!context->is_attacking && context->payload == max_value) { - context->payload = 0x00; - subbrute_send_packet(context); - notification_message(context->notify, &sequence_blink_blue_10); - } - break; - case InputKeyOk: - if(!context->is_attacking) { - if(context->payload == max_value) { - context->payload = 0x00; - //subbrute_counter = 0; - } - context->is_attacking = true; - start_bruthread(context); - notification_message(context->notify, &sequence_blink_start_blue); - } else { - context->is_attacking = false; - //context->close_thread_please = true; - if(context->is_thread_running && context->bruthread) { - furi_thread_join(context->bruthread); // wait until thread is finished - } - //context->close_thread_please = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - } - break; - case InputKeyBack: - locked = false; - //context->close_thread_please = true; - context->is_attacking = false; - if(context->is_thread_running && context->bruthread) { - furi_thread_join(context->bruthread); // wait until thread is finished - } - //context->close_thread_please = false; - string_reset(context->notification_msg); - context->payload = 0x00; - //subbrute_counter = 0; - notification_message(context->notify, &sequence_blink_stop); - if(context->attack == SubBruteAttackLoadFile) { - context->current_scene = SceneSelectField; - } else { - context->current_scene = SceneEntryPoint; - } - break; - } - } - } -} - -void subbrute_scene_run_attack_on_draw(Canvas* canvas, SubBruteState* context) { - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - - // Frame - //canvas_draw_frame(canvas, 0, 0, 128, 64); - - // Title - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 64, 8, AlignCenter, AlignTop, "Fire in the hole!"); - - char msg_index[26]; - snprintf( - msg_index, sizeof(msg_index), "< %04d / %04d >", (int)context->payload, (int)max_value); - - canvas_draw_str_aligned(canvas, 64, 24, AlignCenter, AlignTop, msg_index); - - canvas_set_font(canvas, FontSecondary); - char start_stop_msg[20]; - snprintf(start_stop_msg, sizeof(start_stop_msg), " Press (^) to save "); - if(context->is_attacking) { - elements_button_center(canvas, "Stop"); - } else { - elements_button_center(canvas, "Start"); - } - canvas_draw_str_aligned(canvas, 64, 39, AlignCenter, AlignTop, start_stop_msg); -} diff --git a/applications/plugins/subbrute/scene/subbrute_scene_run_attack.h b/applications/plugins/subbrute/scene/subbrute_scene_run_attack.h deleted file mode 100644 index 1eb9637d0..000000000 --- a/applications/plugins/subbrute/scene/subbrute_scene_run_attack.h +++ /dev/null @@ -1,8 +0,0 @@ -#include "../subbrute.h" - -void subbrute_scene_run_attack_on_enter(SubBruteState* context); -void subbrute_scene_run_attack_on_exit(SubBruteState* context); -void subbrute_scene_run_attack_on_tick(SubBruteState* context); -void subbrute_scene_run_attack_on_event(SubBruteEvent event, SubBruteState* context); -void subbrute_scene_run_attack_on_draw(Canvas* canvas, SubBruteState* context); -void send_packet(); \ No newline at end of file diff --git a/applications/plugins/subbrute/scene/subbrute_scene_save_name.c b/applications/plugins/subbrute/scene/subbrute_scene_save_name.c deleted file mode 100644 index e79cf70ed..000000000 --- a/applications/plugins/subbrute/scene/subbrute_scene_save_name.c +++ /dev/null @@ -1,222 +0,0 @@ -#include "../subbrute.h" -#include "m-string.h" -#include "subghz/types.h" -#include -#include -#include - -#define MAX_TEXT_INPUT_LEN 22 - -bool backpressed = false; - -bool subbrute_path_is_file(string_t path) { - return string_end_with_str_p(path, ".sub"); -} -// method modified from subghz_i.c -// https://github.com/flipperdevices/flipperzero-firmware/blob/b0daa601ad5b87427a45f9089c8b403a01f72c2a/applications/subghz/subghz_i.c#L417-L456 -bool subbrute_save_protocol_to_file(Stream* flipper_format_stream, const char* dev_file_name) { - furi_assert(dev_file_name); - - Storage* storage = furi_record_open(RECORD_STORAGE); - - bool saved = false; - string_t file_dir; - string_init(file_dir); - - path_extract_dirname(dev_file_name, file_dir); - do { - if(!storage_simply_mkdir(storage, string_get_cstr(file_dir))) { - FURI_LOG_E(TAG, "(save) Cannot mkdir"); - break; - } - - if(!storage_simply_remove(storage, dev_file_name)) { - FURI_LOG_E(TAG, "(save) Cannot remove"); - break; - } - - stream_seek(flipper_format_stream, 0, StreamOffsetFromStart); - stream_save_to_file(flipper_format_stream, storage, dev_file_name, FSOM_CREATE_ALWAYS); - - saved = true; - FURI_LOG_D(TAG, "(save) OK Save"); - } while(0); - string_clear(file_dir); - furi_record_close(RECORD_STORAGE); - return saved; -} - -void custom_callback(SubBruteState* context) { - if(strcmp(context->file_name_tmp, "")) { - string_cat_printf(context->file_path, "/%s%s", context->file_name_tmp, ".sub"); - if(subbrute_path_is_file(context->file_path_tmp)) { - context->current_scene = SceneAttack; - return; //false; - - } else { - subbrute_save_protocol_to_file(context->stream, string_get_cstr(context->file_path)); - } - - string_set_str(context->file_path, EXT_PATH("subghz")); - string_reset(context->file_path_tmp); - - //scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveSuccess); - context->current_scene = SceneAttack; - return; //true; - } else { - //error no file name - context->current_scene = SceneAttack; - return; //true; - } -} - -void subbrute_scene_save_name_text_input_callback(void* context) { - furi_assert(context); - SubBruteState* statee = context; - custom_callback(statee); -} - -void subbrute_scene_save_name_on_tick(SubBruteState* context) { - if(backpressed) { - void* validator_context = text_input_get_validator_callback_context(context->text_input); - text_input_set_validator(context->text_input, NULL, NULL); - validator_is_file_free(validator_context); - - // Clear view - text_input_reset(context->text_input); - - // TextInput - view_dispatcher_remove_view(context->view_dispatcher, 0); - text_input_free(context->text_input); - - // Popup - view_dispatcher_remove_view(context->view_dispatcher, 1); - popup_free(context->popup); - - context->current_scene = SceneAttack; - } -} - -bool subbrute_back_event_callback(void* context) { - UNUSED(context); - backpressed = true; - return true; -} - -void subbrute_scene_save_name_on_enter(SubBruteState* context) { - // Text Input - context->text_input = text_input_alloc(); - view_dispatcher_add_view( - context->view_dispatcher, 0, text_input_get_view(context->text_input)); - - // Popup - context->popup = popup_alloc(); - view_dispatcher_add_view(context->view_dispatcher, 1, popup_get_view(context->popup)); - - // Setup view - TextInput* text_input = context->text_input; - bool dev_name_empty = false; - - string_t file_name; - string_t dir_name; - string_init(file_name); - string_init(dir_name); - - if(!subbrute_path_is_file(context->file_path)) { - char file_name_buf[64] = {0}; - set_random_name(file_name_buf, 64); - string_set_str(file_name, file_name_buf); - string_set_str(context->file_path, EXT_PATH("subghz")); - //highlighting the entire filename by default - dev_name_empty = true; - } else { - string_set(context->file_path_tmp, context->file_path); - path_extract_dirname(string_get_cstr(context->file_path), dir_name); - path_extract_filename(context->file_path, file_name, true); - string_set(context->file_path, dir_name); - } - - strncpy(context->file_name_tmp, string_get_cstr(file_name), 64); - text_input_set_header_text(text_input, "Name signal"); - text_input_set_result_callback( - text_input, - subbrute_scene_save_name_text_input_callback, - context, - context->file_name_tmp, - MAX_TEXT_INPUT_LEN, // buffer size - dev_name_empty); - - ValidatorIsFile* validator_is_file = - validator_is_file_alloc_init(string_get_cstr(context->file_path), ".sub", ""); - text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); - - string_clear(file_name); - string_clear(dir_name); - - view_dispatcher_set_navigation_event_callback( - context->view_dispatcher, subbrute_back_event_callback); - - view_dispatcher_switch_to_view(context->view_dispatcher, 0); -} - -void subbrute_scene_save_name_on_event(SubBruteEvent event, SubBruteState* context) { - UNUSED(context); - if(event.evt_type == EventTypeKey) { - if(event.input_type == InputTypeShort) { - switch(event.key) { - case InputKeyDown: - case InputKeyUp: - case InputKeyLeft: - case InputKeyRight: - case InputKeyOk: - break; - case InputKeyBack: - //context->current_scene = SceneAttack; - break; - } - } - } -} - -void subbrute_scene_save_name_on_exit(SubBruteState* context) { - if(!backpressed) { - // Clear validator - void* validator_context = text_input_get_validator_callback_context(context->text_input); - text_input_set_validator(context->text_input, NULL, NULL); - validator_is_file_free(validator_context); - - // Clear view - text_input_reset(context->text_input); - - // Setup view - Popup* popup = context->popup; - popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); - popup_set_header(popup, "Saved!", 13, 22, AlignLeft, AlignBottom); - popup_set_timeout(popup, 1500); - popup_set_context(popup, context); - popup_set_callback(popup, NULL); - popup_enable_timeout(popup); - view_dispatcher_switch_to_view(context->view_dispatcher, 1); - - furi_delay_ms(1050); - // Clear view - //Popup* popup = subghz->popup; - popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); - popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); - popup_set_icon(popup, 0, 0, NULL); - popup_set_callback(popup, NULL); - popup_set_context(popup, NULL); - popup_set_timeout(popup, 0); - popup_disable_timeout(popup); - - // TextInput - view_dispatcher_remove_view(context->view_dispatcher, 0); - text_input_free(context->text_input); - - // Popup - view_dispatcher_remove_view(context->view_dispatcher, 1); - popup_free(context->popup); - } else { - backpressed = false; - } -} \ No newline at end of file diff --git a/applications/plugins/subbrute/scene/subbrute_scene_save_name.h b/applications/plugins/subbrute/scene/subbrute_scene_save_name.h deleted file mode 100644 index 18a931ad8..000000000 --- a/applications/plugins/subbrute/scene/subbrute_scene_save_name.h +++ /dev/null @@ -1,6 +0,0 @@ -#include "../subbrute.h" - -void subbrute_scene_save_name_on_enter(SubBruteState* context); -void subbrute_scene_save_name_on_exit(SubBruteState* context); -void subbrute_scene_save_name_on_event(SubBruteEvent event, SubBruteState* context); -void subbrute_scene_save_name_on_tick(SubBruteState* context); \ No newline at end of file diff --git a/applications/plugins/subbrute/scene/subbrute_scene_select_field.c b/applications/plugins/subbrute/scene/subbrute_scene_select_field.c deleted file mode 100644 index c65cd1663..000000000 --- a/applications/plugins/subbrute/scene/subbrute_scene_select_field.c +++ /dev/null @@ -1,121 +0,0 @@ -#include "subbrute_scene_select_field.h" - -void center_displayed_key(SubBruteState* context, uint8_t index) { - const char* key_cstr = string_get_cstr(context->key); - uint8_t str_index = (index * 3); - - char display_menu[17] = { - 'X', 'X', ' ', 'X', 'X', ' ', '<', 'X', 'X', '>', ' ', 'X', 'X', ' ', 'X', 'X', '\0'}; - - if(index > 1) { - display_menu[0] = key_cstr[str_index - 6]; - display_menu[1] = key_cstr[str_index - 5]; - } else { - display_menu[0] = ' '; - display_menu[1] = ' '; - } - - if(index > 0) { - display_menu[3] = key_cstr[str_index - 3]; - display_menu[4] = key_cstr[str_index - 2]; - } else { - display_menu[3] = ' '; - display_menu[4] = ' '; - } - - display_menu[7] = key_cstr[str_index]; - display_menu[8] = key_cstr[str_index + 1]; - - if((str_index + 4) <= (uint8_t)strlen(key_cstr)) { - display_menu[11] = key_cstr[str_index + 3]; - display_menu[12] = key_cstr[str_index + 4]; - } else { - display_menu[11] = ' '; - display_menu[12] = ' '; - } - - if((str_index + 8) <= (uint8_t)strlen(key_cstr)) { - display_menu[14] = key_cstr[str_index + 6]; - display_menu[15] = key_cstr[str_index + 7]; - } else { - display_menu[14] = ' '; - display_menu[15] = ' '; - } - - string_reset(context->notification_msg); - string_set_str(context->notification_msg, display_menu); -} - -void subbrute_scene_select_field_on_enter(SubBruteState* context) { - string_clear(context->notification_msg); -} - -void subbrute_scene_select_field_on_exit(SubBruteState* context) { - UNUSED(context); -} - -void subbrute_scene_select_field_on_tick(SubBruteState* context) { - UNUSED(context); -} - -void subbrute_scene_select_field_on_event(SubBruteEvent event, SubBruteState* context) { - if(event.evt_type == EventTypeKey) { - if(event.input_type == InputTypeShort) { - //const char* key_cstr = string_get_cstr(context->key); - - // don't look, it's ugly but I'm a python dev so... - /*uint8_t nb_bytes = 0; - for(uint8_t i = 0; i < strlen(key_cstr); i++) { - if(' ' == key_cstr[i]) { - nb_bytes++; - } - }*/ - - switch(event.key) { - case InputKeyDown: - case InputKeyUp: - break; - case InputKeyLeft: - if(context->key_index > 0) { - context->key_index--; - } - break; - case InputKeyRight: - if(context->key_index < 7) { - context->key_index++; - } - break; - case InputKeyOk: - string_reset(context->notification_msg); - context->current_scene = SceneAttack; - break; - case InputKeyBack: - string_reset(context->notification_msg); - context->current_scene = SceneSelectFile; - break; - } - //FURI_LOG_D(TAG, "Position: %d/%d", context->key_index, nb_bytes); - } - } -} - -void subbrute_scene_select_field_on_draw(Canvas* canvas, SubBruteState* context) { - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - - // Frame - //canvas_draw_frame(canvas, 0, 0, 128, 64); - - // Title - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 64, 10, AlignCenter, AlignTop, "use < > to select field"); - - char msg_index[18]; - snprintf(msg_index, sizeof(msg_index), "Field index : %d", context->key_index); - canvas_draw_str_aligned(canvas, 64, 26, AlignCenter, AlignTop, msg_index); - - center_displayed_key(context, context->key_index); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, 64, 40, AlignCenter, AlignTop, string_get_cstr(context->notification_msg)); -} diff --git a/applications/plugins/subbrute/scene/subbrute_scene_select_field.h b/applications/plugins/subbrute/scene/subbrute_scene_select_field.h deleted file mode 100644 index e02a07ee0..000000000 --- a/applications/plugins/subbrute/scene/subbrute_scene_select_field.h +++ /dev/null @@ -1,8 +0,0 @@ -#include "../subbrute.h" - -void subbrute_scene_select_field_on_enter(SubBruteState* context); -void subbrute_scene_select_field_on_exit(SubBruteState* context); -void subbrute_scene_select_field_on_tick(SubBruteState* context); -void subbrute_scene_select_field_on_event(SubBruteEvent event, SubBruteState* context); -void subbrute_scene_select_field_on_draw(Canvas* canvas, SubBruteState* context); -void center_displayed_key(SubBruteState* context, uint8_t index); \ No newline at end of file diff --git a/applications/plugins/subbrute/scenes/subbrute_scene.h b/applications/plugins/subbrute/scenes/subbrute_scene.h new file mode 100644 index 000000000..c048985e2 --- /dev/null +++ b/applications/plugins/subbrute/scenes/subbrute_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) SubBruteScene##id, +typedef enum { +#include "subbrute_scene_config.h" + SubBruteSceneNum, +} SubBruteScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers subbrute_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "subbrute_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "subbrute_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "subbrute_scene_config.h" +#undef ADD_SCENE diff --git a/applications/plugins/subbrute/scenes/subbrute_scene_config.h b/applications/plugins/subbrute/scenes/subbrute_scene_config.h new file mode 100644 index 000000000..3541df9ac --- /dev/null +++ b/applications/plugins/subbrute/scenes/subbrute_scene_config.h @@ -0,0 +1,7 @@ +ADD_SCENE(subbrute, load_file, LoadFile) +ADD_SCENE(subbrute, load_select, LoadSelect) +ADD_SCENE(subbrute, run_attack, RunAttack) +ADD_SCENE(subbrute, save_name, SaveName) +ADD_SCENE(subbrute, save_success, SaveSuccess) +ADD_SCENE(subbrute, setup_attack, SetupAttack) +ADD_SCENE(subbrute, start, Start) \ No newline at end of file diff --git a/applications/plugins/subbrute/scenes/subbrute_scene_load_file.c b/applications/plugins/subbrute/scenes/subbrute_scene_load_file.c new file mode 100644 index 000000000..6557b4ba1 --- /dev/null +++ b/applications/plugins/subbrute/scenes/subbrute_scene_load_file.c @@ -0,0 +1,77 @@ +#include "../subbrute_i.h" +#include "../subbrute_custom_event.h" +#include + +#define TAG "SubBruteSceneLoadFile" + +//void subbrute_scene_load_file_callback(SubBruteCustomEvent event, void* context) { +//// furi_assert(context); +//// +//// SubBruteState* instance = (SubBruteState*)context; +//// view_dispatcher_send_custom_event(instance->view_dispatcher, event); +//} + +void subbrute_scene_load_file_on_enter(void* context) { + furi_assert(context); + SubBruteState* instance = (SubBruteState*)context; + + // Input events and views are managed by file_browser + string_t app_folder; + string_t load_path; + string_init(load_path); + string_init_set_str(app_folder, SUBBRUTE_PATH); + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options(&browser_options, SUBBRUTE_FILE_EXT, &I_sub1_10px); + + SubBruteFileResult load_result = SubBruteFileResultUnknown; + bool res = + dialog_file_browser_show(instance->dialogs, load_path, app_folder, &browser_options); +#ifdef FURI_DEBUG + FURI_LOG_D( + TAG, + "load_path: %s, app_folder: %s", + string_get_cstr(load_path), + string_get_cstr(app_folder)); +#endif + if(res) { + load_result = subbrute_device_load_from_file(instance->device, load_path); + if(load_result == SubBruteFileResultOk) { + load_result = subbrute_device_attack_set(instance->device, SubBruteAttackLoadFile); + if(load_result == SubBruteFileResultOk) { + // Ready to run! + instance->device->state = SubBruteDeviceStateReady; + FURI_LOG_I(TAG, "Ready to run"); + res = true; + } + } + } + + if(load_result == SubBruteFileResultOk) { + scene_manager_next_scene(instance->scene_manager, SubBruteSceneLoadSelect); + } else { + FURI_LOG_E(TAG, "Returned error: %d", load_result); + + string_t dialog_msg; + string_init(dialog_msg); + string_cat_printf( + dialog_msg, "Cannot parse\nfile: %s", subbrute_device_error_get_desc(load_result)); + dialog_message_show_storage_error(instance->dialogs, string_get_cstr(dialog_msg)); + string_clear(dialog_msg); + scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, SubBruteSceneStart); + } + + string_clear(app_folder); + string_clear(load_path); +} + +void subbrute_scene_load_file_on_exit(void* context) { + UNUSED(context); +} + +bool subbrute_scene_load_file_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} \ No newline at end of file diff --git a/applications/plugins/subbrute/scenes/subbrute_scene_load_select.c b/applications/plugins/subbrute/scenes/subbrute_scene_load_select.c new file mode 100644 index 000000000..e3774e407 --- /dev/null +++ b/applications/plugins/subbrute/scenes/subbrute_scene_load_select.c @@ -0,0 +1,61 @@ +#include "../subbrute_i.h" +#include "../subbrute_custom_event.h" +#include "../views/subbrute_main_view.h" + +#define TAG "SubBruteSceneStart" + +void subbrute_scene_load_select_callback(SubBruteCustomEvent event, void* context) { + furi_assert(context); + + SubBruteState* instance = (SubBruteState*)context; +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "subbrute_scene_load_select_callback"); +#endif + view_dispatcher_send_custom_event(instance->view_dispatcher, event); +} + +void subbrute_scene_load_select_on_enter(void* context) { + furi_assert(context); +#ifdef FURI_DEBUG + FURI_LOG_I(TAG, "subbrute_scene_load_select_on_enter"); +#endif + SubBruteState* instance = (SubBruteState*)context; + SubBruteMainView* view = instance->view_main; + + instance->current_view = SubBruteViewMain; + subbrute_main_view_set_callback(view, subbrute_scene_load_select_callback, instance); + subbrute_main_view_set_index(view, 7, true, instance->device->file_key); + + view_dispatcher_switch_to_view(instance->view_dispatcher, instance->current_view); +} + +void subbrute_scene_load_select_on_exit(void* context) { + UNUSED(context); +#ifdef FURI_DEBUG + FURI_LOG_I(TAG, "subbrute_scene_load_select_on_exit"); +#endif +} + +bool subbrute_scene_load_select_on_event(void* context, SceneManagerEvent event) { + SubBruteState* instance = (SubBruteState*)context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubBruteCustomEventTypeIndexSelected) { + instance->device->load_index = subbrute_main_view_get_index(instance->view_main); +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "load_index: %d", instance->device->load_index); +#endif + scene_manager_next_scene(instance->scene_manager, SubBruteSceneSetupAttack); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + if(!scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, SubBruteSceneStart)) { + scene_manager_next_scene(instance->scene_manager, SubBruteSceneStart); + } + consumed = true; + } + + return consumed; +} \ No newline at end of file diff --git a/applications/plugins/subbrute/scenes/subbrute_scene_run_attack.c b/applications/plugins/subbrute/scenes/subbrute_scene_run_attack.c new file mode 100644 index 000000000..36a9e86f9 --- /dev/null +++ b/applications/plugins/subbrute/scenes/subbrute_scene_run_attack.c @@ -0,0 +1,83 @@ +#include "../subbrute_i.h" +#include "../subbrute_custom_event.h" +#include "../views/subbrute_attack_view.h" +#include "../helpers/subbrute_worker.h" + +static void subbrute_scene_run_attack_callback(SubBruteCustomEvent event, void* context) { + furi_assert(context); + + SubBruteState* instance = (SubBruteState*)context; + view_dispatcher_send_custom_event(instance->view_dispatcher, event); +} + +void subbrute_scene_run_attack_on_exit(void* context) { + furi_assert(context); + + SubBruteState* instance = (SubBruteState*)context; + notification_message(instance->notifications, &sequence_blink_stop); +} + +void subbrute_scene_run_attack_on_enter(void* context) { + furi_assert(context); + SubBruteState* instance = (SubBruteState*)context; + SubBruteAttackView* view = instance->view_attack; + + instance->current_view = SubBruteViewAttack; + subbrute_attack_view_set_callback(view, subbrute_scene_run_attack_callback, instance); + view_dispatcher_switch_to_view(instance->view_dispatcher, instance->current_view); + + subbrute_attack_view_init_values( + view, + (uint8_t)instance->device->attack, + instance->device->max_value, + instance->device->key_index, + true); + + // Start worker if not started + subbrute_worker_init_manual_transmit( + instance->worker, + instance->device->frequency, + instance->device->preset, + string_get_cstr(instance->device->protocol_name)); +} + +bool subbrute_scene_run_attack_on_event(void* context, SceneManagerEvent event) { + SubBruteState* instance = (SubBruteState*)context; + SubBruteAttackView* view = instance->view_attack; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubBruteCustomEventTypeTransmitNotStarted || + event.event == SubBruteCustomEventTypeTransmitFinished || + event.event == SubBruteCustomEventTypeBackPressed) { + // Stop transmit + scene_manager_next_scene(instance->scene_manager, SubBruteSceneSetupAttack); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeTick) { + if(subbrute_worker_can_transmit(instance->worker)) { + // Blink + notification_message(instance->notifications, &sequence_blink_yellow_100); + + if(subbrute_worker_manual_transmit(instance->worker, instance->device->payload)) { + // Make payload for new iteration or exit + if(instance->device->key_index + 1 > instance->device->max_value) { + // End of list + scene_manager_next_scene(instance->scene_manager, SubBruteSceneSetupAttack); + } else { + instance->device->key_index++; + subbrute_attack_view_set_current_step(view, instance->device->key_index); + subbrute_device_create_packet_parsed( + instance->device, instance->device->key_index); + } + } + + // Stop + notification_message(instance->notifications, &sequence_blink_stop); + } + + consumed = true; + } + + return consumed; +} diff --git a/applications/plugins/subbrute/scenes/subbrute_scene_save_name.c b/applications/plugins/subbrute/scenes/subbrute_scene_save_name.c new file mode 100644 index 000000000..88a497db5 --- /dev/null +++ b/applications/plugins/subbrute/scenes/subbrute_scene_save_name.c @@ -0,0 +1,87 @@ +#include +#include +#include +#include +#include + +#include "../subbrute_i.h" +#include "../subbrute_custom_event.h" + +#define TAG "SubBruteSceneSaveFile" + +void subbrute_scene_save_name_on_enter(void* context) { + SubBruteState* instance = (SubBruteState*)context; + SubBruteDevice* device = instance->device; + + // Setup view + TextInput* text_input = instance->text_input; + set_random_name(device->text_store, sizeof(device->text_store)); + + text_input_set_header_text(text_input, "Name of file"); + text_input_set_result_callback( + text_input, + subbrute_text_input_callback, + instance, + device->text_store, + SUBBRUTE_MAX_LEN_NAME, + true); + + string_set_str(device->load_path, SUBBRUTE_PATH); + + ValidatorIsFile* validator_is_file = + validator_is_file_alloc_init(string_get_cstr(device->load_path), SUBBRUTE_FILE_EXT, ""); + text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); + + view_dispatcher_switch_to_view(instance->view_dispatcher, SubBruteViewTextInput); +} + +bool subbrute_scene_save_name_on_event(void* context, SceneManagerEvent event) { + SubBruteState* instance = (SubBruteState*)context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeBack) { + scene_manager_previous_scene(instance->scene_manager); + return true; + } else if( + event.type == SceneManagerEventTypeCustom && + event.event == SubBruteCustomEventTypeTextEditDone) { +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "Saving: %s", instance->device->text_store); +#endif + bool success = false; + if(strcmp(instance->device->text_store, "")) { + string_cat_printf( + instance->device->load_path, + "/%s%s", + instance->device->text_store, + SUBBRUTE_FILE_EXT); + + if(subbrute_device_save_file( + instance->device, string_get_cstr(instance->device->load_path))) { + scene_manager_next_scene(instance->scene_manager, SubBruteSceneSaveSuccess); + success = true; + consumed = true; + } + } + + if(!success) { + dialog_message_show_storage_error(instance->dialogs, "Error during saving!"); + consumed = scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, SubBruteSceneSetupAttack); + } + } + return consumed; +} + +void subbrute_scene_save_name_on_exit(void* context) { + SubBruteState* instance = (SubBruteState*)context; + + // Clear view + void* validator_context = text_input_get_validator_callback_context(instance->text_input); + text_input_set_validator(instance->text_input, NULL, NULL); + validator_is_file_free(validator_context); + + text_input_reset(instance->text_input); + + string_reset(instance->device->load_path); +} diff --git a/applications/plugins/subbrute/scenes/subbrute_scene_save_success.c b/applications/plugins/subbrute/scenes/subbrute_scene_save_success.c new file mode 100644 index 000000000..7f0e28809 --- /dev/null +++ b/applications/plugins/subbrute/scenes/subbrute_scene_save_success.c @@ -0,0 +1,51 @@ +#include "../subbrute_i.h" +#include "../subbrute_custom_event.h" + +void subbrute_scene_save_success_on_enter(void* context) { + furi_assert(context); + SubBruteState* instance = context; + + // Setup view + Popup* popup = instance->popup; + popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); + popup_set_header(popup, "Saved!", 13, 22, AlignLeft, AlignBottom); + popup_set_timeout(popup, 1500); + popup_set_context(popup, instance); + popup_set_callback(popup, subbrute_popup_closed_callback); + popup_enable_timeout(popup); + view_dispatcher_switch_to_view(instance->view_dispatcher, SubBruteViewPopup); +} + +bool subbrute_scene_save_success_on_event(void* context, SceneManagerEvent event) { + furi_assert(context); + + SubBruteState* instance = (SubBruteState*)context; + //SubBruteMainView* view = instance->view_main; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubBruteCustomEventTypePopupClosed) { + if(!scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, SubBruteSceneSetupAttack)) { + scene_manager_next_scene(instance->scene_manager, SubBruteSceneStart); + } + return true; + } + } + return false; +} + +void subbrute_scene_save_success_on_exit(void* context) { + furi_assert(context); + + SubBruteState* instance = (SubBruteState*)context; + + // Clear view + Popup* popup = instance->popup; + popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); + popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); + popup_set_icon(popup, 0, 0, NULL); + popup_set_callback(popup, NULL); + popup_set_context(popup, NULL); + popup_set_timeout(popup, 0); + popup_disable_timeout(popup); +} diff --git a/applications/plugins/subbrute/scenes/subbrute_scene_setup_attack.c b/applications/plugins/subbrute/scenes/subbrute_scene_setup_attack.c new file mode 100644 index 000000000..22d0a3a5f --- /dev/null +++ b/applications/plugins/subbrute/scenes/subbrute_scene_setup_attack.c @@ -0,0 +1,177 @@ +#include "../subbrute_i.h" +#include "../subbrute_custom_event.h" +#include "../views/subbrute_attack_view.h" + +#define TAG "SubBruteSceneSetupAttack" + +static void subbrute_scene_setup_attack_callback(SubBruteCustomEvent event, void* context) { + furi_assert(context); + + SubBruteState* instance = (SubBruteState*)context; + view_dispatcher_send_custom_event(instance->view_dispatcher, event); +} + +void subbrute_scene_setup_attack_on_enter(void* context) { + furi_assert(context); + SubBruteState* instance = (SubBruteState*)context; + SubBruteAttackView* view = instance->view_attack; + +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "Enter Attack: %d", instance->device->attack); +#endif + + subbrute_attack_view_init_values( + view, + instance->device->attack, + instance->device->max_value, + instance->device->key_index, + false); + + subbrute_worker_init_manual_transmit( + instance->worker, + instance->device->frequency, + instance->device->preset, + string_get_cstr(instance->device->protocol_name)); + + instance->current_view = SubBruteViewAttack; + subbrute_attack_view_set_callback(view, subbrute_scene_setup_attack_callback, instance); + view_dispatcher_switch_to_view(instance->view_dispatcher, instance->current_view); +} + +void subbrute_scene_setup_attack_on_exit(void* context) { + furi_assert(context); +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "subbrute_scene_setup_attack_on_exit"); +#endif + SubBruteState* instance = (SubBruteState*)context; + subbrute_worker_manual_transmit_stop(instance->worker); + notification_message(instance->notifications, &sequence_blink_stop); +} + +bool subbrute_scene_setup_attack_on_event(void* context, SceneManagerEvent event) { + SubBruteState* instance = (SubBruteState*)context; + SubBruteAttackView* view = instance->view_attack; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubBruteCustomEventTypeTransmitStarted) { + subbrute_device_create_packet_parsed(instance->device, instance->device->key_index); + scene_manager_next_scene(instance->scene_manager, SubBruteSceneRunAttack); + } else if(event.event == SubBruteCustomEventTypeSaveFile) { + subbrute_worker_manual_transmit_stop(instance->worker); + + subbrute_attack_view_init_values( + view, + instance->device->attack, + instance->device->max_value, + instance->device->key_index, + false); + scene_manager_next_scene(instance->scene_manager, SubBruteSceneSaveName); + } else if(event.event == SubBruteCustomEventTypeBackPressed) { +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "SubBruteCustomEventTypeBackPressed"); +#endif + instance->device->key_index = 0x00; + //subbrute_attack_view_stop_worker(view); + subbrute_attack_view_init_values( + view, + instance->device->attack, + instance->device->max_value, + instance->device->key_index, + false); + scene_manager_next_scene(instance->scene_manager, SubBruteSceneStart); + } else if(event.event == SubBruteCustomEventTypeChangeStepUp) { + // +1 + if((instance->device->key_index + 1) - instance->device->max_value == 1) { + instance->device->key_index = 0x00; + } else { + uint64_t value = instance->device->key_index + 1; + if(value == instance->device->max_value) { + instance->device->key_index = value; + } else { + instance->device->key_index = value % instance->device->max_value; + } + } + subbrute_attack_view_set_current_step(view, instance->device->key_index); + } else if(event.event == SubBruteCustomEventTypeChangeStepUpMore) { + // +100 + uint64_t value = instance->device->key_index + 100; + if(value == instance->device->max_value) { + instance->device->key_index += value; + } else { + instance->device->key_index = value % instance->device->max_value; + } + subbrute_attack_view_set_current_step(view, instance->device->key_index); + } else if(event.event == SubBruteCustomEventTypeChangeStepDown) { + // -1 + if(instance->device->key_index - 1 == 0) { + instance->device->key_index = 0x00; + } else if(instance->device->key_index == 0) { + instance->device->key_index = instance->device->max_value; + } else { + uint64_t value = ((instance->device->key_index - 1) + instance->device->max_value); + if(value == instance->device->max_value) { + instance->device->key_index = value; + } else { + instance->device->key_index = value % instance->device->max_value; + } + } + subbrute_attack_view_set_current_step(view, instance->device->key_index); + } else if(event.event == SubBruteCustomEventTypeChangeStepDownMore) { + // -100 + uint64_t value = ((instance->device->key_index - 100) + instance->device->max_value); + if(value == instance->device->max_value) { + instance->device->key_index = value; + } else { + instance->device->key_index = value % instance->device->max_value; + } + subbrute_attack_view_set_current_step(view, instance->device->key_index); + } else if(event.event == SubBruteCustomEventTypeTransmitCustom) { + if(subbrute_worker_can_transmit(instance->worker)) { + // Blink + notification_message(instance->notifications, &sequence_blink_green_100); + + // if(!subbrute_attack_view_is_worker_running(view)) { + // subbrute_attack_view_start_worker( + // view, + // instance->device->frequency, + // instance->device->preset, + // string_get_cstr(instance->device->protocol_name)); + // } + subbrute_device_create_packet_parsed( + instance->device, instance->device->key_index); + subbrute_worker_manual_transmit(instance->worker, instance->device->payload); + + // Stop + notification_message(instance->notifications, &sequence_blink_stop); + } + } + + consumed = true; + } + + // if(event.type == SceneManagerEventTypeCustom) { + // switch(event.event) { + // case SubBruteCustomEventTypeMenuSelected: + // with_view_model( + // view, (SubBruteMainViewModel * model) { + // instance->menu_index = model->index; + // return false; + // }); + // scene_manager_next_scene(instance->scene_manager, SubBruteSceneLoadFile); + // consumed = true; + // break; + // case SubBruteCustomEventTypeLoadFile: + // with_view_model( + // view, (SubBruteMainViewModel * model) { + // instance->menu_index = model->index; + // return false; + // }); + // scene_manager_next_scene(instance->scene_manager, SubBruteSceneSetupAttack); + // consumed = true; + // break; + // } + // } + + return consumed; +} \ No newline at end of file diff --git a/applications/plugins/subbrute/scenes/subbrute_scene_start.c b/applications/plugins/subbrute/scenes/subbrute_scene_start.c new file mode 100644 index 000000000..fe3a5d8b4 --- /dev/null +++ b/applications/plugins/subbrute/scenes/subbrute_scene_start.c @@ -0,0 +1,66 @@ +#include "../subbrute_i.h" +#include "../subbrute_custom_event.h" +#include "../views/subbrute_main_view.h" + +#define TAG "SubBruteSceneStart" + +void subbrute_scene_start_callback(SubBruteCustomEvent event, void* context) { + furi_assert(context); + + SubBruteState* instance = (SubBruteState*)context; +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "subbrute_scene_start_callback"); +#endif + view_dispatcher_send_custom_event(instance->view_dispatcher, event); +} + +void subbrute_scene_start_on_enter(void* context) { + furi_assert(context); +#ifdef FURI_DEBUG + FURI_LOG_I(TAG, "subbrute_scene_start_on_enter"); +#endif + SubBruteState* instance = (SubBruteState*)context; + SubBruteMainView* view = instance->view_main; + + instance->current_view = SubBruteViewMain; + subbrute_main_view_set_callback(view, subbrute_scene_start_callback, instance); + subbrute_main_view_set_index(view, instance->device->attack, false, NULL); + + view_dispatcher_switch_to_view(instance->view_dispatcher, instance->current_view); +} + +void subbrute_scene_start_on_exit(void* context) { + UNUSED(context); +#ifdef FURI_DEBUG + FURI_LOG_I(TAG, "subbrute_scene_start_on_exit"); +#endif +} + +bool subbrute_scene_start_on_event(void* context, SceneManagerEvent event) { + SubBruteState* instance = (SubBruteState*)context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "Event: %d", event.event); +#endif + if(event.event == SubBruteCustomEventTypeMenuSelected) { + SubBruteAttacks attack = subbrute_main_view_get_index(instance->view_main); + + subbrute_device_attack_set(instance->device, attack); + scene_manager_next_scene(instance->scene_manager, SubBruteSceneSetupAttack); + + consumed = true; + } else if(event.event == SubBruteCustomEventTypeLoadFile) { + scene_manager_next_scene(instance->scene_manager, SubBruteSceneLoadFile); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + //exit app + scene_manager_stop(instance->scene_manager); + view_dispatcher_stop(instance->view_dispatcher); + consumed = true; + } + + return consumed; +} \ No newline at end of file diff --git a/applications/plugins/subbrute/scenes/subbute_scene.c b/applications/plugins/subbrute/scenes/subbute_scene.c new file mode 100644 index 000000000..6d9ba9799 --- /dev/null +++ b/applications/plugins/subbrute/scenes/subbute_scene.c @@ -0,0 +1,30 @@ +#include "subbrute_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const subbrute_on_enter_handlers[])(void*) = { +#include "subbrute_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const subbrute_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "subbrute_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const subbrute_on_exit_handlers[])(void* context) = { +#include "subbrute_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers subbrute_scene_handlers = { + .on_enter_handlers = subbrute_on_enter_handlers, + .on_event_handlers = subbrute_on_event_handlers, + .on_exit_handlers = subbrute_on_exit_handlers, + .scene_num = SubBruteSceneNum, +}; diff --git a/applications/plugins/subbrute/subbrute.c b/applications/plugins/subbrute/subbrute.c index e8f447c5e..fb6e15882 100644 --- a/applications/plugins/subbrute/subbrute.c +++ b/applications/plugins/subbrute/subbrute.c @@ -1,267 +1,305 @@ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + #include "subbrute.h" +#include "subbrute_i.h" +#include "subbrute_custom_event.h" -#include "scene/subbrute_scene_load_file.h" -#include "scene/subbrute_scene_select_field.h" -#include "scene/subbrute_scene_run_attack.h" -#include "scene/subbrute_scene_entrypoint.h" -#include "scene/subbrute_scene_save_name.h" +#define TAG "SubBruteApp" -static void draw_callback(Canvas* const canvas, void* ctx) { - SubBruteState* subbrute_state = (SubBruteState*)acquire_mutex((ValueMutex*)ctx, 100); +static const char* subbrute_menu_names[] = { + [SubBruteAttackCAME12bit307] = "CAME 12bit 307mhz", + [SubBruteAttackCAME12bit433] = "CAME 12bit 433mhz", + [SubBruteAttackCAME12bit868] = "CAME 12bit 868mhz", + [SubBruteAttackChamberlain9bit315] = "Chamberlain 9bit 315mhz", + [SubBruteAttackChamberlain9bit390] = "Chamberlain 9bit 390mhz", + [SubBruteAttackLinear10bit300] = "Linear 10bit 300mhz", + [SubBruteAttackLinear10bit310] = "Linear 10bit 310mhz", + [SubBruteAttackNICE12bit433] = "NICE 12bit 433mhz", + [SubBruteAttackNICE12bit868] = "NICE 12bit 868mhz", + [SubBruteAttackLoadFile] = "BF existing dump", + [SubBruteAttackTotalCount] = "Total Count", +}; - if(subbrute_state == NULL) { - return; - } +static const char* subbrute_menu_names_small[] = { + [SubBruteAttackCAME12bit307] = "CAME 307mhz", + [SubBruteAttackCAME12bit433] = "CAME 433mhz", + [SubBruteAttackCAME12bit868] = "CAME 868mhz", + [SubBruteAttackChamberlain9bit315] = "Cham 315mhz", + [SubBruteAttackChamberlain9bit390] = "Cham 390mhz", + [SubBruteAttackLinear10bit300] = "Linear 300mhz", + [SubBruteAttackLinear10bit310] = "Linear 310mhz", + [SubBruteAttackNICE12bit433] = "NICE 433mhz", + [SubBruteAttackNICE12bit868] = "NICE 868mhz", + [SubBruteAttackLoadFile] = "Existing", + [SubBruteAttackTotalCount] = "Total Count", +}; - // Draw correct Canvas - switch(subbrute_state->current_scene) { - case NoneScene: - case SceneSelectFile: - subbrute_scene_load_file_on_draw(canvas, subbrute_state); - break; - case SceneSelectField: - subbrute_scene_select_field_on_draw(canvas, subbrute_state); - break; - case SceneAttack: - subbrute_scene_run_attack_on_draw(canvas, subbrute_state); - break; - case SceneEntryPoint: - subbrute_scene_entrypoint_on_draw(canvas, subbrute_state); - break; - case SceneSaveName: - break; - } - - release_mutex((ValueMutex*)ctx, subbrute_state); +static bool subbrute_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + SubBruteState* instance = context; + return scene_manager_handle_custom_event(instance->scene_manager, event); } -void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { - furi_assert(event_queue); - - SubBruteEvent event = { - .evt_type = EventTypeKey, .key = input_event->key, .input_type = input_event->type}; - furi_message_queue_put(event_queue, &event, 100); +static bool subbrute_back_event_callback(void* context) { + furi_assert(context); + SubBruteState* instance = context; + return scene_manager_handle_back_event(instance->scene_manager); } -static void timer_callback(FuriMessageQueue* event_queue) { - furi_assert(event_queue); - SubBruteEvent event = { - .evt_type = EventTypeTick, .key = InputKeyUp, .input_type = InputTypeRelease}; - furi_message_queue_put(event_queue, &event, 100); +static void subbrute_tick_event_callback(void* context) { + furi_assert(context); + SubBruteState* instance = context; + scene_manager_handle_tick_event(instance->scene_manager); } SubBruteState* subbrute_alloc() { - SubBruteState* subbrute = malloc(sizeof(SubBruteState)); + SubBruteState* instance = malloc(sizeof(SubBruteState)); - string_init(subbrute->protocol); - string_init(subbrute->preset); - string_init(subbrute->file_path); - string_init(subbrute->file_path_tmp); - string_init_set(subbrute->notification_msg, ""); - string_init(subbrute->candidate); - string_init(subbrute->flipper_format_string); + instance->scene_manager = scene_manager_alloc(&subbrute_scene_handlers, instance); + instance->view_dispatcher = view_dispatcher_alloc(); - subbrute->previous_scene = NoneScene; - subbrute->current_scene = SceneSelectFile; - subbrute->is_running = true; - subbrute->is_attacking = false; - subbrute->key_index = 7; - subbrute->notify = furi_record_open(RECORD_NOTIFICATION); + instance->gui = furi_record_open(RECORD_GUI); - subbrute->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(instance->view_dispatcher); + view_dispatcher_set_event_callback_context(instance->view_dispatcher, instance); + view_dispatcher_set_custom_event_callback( + instance->view_dispatcher, subbrute_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + instance->view_dispatcher, subbrute_back_event_callback); + view_dispatcher_set_tick_event_callback( + instance->view_dispatcher, subbrute_tick_event_callback, 100); //Dialog - subbrute->dialogs = furi_record_open(RECORD_DIALOGS); + instance->dialogs = furi_record_open(RECORD_DIALOGS); - subbrute->preset_def = malloc(sizeof(SubGhzPresetDefinition)); + // Notifications + instance->notifications = furi_record_open(RECORD_NOTIFICATION); - //subbrute->flipper_format = flipper_format_string_alloc(); - //subbrute->environment = subghz_environment_alloc(); + // Devices + instance->device = subbrute_device_alloc(); - return subbrute; + // Worker + instance->worker = subbrute_worker_alloc(); + + // TextInput + instance->text_input = text_input_alloc(); + view_dispatcher_add_view( + instance->view_dispatcher, + SubBruteViewTextInput, + text_input_get_view(instance->text_input)); + + // Custom Widget + instance->widget = widget_alloc(); + view_dispatcher_add_view( + instance->view_dispatcher, SubBruteViewWidget, widget_get_view(instance->widget)); + + // Popup + instance->popup = popup_alloc(); + view_dispatcher_add_view( + instance->view_dispatcher, SubBruteViewPopup, popup_get_view(instance->popup)); + + // ViewStack + instance->view_stack = view_stack_alloc(); + view_dispatcher_add_view( + instance->view_dispatcher, SubBruteViewStack, view_stack_get_view(instance->view_stack)); + + // SubBruteMainView + instance->view_main = subbrute_main_view_alloc(); + view_dispatcher_add_view( + instance->view_dispatcher, + SubBruteViewMain, + subbrute_main_view_get_view(instance->view_main)); + + // SubBruteAttackView + instance->view_attack = subbrute_attack_view_alloc(); + view_dispatcher_add_view( + instance->view_dispatcher, + SubBruteViewAttack, + subbrute_attack_view_get_view(instance->view_attack)); + + // Loading + instance->loading = loading_alloc(); + //instance->flipper_format = flipper_format_string_alloc(); + //instance->environment = subghz_environment_alloc(); + + return instance; } -void subbrute_free(SubBruteState* subbrute) { - //Dialog - furi_record_close(RECORD_DIALOGS); +void subbrute_free(SubBruteState* instance) { + furi_assert(instance); - notification_message(subbrute->notify, &sequence_blink_stop); + // SubBruteDevice +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "free SubBruteDevice"); +#endif + subbrute_device_free(instance->device); + // SubBruteWorker +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "free SubBruteDevice"); +#endif + subbrute_worker_stop(instance->worker); + subbrute_worker_free(instance->worker); + + // Notifications +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "free Notifications"); +#endif + notification_message(instance->notifications, &sequence_blink_stop); furi_record_close(RECORD_NOTIFICATION); + instance->notifications = NULL; - view_dispatcher_free(subbrute->view_dispatcher); + // Loading +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "free loading"); +#endif + loading_free(instance->loading); - string_clear(subbrute->preset); - string_clear(subbrute->candidate); + // View Main +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "free SubBruteViewMain"); +#endif + view_dispatcher_remove_view(instance->view_dispatcher, SubBruteViewMain); + subbrute_main_view_free(instance->view_main); - // Path strings - string_clear(subbrute->file_path); - string_clear(subbrute->file_path_tmp); - string_clear(subbrute->notification_msg); - string_clear(subbrute->candidate); - string_clear(subbrute->flipper_format_string); + // View Attack +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "free SubBruteViewAttack"); +#endif + view_dispatcher_remove_view(instance->view_dispatcher, SubBruteViewAttack); + subbrute_attack_view_free(instance->view_attack); - //flipper_format_free(subbrute->flipper_format); - //subghz_environment_free(subbrute->environment); - //subghz_receiver_free(subbrute->receiver); + // TextInput +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "free SubBruteViewTextInput"); +#endif + view_dispatcher_remove_view(instance->view_dispatcher, SubBruteViewTextInput); + text_input_free(instance->text_input); - free(subbrute->preset_def); + // Custom Widget +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "free SubBruteViewWidget"); +#endif + view_dispatcher_remove_view(instance->view_dispatcher, SubBruteViewWidget); + widget_free(instance->widget); + + // Popup +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "free SubBruteViewPopup"); +#endif + view_dispatcher_remove_view(instance->view_dispatcher, SubBruteViewPopup); + popup_free(instance->popup); + + // ViewStack +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "free SubBruteViewStack"); +#endif + view_dispatcher_remove_view(instance->view_dispatcher, SubBruteViewStack); + view_stack_free(instance->view_stack); + + //Dialog +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "free RECORD_DIALOGS"); +#endif + furi_record_close(RECORD_DIALOGS); + instance->dialogs = NULL; + + // Scene manager +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "free scene_manager"); +#endif + scene_manager_free(instance->scene_manager); + + // View Dispatcher +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "free view_dispatcher"); +#endif + view_dispatcher_free(instance->view_dispatcher); + + // GUI +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "free RECORD_GUI"); +#endif + furi_record_close(RECORD_GUI); + instance->gui = NULL; // The rest - free(subbrute); +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "free instance"); +#endif + free(instance); +} + +void subbrute_show_loading_popup(void* context, bool show) { + TaskHandle_t timer_task = xTaskGetHandle(configTIMER_SERVICE_TASK_NAME); + SubBruteState* instance = context; + ViewStack* view_stack = instance->view_stack; + Loading* loading = instance->loading; + + if(show) { + // Raise timer priority so that animations can play + vTaskPrioritySet(timer_task, configMAX_PRIORITIES - 1); + view_stack_add_view(view_stack, loading_get_view(loading)); + } else { + view_stack_remove_view(view_stack, loading_get_view(loading)); + // Restore default timer priority + vTaskPrioritySet(timer_task, configTIMER_TASK_PRIORITY); + } +} + +void subbrute_text_input_callback(void* context) { + furi_assert(context); + SubBruteState* instance = context; + view_dispatcher_send_custom_event( + instance->view_dispatcher, SubBruteCustomEventTypeTextEditDone); +} + +void subbrute_popup_closed_callback(void* context) { + furi_assert(context); + SubBruteState* instance = context; + view_dispatcher_send_custom_event( + instance->view_dispatcher, SubBruteCustomEventTypePopupClosed); +} + +const char* subbrute_get_menu_name(SubBruteAttacks index) { + furi_assert(index < SubBruteAttackTotalCount); + + return subbrute_menu_names[index]; +} + +const char* subbrute_get_small_menu_name(SubBruteAttacks index) { + furi_assert(index < SubBruteAttackTotalCount); + + return subbrute_menu_names_small[index]; } // ENTRYPOINT -int32_t subbrute_start(void* p) { +int32_t subbrute_app(void* p) { UNUSED(p); - // Input - FURI_LOG_I(TAG, "Initializing input"); - FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(SubBruteEvent)); - SubBruteState* subbrute_state = subbrute_alloc(); - ValueMutex subbrute_state_mutex; - // Mutex - FURI_LOG_I(TAG, "Initializing flipfrid mutex"); - if(!init_mutex(&subbrute_state_mutex, subbrute_state, sizeof(SubBruteState))) { - FURI_LOG_E(TAG, "cannot create mutex\r\n"); - furi_message_queue_free(event_queue); - subbrute_free(subbrute_state); - return 255; - } + SubBruteState* instance = subbrute_alloc(); + view_dispatcher_attach_to_gui( + instance->view_dispatcher, instance->gui, ViewDispatcherTypeFullscreen); + scene_manager_next_scene(instance->scene_manager, SubBruteSceneStart); furi_hal_power_suppress_charge_enter(); - - // Configure view port - FURI_LOG_I(TAG, "Initializing viewport"); - ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, draw_callback, &subbrute_state_mutex); - view_port_input_callback_set(view_port, input_callback, event_queue); - - // Configure timer - FURI_LOG_I(TAG, "Initializing timer"); - FuriTimer* timer = furi_timer_alloc(timer_callback, FuriTimerTypePeriodic, event_queue); - furi_timer_start(timer, furi_kernel_get_tick_frequency() / 10); // 10 times per second - - // Register view port in GUI - FURI_LOG_I(TAG, "Initializing gui"); - subbrute_state->gui = furi_record_open(RECORD_GUI); - gui_add_view_port(subbrute_state->gui, view_port, GuiLayerFullscreen); - - view_dispatcher_attach_to_gui( - subbrute_state->view_dispatcher, subbrute_state->gui, ViewDispatcherTypeFullscreen); - - subbrute_state->current_scene = SceneEntryPoint; - - // Init values - SubBruteEvent event; - while(subbrute_state->is_running) { - // Get next event - FuriStatus event_status = furi_message_queue_get(event_queue, &event, 25); - if(event_status == FuriStatusOk) { - if(event.evt_type == EventTypeKey) { - //Handle event key - FURI_LOG_D(TAG, "EVENT ###"); - switch(subbrute_state->current_scene) { - case SceneSelectFile: - subbrute_scene_load_file_on_event(event, subbrute_state); - break; - case SceneSelectField: - subbrute_scene_select_field_on_event(event, subbrute_state); - break; - case SceneSaveName: - subbrute_scene_save_name_on_event(event, subbrute_state); - break; - case SceneAttack: - subbrute_scene_run_attack_on_event(event, subbrute_state); - break; - case NoneScene: - case SceneEntryPoint: - subbrute_scene_entrypoint_on_event(event, subbrute_state); - break; - } - - } else if(event.evt_type == EventTypeTick) { - //Handle event tick - if(subbrute_state->current_scene != subbrute_state->previous_scene) { - // Trigger Exit Scene - switch(subbrute_state->previous_scene) { - case SceneSelectFile: - subbrute_scene_load_file_on_exit(subbrute_state); - break; - case SceneSelectField: - subbrute_scene_select_field_on_exit(subbrute_state); - break; - case SceneAttack: - subbrute_scene_run_attack_on_exit(subbrute_state); - break; - case SceneEntryPoint: - subbrute_scene_entrypoint_on_exit(subbrute_state); - break; - case SceneSaveName: - subbrute_scene_save_name_on_exit(subbrute_state); - break; - case NoneScene: - break; - } - - // Trigger Entry Scene - switch(subbrute_state->current_scene) { - case NoneScene: - case SceneSelectFile: - subbrute_scene_load_file_on_enter(subbrute_state); - break; - case SceneSelectField: - subbrute_scene_select_field_on_enter(subbrute_state); - break; - case SceneAttack: - subbrute_scene_run_attack_on_enter(subbrute_state); - break; - case SceneSaveName: - subbrute_scene_save_name_on_enter(subbrute_state); - break; - case SceneEntryPoint: - subbrute_scene_entrypoint_on_enter(subbrute_state); - break; - } - subbrute_state->previous_scene = subbrute_state->current_scene; - } - - // Trigger Tick Scene - switch(subbrute_state->current_scene) { - case NoneScene: - case SceneSelectFile: - subbrute_scene_load_file_on_tick(subbrute_state); - break; - case SceneSelectField: - subbrute_scene_select_field_on_tick(subbrute_state); - break; - case SceneAttack: - //subbrute_scene_run_attack_on_tick(subbrute_state); - break; - case SceneEntryPoint: - subbrute_scene_entrypoint_on_tick(subbrute_state); - break; - case SceneSaveName: - subbrute_scene_save_name_on_tick(subbrute_state); - break; - } - view_port_update(view_port); - } - } - } - - // Cleanup - furi_timer_stop(timer); - furi_timer_free(timer); - + view_dispatcher_run(instance->view_dispatcher); furi_hal_power_suppress_charge_exit(); - - FURI_LOG_I(TAG, "Cleaning up"); - gui_remove_view_port(subbrute_state->gui, view_port); - view_port_free(view_port); - furi_message_queue_free(event_queue); - furi_record_close(RECORD_GUI); - subbrute_free(subbrute_state); + subbrute_free(instance); return 0; } \ No newline at end of file diff --git a/applications/plugins/subbrute/subbrute.h b/applications/plugins/subbrute/subbrute.h index 76dc45d4b..5fedb9158 100644 --- a/applications/plugins/subbrute/subbrute.h +++ b/applications/plugins/subbrute/subbrute.h @@ -1,110 +1,3 @@ #pragma once -#include -#include -#include -#include -#include "m-string.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define TAG "SUBBRUTE" - -typedef enum { - NoneScene, - SceneSelectFile, - SceneSelectField, - SceneAttack, - SceneEntryPoint, - SceneSaveName -} SubBruteScene; - -typedef enum { - SubBruteAttackLoadFile, - SubBruteAttackCAME12bit307, - SubBruteAttackCAME12bit433, - SubBruteAttackCAME12bit868, - SubBruteAttackChamberlain9bit315, - SubBruteAttackChamberlain9bit390, - SubBruteAttackLinear10bit300, - SubBruteAttackLinear10bit310, - SubBruteAttackNICE12bit433, - SubBruteAttackNICE12bit868, -} SubBruteAttacks; - -typedef enum { - EventTypeTick, - EventTypeKey, - EventTypeCustom, -} EventType; - -typedef struct { - EventType evt_type; - InputKey key; - InputType input_type; -} SubBruteEvent; - -// STRUCTS -typedef struct { - // Application stuff - bool is_running; - bool is_attacking; - bool is_thread_running; - bool close_thread_please; - SubBruteScene current_scene; - SubBruteScene previous_scene; - NotificationApp* notify; - Gui* gui; - ViewDispatcher* view_dispatcher; - TextInput* text_input; - Popup* popup; - - // SubGhz Stuff - FuriThread* bruthread; - FlipperFormat* flipper_format; - SubGhzEnvironment* environment; - SubGhzTransmitter* transmitter; - SubGhzReceiver* receiver; - SubGhzProtocolDecoderBase* decoder_result; - SubGhzPresetDefinition* preset_def; - string_t preset; - Stream* stream; - string_t protocol; - uint32_t frequency; - uint32_t frequency_cal; - uint32_t repeat; - uint32_t bit; - string_t key; - uint32_t te; - - // Context Stuff - DialogsApp* dialogs; - char file_name_tmp[64]; - string_t file_path; - string_t file_path_tmp; - string_t notification_msg; - uint8_t key_index; - uint64_t payload; - string_t candidate; - uint8_t str_index; - string_t flipper_format_string; - - SubBruteAttacks attack; - - //Menu stuff - uint8_t menu_index; - - // RAW stuff - string_t subbrute_raw_one; - string_t subbrute_raw_zero; - string_t subbrute_raw_stop; - -} SubBruteState; \ No newline at end of file +typedef struct SubBruteState SubBruteState; \ No newline at end of file diff --git a/applications/plugins/subbrute/subbrute_custom_event.h b/applications/plugins/subbrute/subbrute_custom_event.h new file mode 100644 index 000000000..5ddab7fa1 --- /dev/null +++ b/applications/plugins/subbrute/subbrute_custom_event.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +typedef enum { + // Reserve first 100 events for button types and indexes, starting from 0 + SubBruteCustomEventTypeReserved = 100, + + SubBruteCustomEventTypeBackPressed, + SubBruteCustomEventTypeIndexSelected, + SubBruteCustomEventTypeTransmitStarted, + SubBruteCustomEventTypeTransmitFinished, + SubBruteCustomEventTypeTransmitNotStarted, + SubBruteCustomEventTypeTransmitCustom, + SubBruteCustomEventTypeSaveFile, + SubBruteCustomEventTypeSaveSuccess, + SubBruteCustomEventTypeChangeStepUp, + SubBruteCustomEventTypeChangeStepDown, + SubBruteCustomEventTypeChangeStepUpMore, + SubBruteCustomEventTypeChangeStepDownMore, + + SubBruteCustomEventTypeMenuSelected, + SubBruteCustomEventTypeTextEditDone, + SubBruteCustomEventTypePopupClosed, + + SubBruteCustomEventTypeLoadFile, +} SubBruteCustomEvent; \ No newline at end of file diff --git a/applications/plugins/subbrute/subbrute_device.c b/applications/plugins/subbrute/subbrute_device.c new file mode 100644 index 000000000..28b47562b --- /dev/null +++ b/applications/plugins/subbrute/subbrute_device.c @@ -0,0 +1,643 @@ +#include "subbrute_device.h" +#include "subbrute_i.h" + +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#define TAG "SubBruteDevice" + +/** + * List of protocols + */ +static const char* protocol_came = "CAME"; +static const char* protocol_cham_code = "Cham_Code"; +static const char* protocol_linear = "Linear"; +static const char* protocol_nice_flo = "Nice FLO"; +static const char* protocol_princeton = "Princeton"; +static const char* protocol_raw = "RAW"; + +/** + * Values to not use less memory for packet parse operations + */ +static const char* subbrute_key_file_start = + "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: %u\nPreset: %s\nProtocol: %s\nBit: %d"; +static const char* subbrute_key_file_key = "%s\nKey: %s\n"; +static const char* subbrute_key_file_princeton_end = "%s\nKey: %s\nTE: %d\n"; + +// Why nobody set in as const in all codebase? +static const char* preset_ook270_async = "FuriHalSubGhzPresetOok270Async"; +static const char* preset_ook650_async = "FuriHalSubGhzPresetOok650Async"; +static const char* preset_2fsk_dev238_async = "FuriHalSubGhzPreset2FSKDev238Async"; +static const char* preset_2fsk_dev476_async = "FuriHalSubGhzPreset2FSKDev476Async"; +static const char* preset_msk99_97_kb_async = "FuriHalSubGhzPresetMSK99_97KbAsync"; +static const char* preset_gfs99_97_kb_async = "FuriHalSubGhzPresetGFS99_97KbAsync"; + +SubBruteDevice* subbrute_device_alloc() { + SubBruteDevice* instance = malloc(sizeof(SubBruteDevice)); + + instance->state = SubBruteDeviceStateIDLE; + instance->key_index = 0; + + string_init(instance->load_path); + string_init(instance->preset_name); + string_init(instance->protocol_name); + + instance->decoder_result = NULL; + instance->receiver = NULL; + instance->environment = NULL; + + subbrute_device_attack_set_default_values(instance, SubBruteAttackCAME12bit307); + + return instance; +} + +void subbrute_device_free(SubBruteDevice* instance) { + furi_assert(instance); +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "subbrute_device_free"); +#endif + + // I don't know how to free this + instance->decoder_result = NULL; + + if(instance->receiver != NULL) { +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "subghz_receiver_free"); +#endif + subghz_receiver_free(instance->receiver); + instance->receiver = NULL; + } + + if(instance->environment != NULL) { +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "subghz_environment_free"); +#endif + subghz_environment_free(instance->environment); + instance->environment = NULL; + } + +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "before free"); +#endif + + string_clear(instance->load_path); + string_clear(instance->preset_name); + string_clear(instance->protocol_name); + + free(instance); +} + +bool subbrute_device_save_file(SubBruteDevice* instance, const char* dev_file_name) { + furi_assert(instance); + +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "subbrute_device_save_file: %s", dev_file_name); +#endif + bool result = subbrute_device_create_packet_parsed(instance, instance->key_index); + + if(!result) { + FURI_LOG_E(TAG, "subbrute_device_create_packet_parsed failed!"); + //subbrute_device_notification_message(instance, &sequence_error); + return false; + } + + Storage* storage = furi_record_open(RECORD_STORAGE); + Stream* stream = buffered_file_stream_alloc(storage); + + result = false; + do { + if(!buffered_file_stream_open(stream, dev_file_name, FSAM_READ_WRITE, FSOM_OPEN_ALWAYS)) { + buffered_file_stream_close(stream); + break; + } + stream_write_cstring(stream, instance->payload); + + result = true; + } while(false); + + buffered_file_stream_close(stream); + stream_free(stream); + if(!result) { + FURI_LOG_E(TAG, "stream_write_string failed!"); + //subbrute_device_notification_message(instance, &sequence_error); + } + + furi_record_close(RECORD_STORAGE); + + return result; +} + +const char* subbrute_device_error_get_desc(SubBruteFileResult error_id) { + const char* result; + switch(error_id) { + case(SubBruteFileResultOk): + result = "OK"; + break; + case(SubBruteFileResultErrorOpenFile): + result = "invalid name/path"; + break; + case(SubBruteFileResultMissingOrIncorrectHeader): + result = "Missing or incorrect header"; + break; + case(SubBruteFileResultFrequencyNotAllowed): + result = "Invalid frequency!"; + break; + case(SubBruteFileResultMissingOrIncorrectFrequency): + result = "Missing or incorrect Frequency"; + break; + case(SubBruteFileResultPresetInvalid): + result = "Preset FAIL"; + break; + case(SubBruteFileResultMissingProtocol): + result = "Missing Protocol"; + break; + case(SubBruteFileResultProtocolNotSupported): + result = "RAW unsupported"; + break; + case(SubBruteFileResultDynamicProtocolNotValid): + result = "Dynamic protocol unsupported"; + break; + case(SubBruteFileResultProtocolNotFound): + result = "Protocol not found"; + break; + case(SubBruteFileResultMissingOrIncorrectBit): + result = "Missing or incorrect Bit"; + break; + case(SubBruteFileResultMissingOrIncorrectKey): + result = "Missing or incorrect Key"; + break; + case(SubBruteFileResultMissingOrIncorrectTe): + result = "Missing or incorrect TE"; + break; + case SubBruteFileResultUnknown: + default: + result = "Unknown error"; + break; + } + return result; +} + +bool subbrute_device_create_packet_parsed(SubBruteDevice* instance, uint64_t step) { + furi_assert(instance); + + //char step_payload[32]; + //memset(step_payload, '0', sizeof(step_payload)); + memset(instance->payload, 0, sizeof(instance->payload)); + string_t candidate; + string_init(candidate); + + if(instance->attack == SubBruteAttackLoadFile) { + if(step >= sizeof(instance->file_key)) { + return false; + } + char subbrute_payload_byte[4]; + string_set_str(candidate, instance->file_key); + snprintf(subbrute_payload_byte, 4, "%02X ", (uint8_t)step); + string_replace_at(candidate, instance->load_index * 3, 3, subbrute_payload_byte); + //snprintf(step_payload, sizeof(step_payload), "%02X", (uint8_t)instance->file_key[step]); + } else { + //snprintf(step_payload, sizeof(step_payload), "%16X", step); + //snprintf(step_payload, sizeof(step_payload), "%016llX", step); + string_t buffer; + string_init(buffer); + string_init_printf(buffer, "%16X", step); + int j = 0; + string_set_str(candidate, " "); + for(uint8_t i = 0; i < 16; i++) { + if(string_get_char(buffer, i) != ' ') { + string_set_char(candidate, i + j, string_get_char(buffer, i)); + } else { + string_set_char(candidate, i + j, '0'); + } + if(i % 2 != 0) { + j++; + } + } + string_clear(buffer); + } + +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "candidate: %s, step: %d", string_get_cstr(candidate), step); +#endif + + if(instance->has_tail) { + snprintf( + instance->payload, + sizeof(instance->payload), + subbrute_key_file_princeton_end, + instance->file_template, + string_get_cstr(candidate), + instance->te); + } else { + snprintf( + instance->payload, + sizeof(instance->payload), + subbrute_key_file_key, + instance->file_template, + string_get_cstr(candidate)); + } + +#ifdef FURI_DEBUG + //FURI_LOG_D(TAG, "payload: %s", instance->payload); +#endif + + string_clear(candidate); + + return true; +} + +SubBruteFileResult subbrute_device_attack_set(SubBruteDevice* instance, SubBruteAttacks type) { + furi_assert(instance); +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "subbrute_device_attack_set: %d", type); +#endif + subbrute_device_attack_set_default_values(instance, type); + switch(type) { + case SubBruteAttackLoadFile: + // In this case values must be already set + // file_result = + // subbrute_device_load_from_file(instance, string_get_cstr(instance->load_path)); + // if(file_result != SubBruteFileResultOk) { + // // Failed load file so failed to set attack type + // return file_result; // RETURN + // } + break; + case SubBruteAttackCAME12bit307: + case SubBruteAttackCAME12bit433: + case SubBruteAttackCAME12bit868: + if(type == SubBruteAttackCAME12bit307) { + instance->frequency = 307800000; + } else if(type == SubBruteAttackCAME12bit433) { + instance->frequency = 433920000; + } else /* ALWAYS TRUE if(type == SubBruteAttackCAME12bit868) */ { + instance->frequency = 868350000; + } + instance->bit = 12; + string_set_str(instance->protocol_name, protocol_came); + string_set_str(instance->preset_name, preset_ook650_async); + break; + case SubBruteAttackChamberlain9bit315: + instance->frequency = 315000000; + instance->bit = 9; + string_set_str(instance->protocol_name, protocol_cham_code); + string_set_str(instance->preset_name, preset_ook650_async); + break; + case SubBruteAttackChamberlain9bit390: + instance->frequency = 390000000; + instance->bit = 9; + string_set_str(instance->protocol_name, protocol_cham_code); + string_set_str(instance->preset_name, preset_ook650_async); + break; + case SubBruteAttackLinear10bit300: + instance->frequency = 300000000; + instance->bit = 10; + string_set_str(instance->protocol_name, protocol_linear); + string_set_str(instance->preset_name, preset_ook650_async); + break; + case SubBruteAttackLinear10bit310: + instance->frequency = 310000000; + instance->bit = 10; + string_set_str(instance->protocol_name, protocol_linear); + string_set_str(instance->preset_name, preset_ook650_async); + break; + case SubBruteAttackNICE12bit433: + instance->frequency = 433920000; + instance->bit = 12; + string_set_str(instance->protocol_name, protocol_nice_flo); + string_set_str(instance->preset_name, preset_ook650_async); + break; + case SubBruteAttackNICE12bit868: + instance->frequency = 868350000; + instance->bit = 12; + string_set_str(instance->protocol_name, protocol_nice_flo); + string_set_str(instance->preset_name, preset_ook650_async); + break; + default: + FURI_LOG_E(TAG, "Unknown attack type: %d", type); + return SubBruteFileResultProtocolNotFound; // RETURN + } + + if(!furi_hal_subghz_is_tx_allowed(instance->frequency)) { + FURI_LOG_E(TAG, "Frequency invalid: %d", instance->frequency); + return SubBruteFileResultMissingOrIncorrectFrequency; // RETURN + } + + // For non-file types we didn't set SubGhzProtocolDecoderBase + instance->environment = subghz_environment_alloc(); + instance->receiver = subghz_receiver_alloc_init(instance->environment); + subghz_receiver_set_filter(instance->receiver, SubGhzProtocolFlag_Decodable); + furi_hal_subghz_reset(); + + uint8_t protocol_check_result = SubBruteFileResultProtocolNotFound; + if(type != SubBruteAttackLoadFile) { + instance->decoder_result = subghz_receiver_search_decoder_base_by_name( + instance->receiver, string_get_cstr(instance->protocol_name)); + + if(!instance->decoder_result || + instance->decoder_result->protocol->type == SubGhzProtocolTypeDynamic) { + FURI_LOG_E(TAG, "Can't load SubGhzProtocolDecoderBase in phase non-file decoder set"); + } else { + protocol_check_result = SubBruteFileResultOk; + } + } else { + // And here we need to set preset enum + instance->preset = subbrute_device_convert_preset(string_get_cstr(instance->preset_name)); + protocol_check_result = SubBruteFileResultOk; + } + + subghz_environment_free(instance->environment); + subghz_receiver_free(instance->receiver); + instance->receiver = NULL; + instance->environment = NULL; + + if(protocol_check_result != SubBruteFileResultOk) { + return SubBruteFileResultProtocolNotFound; + } + + instance->has_tail = + (strcmp(string_get_cstr(instance->protocol_name), protocol_princeton) == 0); + + // Calc max value + if(instance->attack == SubBruteAttackLoadFile) { + instance->max_value = 0xFF; + } else { + string_t max_value_s; + string_init(max_value_s); + for(uint8_t i = 0; i < instance->bit; i++) { + string_cat_printf(max_value_s, "1"); + } + instance->max_value = (uint64_t)strtol(string_get_cstr(max_value_s), NULL, 2); + string_clear(max_value_s); + } + + // Now we are ready to set file template for using in the future with snprintf + // for sending attack payload + snprintf( + instance->file_template, + sizeof(instance->file_template), + subbrute_key_file_start, + instance->frequency, + string_get_cstr(instance->preset_name), + string_get_cstr(instance->protocol_name), + instance->bit); +// strncat(instance->file_template, "\n", sizeof(instance->file_template)); +// strncat(instance->file_template, subbrute_key_file_key, sizeof(instance->file_template)); +// if(instance->has_tail) { +// strncat( +// instance->file_template, +// subbrute_key_file_princeton_end, +// sizeof(instance->file_template)); +// } +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "tail: %d, file_template: %s", instance->has_tail, instance->file_template); +#endif + + // Init payload + subbrute_device_create_packet_parsed(instance, instance->key_index); + + return SubBruteFileResultOk; +} + +uint8_t subbrute_device_load_from_file(SubBruteDevice* instance, string_t file_path) { + furi_assert(instance); +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "subbrute_device_load_from_file: %s", string_get_cstr(file_path)); +#endif + SubBruteFileResult result = SubBruteFileResultUnknown; + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); + + string_t temp_str; + string_init(temp_str); + uint32_t temp_data32; + + instance->environment = subghz_environment_alloc(); + instance->receiver = subghz_receiver_alloc_init(instance->environment); + subghz_receiver_set_filter(instance->receiver, SubGhzProtocolFlag_Decodable); + furi_hal_subghz_reset(); + + do { + if(!flipper_format_file_open_existing(fff_data_file, string_get_cstr(file_path))) { + FURI_LOG_E(TAG, "Error open file %s", string_get_cstr(file_path)); + result = SubBruteFileResultErrorOpenFile; + break; + } + if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) { + FURI_LOG_E(TAG, "Missing or incorrect header"); + result = SubBruteFileResultMissingOrIncorrectHeader; + break; + } + + // Frequency + if(flipper_format_read_uint32(fff_data_file, "Frequency", &temp_data32, 1)) { + instance->frequency = temp_data32; + if(!furi_hal_subghz_is_tx_allowed(instance->frequency)) { + result = SubBruteFileResultFrequencyNotAllowed; + break; + } + } else { + FURI_LOG_E(TAG, "Missing or incorrect Frequency"); + result = SubBruteFileResultMissingOrIncorrectFrequency; + break; + } + // Preset + if(!flipper_format_read_string(fff_data_file, "Preset", temp_str)) { + FURI_LOG_E(TAG, "Preset FAIL"); + result = SubBruteFileResultPresetInvalid; + } else { + string_init_set_str(instance->preset_name, string_get_cstr(temp_str)); + } + + // Protocol + if(!flipper_format_read_string(fff_data_file, "Protocol", temp_str)) { + FURI_LOG_E(TAG, "Missing Protocol"); + result = SubBruteFileResultMissingProtocol; + break; + } else { + string_init_set_str(instance->protocol_name, string_get_cstr(temp_str)); +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "Protocol: %s", string_get_cstr(instance->protocol_name)); +#endif + } + + instance->decoder_result = subghz_receiver_search_decoder_base_by_name( + instance->receiver, string_get_cstr(instance->protocol_name)); + + if(!instance->decoder_result || + strcmp(string_get_cstr(instance->protocol_name), "RAW") == 0) { + FURI_LOG_E(TAG, "RAW unsupported"); + result = SubBruteFileResultProtocolNotSupported; + break; + } + + if(instance->decoder_result->protocol->type == SubGhzProtocolTypeDynamic) { + FURI_LOG_E(TAG, "Protocol is dynamic - not supported"); + result = SubBruteFileResultDynamicProtocolNotValid; + break; + } +#ifdef FURI_DEBUG + else { + FURI_LOG_D(TAG, "Decoder: %s", instance->decoder_result->protocol->name); + } +#endif + + // instance->decoder_result = subghz_receiver_search_decoder_base_by_name( + // instance->receiver, string_get_cstr(instance->protocol_name)); + // + // if(!instance->decoder_result) { + // FURI_LOG_E(TAG, "Protocol not found"); + // result = SubBruteFileResultProtocolNotFound; + // break; + // } + + // Bit + if(!flipper_format_read_uint32(fff_data_file, "Bit", &temp_data32, 1)) { + FURI_LOG_E(TAG, "Missing or incorrect Bit"); + result = SubBruteFileResultMissingOrIncorrectBit; + break; + } else { + instance->bit = temp_data32; +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "Bit: %d", instance->bit); +#endif + } + + // Key + if(!flipper_format_read_string(fff_data_file, "Key", temp_str)) { + FURI_LOG_E(TAG, "Missing or incorrect Key"); + result = SubBruteFileResultMissingOrIncorrectKey; + break; + } else { + snprintf( + instance->file_key, sizeof(instance->file_key), "%s", string_get_cstr(temp_str)); +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "Key: %s", instance->file_key); +#endif + } + + // TE + if(!flipper_format_read_uint32(fff_data_file, "TE", &temp_data32, 1)) { + FURI_LOG_E(TAG, "Missing or incorrect TE"); + //result = SubBruteFileResultMissingOrIncorrectTe; + //break; + } else { + instance->te = temp_data32; + instance->has_tail = true; + } + + // Repeat + if(flipper_format_read_uint32(fff_data_file, "Repeat", &temp_data32, 1)) { +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "Repeat: %d", temp_data32); +#endif + instance->repeat = temp_data32; + } else { +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "Repeat: 3 (default)"); +#endif + instance->repeat = 3; + } + + result = SubBruteFileResultOk; + } while(0); + + string_clear(temp_str); + flipper_format_file_close(fff_data_file); + flipper_format_free(fff_data_file); + furi_record_close(RECORD_STORAGE); + + subghz_environment_free(instance->environment); + subghz_receiver_free(instance->receiver); + + instance->decoder_result = NULL; + instance->receiver = NULL; + instance->environment = NULL; + + if(result == SubBruteFileResultOk) { +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "Loaded successfully"); +#endif + } + + return result; +} + +void subbrute_device_attack_set_default_values( + SubBruteDevice* instance, + SubBruteAttacks default_attack) { + furi_assert(instance); +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "subbrute_device_attack_set_default_values"); +#endif + instance->attack = default_attack; + instance->key_index = 0x00; + instance->load_index = 0x00; + memset(instance->file_template, 0, sizeof(instance->file_template)); + memset(instance->current_key, 0, sizeof(instance->current_key)); + memset(instance->text_store, 0, sizeof(instance->text_store)); + memset(instance->payload, 0, sizeof(instance->payload)); + + if(default_attack != SubBruteAttackLoadFile) { + memset(instance->file_key, 0, sizeof(instance->file_key)); + + instance->max_value = (uint64_t)0x00; + + string_clear(instance->protocol_name); + string_clear(instance->preset_name); + + string_clear(instance->load_path); + string_init(instance->load_path); + + string_init_set_str(instance->protocol_name, protocol_raw); + string_init_set_str(instance->preset_name, preset_ook650_async); + instance->preset = FuriHalSubGhzPresetOok650Async; + + instance->repeat = 5; + instance->te = 0; + instance->has_tail = false; + } +#ifdef FURI_DEBUG + FURI_LOG_D( + TAG, "subbrute_device_attack_set_default_values done. has_tail: %d", instance->has_tail); + //furi_delay_ms(250); +#endif +} + +FuriHalSubGhzPreset subbrute_device_convert_preset(const char* preset_name) { + string_t preset; + string_init_set_str(preset, preset_name); + FuriHalSubGhzPreset preset_value; + if(string_cmp_str(preset, preset_ook270_async) == 0) { + preset_value = FuriHalSubGhzPresetOok270Async; + } else if(string_cmp_str(preset, preset_ook650_async) == 0) { + preset_value = FuriHalSubGhzPresetOok650Async; + } else if(string_cmp_str(preset, preset_2fsk_dev238_async) == 0) { + preset_value = FuriHalSubGhzPreset2FSKDev238Async; + } else if(string_cmp_str(preset, preset_2fsk_dev476_async) == 0) { + preset_value = FuriHalSubGhzPreset2FSKDev476Async; + } else if(string_cmp_str(preset, preset_msk99_97_kb_async) == 0) { + preset_value = FuriHalSubGhzPresetMSK99_97KbAsync; + } else if(string_cmp_str(preset, preset_gfs99_97_kb_async) == 0) { + preset_value = FuriHalSubGhzPresetMSK99_97KbAsync; + } else { + preset_value = FuriHalSubGhzPresetCustom; + } + + string_clear(preset); + return preset_value; +} diff --git a/applications/plugins/subbrute/subbrute_device.h b/applications/plugins/subbrute/subbrute_device.h new file mode 100644 index 000000000..625c53d2a --- /dev/null +++ b/applications/plugins/subbrute/subbrute_device.h @@ -0,0 +1,99 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#define SUBBRUTE_TEXT_STORE_SIZE 256 + +#define SUBBRUTE_MAX_LEN_NAME 64 +#define SUBBRUTE_PATH EXT_PATH("subghz") +#define SUBBRUTE_FILE_EXT ".sub" + +#define SUBBRUTE_PAYLOAD_SIZE 16 + +typedef enum { + SubBruteAttackCAME12bit307, + SubBruteAttackCAME12bit433, + SubBruteAttackCAME12bit868, + SubBruteAttackChamberlain9bit315, + SubBruteAttackChamberlain9bit390, + SubBruteAttackLinear10bit300, + SubBruteAttackLinear10bit310, + SubBruteAttackNICE12bit433, + SubBruteAttackNICE12bit868, + SubBruteAttackLoadFile, + SubBruteAttackTotalCount, +} SubBruteAttacks; + +typedef enum { + SubBruteFileResultUnknown, + SubBruteFileResultOk, + SubBruteFileResultErrorOpenFile, + SubBruteFileResultMissingOrIncorrectHeader, + SubBruteFileResultFrequencyNotAllowed, + SubBruteFileResultMissingOrIncorrectFrequency, + SubBruteFileResultPresetInvalid, + SubBruteFileResultMissingProtocol, + SubBruteFileResultProtocolNotSupported, + SubBruteFileResultDynamicProtocolNotValid, + SubBruteFileResultProtocolNotFound, + SubBruteFileResultMissingOrIncorrectBit, + SubBruteFileResultMissingOrIncorrectKey, + SubBruteFileResultMissingOrIncorrectTe, +} SubBruteFileResult; + +typedef enum { + SubBruteDeviceStateIDLE, + SubBruteDeviceStateReady, + SubBruteDeviceStateTx, + SubBruteDeviceStateFinished, +} SubBruteDeviceState; + +typedef struct { + SubBruteDeviceState state; + + // Current step + uint64_t key_index; + string_t load_path; + // Index of group to bruteforce in loaded file + uint8_t load_index; + + SubGhzReceiver* receiver; + SubGhzProtocolDecoderBase* decoder_result; + SubGhzEnvironment* environment; + + // Attack state + SubBruteAttacks attack; + char file_template[SUBBRUTE_TEXT_STORE_SIZE]; + bool has_tail; + char payload[SUBBRUTE_TEXT_STORE_SIZE * 2]; + uint64_t max_value; + + // Loaded info for attack type + FuriHalSubGhzPreset preset; + string_t preset_name; + string_t protocol_name; + uint32_t frequency; + uint32_t repeat; + uint32_t bit; + char current_key[SUBBRUTE_PAYLOAD_SIZE]; + uint32_t te; + + char file_key[SUBBRUTE_MAX_LEN_NAME]; + char text_store[SUBBRUTE_PAYLOAD_SIZE]; +} SubBruteDevice; + +SubBruteDevice* subbrute_device_alloc(); +void subbrute_device_free(SubBruteDevice* instance); +bool subbrute_device_save_file(SubBruteDevice* instance, const char* key_name); +const char* subbrute_device_error_get_desc(SubBruteFileResult error_id); +bool subbrute_device_create_packet_parsed(SubBruteDevice* context, uint64_t step); +SubBruteFileResult subbrute_device_attack_set(SubBruteDevice* context, SubBruteAttacks type); +uint8_t subbrute_device_load_from_file(SubBruteDevice* context, string_t file_path); +FuriHalSubGhzPreset subbrute_device_convert_preset(const char* preset); +void subbrute_device_attack_set_default_values(SubBruteDevice* context, SubBruteAttacks default_attack); \ No newline at end of file diff --git a/applications/plugins/subbrute/subbrute_i.h b/applications/plugins/subbrute/subbrute_i.h new file mode 100644 index 000000000..28a9a73e3 --- /dev/null +++ b/applications/plugins/subbrute/subbrute_i.h @@ -0,0 +1,83 @@ +#pragma once + +#include +#include +#include + +#include "lib/toolbox/path.h" +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include "subbrute_device.h" +#include "helpers/subbrute_worker.h" +#include "subbrute.h" +#include "scenes/subbrute_scene.h" +#include "views/subbrute_attack_view.h" +#include "views/subbrute_main_view.h" + +typedef enum { + SubBruteViewNone, + SubBruteViewMain, + SubBruteViewAttack, + SubBruteViewTextInput, + SubBruteViewDialogEx, + SubBruteViewPopup, + SubBruteViewWidget, + SubBruteViewStack, +} SubBruteView; + +struct SubBruteState { + // GUI elements + NotificationApp* notifications; + Gui* gui; + ViewDispatcher* view_dispatcher; + ViewStack* view_stack; + TextInput* text_input; + Popup* popup; + Widget* widget; + DialogsApp* dialogs; + Loading* loading; + + // Views + SubBruteMainView* view_main; + SubBruteAttackView* view_attack; + SubBruteView current_view; + + // Scene + SceneManager* scene_manager; + + SubBruteDevice* device; + SubBruteWorker* worker; + + //Menu stuff + // TODO: Do we need it? + uint8_t menu_index; +}; + +void subbrute_show_loading_popup(void* context, bool show); +void subbrute_text_input_callback(void* context); +void subbrute_popup_closed_callback(void* context); +const char* subbrute_get_menu_name(uint8_t index); +const char* subbrute_get_small_menu_name(uint8_t index); \ No newline at end of file diff --git a/applications/plugins/subbrute/subbrute_utils.c b/applications/plugins/subbrute/subbrute_utils.c deleted file mode 100644 index 2aafc7175..000000000 --- a/applications/plugins/subbrute/subbrute_utils.c +++ /dev/null @@ -1,13 +0,0 @@ -#include "subbrute_utils.h" - -bool subbrute_is_frequency_allowed(SubBruteState* context) { - // I know you don't like it but laws are laws - // It's opensource so do whatever you want, but remember the risks :) - // (Yes, this comment is the only purpose of this function) - bool r = furi_hal_subghz_is_tx_allowed(context->frequency); - if(!r) { - FURI_LOG_E(TAG, "Frequency %d is not allowed in your region", context->frequency); - notification_message(context->notify, &sequence_single_vibro); - } - return r; -} \ No newline at end of file diff --git a/applications/plugins/subbrute/subbrute_utils.h b/applications/plugins/subbrute/subbrute_utils.h deleted file mode 100644 index 90f7c60ad..000000000 --- a/applications/plugins/subbrute/subbrute_utils.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -#include "subbrute.h" - -bool subbrute_is_frequency_allowed(SubBruteState* context); \ No newline at end of file diff --git a/applications/plugins/subbrute/views/subbrute_attack_view.c b/applications/plugins/subbrute/views/subbrute_attack_view.c new file mode 100644 index 000000000..4f43f786d --- /dev/null +++ b/applications/plugins/subbrute/views/subbrute_attack_view.c @@ -0,0 +1,374 @@ +#include "subbrute_attack_view.h" +#include "../subbrute_i.h" + +#include "assets_icons.h" +#include +#include +#include +#include + +#define TAG "SubBruteAttackView" + +struct SubBruteAttackView { + View* view; + SubBruteAttackViewCallback callback; + void* context; +}; + +typedef struct { + SubBruteAttacks index; + uint64_t max_value; + uint64_t current_step; + bool is_attacking; + IconAnimation* icon; +} SubBruteAttackViewModel; + +void subbrute_attack_view_set_callback( + SubBruteAttackView* instance, + SubBruteAttackViewCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + + instance->callback = callback; + instance->context = context; +} + +bool subbrute_attack_view_input(InputEvent* event, void* context) { + furi_assert(event); + furi_assert(context); +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "InputKey: %d", event->key); +#endif + SubBruteAttackView* instance = context; + + if(event->key == InputKeyBack && event->type == InputTypeShort) { + instance->callback(SubBruteCustomEventTypeBackPressed, instance->context); + with_view_model( + instance->view, (SubBruteAttackViewModel * model) { + model->is_attacking = false; + return true; + }); + return true; + } + + bool is_attacking = false; + + with_view_model( + instance->view, (SubBruteAttackViewModel * model) { + is_attacking = model->is_attacking; + return false; + }); + + // if(!is_attacking) { + // instance->callback(SubBruteCustomEventTypeTransmitNotStarted, instance->context); + // } else { + // instance->callback(SubBruteCustomEventTypeTransmitStarted, instance->context); + // } + + if(!is_attacking) { + if((event->type == InputTypeShort || event->type == InputTypeRepeat) && + event->key == InputKeyOk) { +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "InputKey: %d OK", event->key); +#endif + with_view_model( + instance->view, (SubBruteAttackViewModel * model) { + model->is_attacking = true; + icon_animation_stop(model->icon); + icon_animation_start(model->icon); + return true; + }); + instance->callback(SubBruteCustomEventTypeTransmitStarted, instance->context); + // } else if(event->key == InputKeyBack) { + // if(previous_scene == SubBruteSceneLoadFile) { + // instance->callback(SubBruteCustomEventTypeLoadFile, instance->context); + // } else { + // instance->callback(SubBruteCustomEventTypeBackPressed, instance->context); + // } + } else if(event->key == InputKeyUp) { + instance->callback(SubBruteCustomEventTypeSaveFile, instance->context); + } else if(event->key == InputKeyDown) { + instance->callback(SubBruteCustomEventTypeTransmitCustom, instance->context); + } else if(event->type == InputTypeShort) { + if(event->key == InputKeyLeft) { + instance->callback(SubBruteCustomEventTypeChangeStepDown, instance->context); + } else if(event->key == InputKeyRight) { + instance->callback(SubBruteCustomEventTypeChangeStepUp, instance->context); + } + // with_view_model( + // instance->view, (SubBruteAttackViewModel * model) { + // if(event->key == InputKeyLeft) { + // model->current_step = + // ((model->current_step - 1) + model->max_value) % model->max_value; + // } else if(event->key == InputKeyRight) { + // model->current_step = (model->current_step + 1) % model->max_value; + // } + // return true; + // }); + // instance->callback(SubBruteCustomEventTypeChangeStep, instance->context); + } else if(event->type == InputTypeRepeat) { + if(event->key == InputKeyLeft) { + instance->callback(SubBruteCustomEventTypeChangeStepDownMore, instance->context); + } else if(event->key == InputKeyRight) { + instance->callback(SubBruteCustomEventTypeChangeStepUpMore, instance->context); + } + /*with_view_model( + instance->view, (SubBruteAttackViewModel * model) { + if(event->key == InputKeyLeft) { + model->current_step = + ((model->current_step - 100) + model->max_value) % model->max_value; + } else if(event->key == InputKeyRight) { + model->current_step = (model->current_step + 100) % model->max_value; + } + return true; + }); + instance->callback(SubBruteCustomEventTypeChangeStep, instance->context);*/ + } + } else { + if((event->type == InputTypeShort || event->type == InputTypeRepeat) && + (event->key == InputKeyOk || event->key == InputKeyBack)) { + with_view_model( + instance->view, (SubBruteAttackViewModel * model) { + model->is_attacking = false; + icon_animation_stop(model->icon); + icon_animation_start(model->icon); + return true; + }); + instance->callback(SubBruteCustomEventTypeTransmitNotStarted, instance->context); + } + } + + return true; +} + +SubBruteAttackView* subbrute_attack_view_alloc() { + SubBruteAttackView* instance = malloc(sizeof(SubBruteAttackView)); + + instance->view = view_alloc(); + view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(SubBruteAttackViewModel)); + view_set_context(instance->view, instance); + + with_view_model( + instance->view, (SubBruteAttackViewModel * model) { + model->icon = icon_animation_alloc(&A_Sub1ghz_14); + view_tie_icon_animation(instance->view, model->icon); + return false; + }); + + view_set_draw_callback(instance->view, (ViewDrawCallback)subbrute_attack_view_draw); + view_set_input_callback(instance->view, subbrute_attack_view_input); + view_set_enter_callback(instance->view, subbrute_attack_view_enter); + view_set_exit_callback(instance->view, subbrute_attack_view_exit); + + + return instance; +} + +void subbrute_attack_view_enter(void* context) { + furi_assert(context); + +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "subbrute_attack_view_enter"); +#endif +} + +void subbrute_attack_view_free(SubBruteAttackView* instance) { + furi_assert(instance); + +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "subbrute_attack_view_free"); +#endif + + with_view_model( + instance->view, (SubBruteAttackViewModel * model) { + icon_animation_free(model->icon); + return false; + }); + + view_free(instance->view); + free(instance); +} + +View* subbrute_attack_view_get_view(SubBruteAttackView* instance) { + furi_assert(instance); + return instance->view; +} + +void subbrute_attack_view_set_current_step(SubBruteAttackView* instance, uint64_t current_step) { + furi_assert(instance); +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "Set step: %d", current_step); +#endif + with_view_model( + instance->view, (SubBruteAttackViewModel * model) { + model->current_step = current_step; + return true; + }); +} + +uint64_t subbrute_attack_view_get_current_step(SubBruteAttackView* instance) { + uint64_t current_step; + with_view_model( + instance->view, (SubBruteAttackViewModel * model) { + current_step = model->current_step; + return false; + }); +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "Get step: %d", current_step); +#endif + return current_step; +} + +// We need to call init every time, because not every time we calls enter +// normally, call enter only once +void subbrute_attack_view_init_values( + SubBruteAttackView* instance, + uint8_t index, + uint64_t max_value, + uint64_t current_step, + bool is_attacking) { +#ifdef FURI_DEBUG + FURI_LOG_D( + TAG, "init, index: %d, max_value: %d, current_step: %d", index, max_value, current_step); +#endif + with_view_model( + instance->view, (SubBruteAttackViewModel * model) { + model->max_value = max_value; + model->index = index; + model->current_step = current_step; + model->is_attacking = is_attacking; + if(is_attacking) { + icon_animation_start(model->icon); + } else { + icon_animation_stop(model->icon); + } + return true; + }); +} + +void subbrute_attack_view_exit(void* context) { + furi_assert(context); + SubBruteAttackView* instance = context; +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "subbrute_attack_view_exit"); +#endif + with_view_model( + instance->view, (SubBruteAttackViewModel * model) { + icon_animation_stop(model->icon); + return false; + }); +} + +void elements_button_top_left(Canvas* canvas, const char* str) { + const Icon* icon = &I_ButtonUp_7x4; + + const uint8_t button_height = 12; + const uint8_t vertical_offset = 9; // + const uint8_t horizontal_offset = 3; + const uint8_t string_width = canvas_string_width(canvas, str); + const uint8_t icon_h_offset = 3; + const uint8_t icon_width_with_offset = icon->width + icon_h_offset; + const uint8_t icon_v_offset = icon->height; // + const uint8_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset + 1; + + const uint8_t x = 0; + const uint8_t y = 0; + + canvas_draw_box(canvas, x, y, button_width, button_height); +#ifdef FURI_DEBUG + FURI_LOG_D( + TAG, "lbox, x: %d, y: %d, width: %d, height: %d", x, y, button_width, button_height); +#endif + // canvas_draw_line(canvas, x + button_width + 0, y, x + button_width + 0, y + button_height - 0); // + // canvas_draw_line(canvas, x + button_width + 1, y, x + button_width + 1, y + button_height - 1); + // canvas_draw_line(canvas, x + button_width + 2, y, x + button_width + 2, y + button_height - 2); + + canvas_invert_color(canvas); + canvas_draw_icon(canvas, x + horizontal_offset, y + icon_v_offset, icon); + canvas_draw_str( + canvas, x + horizontal_offset + icon_width_with_offset, y + vertical_offset, str); + canvas_invert_color(canvas); +} + +void elements_button_top_right(Canvas* canvas, const char* str) { + const Icon* icon = &I_ButtonDown_7x4; + + const uint8_t button_height = 12; + const uint8_t vertical_offset = 9; + const uint8_t horizontal_offset = 3; + const uint8_t string_width = canvas_string_width(canvas, str); + const uint8_t icon_h_offset = 3; + const uint8_t icon_width_with_offset = icon->width + icon_h_offset; + const uint8_t icon_v_offset = icon->height; // + vertical_offset; + const uint8_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset + 1; + + const uint8_t x = canvas_width(canvas); + const uint8_t y = 0; + + canvas_draw_box(canvas, x - button_width, y, button_width, button_height); +#ifdef FURI_DEBUG + FURI_LOG_D( + TAG, + "rbox, x: %d, y: %d, width: %d, height: %d", + x - button_width, + y, + button_width, + button_height); +#endif + // canvas_draw_line(canvas, x - button_width - 1, y, x + button_width - 1, y + button_height - 0); + // canvas_draw_line(canvas, x - button_width - 2, y, x + button_width - 2, y + button_height - 1); + // canvas_draw_line(canvas, x - button_width - 3, y, x + button_width - 3, y + button_height - 2); + + canvas_invert_color(canvas); + canvas_draw_str(canvas, x - button_width + horizontal_offset, y + vertical_offset, str); + canvas_draw_icon(canvas, x - horizontal_offset - icon->width, y + icon_v_offset, icon); + canvas_invert_color(canvas); +} + +void subbrute_attack_view_draw(Canvas* canvas, void* context) { + furi_assert(context); + SubBruteAttackViewModel* model = (SubBruteAttackViewModel*)context; + char buffer[26]; + + const char* attack_name = NULL; + attack_name = subbrute_get_menu_name(model->index); + // Title + if(model->is_attacking) { + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, attack_name); + } + // Value + canvas_set_font(canvas, FontBigNumbers); + snprintf(buffer, sizeof(buffer), "%04d/%04d", (int)model->current_step, (int)model->max_value); + canvas_draw_str_aligned(canvas, 64, 17, AlignCenter, AlignTop, buffer); + canvas_set_font(canvas, FontSecondary); + + if(!model->is_attacking) { + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 64, 44, AlignCenter, AlignBottom, attack_name); + + elements_button_left(canvas, "-1"); + elements_button_right(canvas, "+1"); + elements_button_center(canvas, "Start"); + elements_button_top_left(canvas, "Save"); + elements_button_top_right(canvas, "Resend"); + } else { + // canvas_draw_icon_animation + const uint8_t icon_h_offset = 0; + const uint8_t icon_width_with_offset = model->icon->icon->width + icon_h_offset; + const uint8_t icon_v_offset = model->icon->icon->height; // + vertical_offset; + const uint8_t x = canvas_width(canvas); + const uint8_t y = canvas_height(canvas); + canvas_draw_icon_animation( + canvas, x - icon_width_with_offset, y - icon_v_offset, model->icon); + // Progress bar + // Resolution: 128x64 px + float progress_value = (float)model->current_step / model->max_value; + elements_progress_bar(canvas, 8, 37, 110, progress_value > 1 ? 1 : progress_value); + + elements_button_center(canvas, "Stop"); + } +} diff --git a/applications/plugins/subbrute/views/subbrute_attack_view.h b/applications/plugins/subbrute/views/subbrute_attack_view.h new file mode 100644 index 000000000..e72d69d4c --- /dev/null +++ b/applications/plugins/subbrute/views/subbrute_attack_view.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include "assets_icons.h" +#include +#include +#include +#include +#include "../subbrute_custom_event.h" + +typedef void (*SubBruteAttackViewCallback)(SubBruteCustomEvent event, void* context); +typedef struct SubBruteAttackView SubBruteAttackView; + +void subbrute_attack_view_set_callback( + SubBruteAttackView* instance, + SubBruteAttackViewCallback callback, + void* context); +SubBruteAttackView* subbrute_attack_view_alloc(); +void subbrute_attack_view_free(SubBruteAttackView* instance); +View* subbrute_attack_view_get_view(SubBruteAttackView* instance); +void subbrute_attack_view_set_current_step(SubBruteAttackView* instance, uint64_t current_step); +uint64_t subbrute_attack_view_get_current_step(SubBruteAttackView* instance); +void subbrute_attack_view_init_values( + SubBruteAttackView* instance, + uint8_t index, + uint64_t max_value, + uint64_t current_step, + bool is_attacking); \ No newline at end of file diff --git a/applications/plugins/subbrute/views/subbrute_main_view.c b/applications/plugins/subbrute/views/subbrute_main_view.c new file mode 100644 index 000000000..141e3ce27 --- /dev/null +++ b/applications/plugins/subbrute/views/subbrute_main_view.c @@ -0,0 +1,381 @@ +#include "subbrute_main_view.h" +#include "../subbrute_i.h" + +#include +#include +#include "assets_icons.h" +#include + +#define STATUS_BAR_Y_SHIFT 14 +#define TAG "SubBruteMainView" + +struct SubBruteMainView { + View* view; + SubBruteMainViewCallback callback; + void* context; +}; + +typedef struct { + uint8_t index; + uint8_t window_position; + bool is_select_byte; + const char* key_field; +} SubBruteMainViewModel; + +void subbrute_main_view_set_callback( + SubBruteMainView* instance, + SubBruteMainViewCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + + instance->callback = callback; + instance->context = context; +} + +void center_displayed_key(string_t result, const char* key_cstr, uint8_t index) { + uint8_t str_index = (index * 3); + + char display_menu[] = { + 'X', 'X', ' ', 'X', 'X', ' ', '<', 'X', 'X', '>', ' ', 'X', 'X', ' ', 'X', 'X', '\0'}; + + if(key_cstr != NULL) { + if(index > 1) { + display_menu[0] = key_cstr[str_index - 6]; + display_menu[1] = key_cstr[str_index - 5]; + } else { + display_menu[0] = ' '; + display_menu[1] = ' '; + } + + if(index > 0) { + display_menu[3] = key_cstr[str_index - 3]; + display_menu[4] = key_cstr[str_index - 2]; + } else { + display_menu[3] = ' '; + display_menu[4] = ' '; + } + + display_menu[7] = key_cstr[str_index]; + display_menu[8] = key_cstr[str_index + 1]; + + if((str_index + 4) <= (uint8_t)strlen(key_cstr)) { + display_menu[11] = key_cstr[str_index + 3]; + display_menu[12] = key_cstr[str_index + 4]; + } else { + display_menu[11] = ' '; + display_menu[12] = ' '; + } + + if((str_index + 8) <= (uint8_t)strlen(key_cstr)) { + display_menu[14] = key_cstr[str_index + 6]; + display_menu[15] = key_cstr[str_index + 7]; + } else { + display_menu[14] = ' '; + display_menu[15] = ' '; + } + } + string_init_set_str(result, display_menu); +} + +void subbrute_main_view_draw(Canvas* canvas, SubBruteMainViewModel* model) { + SubBruteMainViewModel* m = model; + + // Title + canvas_set_font(canvas, FontPrimary); + canvas_draw_box(canvas, 0, 0, canvas_width(canvas), STATUS_BAR_Y_SHIFT); + canvas_invert_color(canvas); + canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, "Sub-GHz Bruteforcer"); + canvas_invert_color(canvas); + + if(m->is_select_byte) { +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "key_field: %s", m->key_field); +#endif + char msg_index[18]; + snprintf(msg_index, sizeof(msg_index), "Field index : %d", m->index); + canvas_draw_str_aligned(canvas, 64, 26, AlignCenter, AlignTop, msg_index); + + string_t menu_items; + string_init(menu_items); + + center_displayed_key(menu_items, m->key_field, m->index); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, 64, 40, AlignCenter, AlignTop, string_get_cstr(menu_items)); + + elements_button_center(canvas, "Select"); + elements_button_left(canvas, "<"); + elements_button_right(canvas, ">"); + + string_reset(menu_items); + } else { + // Menu + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontSecondary); + uint8_t items_on_screen = 3; + const uint8_t item_height = 16; + +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "window_position: %d, index: %d", model->window_position, m->index); +#endif + for(uint8_t position = 0; position < SubBruteAttackTotalCount; ++position) { + uint8_t item_position = position - model->window_position; + + if(item_position < items_on_screen) { + const char* str = subbrute_get_menu_name(position); + if(m->index == position) { + canvas_draw_str_aligned( + canvas, + 64, + 9 + (item_position * item_height) + STATUS_BAR_Y_SHIFT, + AlignCenter, + AlignCenter, + str); + elements_frame( + canvas, 1, 1 + (item_position * item_height) + STATUS_BAR_Y_SHIFT, 124, 15); + } else { + canvas_draw_str_aligned( + canvas, + 64, + 9 + (item_position * item_height) + STATUS_BAR_Y_SHIFT, + AlignCenter, + AlignCenter, + str); + } + } + } + + elements_scrollbar_pos( + canvas, + canvas_width(canvas), + STATUS_BAR_Y_SHIFT + 2, + canvas_height(canvas) - STATUS_BAR_Y_SHIFT, + m->index, + SubBruteAttackTotalCount); + } +} + +bool subbrute_main_view_input(InputEvent* event, void* context) { + furi_assert(event); + furi_assert(context); +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "InputKey: %d", event->key); +#endif + + if(event->key == InputKeyBack && event->type == InputTypeShort) { + return false; + } + + SubBruteMainView* instance = context; + const uint8_t min_value = 0; + const uint8_t correct_total = SubBruteAttackTotalCount - 1; + uint8_t index = 0; + bool is_select_byte = false; + with_view_model( + instance->view, (SubBruteMainViewModel * model) { + is_select_byte = model->is_select_byte; + return false; + }); + + bool consumed = false; + if(!is_select_byte) { + if((event->type == InputTypeShort) || (event->type == InputTypeRepeat)) { + with_view_model( + instance->view, (SubBruteMainViewModel * model) { + bool ret = false; + uint8_t items_on_screen = 3; + if(event->key == InputKeyUp) { + if(model->index == min_value) { + model->index = correct_total; + } else { + model->index = CLAMP(model->index - 1, correct_total, min_value); + } + ret = true; + consumed = true; + } else if(event->key == InputKeyDown) { + if(model->index == correct_total) { + model->index = min_value; + } else { + model->index = CLAMP(model->index + 1, correct_total, min_value); + } + ret = true; + consumed = true; + } + if(ret) { + model->window_position = model->index; + if(model->window_position > 0) { + model->window_position -= 1; + } + + if(SubBruteAttackTotalCount <= items_on_screen) { + model->window_position = 0; + } else { + if(model->window_position >= + (SubBruteAttackTotalCount - items_on_screen)) { + model->window_position = + (SubBruteAttackTotalCount - items_on_screen); + } + } + } + index = model->index; + return ret; + }); + } + +#ifdef FURI_DEBUG + with_view_model( + instance->view, (SubBruteMainViewModel * model) { + index = model->index; + return false; + }); + FURI_LOG_I(TAG, "Index: %d", index); +#endif + + if(event->key == InputKeyOk && event->type == InputTypeShort) { + if(index == SubBruteAttackLoadFile) { + instance->callback(SubBruteCustomEventTypeLoadFile, instance->context); + } else { + instance->callback(SubBruteCustomEventTypeMenuSelected, instance->context); + } + consumed = true; + } + } else { + if((event->type == InputTypeShort) || (event->type == InputTypeRepeat)) { + with_view_model( + instance->view, (SubBruteMainViewModel * model) { + if(event->key == InputKeyLeft) { + if(model->index > 0) { + model->index--; + } + } else if(event->key == InputKeyRight) { + if(model->index < 7) { + model->index++; + } + } + + index = model->index; + return true; + }); + } + +#ifdef FURI_DEBUG + with_view_model( + instance->view, (SubBruteMainViewModel * model) { + index = model->index; + return false; + }); + FURI_LOG_I(TAG, "Index: %d", index); +#endif + + if(event->key == InputKeyOk && event->type == InputTypeShort) { + instance->callback(SubBruteCustomEventTypeIndexSelected, instance->context); + consumed = true; + } + } + + return consumed; +} + +void subbrute_main_view_enter(void* context) { + furi_assert(context); + +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "subbrute_main_view_enter"); +#endif +} + +void subbrute_main_view_exit(void* context) { + furi_assert(context); + +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "subbrute_main_view_exit"); +#endif +} + +SubBruteMainView* subbrute_main_view_alloc() { + SubBruteMainView* instance = malloc(sizeof(SubBruteMainView)); + instance->view = view_alloc(); + view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(SubBruteMainViewModel)); + view_set_context(instance->view, instance); + view_set_draw_callback(instance->view, (ViewDrawCallback)subbrute_main_view_draw); + view_set_input_callback(instance->view, subbrute_main_view_input); + view_set_enter_callback(instance->view, subbrute_main_view_enter); + view_set_exit_callback(instance->view, subbrute_main_view_exit); + + with_view_model( + instance->view, (SubBruteMainViewModel * model) { + model->index = 0; + model->window_position = 0; + model->key_field = NULL; + model->is_select_byte = false; + return true; + }); + + return instance; +} + +void subbrute_main_view_free(SubBruteMainView* instance) { + furi_assert(instance); + + view_free(instance->view); + free(instance); +} + +View* subbrute_main_view_get_view(SubBruteMainView* instance) { + furi_assert(instance); + return instance->view; +} + +void subbrute_main_view_set_index( + SubBruteMainView* instance, + uint8_t idx, + bool is_select_byte, + const char* key_field) { + furi_assert(instance); + furi_assert(idx < SubBruteAttackTotalCount); +#ifdef FURI_DEBUG + FURI_LOG_I(TAG, "Set index: %d", idx); +#endif + with_view_model( + instance->view, (SubBruteMainViewModel * model) { + model->is_select_byte = is_select_byte; + model->key_field = key_field; + model->index = idx; + model->window_position = idx; + + if(!is_select_byte) { + uint8_t items_on_screen = 3; + + if(model->window_position > 0) { + model->window_position -= 1; + } + + if(SubBruteAttackTotalCount <= items_on_screen) { + model->window_position = 0; + } else { + if(model->window_position >= (SubBruteAttackTotalCount - items_on_screen)) { + model->window_position = (SubBruteAttackTotalCount - items_on_screen); + } + } + } + return true; + }); +} + +SubBruteAttacks subbrute_main_view_get_index(SubBruteMainView* instance) { + furi_assert(instance); + + uint8_t idx = 0; + with_view_model( + instance->view, (SubBruteMainViewModel * model) { + idx = model->index; + return false; + }); + +#ifdef FURI_DEBUG + FURI_LOG_D(TAG, "Get index: %d", idx); +#endif + + return idx; +} \ No newline at end of file diff --git a/applications/plugins/subbrute/views/subbrute_main_view.h b/applications/plugins/subbrute/views/subbrute_main_view.h new file mode 100644 index 000000000..02eb3305a --- /dev/null +++ b/applications/plugins/subbrute/views/subbrute_main_view.h @@ -0,0 +1,30 @@ +#pragma once + +#include "../subbrute_custom_event.h" +#include +#include "assets_icons.h" +#include +#include +#include + +typedef void (*SubBruteMainViewCallback)(SubBruteCustomEvent event, void* context); +typedef struct SubBruteMainView SubBruteMainView; + +void subbrute_main_view_set_callback( + SubBruteMainView* instance, + SubBruteMainViewCallback callback, + void* context); + +SubBruteMainView* subbrute_main_view_alloc(); +void subbrute_main_view_free(SubBruteMainView* instance); +View* subbrute_main_view_get_view(SubBruteMainView* instance); +void subbrute_main_view_set_index( + SubBruteMainView* instance, + uint8_t idx, + bool is_select_byte, + const char* key_field); +uint8_t subbrute_main_view_get_index(SubBruteMainView* instance); +void subbrute_attack_view_enter(void* context); +void subbrute_attack_view_exit(void* context); +bool subbrute_attack_view_input(InputEvent* event, void* context); +void subbrute_attack_view_draw(Canvas* canvas, void* context); \ No newline at end of file