mirror of
https://github.com/DarkFlippers/unleashed-firmware
synced 2024-11-27 15:00:46 +00:00
Deep refactor of SubBrute was made, but it doesn't start. Debug device needed
This commit is contained in:
parent
1e71d212fe
commit
aeb02500de
34 changed files with 2334 additions and 1537 deletions
|
@ -2,7 +2,7 @@ App(
|
||||||
appid="subbrute",
|
appid="subbrute",
|
||||||
name="Sub-GHz Bruteforcer",
|
name="Sub-GHz Bruteforcer",
|
||||||
apptype=FlipperAppType.EXTERNAL,
|
apptype=FlipperAppType.EXTERNAL,
|
||||||
entry_point="subbrute_start",
|
entry_point="subbrute_app",
|
||||||
cdefines=["APP_SUB_BRUTE"],
|
cdefines=["APP_SUB_BRUTE"],
|
||||||
requires=["gui","dialogs"],
|
requires=["gui","dialogs"],
|
||||||
stack_size=2 * 1024,
|
stack_size=2 * 1024,
|
||||||
|
|
203
applications/plugins/subbrute/helpers/subbrute_worker.c
Normal file
203
applications/plugins/subbrute/helpers/subbrute_worker.c
Normal file
|
@ -0,0 +1,203 @@
|
||||||
|
#include <flipper_format.h>
|
||||||
|
#include <flipper_format_i.h>
|
||||||
|
|
||||||
|
#include <subghz/types.h>
|
||||||
|
#include <lib/subghz/protocols/raw.h>
|
||||||
|
|
||||||
|
#include "subbrute_worker.h"
|
||||||
|
|
||||||
|
#define TAG "SubBruteWorker"
|
||||||
|
|
||||||
|
struct SubBruteWorker {
|
||||||
|
FuriThread* thread;
|
||||||
|
volatile bool worker_running;
|
||||||
|
|
||||||
|
SubGhzEnvironment* environment;
|
||||||
|
SubGhzReceiver* receiver;
|
||||||
|
SubGhzTransmitter* transmitter;
|
||||||
|
SubGhzProtocolDecoderBase* decoder_result;
|
||||||
|
FlipperFormat* flipper_format;
|
||||||
|
|
||||||
|
uint32_t last_time_tx_data;
|
||||||
|
FuriMessageQueue* event_queue;
|
||||||
|
|
||||||
|
// 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 200
|
||||||
|
#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);
|
||||||
|
|
||||||
|
furi_hal_power_suppress_charge_enter();
|
||||||
|
|
||||||
|
// 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(250);
|
||||||
|
}
|
||||||
|
|
||||||
|
furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate);
|
||||||
|
furi_hal_subghz_sleep();
|
||||||
|
|
||||||
|
furi_hal_power_suppress_charge_exit();
|
||||||
|
|
||||||
|
subghz_transmitter_free(instance->transmitter);
|
||||||
|
subghz_environment_free(instance->environment);
|
||||||
|
|
||||||
|
#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->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);
|
||||||
|
|
||||||
|
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,
|
||||||
|
string_t protocol_name) {
|
||||||
|
furi_assert(instance);
|
||||||
|
furi_assert(!instance->worker_running);
|
||||||
|
|
||||||
|
instance->frequency = frequency;
|
||||||
|
instance->preset = preset;
|
||||||
|
string_init_move(instance->protocol_name, 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, string_t 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();
|
||||||
|
|
||||||
|
Stream* stream = flipper_format_get_raw_stream(instance->flipper_format);
|
||||||
|
stream_clean(stream);
|
||||||
|
stream_write_cstring(stream, string_get_cstr(payload));
|
||||||
|
subghz_transmitter_deserialize(instance->transmitter, instance->flipper_format);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
32
applications/plugins/subbrute/helpers/subbrute_worker.h
Normal file
32
applications/plugins/subbrute/helpers/subbrute_worker.h
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <furi_hal_subghz.h>
|
||||||
|
#include <subghz/environment.h>
|
||||||
|
#include <subghz/transmitter.h>
|
||||||
|
#include <subghz/receiver.h>
|
||||||
|
|
||||||
|
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,
|
||||||
|
string_t 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, string_t payload);
|
|
@ -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]));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
|
@ -1,222 +0,0 @@
|
||||||
#include "subbrute_scene_load_file.h"
|
|
||||||
#include "subbrute_scene_entrypoint.h"
|
|
||||||
#include "../subbrute_utils.h"
|
|
||||||
#include <lib/subghz/protocols/registry.h>
|
|
||||||
|
|
||||||
#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;
|
|
||||||
}
|
|
|
@ -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);
|
|
|
@ -1,384 +0,0 @@
|
||||||
#include "subbrute_scene_run_attack.h"
|
|
||||||
#include <lib/subghz/transmitter.h>
|
|
||||||
#include <gui/elements.h>
|
|
||||||
|
|
||||||
//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);
|
|
||||||
}
|
|
|
@ -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();
|
|
|
@ -1,222 +0,0 @@
|
||||||
#include "../subbrute.h"
|
|
||||||
#include "m-string.h"
|
|
||||||
#include "subghz/types.h"
|
|
||||||
#include <lib/toolbox/random_name.h>
|
|
||||||
#include <gui/modules/validators.h>
|
|
||||||
#include <lib/toolbox/path.h>
|
|
||||||
|
|
||||||
#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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
|
@ -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));
|
|
||||||
}
|
|
|
@ -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);
|
|
29
applications/plugins/subbrute/scenes/subbrute_scene.h
Normal file
29
applications/plugins/subbrute/scenes/subbrute_scene.h
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gui/scene_manager.h>
|
||||||
|
|
||||||
|
// 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
|
|
@ -0,0 +1,6 @@
|
||||||
|
ADD_SCENE(subbrute, load_file, LoadFile)
|
||||||
|
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)
|
|
@ -0,0 +1,61 @@
|
||||||
|
#include <lib/subghz/protocols/registry.h>
|
||||||
|
#include "../subbrute_i.h"
|
||||||
|
#include "../subbrute_custom_event.h"
|
||||||
|
#include "../views/subbrute_main_view.h"
|
||||||
|
|
||||||
|
//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;
|
||||||
|
string_t file_path;
|
||||||
|
string_init(file_path);
|
||||||
|
|
||||||
|
DialogsFileBrowserOptions browser_options;
|
||||||
|
dialog_file_browser_set_basic_options(&browser_options, SUBBRUTE_FILE_EXT, &I_sub1_10px);
|
||||||
|
|
||||||
|
// Input events and views are managed by file_select
|
||||||
|
bool res = dialog_file_browser_show(
|
||||||
|
instance->dialogs,
|
||||||
|
instance->device->load_path,
|
||||||
|
instance->device->load_path,
|
||||||
|
&browser_options);
|
||||||
|
|
||||||
|
if(res) {
|
||||||
|
SubBruteFileResult load_result = subbrute_device_load_protocol_from_file(instance->device);
|
||||||
|
if(load_result == SubBruteFileResultOk) {
|
||||||
|
scene_manager_search_and_switch_to_previous_scene(
|
||||||
|
instance->scene_manager, SubBruteSceneSetupAttack);
|
||||||
|
} else {
|
||||||
|
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);
|
||||||
|
res = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string_clear(file_path);
|
||||||
|
|
||||||
|
if(!res) {
|
||||||
|
scene_manager_search_and_switch_to_previous_scene(
|
||||||
|
instance->scene_manager, SubBruteSceneLoadFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
158
applications/plugins/subbrute/scenes/subbrute_scene_run_attack.c
Normal file
158
applications/plugins/subbrute/scenes/subbrute_scene_run_attack.c
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
#include "../subbrute_i.h"
|
||||||
|
#include "../subbrute_custom_event.h"
|
||||||
|
#include "../views/subbrute_attack_view.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);
|
||||||
|
|
||||||
|
// Start worker if not started
|
||||||
|
subbrute_attack_view_start_worker(
|
||||||
|
view,
|
||||||
|
instance->device->frequency,
|
||||||
|
instance->device->preset,
|
||||||
|
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_attack_view_can_send(view)) {
|
||||||
|
// Blink
|
||||||
|
notification_message(instance->notifications, &sequence_blink_yellow_100);
|
||||||
|
|
||||||
|
if(subbrute_attack_view_transmit(view, 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;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
#include <m-string.h>
|
||||||
|
#include <subghz/types.h>
|
||||||
|
#include <lib/toolbox/random_name.h>
|
||||||
|
#include <gui/modules/validators.h>
|
||||||
|
#include <lib/toolbox/path.h>
|
||||||
|
|
||||||
|
#include "../subbrute_i.h"
|
||||||
|
#include "../subbrute_custom_event.h"
|
||||||
|
#include "../views/subbrute_attack_view.h"
|
||||||
|
|
||||||
|
#define TAG "SubBruteSceneSaveFile"
|
||||||
|
|
||||||
|
void subbrute_scene_save_name_on_enter(void* context) {
|
||||||
|
SubBruteState* instance = (SubBruteState*)context;
|
||||||
|
|
||||||
|
// Setup view
|
||||||
|
TextInput* text_input = instance->text_input;
|
||||||
|
set_random_name(instance->device->text_store, sizeof(instance->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,
|
||||||
|
instance->device->text_store,
|
||||||
|
SUBBRUTE_MAX_LEN_NAME,
|
||||||
|
true);
|
||||||
|
|
||||||
|
string_t folder_path;
|
||||||
|
string_init(folder_path);
|
||||||
|
|
||||||
|
SubBruteDevice* device = instance->device;
|
||||||
|
if(string_end_with_str_p(device->load_path, SUBBRUTE_FILE_EXT)) {
|
||||||
|
path_extract_dirname(string_get_cstr(device->load_path), folder_path);
|
||||||
|
} else {
|
||||||
|
string_set_str(folder_path, SUBBRUTE_PATH);
|
||||||
|
}
|
||||||
|
|
||||||
|
ValidatorIsFile* validator_is_file =
|
||||||
|
validator_is_file_alloc_init(string_get_cstr(folder_path), SUBBRUTE_FILE_EXT, TAG);
|
||||||
|
text_input_set_validator(text_input, validator_is_file_callback, validator_is_file);
|
||||||
|
|
||||||
|
view_dispatcher_switch_to_view(instance->view_dispatcher, SubBruteViewTextInput);
|
||||||
|
|
||||||
|
string_clear(folder_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool subbrute_scene_save_name_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
SubBruteState* instance = (SubBruteState*)context;
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom &&
|
||||||
|
event.event == SubBruteCustomEventTypeTextEditDone) {
|
||||||
|
if(subbrute_device_save_file(instance->device, instance->device->text_store)) {
|
||||||
|
scene_manager_next_scene(instance->scene_manager, SubBruteSceneSaveSuccess);
|
||||||
|
consumed = true;
|
||||||
|
} else {
|
||||||
|
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);
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
#include "../subbrute_i.h"
|
||||||
|
#include "../subbrute_custom_event.h"
|
||||||
|
#include "../views/subbrute_attack_view.h"
|
||||||
|
|
||||||
|
void subbrute_scene_save_success_callback(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
|
||||||
|
SubBruteState* instance = (SubBruteState*)context;
|
||||||
|
view_dispatcher_send_custom_event(instance->view_dispatcher, SubBruteCustomEventTypeSaveSuccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
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_scene_save_success_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 == SubBruteCustomEventTypeSaveSuccess) {
|
||||||
|
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);
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
#include "../subbrute_i.h"
|
||||||
|
#include "../subbrute_custom_event.h"
|
||||||
|
#include "../views/subbrute_attack_view.h"
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
instance->device->key_index = subbrute_attack_view_get_current_step(view);
|
||||||
|
subbrute_attack_view_init_values(
|
||||||
|
view,
|
||||||
|
(uint8_t)instance->device->attack,
|
||||||
|
instance->device->max_value,
|
||||||
|
instance->device->key_index);
|
||||||
|
|
||||||
|
// Run worker anyway
|
||||||
|
subbrute_attack_view_start_worker(
|
||||||
|
view,
|
||||||
|
instance->device->frequency,
|
||||||
|
instance->device->preset,
|
||||||
|
instance->device->protocol_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void subbrute_scene_setup_attack_on_exit(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
SubBruteState* instance = (SubBruteState*)context;
|
||||||
|
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) {
|
||||||
|
scene_manager_next_scene(instance->scene_manager, SubBruteSceneRunAttack);
|
||||||
|
} else if (event.event == SubBruteCustomEventTypeBackPressed) {
|
||||||
|
subbrute_attack_view_stop_worker(view);
|
||||||
|
scene_manager_next_scene(instance->scene_manager, SubBruteSceneStart);
|
||||||
|
} else if (event.event == SubBruteCustomEventTypeChangeStep) {
|
||||||
|
instance->device->key_index = subbrute_attack_view_get_current_step(view);
|
||||||
|
} else if(event.event == SubBruteCustomEventTypeTransmitCustom) {
|
||||||
|
if(subbrute_attack_view_can_send(view)) {
|
||||||
|
// Blink
|
||||||
|
notification_message(instance->notifications, &sequence_blink_green_100);
|
||||||
|
|
||||||
|
subbrute_device_create_packet_parsed(
|
||||||
|
instance->device, instance->device->key_index);
|
||||||
|
subbrute_attack_view_transmit(view, 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;
|
||||||
|
}
|
50
applications/plugins/subbrute/scenes/subbrute_scene_start.c
Normal file
50
applications/plugins/subbrute/scenes/subbrute_scene_start.c
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
#include "../subbrute_i.h"
|
||||||
|
#include "../subbrute_custom_event.h"
|
||||||
|
#include "../views/subbrute_main_view.h"
|
||||||
|
|
||||||
|
void subbrute_scene_start_callback(SubBruteCustomEvent event, void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
|
||||||
|
SubBruteState* instance = (SubBruteState*)context;
|
||||||
|
view_dispatcher_send_custom_event(instance->view_dispatcher, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void subbrute_scene_start_on_enter(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
|
||||||
|
SubBruteState* instance = (SubBruteState*)context;
|
||||||
|
SubBruteMainView* view = instance->view_main;
|
||||||
|
|
||||||
|
instance->current_view = SubBruteViewMain;
|
||||||
|
subbrute_main_view_set_callback(view,
|
||||||
|
subbrute_scene_start_callback,
|
||||||
|
instance);
|
||||||
|
|
||||||
|
view_dispatcher_switch_to_view(instance->view_dispatcher, instance->current_view);
|
||||||
|
subbrute_main_view_set_index(view, (uint8_t)instance->device->attack);
|
||||||
|
}
|
||||||
|
|
||||||
|
void subbrute_scene_start_on_exit(void* context) {
|
||||||
|
UNUSED(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool subbrute_scene_start_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
SubBruteState* instance = (SubBruteState*)context;
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom && event.event == SubBruteCustomEventTypeMenuSelected) {
|
||||||
|
//subbrute_device_attack_set
|
||||||
|
SubBruteAttacks attack = subbrute_main_view_get_index(instance->view_main);
|
||||||
|
|
||||||
|
if (attack == SubBruteAttackLoadFile) {
|
||||||
|
scene_manager_next_scene(instance->scene_manager, SubBruteSceneLoadFile);
|
||||||
|
} else {
|
||||||
|
subbrute_device_attack_set(instance->device, attack, NULL);
|
||||||
|
scene_manager_next_scene(instance->scene_manager, SubBruteSceneSetupAttack);
|
||||||
|
}
|
||||||
|
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return consumed;
|
||||||
|
}
|
30
applications/plugins/subbrute/scenes/subbute_scene.c
Normal file
30
applications/plugins/subbrute/scenes/subbute_scene.c
Normal file
|
@ -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,
|
||||||
|
};
|
|
@ -1,267 +1,239 @@
|
||||||
|
#include <furi.h>
|
||||||
|
#include <furi_hal.h>
|
||||||
|
#include <input/input.h>
|
||||||
|
#include <m-string.h>
|
||||||
|
|
||||||
|
#include <gui/gui.h>
|
||||||
|
#include <gui/view_dispatcher.h>
|
||||||
|
#include <gui/view_stack.h>
|
||||||
|
#include <gui/scene_manager.h>
|
||||||
|
#include <gui/modules/text_input.h>
|
||||||
|
#include <gui/modules/popup.h>
|
||||||
|
#include <gui/modules/widget.h>
|
||||||
|
#include <gui/modules/loading.h>
|
||||||
|
|
||||||
|
#include <dialogs/dialogs.h>
|
||||||
|
|
||||||
#include "subbrute.h"
|
#include "subbrute.h"
|
||||||
|
#include "subbrute_i.h"
|
||||||
|
#include "subbrute_custom_event.h"
|
||||||
|
|
||||||
#include "scene/subbrute_scene_load_file.h"
|
#define TAG "SubBruteApp"
|
||||||
#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"
|
|
||||||
|
|
||||||
static void draw_callback(Canvas* const canvas, void* ctx) {
|
static const char* subbrute_menu_names[] = {
|
||||||
SubBruteState* subbrute_state = (SubBruteState*)acquire_mutex((ValueMutex*)ctx, 100);
|
[SubBruteAttackNone] = "None",
|
||||||
|
[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) {
|
bool subbrute_custom_event_callback(void* context, uint32_t event) {
|
||||||
return;
|
furi_assert(context);
|
||||||
}
|
SubBruteState* instance = context;
|
||||||
|
return scene_manager_handle_custom_event(instance->scene_manager, event);
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
|
bool subbrute_back_event_callback(void* context) {
|
||||||
furi_assert(event_queue);
|
furi_assert(context);
|
||||||
|
SubBruteState* instance = context;
|
||||||
SubBruteEvent event = {
|
return scene_manager_handle_back_event(instance->scene_manager);
|
||||||
.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) {
|
void subbrute_tick_event_callback(void* context) {
|
||||||
furi_assert(event_queue);
|
furi_assert(context);
|
||||||
SubBruteEvent event = {
|
SubBruteState* instance = context;
|
||||||
.evt_type = EventTypeTick, .key = InputKeyUp, .input_type = InputTypeRelease};
|
scene_manager_handle_tick_event(instance->scene_manager);
|
||||||
furi_message_queue_put(event_queue, &event, 100);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SubBruteState* subbrute_alloc() {
|
SubBruteState* subbrute_alloc() {
|
||||||
SubBruteState* subbrute = malloc(sizeof(SubBruteState));
|
SubBruteState* instance = malloc(sizeof(SubBruteState));
|
||||||
|
|
||||||
string_init(subbrute->protocol);
|
instance->scene_manager = scene_manager_alloc(&subbrute_scene_handlers, instance);
|
||||||
string_init(subbrute->preset);
|
instance->view_dispatcher = view_dispatcher_alloc();
|
||||||
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;
|
view_dispatcher_enable_queue(instance->view_dispatcher);
|
||||||
subbrute->current_scene = SceneSelectFile;
|
view_dispatcher_set_event_callback_context(instance->view_dispatcher, instance);
|
||||||
subbrute->is_running = true;
|
view_dispatcher_set_custom_event_callback(
|
||||||
subbrute->is_attacking = false;
|
instance->view_dispatcher, subbrute_custom_event_callback);
|
||||||
subbrute->key_index = 7;
|
view_dispatcher_set_navigation_event_callback(
|
||||||
subbrute->notify = furi_record_open(RECORD_NOTIFICATION);
|
instance->view_dispatcher, subbrute_back_event_callback);
|
||||||
|
view_dispatcher_set_tick_event_callback(
|
||||||
|
instance->view_dispatcher, subbrute_tick_event_callback, 100);
|
||||||
|
|
||||||
subbrute->view_dispatcher = view_dispatcher_alloc();
|
// Devices
|
||||||
|
instance->device = subbrute_device_alloc();
|
||||||
|
|
||||||
|
instance->gui = furi_record_open(RECORD_GUI);
|
||||||
|
instance->dialogs = furi_record_open(RECORD_DIALOGS);
|
||||||
|
instance->notifications = furi_record_open(RECORD_NOTIFICATION);
|
||||||
|
instance->view_dispatcher = view_dispatcher_alloc();
|
||||||
|
|
||||||
//Dialog
|
//Dialog
|
||||||
subbrute->dialogs = furi_record_open(RECORD_DIALOGS);
|
instance->dialogs = furi_record_open(RECORD_DIALOGS);
|
||||||
|
|
||||||
subbrute->preset_def = malloc(sizeof(SubGhzPresetDefinition));
|
// 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));
|
||||||
|
|
||||||
//subbrute->flipper_format = flipper_format_string_alloc();
|
// Popup
|
||||||
//subbrute->environment = subghz_environment_alloc();
|
instance->popup = popup_alloc();
|
||||||
|
view_dispatcher_add_view(instance->view_dispatcher, SubBruteViewPopup, popup_get_view(instance->popup));
|
||||||
|
|
||||||
return subbrute;
|
// 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) {
|
void subbrute_free(SubBruteState* instance) {
|
||||||
|
furi_assert(instance);
|
||||||
|
|
||||||
|
// Notifications
|
||||||
|
notification_message(instance->notifications, &sequence_blink_stop);
|
||||||
|
furi_record_close(RECORD_NOTIFICATION);
|
||||||
|
instance->notifications = NULL;
|
||||||
|
|
||||||
|
// Loading
|
||||||
|
loading_free(instance->loading);
|
||||||
|
|
||||||
|
// View Main
|
||||||
|
view_dispatcher_remove_view(instance->view_dispatcher, SubBruteViewMain);
|
||||||
|
subbrute_main_view_free(instance->view_main);
|
||||||
|
|
||||||
|
// View Attack
|
||||||
|
view_dispatcher_remove_view(instance->view_dispatcher, SubBruteViewAttack);
|
||||||
|
subbrute_attack_view_free(instance->view_attack);
|
||||||
|
|
||||||
|
// TextInput
|
||||||
|
view_dispatcher_remove_view(instance->view_dispatcher, SubBruteViewTextInput);
|
||||||
|
text_input_free(instance->text_input);
|
||||||
|
|
||||||
|
// Custom Widget
|
||||||
|
view_dispatcher_remove_view(instance->view_dispatcher, SubBruteViewWidget);
|
||||||
|
widget_free(instance->widget);
|
||||||
|
|
||||||
|
// Popup
|
||||||
|
view_dispatcher_remove_view(instance->view_dispatcher, SubBruteViewPopup);
|
||||||
|
popup_free(instance->popup);
|
||||||
|
|
||||||
|
// ViewStack
|
||||||
|
view_dispatcher_remove_view(instance->view_dispatcher, SubBruteViewStack);
|
||||||
|
view_stack_free(instance->view_stack);
|
||||||
|
|
||||||
//Dialog
|
//Dialog
|
||||||
furi_record_close(RECORD_DIALOGS);
|
furi_record_close(RECORD_DIALOGS);
|
||||||
|
|
||||||
notification_message(subbrute->notify, &sequence_blink_stop);
|
// Scene manager
|
||||||
|
scene_manager_free(instance->scene_manager);
|
||||||
|
|
||||||
furi_record_close(RECORD_NOTIFICATION);
|
// View Dispatcher
|
||||||
|
view_dispatcher_free(instance->view_dispatcher);
|
||||||
|
|
||||||
view_dispatcher_free(subbrute->view_dispatcher);
|
// GUI
|
||||||
|
furi_record_close(RECORD_GUI);
|
||||||
|
instance->gui = NULL;
|
||||||
|
|
||||||
string_clear(subbrute->preset);
|
// SubBruteDevice
|
||||||
string_clear(subbrute->candidate);
|
subbrute_device_free(instance->device);
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
//flipper_format_free(subbrute->flipper_format);
|
|
||||||
//subghz_environment_free(subbrute->environment);
|
|
||||||
//subghz_receiver_free(subbrute->receiver);
|
|
||||||
|
|
||||||
free(subbrute->preset_def);
|
|
||||||
|
|
||||||
// The rest
|
// The rest
|
||||||
free(subbrute);
|
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, SubBruteCustomEventTypeTextEditResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 - 1);
|
||||||
|
|
||||||
|
return subbrute_menu_names[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
// ENTRYPOINT
|
// ENTRYPOINT
|
||||||
int32_t subbrute_start(void* p) {
|
int32_t subbrute_app(void* p) {
|
||||||
UNUSED(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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
|
SubBruteState* instance = subbrute_alloc();
|
||||||
|
#ifdef FURI_DEBUG
|
||||||
|
FURI_LOG_I(TAG, "Starting subbrute_alloc done");
|
||||||
|
#endif
|
||||||
view_dispatcher_attach_to_gui(
|
view_dispatcher_attach_to_gui(
|
||||||
subbrute_state->view_dispatcher, subbrute_state->gui, ViewDispatcherTypeFullscreen);
|
instance->view_dispatcher, instance->gui, ViewDispatcherTypeFullscreen);
|
||||||
|
scene_manager_next_scene(instance->scene_manager, SubBruteSceneStart);
|
||||||
subbrute_state->current_scene = SceneEntryPoint;
|
#ifdef FURI_DEBUG
|
||||||
|
FURI_LOG_I(TAG, "scene_manager_next_scene set");
|
||||||
// Init values
|
#endif
|
||||||
SubBruteEvent event;
|
furi_hal_power_suppress_charge_enter();
|
||||||
while(subbrute_state->is_running) {
|
#ifdef FURI_DEBUG
|
||||||
// Get next event
|
FURI_LOG_I(TAG, "view_dispatcher_run");
|
||||||
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 25);
|
#endif
|
||||||
if(event_status == FuriStatusOk) {
|
view_dispatcher_run(instance->view_dispatcher);
|
||||||
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);
|
|
||||||
|
|
||||||
furi_hal_power_suppress_charge_exit();
|
furi_hal_power_suppress_charge_exit();
|
||||||
|
|
||||||
FURI_LOG_I(TAG, "Cleaning up");
|
subbrute_free(instance);
|
||||||
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);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
|
@ -1,110 +1,3 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <furi.h>
|
|
||||||
#include <furi_hal.h>
|
|
||||||
#include <input/input.h>
|
|
||||||
#include <gui/gui.h>
|
|
||||||
#include "m-string.h"
|
|
||||||
|
|
||||||
#include <toolbox/stream/stream.h>
|
typedef struct SubBruteState SubBruteState;
|
||||||
#include <lib/subghz/transmitter.h>
|
|
||||||
#include <lib/subghz/receiver.h>
|
|
||||||
#include <flipper_format/flipper_format_i.h>
|
|
||||||
#include <dialogs/dialogs.h>
|
|
||||||
#include <notification/notification.h>
|
|
||||||
#include <notification/notification_messages.h>
|
|
||||||
#include <gui/view_dispatcher.h>
|
|
||||||
#include <gui/modules/text_input.h>
|
|
||||||
#include <gui/modules/popup.h>
|
|
||||||
|
|
||||||
#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;
|
|
25
applications/plugins/subbrute/subbrute_custom_event.h
Normal file
25
applications/plugins/subbrute/subbrute_custom_event.h
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
// Reserve first 100 events for button types and indexes, starting from 0
|
||||||
|
SubBruteCustomEventTypeReserved = 100,
|
||||||
|
|
||||||
|
SubBruteCustomEventTypeBackPressed,
|
||||||
|
SubBruteCustomEventTypeTextEditResult,
|
||||||
|
SubBruteCustomEventTypeTransmitStarted,
|
||||||
|
SubBruteCustomEventTypeTransmitFinished,
|
||||||
|
SubBruteCustomEventTypeTransmitNotStarted,
|
||||||
|
SubBruteCustomEventTypeTransmitCustom,
|
||||||
|
SubBruteCustomEventTypeSaveFile,
|
||||||
|
SubBruteCustomEventTypeSaveSuccess,
|
||||||
|
SubBruteCustomEventTypeChangeStep,
|
||||||
|
|
||||||
|
SubBruteCustomEventTypeMenuSelected,
|
||||||
|
SubBruteCustomEventTypeTextEditDone,
|
||||||
|
SubBruteCustomEventTypePopupClosed,
|
||||||
|
|
||||||
|
SubBruteCustomEventTypeLoadFile,
|
||||||
|
} SubBruteCustomEvent;
|
582
applications/plugins/subbrute/subbrute_device.c
Normal file
582
applications/plugins/subbrute/subbrute_device.c
Normal file
|
@ -0,0 +1,582 @@
|
||||||
|
#include "subbrute_device.h"
|
||||||
|
#include "subbrute_i.h"
|
||||||
|
|
||||||
|
#include <furi.h>
|
||||||
|
#include <furi_hal.h>
|
||||||
|
#include <furi_hal_subghz.h>
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include <lib/subghz/types.h>
|
||||||
|
#include <lib/subghz/protocols/base.h>
|
||||||
|
|
||||||
|
#include <storage/storage.h>
|
||||||
|
#include <dialogs/dialogs.h>
|
||||||
|
#include <stream/stream.h>
|
||||||
|
#include <stream/buffered_file_stream.h>
|
||||||
|
#include <lib/toolbox/path.h>
|
||||||
|
#include <lib/flipper_format/flipper_format_i.h>
|
||||||
|
|
||||||
|
#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\n";
|
||||||
|
static const char* subbrute_key_file_key = "Key: %s\n";
|
||||||
|
static const char* subbrute_key_file_princeton_end = "TE: %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;
|
||||||
|
instance->dialogs = furi_record_open(RECORD_DIALOGS);
|
||||||
|
|
||||||
|
string_init(instance->load_path);
|
||||||
|
|
||||||
|
string_init(instance->payload);
|
||||||
|
string_init(instance->preset_name);
|
||||||
|
string_init(instance->protocol_name);
|
||||||
|
|
||||||
|
subbrute_device_attack_set_default_values(instance);
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void subbrute_device_free(SubBruteDevice* instance) {
|
||||||
|
furi_assert(instance);
|
||||||
|
|
||||||
|
furi_record_close(RECORD_DIALOGS);
|
||||||
|
|
||||||
|
string_clear(instance->payload);
|
||||||
|
string_clear(instance->load_path);
|
||||||
|
string_clear(instance->preset_name);
|
||||||
|
string_clear(instance->protocol_name);
|
||||||
|
|
||||||
|
string_clear(instance->load_path);
|
||||||
|
|
||||||
|
free(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
SubBruteFileResult subbrute_device_load_protocol_from_file(SubBruteDevice* instance) {
|
||||||
|
furi_assert(instance);
|
||||||
|
|
||||||
|
// Input events and views are managed by file_browser
|
||||||
|
string_t app_directory;
|
||||||
|
string_init_set_str(app_directory, 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, instance->load_path, app_directory, &browser_options);
|
||||||
|
|
||||||
|
string_clear(app_directory);
|
||||||
|
if(res) {
|
||||||
|
load_result = subbrute_device_attack_set(
|
||||||
|
instance, SubBruteAttackLoadFile, string_get_cstr(instance->load_path));
|
||||||
|
if(load_result == SubBruteFileResultOk) {
|
||||||
|
// Ready to run!
|
||||||
|
instance->state = SubBruteDeviceStateReady;
|
||||||
|
FURI_LOG_I(TAG, "Ready to run");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
FURI_LOG_I(TAG, "Returned error: %sd", load_result);
|
||||||
|
// res = false;
|
||||||
|
//
|
||||||
|
// char file_info_message[128];
|
||||||
|
// snprintf(
|
||||||
|
// file_info_message,
|
||||||
|
// sizeof(file_info_message),
|
||||||
|
// "Can not load file\n%s",
|
||||||
|
// (char*)subbrute_device_error_get_desc(set_result));
|
||||||
|
// dialog_message_show_storage_error(instance->dialogs, file_info_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return load_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool subbrute_device_save_file(SubBruteDevice* instance, const char* dev_file_name) {
|
||||||
|
furi_assert(instance);
|
||||||
|
bool result = subbrute_device_create_packet_parsed(instance, instance->key_index);
|
||||||
|
|
||||||
|
if(!result) {
|
||||||
|
//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_string(stream, instance->payload);
|
||||||
|
|
||||||
|
result = true;
|
||||||
|
} while(false);
|
||||||
|
|
||||||
|
buffered_file_stream_close(stream);
|
||||||
|
stream_free(stream);
|
||||||
|
if(!result) {
|
||||||
|
//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, uint8_t step) {
|
||||||
|
furi_assert(instance);
|
||||||
|
|
||||||
|
char step_payload[SUBBRUTE_PAYLOAD_SIZE] = {0};
|
||||||
|
string_reset(instance->payload);
|
||||||
|
|
||||||
|
if(instance->attack == SubBruteAttackLoadFile) {
|
||||||
|
if(step >= sizeof(instance->file_key)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
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), "%02X", step);
|
||||||
|
}
|
||||||
|
|
||||||
|
FURI_LOG_D(TAG, "step_payload: %s, step: %d", step_payload, step);
|
||||||
|
|
||||||
|
if(instance->has_tail) {
|
||||||
|
string_init_printf(
|
||||||
|
instance->payload,
|
||||||
|
instance->file_template,
|
||||||
|
step_payload,
|
||||||
|
instance->te);
|
||||||
|
} else {
|
||||||
|
string_init_printf(
|
||||||
|
instance->payload, instance->file_template, step_payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SubBruteFileResult subbrute_device_attack_set(
|
||||||
|
SubBruteDevice* instance,
|
||||||
|
SubBruteAttacks type,
|
||||||
|
const char* file_path) {
|
||||||
|
furi_assert(instance);
|
||||||
|
subbrute_device_attack_set_default_values(instance);
|
||||||
|
uint8_t file_result;
|
||||||
|
|
||||||
|
switch(type) {
|
||||||
|
case SubBruteAttackLoadFile:
|
||||||
|
file_result = subbrute_device_load_from_file(instance, file_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(instance->preset_name);
|
||||||
|
protocol_check_result = SubBruteFileResultOk;
|
||||||
|
}
|
||||||
|
|
||||||
|
subghz_environment_free(instance->environment);
|
||||||
|
subghz_receiver_free(instance->receiver);
|
||||||
|
|
||||||
|
if(protocol_check_result != SubBruteFileResultOk) {
|
||||||
|
return SubBruteFileResultProtocolNotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(strcmp(string_get_cstr(instance->protocol_name), protocol_princeton) == 0) {
|
||||||
|
instance->has_tail = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
return SubBruteFileResultOk;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t subbrute_device_load_from_file(SubBruteDevice* instance, const char* file_path) {
|
||||||
|
furi_assert(instance);
|
||||||
|
|
||||||
|
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, file_path)) {
|
||||||
|
FURI_LOG_E(TAG, "Error open file %s", 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", instance->preset_name)) {
|
||||||
|
FURI_LOG_E(TAG, "Preset FAIL");
|
||||||
|
result = SubBruteFileResultPresetInvalid;
|
||||||
|
}
|
||||||
|
// Protocol
|
||||||
|
if(!flipper_format_read_string(
|
||||||
|
fff_data_file, "Protocol", instance->protocol_name)) {
|
||||||
|
FURI_LOG_E(TAG, "Missing Protocol");
|
||||||
|
result = SubBruteFileResultMissingProtocol;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#ifdef FURI_DEBUG
|
||||||
|
else {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
if(result == SubBruteFileResultOk) {
|
||||||
|
#ifdef FURI_DEBUG
|
||||||
|
FURI_LOG_D(TAG, "Loaded successfully");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void subbrute_device_attack_set_default_values(SubBruteDevice* instance) {
|
||||||
|
furi_assert(instance);
|
||||||
|
|
||||||
|
instance->attack = SubBruteAttackNone;
|
||||||
|
instance->max_value = 0;
|
||||||
|
instance->key_index = 0;
|
||||||
|
|
||||||
|
memset(instance->file_template, 0, sizeof(instance->file_template));
|
||||||
|
memset(instance->current_key, 0, sizeof(instance->current_key));
|
||||||
|
memset(instance->file_key, 0, sizeof(instance->file_key));
|
||||||
|
|
||||||
|
string_set_str(instance->protocol_name, protocol_raw);
|
||||||
|
|
||||||
|
string_set_str(instance->preset_name, preset_ook650_async);
|
||||||
|
instance->preset = FuriHalSubGhzPresetOok650Async;
|
||||||
|
|
||||||
|
string_reset(instance->payload);
|
||||||
|
|
||||||
|
instance->repeat = 5;
|
||||||
|
instance->te = 0;
|
||||||
|
instance->has_tail = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
FuriHalSubGhzPreset subbrute_device_convert_preset(string_t preset) {
|
||||||
|
if(string_cmp_str(preset, preset_ook270_async) == 0) {
|
||||||
|
return FuriHalSubGhzPresetOok270Async;
|
||||||
|
}
|
||||||
|
if(string_cmp_str(preset, preset_ook650_async) == 0) {
|
||||||
|
return FuriHalSubGhzPresetOok650Async;
|
||||||
|
}
|
||||||
|
if(string_cmp_str(preset, preset_2fsk_dev238_async) == 0) {
|
||||||
|
return FuriHalSubGhzPreset2FSKDev238Async;
|
||||||
|
}
|
||||||
|
if(string_cmp_str(preset, preset_2fsk_dev476_async) == 0) {
|
||||||
|
return FuriHalSubGhzPreset2FSKDev476Async;
|
||||||
|
}
|
||||||
|
if(string_cmp_str(preset, preset_msk99_97_kb_async) == 0) {
|
||||||
|
return FuriHalSubGhzPresetMSK99_97KbAsync;
|
||||||
|
}
|
||||||
|
if(string_cmp_str(preset, preset_gfs99_97_kb_async) == 0) {
|
||||||
|
return FuriHalSubGhzPresetMSK99_97KbAsync;
|
||||||
|
}
|
||||||
|
return FuriHalSubGhzPresetCustom;
|
||||||
|
}
|
103
applications/plugins/subbrute/subbrute_device.h
Normal file
103
applications/plugins/subbrute/subbrute_device.h
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <lib/toolbox/stream/stream.h>
|
||||||
|
#include <gui/gui.h>
|
||||||
|
#include <dialogs/dialogs.h>
|
||||||
|
#include <lib/subghz/protocols/base.h>
|
||||||
|
#include <lib/subghz/transmitter.h>
|
||||||
|
#include <lib/subghz/receiver.h>
|
||||||
|
#include <lib/subghz/environment.h>
|
||||||
|
|
||||||
|
#define SUBBRUTE_TEXT_STORE_SIZE 128
|
||||||
|
|
||||||
|
#define SUBBRUTE_MAX_LEN_NAME 64
|
||||||
|
#define SUBBRUTE_PATH EXT_PATH("subghz")
|
||||||
|
#define SUBBRUTE_FILE_EXT ".sub"
|
||||||
|
|
||||||
|
#define SUBBRUTE_PAYLOAD_SIZE 8
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SubBruteAttackNone,
|
||||||
|
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 {
|
||||||
|
DialogsApp* dialogs;
|
||||||
|
SubBruteDeviceState state;
|
||||||
|
|
||||||
|
// Current step
|
||||||
|
uint8_t key_index;
|
||||||
|
string_t load_path;
|
||||||
|
|
||||||
|
SubGhzReceiver* receiver;
|
||||||
|
SubGhzProtocolDecoderBase* decoder_result;
|
||||||
|
SubGhzEnvironment* environment;
|
||||||
|
|
||||||
|
// Attack state
|
||||||
|
SubBruteAttacks attack;
|
||||||
|
char file_template[SUBBRUTE_TEXT_STORE_SIZE];
|
||||||
|
bool has_tail;
|
||||||
|
string_t payload;
|
||||||
|
uint8_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_PAYLOAD_SIZE];
|
||||||
|
char text_store[SUBBRUTE_PAYLOAD_SIZE];
|
||||||
|
} SubBruteDevice;
|
||||||
|
|
||||||
|
SubBruteDevice* subbrute_device_alloc();
|
||||||
|
void subbrute_device_free(SubBruteDevice* instance);
|
||||||
|
SubBruteFileResult subbrute_device_load_protocol_from_file(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, uint8_t step);
|
||||||
|
SubBruteFileResult subbrute_device_attack_set(
|
||||||
|
SubBruteDevice* context,
|
||||||
|
SubBruteAttacks type,
|
||||||
|
const char* file_path);
|
||||||
|
uint8_t subbrute_device_load_from_file(SubBruteDevice* context, const char* file_path);
|
||||||
|
FuriHalSubGhzPreset subbrute_device_convert_preset(string_t preset);
|
||||||
|
void subbrute_device_attack_set_default_values(SubBruteDevice* context);
|
87
applications/plugins/subbrute/subbrute_i.h
Normal file
87
applications/plugins/subbrute/subbrute_i.h
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <furi.h>
|
||||||
|
#include <furi_hal.h>
|
||||||
|
#include <input/input.h>
|
||||||
|
|
||||||
|
#include "lib/toolbox/path.h"
|
||||||
|
#include <notification/notification.h>
|
||||||
|
#include <notification/notification_messages.h>
|
||||||
|
#include <m-string.h>
|
||||||
|
|
||||||
|
#include <lib/toolbox/stream/stream.h>
|
||||||
|
#include <stream_buffer.h>
|
||||||
|
|
||||||
|
#include <gui/gui.h>
|
||||||
|
#include <gui/view_dispatcher.h>
|
||||||
|
#include <gui/view_stack.h>
|
||||||
|
#include <gui/scene_manager.h>
|
||||||
|
#include <gui/modules/text_input.h>
|
||||||
|
#include <gui/modules/popup.h>
|
||||||
|
#include <gui/modules/widget.h>
|
||||||
|
#include <gui/modules/loading.h>
|
||||||
|
|
||||||
|
#include <dialogs/dialogs.h>
|
||||||
|
|
||||||
|
#include <lib/subghz/protocols/base.h>
|
||||||
|
#include <lib/subghz/transmitter.h>
|
||||||
|
#include <lib/subghz/receiver.h>
|
||||||
|
#include <lib/subghz/environment.h>
|
||||||
|
#include <notification/notification.h>
|
||||||
|
#include <notification/notification_messages.h>
|
||||||
|
|
||||||
|
#include "subbrute_device.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;
|
||||||
|
|
||||||
|
//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);
|
||||||
|
|
||||||
|
int32_t subbrute_app(void* p);
|
||||||
|
SubBruteState* subbrute_alloc();
|
||||||
|
void subbrute_free(SubBruteState* instance);
|
||||||
|
bool subbrute_custom_event_callback(void* context, uint32_t event);
|
||||||
|
bool subbrute_back_event_callback(void* context);
|
||||||
|
void subbrute_tick_event_callback(void* context);
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -1,4 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include "subbrute.h"
|
|
||||||
|
|
||||||
bool subbrute_is_frequency_allowed(SubBruteState* context);
|
|
321
applications/plugins/subbrute/views/subbrute_attack_view.c
Normal file
321
applications/plugins/subbrute/views/subbrute_attack_view.c
Normal file
|
@ -0,0 +1,321 @@
|
||||||
|
#include "subbrute_attack_view.h"
|
||||||
|
#include "../subbrute_i.h"
|
||||||
|
#include "../helpers/subbrute_worker.h"
|
||||||
|
|
||||||
|
#include "assets_icons.h"
|
||||||
|
#include "../../../services/gui/icon_i.h"
|
||||||
|
#include <input/input.h>
|
||||||
|
#include <gui/elements.h>
|
||||||
|
#include <gui/icon_i.h>
|
||||||
|
|
||||||
|
struct SubBruteAttackView {
|
||||||
|
View* view;
|
||||||
|
SubBruteAttackViewCallback callback;
|
||||||
|
void* context;
|
||||||
|
SubBruteWorker* worker;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
SubBruteAttacks index;
|
||||||
|
uint8_t max_value;
|
||||||
|
uint8_t current_step;
|
||||||
|
bool is_attacking;
|
||||||
|
} 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);
|
||||||
|
|
||||||
|
SubBruteAttackView* instance = context;
|
||||||
|
|
||||||
|
if(event->key == InputKeyBack && event->type == InputTypeShort) {
|
||||||
|
instance->callback(SubBruteCustomEventTypeBackPressed, instance->context);
|
||||||
|
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->key == InputKeyOk) {
|
||||||
|
with_view_model(
|
||||||
|
instance->view, (SubBruteAttackViewModel * model) {
|
||||||
|
model->is_attacking = true;
|
||||||
|
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 == InputTypePress) {
|
||||||
|
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) {
|
||||||
|
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 == InputTypeShort || event->type == InputTypeRepeat) &&
|
||||||
|
(event->key == InputKeyOk || event->key == InputKeyBack)) {
|
||||||
|
with_view_model(
|
||||||
|
instance->view, (SubBruteAttackViewModel * model) {
|
||||||
|
model->is_attacking = false;
|
||||||
|
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);
|
||||||
|
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);
|
||||||
|
|
||||||
|
instance->worker = subbrute_worker_alloc();
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void subbrute_attack_view_enter(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
void subbrute_attack_view_free(SubBruteAttackView* instance) {
|
||||||
|
furi_assert(instance);
|
||||||
|
|
||||||
|
subbrute_worker_free(instance->worker);
|
||||||
|
|
||||||
|
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, uint8_t current_step) {
|
||||||
|
with_view_model(
|
||||||
|
instance->view, (SubBruteAttackViewModel * model) {
|
||||||
|
model->current_step = current_step;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t subbrute_attack_view_get_current_step(SubBruteAttackView* instance) {
|
||||||
|
uint8_t current_step;
|
||||||
|
with_view_model(
|
||||||
|
instance->view, (SubBruteAttackViewModel * model) {
|
||||||
|
current_step = model->current_step;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
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,
|
||||||
|
uint8_t max_value,
|
||||||
|
uint8_t current_step) {
|
||||||
|
with_view_model(
|
||||||
|
instance->view, (SubBruteAttackViewModel * model) {
|
||||||
|
model->max_value = max_value;
|
||||||
|
model->index = index;
|
||||||
|
model->current_step = current_step;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void subbrute_attack_view_stop_worker(SubBruteAttackView* instance) {
|
||||||
|
furi_assert(instance);
|
||||||
|
subbrute_worker_stop(instance->worker);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool subbrute_attack_view_can_send(SubBruteAttackView* instance) {
|
||||||
|
furi_assert(instance);
|
||||||
|
|
||||||
|
return subbrute_worker_can_transmit(instance->worker);
|
||||||
|
}
|
||||||
|
|
||||||
|
void subbrute_attack_view_start_worker(
|
||||||
|
SubBruteAttackView* instance,
|
||||||
|
uint32_t frequency,
|
||||||
|
FuriHalSubGhzPreset preset,
|
||||||
|
string_t protocol_name) {
|
||||||
|
furi_assert(instance);
|
||||||
|
if(!subbrute_worker_is_running(instance->worker)) {
|
||||||
|
subbrute_worker_start(instance->worker, frequency, preset, protocol_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool subbrute_attack_view_transmit(SubBruteAttackView* instance, string_t payload) {
|
||||||
|
furi_assert(instance);
|
||||||
|
|
||||||
|
return subbrute_worker_transmit(instance->worker, payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool subbrute_attack_view_is_worker_running(SubBruteAttackView* instance) {
|
||||||
|
furi_assert(instance);
|
||||||
|
|
||||||
|
return subbrute_worker_is_running(instance->worker);
|
||||||
|
}
|
||||||
|
|
||||||
|
void subbrute_attack_view_exit(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
SubBruteAttackView* instance = context;
|
||||||
|
|
||||||
|
// Just stop, make free in free method
|
||||||
|
subbrute_worker_stop(instance->worker);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = 3;
|
||||||
|
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;
|
||||||
|
|
||||||
|
const uint8_t x = 0;
|
||||||
|
const uint8_t y = 0;
|
||||||
|
|
||||||
|
canvas_draw_box(canvas, x, y - button_height, button_width, button_height);
|
||||||
|
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 = 3;
|
||||||
|
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;
|
||||||
|
|
||||||
|
const uint8_t x = canvas_width(canvas);
|
||||||
|
const uint8_t y = 0;
|
||||||
|
|
||||||
|
canvas_draw_box(canvas, x - button_width, y - button_height, button_width, button_height);
|
||||||
|
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 = context;
|
||||||
|
//char buffer[64];
|
||||||
|
|
||||||
|
// Title
|
||||||
|
const char* attack_name = NULL;
|
||||||
|
attack_name = subbrute_get_menu_name(model->index);
|
||||||
|
canvas_set_color(canvas, ColorBlack);
|
||||||
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
canvas_draw_str(canvas, 20, 8, attack_name ? attack_name : "Sub-GHz Bruteforcer");
|
||||||
|
|
||||||
|
// Progress bar
|
||||||
|
// Resolution: 128x64 px
|
||||||
|
float progress_value = model->max_value / model->current_step;
|
||||||
|
elements_progress_bar(canvas, 8, 30, 98, progress_value > 1 ? 1 : progress_value);
|
||||||
|
|
||||||
|
// Selected attack type
|
||||||
|
// const char* attack_name = NULL;
|
||||||
|
// attack_name = subbrute_get_menu_name(model->index);
|
||||||
|
//
|
||||||
|
// canvas_set_font(canvas, FontSecondary);
|
||||||
|
// if(attack_name) {
|
||||||
|
// snprintf(buffer, sizeof(buffer), "%s", attack_name);
|
||||||
|
// } else {
|
||||||
|
// snprintf(buffer, sizeof(buffer), "%s", "Unknown";
|
||||||
|
// }
|
||||||
|
// canvas_draw_str(canvas, 9, 42, buffer);
|
||||||
|
|
||||||
|
if(!model->is_attacking) {
|
||||||
|
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, "Repeat");
|
||||||
|
} else {
|
||||||
|
elements_button_center(canvas, "Stop");
|
||||||
|
}
|
||||||
|
}
|
36
applications/plugins/subbrute/views/subbrute_attack_view.h
Normal file
36
applications/plugins/subbrute/views/subbrute_attack_view.h
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gui/view.h>
|
||||||
|
#include "assets_icons.h"
|
||||||
|
#include <input/input.h>
|
||||||
|
#include <gui/elements.h>
|
||||||
|
#include <gui/icon.h>
|
||||||
|
#include <subghz/types.h>
|
||||||
|
#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, uint8_t current_step);
|
||||||
|
uint8_t subbrute_attack_view_get_current_step(SubBruteAttackView* instance);
|
||||||
|
void subbrute_attack_view_init_values(
|
||||||
|
SubBruteAttackView* instance,
|
||||||
|
uint8_t index,
|
||||||
|
uint8_t max_value,
|
||||||
|
uint8_t current_step);
|
||||||
|
void subbrute_attack_view_stop_worker(SubBruteAttackView* instance);
|
||||||
|
bool subbrute_attack_view_can_send(SubBruteAttackView* instance);
|
||||||
|
void subbrute_attack_view_start_worker(
|
||||||
|
SubBruteAttackView* instance,
|
||||||
|
uint32_t frequency,
|
||||||
|
FuriHalSubGhzPreset preset,
|
||||||
|
string_t protocol_name);
|
||||||
|
bool subbrute_attack_view_transmit(SubBruteAttackView* instance, string_t payload);
|
||||||
|
bool subbrute_attack_view_is_worker_running(SubBruteAttackView* instance);
|
152
applications/plugins/subbrute/views/subbrute_main_view.c
Normal file
152
applications/plugins/subbrute/views/subbrute_main_view.c
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
#include "subbrute_main_view.h"
|
||||||
|
#include "../subbrute_i.h"
|
||||||
|
|
||||||
|
#include <input/input.h>
|
||||||
|
#include <gui/elements.h>
|
||||||
|
#include <gui/icon.h>
|
||||||
|
|
||||||
|
#define STATUS_BAR_Y_SHIFT 13
|
||||||
|
|
||||||
|
struct SubBruteMainView {
|
||||||
|
View* view;
|
||||||
|
SubBruteMainViewCallback callback;
|
||||||
|
void* context;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t index;
|
||||||
|
} 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 subbrute_main_view_draw(Canvas* canvas, SubBruteMainViewModel* model) {
|
||||||
|
SubBruteMainViewModel* m = model;
|
||||||
|
|
||||||
|
canvas_set_color(canvas, ColorBlack);
|
||||||
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
|
||||||
|
for(uint8_t i = 1; i < SubBruteAttackTotalCount - 1; ++i) {
|
||||||
|
const char* str = subbrute_get_menu_name(i);
|
||||||
|
canvas_draw_str_aligned(
|
||||||
|
canvas, 64, 9 + (i * 17) + STATUS_BAR_Y_SHIFT, AlignCenter, AlignCenter, str);
|
||||||
|
|
||||||
|
if(m->index == i) {
|
||||||
|
elements_frame(canvas, 15, 1 + (i * 17) + STATUS_BAR_Y_SHIFT, 98, 15);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool subbrute_main_view_input(InputEvent* event, void* context) {
|
||||||
|
furi_assert(event);
|
||||||
|
furi_assert(context);
|
||||||
|
|
||||||
|
SubBruteMainView* instance = context;
|
||||||
|
const uint8_t min_value = SubBruteAttackNone + 1;
|
||||||
|
const uint8_t correct_total = SubBruteAttackTotalCount - 1;
|
||||||
|
//uint8_t idx = min_value;
|
||||||
|
bool consumed = false;
|
||||||
|
if((event->type == InputTypeShort) || (event->type == InputTypeRepeat)) {
|
||||||
|
with_view_model(
|
||||||
|
instance->view, (SubBruteMainViewModel * model) {
|
||||||
|
bool ret = false;
|
||||||
|
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->index++;
|
||||||
|
}
|
||||||
|
//idx = model->index;
|
||||||
|
return ret;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(event->key == InputKeyOk && event->type == InputTypeShort) {
|
||||||
|
/*if(idx == SubBruteAttackLoadFile) {
|
||||||
|
instance->callback(SubBruteCustomEventTypeLoadFile, instance->context);
|
||||||
|
} else if(idx > SubBruteAttackNone && idx < SubBruteAttackLoadFile) {*/
|
||||||
|
instance->callback(SubBruteCustomEventTypeMenuSelected, instance->context);
|
||||||
|
/*}*/
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void subbrute_main_view_enter(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
void subbrute_main_view_exit(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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) {
|
||||||
|
furi_assert(instance);
|
||||||
|
furi_assert(idx < SubBruteAttackTotalCount - 2);
|
||||||
|
with_view_model(
|
||||||
|
instance->view, (SubBruteMainViewModel * model) {
|
||||||
|
model->index = idx <= 0 ? 1 : idx;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
SubBruteAttacks subbrute_main_view_get_index(SubBruteMainView* instance) {
|
||||||
|
furi_assert(instance);
|
||||||
|
|
||||||
|
uint8_t attack = 0;
|
||||||
|
with_view_model(
|
||||||
|
instance->view, (SubBruteMainViewModel * model) {
|
||||||
|
attack = model->index;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
return attack;
|
||||||
|
}
|
28
applications/plugins/subbrute/views/subbrute_main_view.h
Normal file
28
applications/plugins/subbrute/views/subbrute_main_view.h
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../subbrute_custom_event.h"
|
||||||
|
#include <gui/view.h>
|
||||||
|
#include "assets_icons.h"
|
||||||
|
#include <input/input.h>
|
||||||
|
#include <gui/elements.h>
|
||||||
|
#include <gui/icon.h>
|
||||||
|
|
||||||
|
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);
|
||||||
|
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);
|
Loading…
Reference in a new issue