diff --git a/applications/subbrute/LICENSE.md b/applications/subbrute/LICENSE.md new file mode 100644 index 000000000..a856581c9 --- /dev/null +++ b/applications/subbrute/LICENSE.md @@ -0,0 +1,8 @@ +/* + * ---------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * @G4N4P4T1 wrote this file. As long as you retain this notice you + * can do whatever you want with this stuff. If we meet some day, and you think + * this stuff is worth it, you can buy me a beer in return. + * ---------------------------------------------------------------------------- + */ \ No newline at end of file diff --git a/applications/subbrute/README.md b/applications/subbrute/README.md new file mode 100644 index 000000000..04a0c975d --- /dev/null +++ b/applications/subbrute/README.md @@ -0,0 +1,4 @@ +# FlipFrid + +SubGhz Fuzzer +select your base message, the field to fuzz and let's get fuzzy ! \ No newline at end of file diff --git a/applications/subbrute/application.fam b/applications/subbrute/application.fam new file mode 100644 index 000000000..4002421dd --- /dev/null +++ b/applications/subbrute/application.fam @@ -0,0 +1,10 @@ +App( + appid="subbrute", + name="SubGhz Bruteforcer", + apptype=FlipperAppType.PLUGIN, + entry_point="subbrute_start", + cdefines=["APP_SUB_BRUTE"], + requires=["gui","dialogs"], + stack_size=2 * 1024, + order=70, +) diff --git a/applications/subbrute/scene/subbrute_scene_entrypoint.c b/applications/subbrute/scene/subbrute_scene_entrypoint.c new file mode 100644 index 000000000..9cdc83623 --- /dev/null +++ b/applications/subbrute/scene/subbrute_scene_entrypoint.c @@ -0,0 +1,198 @@ +#include "subbrute_scene_entrypoint.h" +#include "../subbrute_utils.h" + +string_t subbrute_menu_items[9]; + +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 SubBruteAttackCAME12bit433: + context->frequency = 433920000; + 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 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 < 9; i++) { + string_init(subbrute_menu_items[i]); + } + + string_set(subbrute_menu_items[0], "BF existing dump"); + string_set(subbrute_menu_items[1], "CAME 12bit 433mhz"); + string_set(subbrute_menu_items[2], "CAME 12bit 868mhz"); + string_set(subbrute_menu_items[3], "Chamberlain 9bit 315mhz"); + string_set(subbrute_menu_items[4], "Chamberlain 9bit 390mhz"); + string_set(subbrute_menu_items[5], "Linear 10bit 300mhz"); + string_set(subbrute_menu_items[6], "Linear 10bit 310mhz"); + string_set(subbrute_menu_items[7], "NICE 12bit 433mhz"); + string_set(subbrute_menu_items[8], "NICE 12bit 868mhz"); +} + +void subbrute_scene_entrypoint_on_exit(SubBruteState* context) { + UNUSED(context); + for(uint32_t i = 0; i < 9; 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, "SubGhz Fuzzer"); + + 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/subbrute/scene/subbrute_scene_entrypoint.h b/applications/subbrute/scene/subbrute_scene_entrypoint.h new file mode 100644 index 000000000..af6b3bc49 --- /dev/null +++ b/applications/subbrute/scene/subbrute_scene_entrypoint.h @@ -0,0 +1,8 @@ +#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/subbrute/scene/subbrute_scene_load_file.c b/applications/subbrute/scene/subbrute_scene_load_file.c new file mode 100644 index 000000000..23a555f06 --- /dev/null +++ b/applications/subbrute/scene/subbrute_scene_load_file.c @@ -0,0 +1,201 @@ +#include "subbrute_scene_load_file.h" +#include "subbrute_scene_entrypoint.h" +#include "../subbrute_utils.h" + +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; + } + + 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_free(fff_data_file); + 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: + case InputKeyBack: + 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, "Press OK to choose file"); +} + +bool subbrute_load_protocol_from_file(SubBruteState* context) { + string_t file_path; + string_init(file_path); + + // Input events and views are managed by file_select + bool res = dialog_file_browser_show( + context->dialogs, + context->file_path, + context->file_path, + SUBGHZ_APP_EXTENSION, + true, + &I_sub1_10px, + true); + + if(res) { + res = subbrute_load(context, string_get_cstr(context->file_path)); + } + + string_clear(file_path); + + return res; +} \ No newline at end of file diff --git a/applications/subbrute/scene/subbrute_scene_load_file.h b/applications/subbrute/scene/subbrute_scene_load_file.h new file mode 100644 index 000000000..9e186b1c9 --- /dev/null +++ b/applications/subbrute/scene/subbrute_scene_load_file.h @@ -0,0 +1,8 @@ +#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/subbrute/scene/subbrute_scene_run_attack.c b/applications/subbrute/scene/subbrute_scene_run_attack.c new file mode 100644 index 000000000..d865588a5 --- /dev/null +++ b/applications/subbrute/scene/subbrute_scene_run_attack.c @@ -0,0 +1,298 @@ +#include "subbrute_scene_run_attack.h" +#include + +uint64_t subbrute_counter = 0; +uint64_t max_value; +bool is_running = false; +bool locked = 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_I(TAG, string_get_cstr(context->flipper_format_string)); + + furi_hal_subghz_start_async_tx(subghz_transmitter_yield, context->transmitter); + while(!(furi_hal_subghz_is_async_tx_complete())) { + furi_delay_ms(50); + } + + furi_hal_subghz_stop_async_tx(); + furi_hal_subghz_sleep(); + furi_hal_power_suppress_charge_exit(); +} + +void prepare_emit(SubBruteState* context) { + is_running = true; + furi_hal_subghz_init(); + + stream_clean(context->stream); + stream_write_string(context->stream, 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 = furi_hal_subghz_set_frequency_and_path(context->frequency); + + furi_hal_power_suppress_charge_enter(); +} + +void clear_emit(SubBruteState* context) { + furi_hal_subghz_stop_async_tx(); + furi_hal_subghz_sleep(); + + furi_hal_power_suppress_charge_exit(); + if(context->attack == SubBruteAttackLoadFile) { + subghz_transmitter_free(context->transmitter); + } + subghz_transmitter_free(context->transmitter); + is_running = false; +} + +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)); + + prepare_emit(context); + subbrute_emit(context); + clear_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); + } + string_init_printf( + context->flipper_format_string, + "Filetype: Flipper SubGhz Key File\n" + "Version: 1\n" + "Protocol: %s\n" + "Bit: %d\n" + "Key: %s\n" + "TE: %d\n", + string_get_cstr(context->protocol), + context->bit, + string_get_cstr(context->candidate), + context->te); + + prepare_emit(context); + subbrute_emit(context); + clear_emit(context); +} + +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); + } + string_clear(context->flipper_format_string); +} + +void subbrute_scene_run_attack_on_enter(SubBruteState* context) { + 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->transmitter = + subghz_transmitter_alloc_init(context->environment, string_get_cstr(context->protocol)); +} + +void subbrute_scene_run_attack_on_exit(SubBruteState* context) { + if(is_running) { + is_running = false; + clear_emit(context); + } +} + +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_scene_run_attack_on_event(SubBruteEvent event, SubBruteState* context) { + if(event.evt_type == EventTypeKey) { + if(event.input_type == InputTypeShort) { + switch(event.key) { + case InputKeyDown: + if(!context->is_attacking && context->payload > 0x00) { + context->payload--; + subbrute_send_packet(context); + notification_message(context->notify, &sequence_blink_blue_10); + } + break; + case InputKeyUp: + if(!context->is_attacking && context->payload < max_value) { + context->payload++; + subbrute_send_packet(context); + notification_message(context->notify, &sequence_blink_blue_10); + } + break; + case InputKeyLeft: + case InputKeyRight: + case InputKeyOk: + if(!context->is_attacking) { + context->is_attacking = true; + notification_message(context->notify, &sequence_blink_start_blue); + } else { + context->is_attacking = false; + notification_message(context->notify, &sequence_blink_stop); + notification_message(context->notify, &sequence_single_vibro); + } + break; + case InputKeyBack: + locked = false; + context->is_attacking = false; + string_reset(context->notification_msg); + context->payload = 0x00; + 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[22]; + 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]; + if(context->is_attacking) { + snprintf(start_stop_msg, sizeof(start_stop_msg), " Press OK to stop "); + } else { + snprintf(start_stop_msg, sizeof(start_stop_msg), " Press OK to start "); + } + canvas_draw_str_aligned(canvas, 64, 44, AlignCenter, AlignTop, start_stop_msg); +} diff --git a/applications/subbrute/scene/subbrute_scene_run_attack.h b/applications/subbrute/scene/subbrute_scene_run_attack.h new file mode 100644 index 000000000..1eb9637d0 --- /dev/null +++ b/applications/subbrute/scene/subbrute_scene_run_attack.h @@ -0,0 +1,8 @@ +#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/subbrute/scene/subbrute_scene_select_field.c b/applications/subbrute/scene/subbrute_scene_select_field.c new file mode 100644 index 000000000..82b227f5d --- /dev/null +++ b/applications/subbrute/scene/subbrute_scene_select_field.c @@ -0,0 +1,121 @@ +#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 = context->key_index - 1; + } + break; + case InputKeyRight: + if(context->key_index < nb_bytes) { + context->key_index = context->key_index + 1; + } + 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/subbrute/scene/subbrute_scene_select_field.h b/applications/subbrute/scene/subbrute_scene_select_field.h new file mode 100644 index 000000000..e02a07ee0 --- /dev/null +++ b/applications/subbrute/scene/subbrute_scene_select_field.h @@ -0,0 +1,8 @@ +#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/subbrute/subbrute.c b/applications/subbrute/subbrute.c new file mode 100644 index 000000000..26aededec --- /dev/null +++ b/applications/subbrute/subbrute.c @@ -0,0 +1,231 @@ +#include "subbrute.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" + +static void draw_callback(Canvas* const canvas, void* ctx) { + SubBruteState* subbrute_state = (SubBruteState*)acquire_mutex((ValueMutex*)ctx, 100); + + if(subbrute_state == NULL) { + return; + } + + // 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; + } + + release_mutex((ValueMutex*)ctx, subbrute_state); +} + +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 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); +} + +SubBruteState* subbrute_alloc() { + SubBruteState* subbrute = 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); + + subbrute->previous_scene = NoneScene; + subbrute->current_scene = SceneSelectFile; + subbrute->is_running = true; + subbrute->is_attacking = false; + subbrute->key_index = 0; + subbrute->notify = furi_record_open(RECORD_NOTIFICATION); + + //Dialog + subbrute->dialogs = furi_record_open(RECORD_DIALOGS); + + subbrute->flipper_format = flipper_format_string_alloc(); + subbrute->environment = subghz_environment_alloc(); + subbrute->receiver = subghz_receiver_alloc_init(subbrute->environment); + subghz_receiver_set_filter(subbrute->receiver, SubGhzProtocolFlag_Decodable); + + return subbrute; +} + +void subbrute_free(SubBruteState* subbrute) { + //Dialog + furi_record_close(RECORD_DIALOGS); + notification_message(subbrute->notify, &sequence_blink_stop); + + string_clear(subbrute->preset); + string_clear(subbrute->candidate); + + // 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); + + // The rest + free(subbrute); +} + +// ENTRYPOINT +int32_t subbrute_start(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); + free(subbrute_state); + } + + // 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"); + Gui* gui = (Gui*)furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + 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 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 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 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; + } + view_port_update(view_port); + } + } + } + + // Cleanup + furi_timer_stop(timer); + furi_timer_free(timer); + + FURI_LOG_I(TAG, "Cleaning up"); + gui_remove_view_port(gui, view_port); + view_port_free(view_port); + furi_message_queue_free(event_queue); + furi_record_close(RECORD_GUI); + subbrute_free(subbrute_state); + + return 0; +} \ No newline at end of file diff --git a/applications/subbrute/subbrute.h b/applications/subbrute/subbrute.h new file mode 100644 index 000000000..29b2f21fe --- /dev/null +++ b/applications/subbrute/subbrute.h @@ -0,0 +1,94 @@ +#pragma once +#include +#include +#include +#include +#include "m-string.h" + +#include +#include +#include +#include +#include +#include +#include + +#define TAG "SUBBRUTE" + +typedef enum { + NoneScene, + SceneSelectFile, + SceneSelectField, + SceneAttack, + SceneEntryPoint +} SubBruteScene; + +typedef enum { + SubBruteAttackLoadFile, + SubBruteAttackCAME12bit433, + SubBruteAttackCAME12bit868, + SubBruteAttackChamberlain9bit315, + SubBruteAttackChamberlain9bit390, + SubBruteAttackLinear10bit300, + SubBruteAttackLinear10bit310, + SubBruteAttackNICE12bit433, + SubBruteAttackNICE12bit868, +} SubBruteAttacks; + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType evt_type; + InputKey key; + InputType input_type; +} SubBruteEvent; + +// STRUCTS +typedef struct { + // Application stuff + bool is_running; + bool is_attacking; + SubBruteScene current_scene; + SubBruteScene previous_scene; + NotificationApp* notify; + + // SubGhz Stuff + FlipperFormat* flipper_format; + SubGhzEnvironment* environment; + SubGhzTransmitter* transmitter; + SubGhzReceiver* receiver; + SubGhzProtocolDecoderBase* decoder_result; + string_t preset; + Stream* stream; + string_t protocol; + uint32_t frequency; + uint32_t repeat; + uint32_t bit; + string_t key; + uint32_t te; + + // Context Stuff + DialogsApp* dialogs; + 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 diff --git a/applications/subbrute/subbrute_utils.c b/applications/subbrute/subbrute_utils.c new file mode 100644 index 000000000..e045eb593 --- /dev/null +++ b/applications/subbrute/subbrute_utils.c @@ -0,0 +1,14 @@ +#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_region_is_frequency_allowed(context->frequency); + bool r = true; + 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/subbrute/subbrute_utils.h b/applications/subbrute/subbrute_utils.h new file mode 100644 index 000000000..90f7c60ad --- /dev/null +++ b/applications/subbrute/subbrute_utils.h @@ -0,0 +1,4 @@ +#pragma once +#include "subbrute.h" + +bool subbrute_is_frequency_allowed(SubBruteState* context); \ No newline at end of file