mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2025-12-13 13:09:49 +04:00
Compare commits
95 Commits
un3-60bce7
...
un1-a6b98c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5e30b14d90 | ||
|
|
c07e3a34dd | ||
|
|
be7e11e60f | ||
|
|
e96e414561 | ||
|
|
0c99cb52ec | ||
|
|
ad9e1ce4df | ||
|
|
22dc5190d1 | ||
|
|
f2fd97d9c5 | ||
|
|
08084d5763 | ||
|
|
add1ad6949 | ||
|
|
87654e60b8 | ||
|
|
6f92cd645e | ||
|
|
23f6ea2e05 | ||
|
|
a6b98ccbbe | ||
|
|
ba5f590dab | ||
|
|
f1048733d2 | ||
|
|
ea7f68fcab | ||
|
|
8013aacd94 | ||
|
|
be8f409098 | ||
|
|
97e6fe8f4e | ||
|
|
54757428e6 | ||
|
|
bd39d81324 | ||
|
|
2a2078d9b5 | ||
|
|
01ca588488 | ||
|
|
f86eada292 | ||
|
|
bc777b2eff | ||
|
|
6f91fa42f0 | ||
|
|
e6d22ed147 | ||
|
|
436f70b69b | ||
|
|
ec9ce0cad7 | ||
|
|
7e2008095e | ||
|
|
92e440c77d | ||
|
|
666821e9ce | ||
|
|
1bca477a43 | ||
|
|
41571ce9ad | ||
|
|
038d098c85 | ||
|
|
b03cc8ddc3 | ||
|
|
c8e3d9b040 | ||
|
|
aeb02500de | ||
|
|
eadd7801af | ||
|
|
6d2b0a3b6c | ||
|
|
32a7642761 | ||
|
|
e8bb45496d | ||
|
|
3846852f2b | ||
|
|
17d01f5c29 | ||
|
|
e6bcba6959 | ||
|
|
e13edc2f70 | ||
|
|
de6ff1d9c9 | ||
|
|
bea15134ba | ||
|
|
28a55bf576 | ||
|
|
e70121e20f | ||
|
|
432ff41d6a | ||
|
|
87393a086c | ||
|
|
6000d47a0f | ||
|
|
d986ef4104 | ||
|
|
f85dc1675d | ||
|
|
7c7ac07e6a | ||
|
|
ca02826cfd | ||
|
|
96ad7f3cef | ||
|
|
c213ff596a | ||
|
|
b2589698ff | ||
|
|
3360f818a1 | ||
|
|
066da4080b | ||
|
|
b2c118f267 | ||
|
|
a8db46124e | ||
|
|
672e27f258 | ||
|
|
e762a68265 | ||
|
|
8659becc9d | ||
|
|
82e1e8af6a | ||
|
|
a71d05a114 | ||
|
|
8d68bf62a5 | ||
|
|
2c85adb270 | ||
|
|
e2123c55bb | ||
|
|
f5ff6438d1 | ||
|
|
9f3b80e606 | ||
|
|
111656d2c1 | ||
|
|
2045a29d3f | ||
|
|
26e46f9267 | ||
|
|
d003db0404 | ||
|
|
5a31e35dc2 | ||
|
|
c7cd5721ed | ||
|
|
fb476c29e6 | ||
|
|
d80329b323 | ||
|
|
3d3c422751 | ||
|
|
ed385594a3 | ||
|
|
787df44c79 | ||
|
|
f0eedc3243 | ||
|
|
365d055dc5 | ||
|
|
650ba8a91f | ||
|
|
654d6dc6ec | ||
|
|
18b70ac6b5 | ||
|
|
51369d6219 | ||
|
|
181533df1b | ||
|
|
d85731636f | ||
|
|
6f66f87fab |
1
.github/CODEOWNERS
vendored
Normal file
1
.github/CODEOWNERS
vendored
Normal file
@@ -0,0 +1 @@
|
||||
* @xMasterX
|
||||
4
.vscode/example/launch.json
vendored
4
.vscode/example/launch.json
vendored
@@ -9,6 +9,10 @@
|
||||
"type": "command",
|
||||
"command": "shellCommand.execute",
|
||||
"args": {
|
||||
"useSingleResult": true,
|
||||
"env": {
|
||||
"PATH": "${workspaceFolder};${env:PATH}"
|
||||
},
|
||||
"command": "./fbt get_blackmagic",
|
||||
"description": "Get Blackmagic device",
|
||||
}
|
||||
|
||||
15
CHANGELOG.md
15
CHANGELOG.md
@@ -1,15 +1,16 @@
|
||||
### New changes
|
||||
* Fixed all known issues with SubGHz settings (broken menu when you select last item in read and go into read raw config, issues with hopper and detect raw modes, bugs with frequency selector) (@xMasterX)
|
||||
* Removed subghz last settings saving feature (may be returned in future) (caused issues with freq selector, hopper mode)
|
||||
* Updated universal IR assets (by @Amec0e)
|
||||
* PR: SubGHz bruteforcer plugin - deep refactoring (huge thanks to @derskythe ! | PR #75)
|
||||
* OFW: Preliminary Rust support
|
||||
|
||||
#### **DFU files no longer included in releases to avoid issues with wrong manual installation of assets - use web updater or microSD update package**
|
||||
#### **DFU files no longer included in releases to avoid issues with wrong manual installation of assets - use .tgz file with qFlipper, or install automatically via web updater or use microSD update package**
|
||||
|
||||
[- How to install](https://github.com/Eng1n33r/flipperzero-firmware/blob/dev/documentation/HowToInstall.md)
|
||||
|
||||
**Note: To avoid issues prefer installing using web updater or by self update package, all needed assets will be installed**
|
||||
[- Download qFlipper 1.2.0 (allows .tgz installation) (official link)](https://update.flipperzero.one/builds/qFlipper/1.2.0/)
|
||||
|
||||
Self-update package (update from microSD) - `flipper-z-f7-update-(version).zip` or `.tgz` for iOS mobile app
|
||||
**Note: To avoid issues with .dfu, prefer installing using .tgz with qFlipper, web updater or by self update package, all needed assets will be installed**
|
||||
|
||||
DFU for update using qFlipper is no longer included in releases to avoid issues with assets - Use Web Updater or self-update package!
|
||||
Self-update package (update from microSD) - `flipper-z-f7-update-(version).zip` or download `.tgz` for iOS mobile app / qFlipper
|
||||
|
||||
Update using qFlipper (1.2.0) is now possible with `.tgz` update package! Also you can use Web Updater or self-update package.
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ See changelog in releases for latest updates!
|
||||
- Keeloq [Not ALL systems supported yet!]
|
||||
- Nice Flor S
|
||||
- Security+ v1 & v2
|
||||
- Star Line
|
||||
- Star Line (saving only)
|
||||
|
||||
## Support us so we can buy equipment and develop new features
|
||||
* ETH/BSC/ERC20-Tokens: `0xFebF1bBc8229418FF2408C07AF6Afa49152fEc6a`
|
||||
@@ -87,7 +87,6 @@ Games:
|
||||
- BadUSB -> Keyboard layouts [(by rien > dummy-decoy)](https://github.com/dummy-decoy/flipperzero-firmware/tree/dummy_decoy/bad_usb_keyboard_layout)
|
||||
- SubGHz -> New frequency analyzer - [(by ClusterM)](https://github.com/ClusterM)
|
||||
- SubGHz -> Detect RAW feature - [(by perspecdev)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/152)
|
||||
- SubGHz -> Save last used config settings - [(by derskythe)](https://github.com/Eng1n33r/flipperzero-firmware/pull/67)
|
||||
|
||||
# Instructions
|
||||
## [- How to install firmware](https://github.com/Eng1n33r/flipperzero-firmware/blob/dev/documentation/HowToInstall.md)
|
||||
@@ -167,4 +166,4 @@ Games:
|
||||
- `site_scons` - Build helpers
|
||||
- `scripts` - Supplementary scripts and python libraries home
|
||||
|
||||
Also pay attention to `ReadMe.md` files inside of those directories.
|
||||
Also pay attention to `ReadMe.md` files inside those directories.
|
||||
|
||||
13
SConstruct
13
SConstruct
@@ -44,6 +44,8 @@ distenv = coreenv.Clone(
|
||||
"target extended-remote ${GDBREMOTE}",
|
||||
"-ex",
|
||||
"set confirm off",
|
||||
"-ex",
|
||||
"set pagination off",
|
||||
],
|
||||
GDBOPTS_BLACKMAGIC=[
|
||||
"-ex",
|
||||
@@ -234,10 +236,19 @@ distenv.PhonyTarget(
|
||||
distenv.PhonyTarget(
|
||||
"debug_other",
|
||||
"${GDBPYCOM}",
|
||||
GDBPYOPTS='-ex "source debug/PyCortexMDebug/PyCortexMDebug.py" ',
|
||||
GDBOPTS="${GDBOPTS_BASE}",
|
||||
GDBREMOTE="${OPENOCD_GDB_PIPE}",
|
||||
GDBPYOPTS='-ex "source debug/PyCortexMDebug/PyCortexMDebug.py" ',
|
||||
)
|
||||
|
||||
distenv.PhonyTarget(
|
||||
"debug_other_blackmagic",
|
||||
"${GDBPYCOM}",
|
||||
GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}",
|
||||
GDBREMOTE="$${BLACKMAGIC_ADDR}",
|
||||
)
|
||||
|
||||
|
||||
# Just start OpenOCD
|
||||
distenv.PhonyTarget(
|
||||
"openocd",
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <storage/storage.h>
|
||||
#include <lib/flipper_format/flipper_format.h>
|
||||
#include <lib/nfc/protocols/nfca.h>
|
||||
#include <lib/nfc/helpers/mf_classic_dict.h>
|
||||
#include <lib/digital_signal/digital_signal.h>
|
||||
|
||||
#include <lib/flipper_format/flipper_format_i.h>
|
||||
@@ -170,10 +171,59 @@ MU_TEST(nfc_digital_signal_test) {
|
||||
"NFC long digital signal test failed\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(mf_classic_dict_test) {
|
||||
MfClassicDict* instance = NULL;
|
||||
uint64_t key = 0;
|
||||
string_t temp_str;
|
||||
string_init(temp_str);
|
||||
|
||||
instance = mf_classic_dict_alloc(MfClassicDictTypeUnitTest);
|
||||
mu_assert(instance != NULL, "mf_classic_dict_alloc\r\n");
|
||||
|
||||
mu_assert(
|
||||
mf_classic_dict_get_total_keys(instance) == 0,
|
||||
"mf_classic_dict_get_total_keys == 0 assert failed\r\n");
|
||||
|
||||
string_set(temp_str, "2196FAD8115B");
|
||||
mu_assert(
|
||||
mf_classic_dict_add_key_str(instance, temp_str),
|
||||
"mf_classic_dict_add_key == true assert failed\r\n");
|
||||
|
||||
mu_assert(
|
||||
mf_classic_dict_get_total_keys(instance) == 1,
|
||||
"mf_classic_dict_get_total_keys == 1 assert failed\r\n");
|
||||
|
||||
mu_assert(mf_classic_dict_rewind(instance), "mf_classic_dict_rewind == 1 assert failed\r\n");
|
||||
|
||||
mu_assert(
|
||||
mf_classic_dict_get_key_at_index_str(instance, temp_str, 0),
|
||||
"mf_classic_dict_get_key_at_index_str == true assert failed\r\n");
|
||||
mu_assert(
|
||||
string_cmp(temp_str, "2196FAD8115B") == 0,
|
||||
"string_cmp(temp_str, \"2196FAD8115B\") == 0 assert failed\r\n");
|
||||
|
||||
mu_assert(mf_classic_dict_rewind(instance), "mf_classic_dict_rewind == 1 assert failed\r\n");
|
||||
|
||||
mu_assert(
|
||||
mf_classic_dict_get_key_at_index(instance, &key, 0),
|
||||
"mf_classic_dict_get_key_at_index == true assert failed\r\n");
|
||||
mu_assert(key == 0x2196FAD8115B, "key == 0x2196FAD8115B assert failed\r\n");
|
||||
|
||||
mu_assert(mf_classic_dict_rewind(instance), "mf_classic_dict_rewind == 1 assert failed\r\n");
|
||||
|
||||
mu_assert(
|
||||
mf_classic_dict_delete_index(instance, 0),
|
||||
"mf_classic_dict_delete_index == true assert failed\r\n");
|
||||
|
||||
mf_classic_dict_free(instance);
|
||||
string_clear(temp_str);
|
||||
}
|
||||
|
||||
MU_TEST_SUITE(nfc) {
|
||||
nfc_test_alloc();
|
||||
|
||||
MU_RUN_TEST(nfc_digital_signal_test);
|
||||
MU_RUN_TEST(mf_classic_dict_test);
|
||||
|
||||
nfc_test_free();
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#define CAME_ATOMO_DIR_NAME EXT_PATH("subghz/assets/came_atomo")
|
||||
#define NICE_FLOR_S_DIR_NAME EXT_PATH("subghz/assets/nice_flor_s")
|
||||
#define TEST_RANDOM_DIR_NAME EXT_PATH("unit_tests/subghz/test_random_raw.sub")
|
||||
#define TEST_RANDOM_COUNT_PARSE 232
|
||||
#define TEST_RANDOM_COUNT_PARSE 233
|
||||
#define TEST_TIMEOUT 10000
|
||||
|
||||
static SubGhzEnvironment* environment_handler;
|
||||
@@ -434,6 +434,13 @@ MU_TEST(subghz_decoder_clemsa_test) {
|
||||
"Test decoder " SUBGHZ_PROTOCOL_CLEMSA_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_decoder_oregon2_test) {
|
||||
mu_assert(
|
||||
subghz_decoder_test(
|
||||
EXT_PATH("unit_tests/subghz/oregon2_raw.sub"), SUBGHZ_PROTOCOL_OREGON2_NAME),
|
||||
"Test decoder " SUBGHZ_PROTOCOL_OREGON2_NAME " error\r\n");
|
||||
}
|
||||
|
||||
//test encoders
|
||||
MU_TEST(subghz_encoder_princeton_test) {
|
||||
mu_assert(
|
||||
@@ -595,6 +602,7 @@ MU_TEST_SUITE(subghz) {
|
||||
MU_RUN_TEST(subghz_decoder_magellen_test);
|
||||
MU_RUN_TEST(subghz_decoder_intertechno_v3_test);
|
||||
MU_RUN_TEST(subghz_decoder_clemsa_test);
|
||||
MU_RUN_TEST(subghz_decoder_oregon2_test);
|
||||
|
||||
MU_RUN_TEST(subghz_encoder_princeton_test);
|
||||
MU_RUN_TEST(subghz_encoder_came_test);
|
||||
|
||||
@@ -15,6 +15,23 @@ App(
|
||||
"archive",
|
||||
"clock",
|
||||
"unirfremix",
|
||||
"spectrum_analyzer",
|
||||
],
|
||||
)
|
||||
|
||||
App(
|
||||
appid="main_apps_default",
|
||||
name="Basic applications for main menu",
|
||||
apptype=FlipperAppType.METAPACKAGE,
|
||||
provides=[
|
||||
"gpio",
|
||||
#"ibutton",
|
||||
"infrared",
|
||||
"lfrfid",
|
||||
"nfc",
|
||||
"subghz",
|
||||
#"bad_usb",
|
||||
#"u2f",
|
||||
"fap_loader",
|
||||
"archive",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -16,6 +16,7 @@ static const char* tab_default_paths[] = {
|
||||
[ArchiveTabInfrared] = ANY_PATH("infrared"),
|
||||
[ArchiveTabBadUsb] = ANY_PATH("badusb"),
|
||||
[ArchiveTabU2f] = "/app:u2f",
|
||||
[ArchiveTabApps] = ANY_PATH("apps"),
|
||||
[ArchiveTabBrowser] = STORAGE_ANY_PATH_PREFIX,
|
||||
};
|
||||
|
||||
@@ -27,6 +28,7 @@ static const char* known_ext[] = {
|
||||
[ArchiveFileTypeInfrared] = ".ir",
|
||||
[ArchiveFileTypeBadUsb] = ".txt",
|
||||
[ArchiveFileTypeU2f] = "?",
|
||||
[ArchiveFileTypeApps] = ".fap",
|
||||
[ArchiveFileTypeUpdateManifest] = ".fuf",
|
||||
[ArchiveFileTypeFolder] = "?",
|
||||
[ArchiveFileTypeUnknown] = "*",
|
||||
@@ -41,6 +43,7 @@ static const ArchiveFileTypeEnum known_type[] = {
|
||||
[ArchiveTabInfrared] = ArchiveFileTypeInfrared,
|
||||
[ArchiveTabBadUsb] = ArchiveFileTypeBadUsb,
|
||||
[ArchiveTabU2f] = ArchiveFileTypeU2f,
|
||||
[ArchiveTabApps] = ArchiveFileTypeApps,
|
||||
[ArchiveTabBrowser] = ArchiveFileTypeUnknown,
|
||||
};
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ typedef enum {
|
||||
ArchiveFileTypeInfrared,
|
||||
ArchiveFileTypeBadUsb,
|
||||
ArchiveFileTypeU2f,
|
||||
ArchiveFileTypeApps,
|
||||
ArchiveFileTypeUpdateManifest,
|
||||
ArchiveFileTypeFolder,
|
||||
ArchiveFileTypeUnknown,
|
||||
|
||||
@@ -42,10 +42,7 @@ ARRAY_DEF(
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-function"
|
||||
// Using in applications/archive/views/archive_browser_view.c
|
||||
static void archive_menu_add_item(
|
||||
ArchiveContextMenuItem_t* obj,
|
||||
string_t text,
|
||||
uint32_t event) {
|
||||
static void archive_menu_add_item(ArchiveContextMenuItem_t* obj, string_t text, uint32_t event) {
|
||||
string_init_move(obj->text, text);
|
||||
obj->event = event;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ static const char* flipper_app_name[] = {
|
||||
[ArchiveFileTypeInfrared] = "Infrared",
|
||||
[ArchiveFileTypeBadUsb] = "Bad USB",
|
||||
[ArchiveFileTypeU2f] = "U2F",
|
||||
[ArchiveFileTypeApps] = "Applications",
|
||||
[ArchiveFileTypeUpdateManifest] = "UpdaterApp",
|
||||
};
|
||||
|
||||
|
||||
@@ -37,7 +37,11 @@ void archive_scene_info_on_enter(void* context) {
|
||||
|
||||
// Directory path
|
||||
path_extract_dirname(string_get_cstr(current->path), dirname);
|
||||
if(strcmp(string_get_cstr(dirname), "/any") == 0) {
|
||||
string_replace_str(dirname, STORAGE_ANY_PATH_PREFIX, "/");
|
||||
} else {
|
||||
string_replace_str(dirname, STORAGE_ANY_PATH_PREFIX, "");
|
||||
}
|
||||
|
||||
// File size
|
||||
FileInfo fileinfo;
|
||||
@@ -60,7 +64,7 @@ void archive_scene_info_on_enter(void* context) {
|
||||
string_get_cstr(dirname));
|
||||
}
|
||||
widget_add_text_box_element(
|
||||
instance->widget, 0, 25, 128, 25, AlignLeft, AlignCenter, file_info_message, false);
|
||||
instance->widget, 0, 25, 128, 25, AlignLeft, AlignCenter, file_info_message, true);
|
||||
|
||||
// This one to return and cursor select this file
|
||||
path_extract_filename_no_ext(string_get_cstr(current->path), filename);
|
||||
|
||||
@@ -16,6 +16,7 @@ static const char* ArchiveTabNames[] = {
|
||||
[ArchiveTabInfrared] = "Infrared",
|
||||
[ArchiveTabBadUsb] = "Bad USB",
|
||||
[ArchiveTabU2f] = "U2F",
|
||||
[ArchiveTabApps] = "Apps",
|
||||
[ArchiveTabBrowser] = "Browser",
|
||||
};
|
||||
|
||||
@@ -27,6 +28,7 @@ static const Icon* ArchiveItemIcons[] = {
|
||||
[ArchiveFileTypeInfrared] = &I_ir_10px,
|
||||
[ArchiveFileTypeBadUsb] = &I_badusb_10px,
|
||||
[ArchiveFileTypeU2f] = &I_u2f_10px,
|
||||
[ArchiveFileTypeApps] = &I_Apps_10px,
|
||||
[ArchiveFileTypeUpdateManifest] = &I_update_10px,
|
||||
[ArchiveFileTypeFolder] = &I_dir_10px,
|
||||
[ArchiveFileTypeUnknown] = &I_unknown_10px,
|
||||
|
||||
@@ -27,6 +27,7 @@ typedef enum {
|
||||
ArchiveTabIButton,
|
||||
ArchiveTabBadUsb,
|
||||
ArchiveTabU2f,
|
||||
ArchiveTabApps,
|
||||
ArchiveTabBrowser,
|
||||
ArchiveTabTotal,
|
||||
} ArchiveTabEnum;
|
||||
|
||||
@@ -33,5 +33,4 @@ typedef enum {
|
||||
typedef struct {
|
||||
TimeFormat time_format;
|
||||
DateFormat date_format;
|
||||
uint8_t increment_precision;
|
||||
} ClockSettings;
|
||||
@@ -25,7 +25,7 @@ static bool
|
||||
FlipperApplication* app = flipper_application_alloc(loader->storage, &hashtable_api_interface);
|
||||
|
||||
FlipperApplicationPreloadStatus preload_res =
|
||||
flipper_application_preload(app, string_get_cstr(path));
|
||||
flipper_application_preload_manifest(app, string_get_cstr(path));
|
||||
|
||||
bool load_success = false;
|
||||
|
||||
|
||||
@@ -33,8 +33,6 @@ static void infrared_scene_universal_common_hide_popup(Infrared* infrared) {
|
||||
|
||||
void infrared_scene_universal_common_on_enter(void* context) {
|
||||
Infrared* infrared = context;
|
||||
infrared_brute_force_clear_records(infrared->brute_force);
|
||||
button_panel_reset_selection(infrared->button_panel);
|
||||
view_stack_add_view(infrared->view_stack, button_panel_get_view(infrared->button_panel));
|
||||
}
|
||||
|
||||
@@ -89,5 +87,6 @@ void infrared_scene_universal_common_on_exit(void* context) {
|
||||
Infrared* infrared = context;
|
||||
ButtonPanel* button_panel = infrared->button_panel;
|
||||
view_stack_remove_view(infrared->view_stack, button_panel_get_view(button_panel));
|
||||
infrared_brute_force_clear_records(infrared->brute_force);
|
||||
button_panel_reset(button_panel);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ static void lfrfid_view_read_draw_callback(Canvas* canvas, void* _model) {
|
||||
LfRfidReadViewModel* model = _model;
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
canvas_draw_icon(canvas, 0, 8, &I_NFC_manual);
|
||||
canvas_draw_icon(canvas, 0, 8, &I_NFC_manual_60x50);
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
|
||||
|
||||
@@ -231,7 +231,30 @@ void nfc_show_loading_popup(void* context, bool show) {
|
||||
}
|
||||
}
|
||||
|
||||
static bool nfc_is_hal_ready() {
|
||||
if(!furi_hal_nfc_is_init()) {
|
||||
// No connection to the chip, show an error screen
|
||||
DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
|
||||
DialogMessage* message = dialog_message_alloc();
|
||||
dialog_message_set_text(
|
||||
message,
|
||||
"Error!\nNFC chip failed to start\n\n\nSend a photo of this to:\nsupport@flipperzero.one",
|
||||
0,
|
||||
0,
|
||||
AlignLeft,
|
||||
AlignTop);
|
||||
dialog_message_show(dialogs, message);
|
||||
dialog_message_free(message);
|
||||
furi_record_close(RECORD_DIALOGS);
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t nfc_app(void* p) {
|
||||
if(!nfc_is_hal_ready()) return 0;
|
||||
|
||||
Nfc* nfc = nfc_alloc();
|
||||
char* args = p;
|
||||
|
||||
|
||||
@@ -33,8 +33,14 @@
|
||||
#include <nfc/scenes/nfc_scene.h>
|
||||
#include <nfc/helpers/nfc_custom_event.h>
|
||||
|
||||
#include <dialogs/dialogs.h>
|
||||
|
||||
#include "rpc/rpc_app.h"
|
||||
|
||||
#include <m-array.h>
|
||||
|
||||
ARRAY_DEF(MfClassicUserKeys, char*, M_PTR_OPLIST);
|
||||
|
||||
#define NFC_TEXT_STORE_SIZE 128
|
||||
|
||||
typedef enum {
|
||||
@@ -58,6 +64,7 @@ struct Nfc {
|
||||
char text_store[NFC_TEXT_STORE_SIZE + 1];
|
||||
string_t text_box_store;
|
||||
uint8_t byte_input_store[6];
|
||||
MfClassicUserKeys_t mfc_key_strs; // Used in MFC key listing
|
||||
|
||||
void* rpc_ctx;
|
||||
NfcRpcState rpc_state;
|
||||
|
||||
@@ -32,6 +32,9 @@ ADD_SCENE(nfc, mf_classic_menu, MfClassicMenu)
|
||||
ADD_SCENE(nfc, mf_classic_emulate, MfClassicEmulate)
|
||||
ADD_SCENE(nfc, mf_classic_keys, MfClassicKeys)
|
||||
ADD_SCENE(nfc, mf_classic_keys_add, MfClassicKeysAdd)
|
||||
ADD_SCENE(nfc, mf_classic_keys_list, MfClassicKeysList)
|
||||
ADD_SCENE(nfc, mf_classic_keys_delete, MfClassicKeysDelete)
|
||||
ADD_SCENE(nfc, mf_classic_keys_warn_duplicate, MfClassicKeysWarnDuplicate)
|
||||
ADD_SCENE(nfc, mf_classic_dict_attack, MfClassicDictAttack)
|
||||
ADD_SCENE(nfc, emv_read_success, EmvReadSuccess)
|
||||
ADD_SCENE(nfc, emv_menu, EmvMenu)
|
||||
|
||||
@@ -25,8 +25,13 @@ bool nfc_scene_delete_success_on_event(void* context, SceneManagerEvent event) {
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == NfcCustomEventViewExit) {
|
||||
if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicKeys)) {
|
||||
consumed = scene_manager_search_and_switch_to_previous_scene(
|
||||
nfc->scene_manager, NfcSceneFileSelect);
|
||||
nfc->scene_manager, NfcSceneMfClassicKeys);
|
||||
} else {
|
||||
consumed = scene_manager_search_and_switch_to_previous_scene(
|
||||
nfc->scene_manager, NfcSceneStart);
|
||||
}
|
||||
}
|
||||
}
|
||||
return consumed;
|
||||
|
||||
@@ -47,7 +47,9 @@ void nfc_scene_device_info_on_enter(void* context) {
|
||||
}
|
||||
string_clear(country_name);
|
||||
}
|
||||
} else if(dev_data->protocol == NfcDeviceProtocolMifareClassic) {
|
||||
} else if(
|
||||
dev_data->protocol == NfcDeviceProtocolMifareClassic ||
|
||||
dev_data->protocol == NfcDeviceProtocolMifareUl) {
|
||||
string_set(temp_str, nfc->dev->dev_data.parsed_data);
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,10 @@ bool nfc_scene_dict_not_found_on_event(void* context, SceneManagerEvent event) {
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == NfcCustomEventViewExit) {
|
||||
if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneExtraActions)) {
|
||||
if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicKeys)) {
|
||||
consumed = scene_manager_search_and_switch_to_previous_scene(
|
||||
nfc->scene_manager, NfcSceneMfClassicKeys);
|
||||
} else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneExtraActions)) {
|
||||
consumed = scene_manager_search_and_switch_to_previous_scene(
|
||||
nfc->scene_manager, NfcSceneExtraActions);
|
||||
} else {
|
||||
|
||||
@@ -17,7 +17,7 @@ void nfc_scene_extra_actions_on_enter(void* context) {
|
||||
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Mf Classic Keys",
|
||||
"Mifare Classic Keys",
|
||||
SubmenuIndexMfClassicKeys,
|
||||
nfc_scene_extra_actions_submenu_callback,
|
||||
nfc);
|
||||
|
||||
@@ -26,15 +26,25 @@ void nfc_scene_mf_classic_keys_on_enter(void* context) {
|
||||
}
|
||||
|
||||
widget_add_string_element(
|
||||
nfc->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "MF Classic Keys");
|
||||
nfc->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "Mifare Classic Keys");
|
||||
char temp_str[32];
|
||||
snprintf(temp_str, sizeof(temp_str), "Flipper dict: %ld", flipper_dict_keys_total);
|
||||
snprintf(temp_str, sizeof(temp_str), "Flipper list: %ld", flipper_dict_keys_total);
|
||||
widget_add_string_element(nfc->widget, 0, 20, AlignLeft, AlignTop, FontSecondary, temp_str);
|
||||
snprintf(temp_str, sizeof(temp_str), "User dict: %ld", user_dict_keys_total);
|
||||
snprintf(temp_str, sizeof(temp_str), "User list: %ld", user_dict_keys_total);
|
||||
widget_add_string_element(nfc->widget, 0, 32, AlignLeft, AlignTop, FontSecondary, temp_str);
|
||||
widget_add_button_element(
|
||||
nfc->widget, GuiButtonTypeCenter, "Add", nfc_scene_mf_classic_keys_widget_callback, nfc);
|
||||
widget_add_icon_element(nfc->widget, 90, 12, &I_Keychain);
|
||||
widget_add_button_element(
|
||||
nfc->widget, GuiButtonTypeLeft, "Back", nfc_scene_mf_classic_keys_widget_callback, nfc);
|
||||
widget_add_icon_element(nfc->widget, 87, 13, &I_Keychain_39x36);
|
||||
if(user_dict_keys_total > 0) {
|
||||
widget_add_button_element(
|
||||
nfc->widget,
|
||||
GuiButtonTypeRight,
|
||||
"List",
|
||||
nfc_scene_mf_classic_keys_widget_callback,
|
||||
nfc);
|
||||
}
|
||||
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
|
||||
}
|
||||
@@ -47,6 +57,12 @@ bool nfc_scene_mf_classic_keys_on_event(void* context, SceneManagerEvent event)
|
||||
if(event.event == GuiButtonTypeCenter) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeysAdd);
|
||||
consumed = true;
|
||||
} else if(event.event == GuiButtonTypeLeft) {
|
||||
scene_manager_previous_scene(nfc->scene_manager);
|
||||
consumed = true;
|
||||
} else if(event.event == GuiButtonTypeRight) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeysList);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,18 +29,19 @@ bool nfc_scene_mf_classic_keys_add_on_event(void* context, SceneManagerEvent eve
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == NfcCustomEventByteInputDone) {
|
||||
// Add key to dict
|
||||
bool key_added = false;
|
||||
MfClassicDict* dict = mf_classic_dict_alloc(MfClassicDictTypeUser);
|
||||
if(dict) {
|
||||
if(mf_classic_dict_add_key(dict, nfc->byte_input_store)) {
|
||||
key_added = true;
|
||||
}
|
||||
}
|
||||
if(key_added) {
|
||||
if(mf_classic_dict_is_key_present(dict, nfc->byte_input_store)) {
|
||||
scene_manager_next_scene(
|
||||
nfc->scene_manager, NfcSceneMfClassicKeysWarnDuplicate);
|
||||
} else if(mf_classic_dict_add_key(dict, nfc->byte_input_store)) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess);
|
||||
} else {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound);
|
||||
}
|
||||
} else {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound);
|
||||
}
|
||||
mf_classic_dict_free(dict);
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
#include "../nfc_i.h"
|
||||
|
||||
void nfc_scene_mf_classic_keys_delete_widget_callback(
|
||||
GuiButtonType result,
|
||||
InputType type,
|
||||
void* context) {
|
||||
Nfc* nfc = context;
|
||||
if(type == InputTypeShort) {
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
|
||||
}
|
||||
}
|
||||
|
||||
void nfc_scene_mf_classic_keys_delete_on_enter(void* context) {
|
||||
Nfc* nfc = context;
|
||||
MfClassicDict* dict = mf_classic_dict_alloc(MfClassicDictTypeUser);
|
||||
uint32_t key_index =
|
||||
scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfClassicKeysDelete);
|
||||
// Setup Custom Widget view
|
||||
string_t key_str;
|
||||
string_init(key_str);
|
||||
|
||||
widget_add_string_element(
|
||||
nfc->widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "Delete this key?");
|
||||
widget_add_button_element(
|
||||
nfc->widget,
|
||||
GuiButtonTypeLeft,
|
||||
"Cancel",
|
||||
nfc_scene_mf_classic_keys_delete_widget_callback,
|
||||
nfc);
|
||||
widget_add_button_element(
|
||||
nfc->widget,
|
||||
GuiButtonTypeRight,
|
||||
"Delete",
|
||||
nfc_scene_mf_classic_keys_delete_widget_callback,
|
||||
nfc);
|
||||
|
||||
mf_classic_dict_get_key_at_index_str(dict, key_str, key_index);
|
||||
widget_add_string_element(
|
||||
nfc->widget, 64, 32, AlignCenter, AlignCenter, FontSecondary, string_get_cstr(key_str));
|
||||
|
||||
string_clear(key_str);
|
||||
mf_classic_dict_free(dict);
|
||||
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
|
||||
}
|
||||
|
||||
bool nfc_scene_mf_classic_keys_delete_on_event(void* context, SceneManagerEvent event) {
|
||||
Nfc* nfc = context;
|
||||
bool consumed = false;
|
||||
uint32_t key_index =
|
||||
scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfClassicKeysDelete);
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == GuiButtonTypeLeft) {
|
||||
consumed = scene_manager_search_and_switch_to_previous_scene(
|
||||
nfc->scene_manager, NfcSceneMfClassicKeys);
|
||||
} else if(event.event == GuiButtonTypeRight) {
|
||||
MfClassicDict* dict = mf_classic_dict_alloc(MfClassicDictTypeUser);
|
||||
if(mf_classic_dict_delete_index(dict, key_index)) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneDeleteSuccess);
|
||||
} else {
|
||||
scene_manager_search_and_switch_to_previous_scene(
|
||||
nfc->scene_manager, NfcSceneMfClassicKeys);
|
||||
}
|
||||
mf_classic_dict_free(dict);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void nfc_scene_mf_classic_keys_delete_on_exit(void* context) {
|
||||
Nfc* nfc = context;
|
||||
|
||||
widget_reset(nfc->widget);
|
||||
}
|
||||
100
applications/main/nfc/scenes/nfc_scene_mf_classic_keys_list.c
Normal file
100
applications/main/nfc/scenes/nfc_scene_mf_classic_keys_list.c
Normal file
@@ -0,0 +1,100 @@
|
||||
#include "../nfc_i.h"
|
||||
|
||||
#define NFC_SCENE_MF_CLASSIC_KEYS_LIST_MAX (100)
|
||||
|
||||
void nfc_scene_mf_classic_keys_list_submenu_callback(void* context, uint32_t index) {
|
||||
furi_assert(context);
|
||||
|
||||
Nfc* nfc = context;
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, index);
|
||||
}
|
||||
|
||||
void nfc_scene_mf_classic_keys_list_popup_callback(void* context) {
|
||||
furi_assert(context);
|
||||
|
||||
Nfc* nfc = context;
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit);
|
||||
}
|
||||
|
||||
void nfc_scene_mf_classic_keys_list_prepare(Nfc* nfc, MfClassicDict* dict) {
|
||||
Submenu* submenu = nfc->submenu;
|
||||
uint32_t index = 0;
|
||||
string_t temp_key;
|
||||
string_init(temp_key);
|
||||
|
||||
submenu_set_header(submenu, "Select key to delete:");
|
||||
while(mf_classic_dict_get_next_key_str(dict, temp_key)) {
|
||||
char* current_key = (char*)malloc(sizeof(char) * 13);
|
||||
strncpy(current_key, string_get_cstr(temp_key), 12);
|
||||
MfClassicUserKeys_push_back(nfc->mfc_key_strs, current_key);
|
||||
FURI_LOG_D("ListKeys", "Key %d: %s", index, current_key);
|
||||
submenu_add_item(
|
||||
submenu, current_key, index++, nfc_scene_mf_classic_keys_list_submenu_callback, nfc);
|
||||
}
|
||||
string_clear(temp_key);
|
||||
}
|
||||
|
||||
void nfc_scene_mf_classic_keys_list_on_enter(void* context) {
|
||||
Nfc* nfc = context;
|
||||
MfClassicDict* dict = mf_classic_dict_alloc(MfClassicDictTypeUser);
|
||||
MfClassicUserKeys_init(nfc->mfc_key_strs);
|
||||
if(dict) {
|
||||
uint32_t total_user_keys = mf_classic_dict_get_total_keys(dict);
|
||||
if(total_user_keys < NFC_SCENE_MF_CLASSIC_KEYS_LIST_MAX) {
|
||||
nfc_scene_mf_classic_keys_list_prepare(nfc, dict);
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
|
||||
} else {
|
||||
popup_set_header(nfc->popup, "Too many keys!", 64, 0, AlignCenter, AlignTop);
|
||||
popup_set_text(
|
||||
nfc->popup,
|
||||
"Edit user dictionary\nwith file browser",
|
||||
64,
|
||||
12,
|
||||
AlignCenter,
|
||||
AlignTop);
|
||||
popup_set_callback(nfc->popup, nfc_scene_mf_classic_keys_list_popup_callback);
|
||||
popup_set_context(nfc->popup, nfc);
|
||||
popup_set_timeout(nfc->popup, 3000);
|
||||
popup_enable_timeout(nfc->popup);
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
|
||||
}
|
||||
mf_classic_dict_free(dict);
|
||||
} else {
|
||||
popup_set_header(
|
||||
nfc->popup, "Failed to load dictionary", 64, 32, AlignCenter, AlignCenter);
|
||||
popup_set_callback(nfc->popup, nfc_scene_mf_classic_keys_list_popup_callback);
|
||||
popup_set_context(nfc->popup, nfc);
|
||||
popup_set_timeout(nfc->popup, 3000);
|
||||
popup_enable_timeout(nfc->popup);
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
|
||||
}
|
||||
}
|
||||
|
||||
bool nfc_scene_mf_classic_keys_list_on_event(void* context, SceneManagerEvent event) {
|
||||
Nfc* nfc = context;
|
||||
bool consumed = false;
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == NfcCustomEventViewExit) {
|
||||
consumed = scene_manager_previous_scene(nfc->scene_manager);
|
||||
} else {
|
||||
scene_manager_set_scene_state(
|
||||
nfc->scene_manager, NfcSceneMfClassicKeysDelete, event.event);
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeysDelete);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void nfc_scene_mf_classic_keys_list_on_exit(void* context) {
|
||||
Nfc* nfc = context;
|
||||
|
||||
MfClassicUserKeys_it_t it;
|
||||
for(MfClassicUserKeys_it(it, nfc->mfc_key_strs); !MfClassicUserKeys_end_p(it);
|
||||
MfClassicUserKeys_next(it)) {
|
||||
free(*MfClassicUserKeys_ref(it));
|
||||
}
|
||||
MfClassicUserKeys_clear(nfc->mfc_key_strs);
|
||||
submenu_reset(nfc->submenu);
|
||||
popup_reset(nfc->popup);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
#include "../nfc_i.h"
|
||||
|
||||
void nfc_scene_mf_classic_keys_warn_duplicate_popup_callback(void* context) {
|
||||
Nfc* nfc = context;
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit);
|
||||
}
|
||||
|
||||
void nfc_scene_mf_classic_keys_warn_duplicate_on_enter(void* context) {
|
||||
Nfc* nfc = context;
|
||||
|
||||
// Setup view
|
||||
Popup* popup = nfc->popup;
|
||||
popup_set_icon(popup, 72, 16, &I_DolphinCommon_56x48);
|
||||
popup_set_header(popup, "Key already exists!", 64, 3, AlignCenter, AlignTop);
|
||||
popup_set_text(
|
||||
popup,
|
||||
"Please enter a\n"
|
||||
"different key.",
|
||||
4,
|
||||
24,
|
||||
AlignLeft,
|
||||
AlignTop);
|
||||
popup_set_timeout(popup, 5000);
|
||||
popup_set_context(popup, nfc);
|
||||
popup_set_callback(popup, nfc_scene_mf_classic_keys_warn_duplicate_popup_callback);
|
||||
popup_enable_timeout(popup);
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
|
||||
}
|
||||
|
||||
bool nfc_scene_mf_classic_keys_warn_duplicate_on_event(void* context, SceneManagerEvent event) {
|
||||
Nfc* nfc = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == NfcCustomEventViewExit) {
|
||||
consumed = scene_manager_search_and_switch_to_previous_scene(
|
||||
nfc->scene_manager, NfcSceneMfClassicKeysAdd);
|
||||
}
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void nfc_scene_mf_classic_keys_warn_duplicate_on_exit(void* context) {
|
||||
Nfc* nfc = context;
|
||||
|
||||
popup_reset(nfc->popup);
|
||||
}
|
||||
@@ -7,6 +7,13 @@ enum SubmenuIndex {
|
||||
SubmenuIndexDynamic, // dynamic indexes start here
|
||||
};
|
||||
|
||||
void nfc_scene_mf_desfire_popup_callback(void* context) {
|
||||
furi_assert(context);
|
||||
|
||||
Nfc* nfc = context;
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit);
|
||||
}
|
||||
|
||||
MifareDesfireApplication* nfc_scene_mf_desfire_app_get_app(Nfc* nfc) {
|
||||
uint32_t app_idx = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp) >>
|
||||
1;
|
||||
@@ -25,27 +32,24 @@ void nfc_scene_mf_desfire_app_submenu_callback(void* context, uint32_t index) {
|
||||
|
||||
void nfc_scene_mf_desfire_app_on_enter(void* context) {
|
||||
Nfc* nfc = context;
|
||||
Submenu* submenu = nfc->submenu;
|
||||
MifareDesfireApplication* app = nfc_scene_mf_desfire_app_get_app(nfc);
|
||||
if(!app) {
|
||||
popup_set_icon(nfc->popup, 5, 5, &I_WarningDolphin_45x42);
|
||||
popup_set_header(nfc->popup, "Internal Error!", 55, 12, AlignLeft, AlignBottom);
|
||||
popup_set_text(
|
||||
nfc->popup,
|
||||
"No app selected.\nThis should\nnever happen,\nplease file a bug.",
|
||||
55,
|
||||
15,
|
||||
AlignLeft,
|
||||
AlignTop);
|
||||
popup_set_header(nfc->popup, "Empty card!", 55, 12, AlignLeft, AlignBottom);
|
||||
popup_set_callback(nfc->popup, nfc_scene_mf_desfire_popup_callback);
|
||||
popup_set_context(nfc->popup, nfc);
|
||||
popup_set_timeout(nfc->popup, 3000);
|
||||
popup_enable_timeout(nfc->popup);
|
||||
popup_set_text(nfc->popup, "No application\nfound.", 55, 15, AlignLeft, AlignTop);
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
|
||||
FURI_LOG_E(TAG, "Bad state. No app selected?");
|
||||
return;
|
||||
}
|
||||
|
||||
} else {
|
||||
text_box_set_font(nfc->text_box, TextBoxFontHex);
|
||||
|
||||
submenu_add_item(
|
||||
submenu, "App info", SubmenuIndexAppInfo, nfc_scene_mf_desfire_app_submenu_callback, nfc);
|
||||
nfc->submenu,
|
||||
"App info",
|
||||
SubmenuIndexAppInfo,
|
||||
nfc_scene_mf_desfire_app_submenu_callback,
|
||||
nfc);
|
||||
|
||||
uint16_t cap = NFC_TEXT_STORE_SIZE;
|
||||
char* buf = nfc->text_store;
|
||||
@@ -61,11 +65,13 @@ void nfc_scene_mf_desfire_app_on_enter(void* context) {
|
||||
char* label = buf;
|
||||
cap -= size + 1;
|
||||
buf += size + 1;
|
||||
submenu_add_item(submenu, label, idx++, nfc_scene_mf_desfire_app_submenu_callback, nfc);
|
||||
submenu_add_item(
|
||||
nfc->submenu, label, idx++, nfc_scene_mf_desfire_app_submenu_callback, nfc);
|
||||
}
|
||||
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
|
||||
}
|
||||
}
|
||||
|
||||
bool nfc_scene_mf_desfire_app_on_event(void* context, SceneManagerEvent event) {
|
||||
Nfc* nfc = context;
|
||||
@@ -73,6 +79,9 @@ bool nfc_scene_mf_desfire_app_on_event(void* context, SceneManagerEvent event) {
|
||||
uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp);
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == NfcCustomEventViewExit) {
|
||||
consumed = scene_manager_previous_scene(nfc->scene_manager);
|
||||
} else {
|
||||
MifareDesfireApplication* app = nfc_scene_mf_desfire_app_get_app(nfc);
|
||||
TextBox* text_box = nfc->text_box;
|
||||
string_reset(nfc->text_box_store);
|
||||
@@ -93,6 +102,7 @@ bool nfc_scene_mf_desfire_app_on_event(void* context, SceneManagerEvent event) {
|
||||
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp, state | 1);
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox);
|
||||
consumed = true;
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeBack) {
|
||||
if(state & 1) {
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
|
||||
@@ -108,6 +118,7 @@ void nfc_scene_mf_desfire_app_on_exit(void* context) {
|
||||
Nfc* nfc = context;
|
||||
|
||||
// Clear views
|
||||
popup_reset(nfc->popup);
|
||||
text_box_reset(nfc->text_box);
|
||||
string_reset(nfc->text_box_store);
|
||||
submenu_reset(nfc->submenu);
|
||||
|
||||
@@ -27,7 +27,7 @@ void nfc_scene_mf_ultralight_read_auth_set_state(Nfc* nfc, NfcSceneMfUlReadState
|
||||
popup_reset(nfc->popup);
|
||||
popup_set_text(
|
||||
nfc->popup, "Apply card to\nFlipper's back", 97, 24, AlignCenter, AlignTop);
|
||||
popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual);
|
||||
popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual_60x50);
|
||||
} else if(state == NfcSceneMfUlReadStateReading) {
|
||||
popup_reset(nfc->popup);
|
||||
popup_set_header(
|
||||
|
||||
@@ -34,6 +34,9 @@ void nfc_scene_mf_ultralight_read_success_on_enter(void* context) {
|
||||
nfc);
|
||||
|
||||
string_t temp_str;
|
||||
if(string_size(nfc->dev->dev_data.parsed_data)) {
|
||||
string_init_set(temp_str, nfc->dev->dev_data.parsed_data);
|
||||
} else {
|
||||
string_init_printf(temp_str, "\e#%s\n", nfc_mf_ul_type(mf_ul_data->type, true));
|
||||
string_cat_printf(temp_str, "UID:");
|
||||
for(size_t i = 0; i < data->uid_len; i++) {
|
||||
@@ -44,6 +47,7 @@ void nfc_scene_mf_ultralight_read_success_on_enter(void* context) {
|
||||
if(mf_ul_data->data_read != mf_ul_data->data_size) {
|
||||
string_cat_printf(temp_str, "\nPassword-protected pages!");
|
||||
}
|
||||
}
|
||||
widget_add_text_scroll_element(widget, 0, 0, 128, 52, string_get_cstr(temp_str));
|
||||
string_clear(temp_str);
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ void nfc_scene_mf_ultralight_unlock_menu_on_enter(void* context) {
|
||||
scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu);
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Enter Password Manually",
|
||||
"Enter PWD Manually",
|
||||
SubmenuIndexMfUlUnlockMenuManual,
|
||||
nfc_scene_mf_ultralight_unlock_menu_submenu_callback,
|
||||
nfc);
|
||||
|
||||
@@ -26,7 +26,7 @@ void nfc_scene_read_set_state(Nfc* nfc, NfcSceneReadState state) {
|
||||
popup_reset(nfc->popup);
|
||||
popup_set_text(
|
||||
nfc->popup, "Apply card to\nFlipper's back", 97, 24, AlignCenter, AlignTop);
|
||||
popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual);
|
||||
popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual_60x50);
|
||||
} else if(state == NfcSceneReadStateReading) {
|
||||
popup_reset(nfc->popup);
|
||||
popup_set_header(
|
||||
|
||||
@@ -11,7 +11,7 @@ void nfc_scene_restore_original_confirm_on_enter(void* context) {
|
||||
DialogEx* dialog_ex = nfc->dialog_ex;
|
||||
|
||||
dialog_ex_set_header(dialog_ex, "Restore Card Data?", 64, 0, AlignCenter, AlignTop);
|
||||
dialog_ex_set_icon(dialog_ex, 5, 15, &I_Restoring);
|
||||
dialog_ex_set_icon(dialog_ex, 5, 15, &I_Restoring_38x32);
|
||||
dialog_ex_set_text(
|
||||
dialog_ex, "It will be returned\nto its original state.", 47, 21, AlignLeft, AlignTop);
|
||||
dialog_ex_set_left_button_text(dialog_ex, "Cancel");
|
||||
|
||||
@@ -27,7 +27,10 @@ bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) {
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == NfcCustomEventViewExit) {
|
||||
if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) {
|
||||
if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicKeys)) {
|
||||
consumed = scene_manager_search_and_switch_to_previous_scene(
|
||||
nfc->scene_manager, NfcSceneMfClassicKeys);
|
||||
} else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) {
|
||||
consumed = scene_manager_search_and_switch_to_previous_scene(
|
||||
nfc->scene_manager, NfcSceneSavedMenu);
|
||||
} else {
|
||||
|
||||
@@ -91,7 +91,9 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) {
|
||||
bool application_info_present = false;
|
||||
if(dev_data->protocol == NfcDeviceProtocolEMV) {
|
||||
application_info_present = true;
|
||||
} else if(dev_data->protocol == NfcDeviceProtocolMifareClassic) {
|
||||
} else if(
|
||||
dev_data->protocol == NfcDeviceProtocolMifareClassic ||
|
||||
dev_data->protocol == NfcDeviceProtocolMifareUl) {
|
||||
application_info_present = nfc_supported_card_verify_and_parse(dev_data);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,22 @@ void subghz_scene_save_name_text_input_callback(void* context) {
|
||||
view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneSaveName);
|
||||
}
|
||||
|
||||
void subghz_scene_save_name_get_timefilename(string_t name, uint32_t frequency) {
|
||||
FuriHalRtcDateTime datetime = {0};
|
||||
furi_hal_rtc_get_datetime(&datetime);
|
||||
string_printf(
|
||||
name,
|
||||
"RAW_%.4d.%.2d.%.2d-%.2d.%.2d.%.2d-%d.%.2dMHz",
|
||||
datetime.year,
|
||||
datetime.month,
|
||||
datetime.day,
|
||||
datetime.hour,
|
||||
datetime.minute,
|
||||
datetime.second,
|
||||
frequency / 1000000,
|
||||
(frequency / 10000) % 100);
|
||||
}
|
||||
|
||||
void subghz_scene_save_name_on_enter(void* context) {
|
||||
SubGhz* subghz = context;
|
||||
|
||||
@@ -42,9 +58,9 @@ void subghz_scene_save_name_on_enter(void* context) {
|
||||
if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) ==
|
||||
SubGhzCustomEventManagerSetRAW) {
|
||||
dev_name_empty = true;
|
||||
subghz_get_next_name_file(subghz, SUBGHZ_MAX_LEN_NAME);
|
||||
subghz_scene_save_name_get_timefilename(
|
||||
file_name, subghz->txrx->preset->frequency);
|
||||
}
|
||||
path_extract_filename(subghz->file_path, file_name, true);
|
||||
}
|
||||
string_set(subghz->file_path, dir_name);
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ void subghz_view_transmitter_add_data_to_show(
|
||||
}
|
||||
|
||||
static void subghz_view_transmitter_button_right(Canvas* canvas, const char* str) {
|
||||
const uint8_t button_height = 13;
|
||||
const uint8_t button_height = 12;
|
||||
const uint8_t vertical_offset = 3;
|
||||
const uint8_t horizontal_offset = 1;
|
||||
const uint8_t string_width = canvas_string_width(canvas, str);
|
||||
@@ -69,7 +69,10 @@ static void subghz_view_transmitter_button_right(Canvas* canvas, const char* str
|
||||
|
||||
canvas_invert_color(canvas);
|
||||
canvas_draw_icon(
|
||||
canvas, x + horizontal_offset, y - button_height + vertical_offset, &I_ButtonCenter_7x7);
|
||||
canvas,
|
||||
x + horizontal_offset,
|
||||
y - button_height + vertical_offset - 1,
|
||||
&I_ButtonCenter_7x7);
|
||||
canvas_draw_str(
|
||||
canvas, x + horizontal_offset + icon_width_with_offset, y - vertical_offset, str);
|
||||
canvas_invert_color(canvas);
|
||||
|
||||
@@ -790,7 +790,7 @@ static void render_callback(Canvas* canvas, void* ctx) {
|
||||
break;
|
||||
case 1:
|
||||
canvas_draw_icon(canvas, 113, 15, &I_Pin_cell_13x13);
|
||||
canvas_draw_icon(canvas, 116, 17, &I_Pin_arrow_up7x9);
|
||||
canvas_draw_icon(canvas, 116, 17, &I_Pin_arrow_up_7x9);
|
||||
break;
|
||||
case 2:
|
||||
canvas_draw_icon(canvas, 113, 15, &I_Pin_cell_13x13);
|
||||
@@ -845,6 +845,8 @@ void unirfremix_subghz_alloc(UniRFRemix* app) {
|
||||
UniRFRemix* unirfremix_alloc(void) {
|
||||
UniRFRemix* app = malloc(sizeof(UniRFRemix));
|
||||
|
||||
furi_hal_power_suppress_charge_enter();
|
||||
|
||||
app->model_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
|
||||
|
||||
app->input_queue = furi_message_queue_alloc(32, sizeof(InputEvent));
|
||||
@@ -862,7 +864,9 @@ UniRFRemix* unirfremix_alloc(void) {
|
||||
return app;
|
||||
}
|
||||
|
||||
void unirfremix_free(UniRFRemix* app) {
|
||||
void unirfremix_free(UniRFRemix* app, bool with_subghz) {
|
||||
furi_hal_power_suppress_charge_exit();
|
||||
|
||||
string_clear(app->up_file);
|
||||
string_clear(app->down_file);
|
||||
string_clear(app->left_file);
|
||||
@@ -888,10 +892,12 @@ void unirfremix_free(UniRFRemix* app) {
|
||||
|
||||
furi_mutex_free(app->model_mutex);
|
||||
|
||||
if(with_subghz) {
|
||||
furi_hal_subghz_sleep();
|
||||
subghz_setting_free(app->setting);
|
||||
subghz_receiver_free(app->subghz_receiver);
|
||||
subghz_environment_free(app->environment);
|
||||
}
|
||||
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
app->notification = NULL;
|
||||
@@ -939,6 +945,8 @@ int32_t unirfremix_app(void* p) {
|
||||
furi_record_close(RECORD_DIALOGS);
|
||||
if(!res) {
|
||||
FURI_LOG_E(TAG, "No file selected");
|
||||
unirfremix_free(app, false);
|
||||
return 255;
|
||||
} else {
|
||||
//check map and population variables
|
||||
unirfremix_cfg_set_check(app, app->file_path);
|
||||
@@ -970,8 +978,6 @@ int32_t unirfremix_app(void* p) {
|
||||
furi_mutex_release(app->model_mutex);
|
||||
view_port_update(app->view_port);
|
||||
|
||||
furi_hal_power_suppress_charge_enter();
|
||||
|
||||
//input detect loop start
|
||||
InputEvent input;
|
||||
while(1) {
|
||||
@@ -1164,9 +1170,7 @@ int32_t unirfremix_app(void* p) {
|
||||
}
|
||||
|
||||
// remove & free all stuff created by app
|
||||
unirfremix_free(app);
|
||||
|
||||
furi_hal_power_suppress_charge_exit();
|
||||
unirfremix_free(app, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -109,7 +109,7 @@ const BtHidKeyboardKey bt_hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = {
|
||||
{.width = 1, .icon = NULL, .key = "-", .shift_key = "_", .value = HID_KEYBOARD_MINUS},
|
||||
},
|
||||
{
|
||||
{.width = 1, .icon = &I_Pin_arrow_up7x9, .value = HID_KEYBOARD_L_SHIFT},
|
||||
{.width = 1, .icon = &I_Pin_arrow_up_7x9, .value = HID_KEYBOARD_L_SHIFT},
|
||||
{.width = 1, .icon = NULL, .key = ",", .shift_key = "<", .value = HID_KEYPAD_COMMA},
|
||||
{.width = 1, .icon = NULL, .key = ".", .shift_key = ">", .value = HID_KEYBOARD_DOT},
|
||||
{.width = 4, .icon = NULL, .key = " ", .value = HID_KEYBOARD_SPACEBAR},
|
||||
|
||||
@@ -53,7 +53,7 @@ static void bt_hid_mouse_draw_callback(Canvas* canvas, void* context) {
|
||||
canvas_set_bitmap_mode(canvas, 0);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_icon(canvas, 84, 10, &I_Pin_arrow_up7x9);
|
||||
canvas_draw_icon(canvas, 84, 10, &I_Pin_arrow_up_7x9);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Down
|
||||
|
||||
@@ -1,21 +1,35 @@
|
||||
# Flipfrid
|
||||
|
||||
Basic EM4100 Fuzzer
|
||||
Basic EM4100 and HIDProx Fuzzer.
|
||||
|
||||
## Why
|
||||
|
||||
Flipfrid is a simple Rfid fuzzer using EM4100 protocol (125khz).
|
||||
Objective is to provide a simple to use fuzzer to test readers by emulating various cards.
|
||||
|
||||
EM4100 cards use a 1 byte customer id and 4 bytes card id.
|
||||
- EM4100 cards use a 1 byte customer id and 4 bytes card id.
|
||||
- HIDProx cards use a 2 byte customer id and 3 byte card id.
|
||||
|
||||
## How
|
||||
|
||||
There is 4 modes :
|
||||
- Default key loop over 16 factory/default keys and emulate each one after one ;
|
||||
- BF customer id. just an iteration from 0X00 to 0XFF on the first byte ;
|
||||
- Load Dump file : Load an existing EM4100 dump generated by Flipperzero, select an index and bruteforce from 0X00 to 0XFF;
|
||||
- Uids list: loop over a text file (one uid per line)
|
||||
1) Select the Protocol with the left and right arrows
|
||||
2) Select the Mode with the up and down arrows
|
||||
|
||||
### Info
|
||||
|
||||
There are 2 Protocols:
|
||||
- EM4100
|
||||
- HIDProx
|
||||
|
||||
There are 4 modes:
|
||||
- Default Values: Try factory/default keys and emulate one after the other.
|
||||
- BF customer id: An iteration from 0X00 to 0XFF on the first byte.
|
||||
- Load Dump file: Load an existing dump (.rfid) generated by Flipperzero, select an index and bruteforce from 0X00 to 0XFF;
|
||||
- Uids list: Iterate over an input text file (one uid per line) and emulate one after the other.
|
||||
|
||||
|
||||
|
||||
|
||||
TODO :
|
||||
- blank screen on back press
|
||||
- Add second byte test to `BF customer id`
|
||||
|
||||
@@ -64,6 +64,7 @@ FlipFridState* flipfrid_alloc() {
|
||||
flipfrid->is_attacking = false;
|
||||
flipfrid->key_index = 0;
|
||||
flipfrid->menu_index = 0;
|
||||
flipfrid->menu_proto_index = 0;
|
||||
|
||||
flipfrid->attack = FlipFridAttackDefaultValues;
|
||||
flipfrid->notify = furi_record_open(RECORD_NOTIFICATION);
|
||||
@@ -73,12 +74,14 @@ FlipFridState* flipfrid_alloc() {
|
||||
flipfrid->data[2] = 0x00;
|
||||
flipfrid->data[3] = 0x00;
|
||||
flipfrid->data[4] = 0x00;
|
||||
flipfrid->data[5] = 0x00;
|
||||
|
||||
flipfrid->payload[0] = 0x00;
|
||||
flipfrid->payload[1] = 0x00;
|
||||
flipfrid->payload[2] = 0x00;
|
||||
flipfrid->payload[3] = 0x00;
|
||||
flipfrid->payload[4] = 0x00;
|
||||
flipfrid->payload[5] = 0x00;
|
||||
|
||||
//Dialog
|
||||
flipfrid->dialogs = furi_record_open(RECORD_DIALOGS);
|
||||
|
||||
@@ -28,6 +28,11 @@ typedef enum {
|
||||
FlipFridAttackLoadFileCustomUids,
|
||||
} FlipFridAttacks;
|
||||
|
||||
typedef enum {
|
||||
EM4100,
|
||||
HIDProx,
|
||||
} FlipFridProtos;
|
||||
|
||||
typedef enum {
|
||||
NoneScene,
|
||||
SceneEntryPoint,
|
||||
@@ -56,13 +61,16 @@ typedef struct {
|
||||
FlipFridScene previous_scene;
|
||||
NotificationApp* notify;
|
||||
u_int8_t menu_index;
|
||||
u_int8_t menu_proto_index;
|
||||
|
||||
string_t data_str;
|
||||
uint8_t data[5];
|
||||
uint8_t payload[5];
|
||||
uint8_t data[6];
|
||||
uint8_t payload[6];
|
||||
uint8_t attack_step;
|
||||
FlipFridAttacks attack;
|
||||
FlipFridProtos proto;
|
||||
string_t attack_name;
|
||||
string_t proto_name;
|
||||
|
||||
DialogsApp* dialogs;
|
||||
string_t notification_msg;
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
#include "flipfrid_scene_entrypoint.h"
|
||||
|
||||
string_t menu_items[4];
|
||||
string_t menu_proto_items[2];
|
||||
|
||||
void flipfrid_scene_entrypoint_menu_callback(FlipFridState* context, uint32_t index) {
|
||||
void flipfrid_scene_entrypoint_menu_callback(
|
||||
FlipFridState* context,
|
||||
uint32_t index,
|
||||
uint32_t proto_index) {
|
||||
switch(index) {
|
||||
case FlipFridAttackDefaultValues:
|
||||
context->attack = FlipFridAttackDefaultValues;
|
||||
@@ -27,6 +31,19 @@ void flipfrid_scene_entrypoint_menu_callback(FlipFridState* context, uint32_t in
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch(proto_index) {
|
||||
case EM4100:
|
||||
context->proto = EM4100;
|
||||
string_set_str(context->proto_name, "EM4100");
|
||||
break;
|
||||
case HIDProx:
|
||||
context->proto = HIDProx;
|
||||
string_set_str(context->proto_name, "HIDProx");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void flipfrid_scene_entrypoint_on_enter(FlipFridState* context) {
|
||||
@@ -36,6 +53,7 @@ void flipfrid_scene_entrypoint_on_enter(FlipFridState* context) {
|
||||
context->payload[2] = 0x00;
|
||||
context->payload[3] = 0x00;
|
||||
context->payload[4] = 0x00;
|
||||
context->payload[5] = 0x00;
|
||||
|
||||
context->menu_index = 0;
|
||||
for(uint32_t i = 0; i < 4; i++) {
|
||||
@@ -46,6 +64,14 @@ void flipfrid_scene_entrypoint_on_enter(FlipFridState* context) {
|
||||
string_set(menu_items[1], "BF Customer ID");
|
||||
string_set(menu_items[2], "Load File");
|
||||
string_set(menu_items[3], "Load uids from file");
|
||||
|
||||
context->menu_proto_index = 0;
|
||||
for(uint32_t i = 0; i < 2; i++) {
|
||||
string_init(menu_proto_items[i]);
|
||||
}
|
||||
|
||||
string_set(menu_proto_items[0], "EM4100");
|
||||
string_set(menu_proto_items[1], "HIDProx");
|
||||
}
|
||||
|
||||
void flipfrid_scene_entrypoint_on_exit(FlipFridState* context) {
|
||||
@@ -53,6 +79,10 @@ void flipfrid_scene_entrypoint_on_exit(FlipFridState* context) {
|
||||
for(uint32_t i = 0; i < 4; i++) {
|
||||
string_clear(menu_items[i]);
|
||||
}
|
||||
|
||||
for(uint32_t i = 0; i < 2; i++) {
|
||||
string_clear(menu_proto_items[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void flipfrid_scene_entrypoint_on_tick(FlipFridState* context) {
|
||||
@@ -74,10 +104,18 @@ void flipfrid_scene_entrypoint_on_event(FlipFridEvent event, FlipFridState* cont
|
||||
}
|
||||
break;
|
||||
case InputKeyLeft:
|
||||
if(context->menu_proto_index > EM4100) {
|
||||
context->menu_proto_index--;
|
||||
}
|
||||
break;
|
||||
case InputKeyRight:
|
||||
if(context->menu_proto_index < HIDProx) {
|
||||
context->menu_proto_index++;
|
||||
}
|
||||
break;
|
||||
case InputKeyOk:
|
||||
flipfrid_scene_entrypoint_menu_callback(context, context->menu_index);
|
||||
flipfrid_scene_entrypoint_menu_callback(
|
||||
context, context->menu_index, context->menu_proto_index);
|
||||
break;
|
||||
case InputKeyBack:
|
||||
context->is_running = false;
|
||||
@@ -91,10 +129,6 @@ void flipfrid_scene_entrypoint_on_draw(Canvas* canvas, FlipFridState* context) {
|
||||
canvas_clear(canvas);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Title
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(canvas, 64, 6, AlignCenter, AlignTop, "RFID Fuzzer");
|
||||
|
||||
if(context->menu_index > FlipFridAttackDefaultValues) {
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str_aligned(
|
||||
@@ -120,4 +154,41 @@ void flipfrid_scene_entrypoint_on_draw(Canvas* canvas, FlipFridState* context) {
|
||||
AlignTop,
|
||||
string_get_cstr(menu_items[context->menu_index + 1]));
|
||||
}
|
||||
|
||||
if(context->menu_proto_index > EM4100) {
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str_aligned(
|
||||
canvas,
|
||||
64,
|
||||
-12,
|
||||
AlignCenter,
|
||||
AlignTop,
|
||||
string_get_cstr(menu_proto_items[context->menu_proto_index - 1]));
|
||||
}
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(canvas, 34, 4, AlignCenter, AlignTop, "<");
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(
|
||||
canvas,
|
||||
64,
|
||||
4,
|
||||
AlignCenter,
|
||||
AlignTop,
|
||||
string_get_cstr(menu_proto_items[context->menu_proto_index]));
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(canvas, 94, 4, AlignCenter, AlignTop, ">");
|
||||
|
||||
if(context->menu_proto_index < HIDProx) {
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str_aligned(
|
||||
canvas,
|
||||
64,
|
||||
-12,
|
||||
AlignCenter,
|
||||
AlignTop,
|
||||
string_get_cstr(menu_proto_items[context->menu_proto_index + 1]));
|
||||
}
|
||||
}
|
||||
@@ -36,12 +36,22 @@ bool flipfrid_load(FlipFridState* context, const char* file_path) {
|
||||
break;
|
||||
} else {
|
||||
FURI_LOG_I(TAG, "Key type: %s", string_get_cstr(temp_str));
|
||||
|
||||
if(context->proto == EM4100) {
|
||||
if(strcmp(string_get_cstr(temp_str), "EM4100") != 0) {
|
||||
FURI_LOG_E(TAG, "Unsupported Key type");
|
||||
string_reset(context->notification_msg);
|
||||
string_set_str(context->notification_msg, "Unsupported Key type");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if(strcmp(string_get_cstr(temp_str), "HIDProx") != 0) {
|
||||
FURI_LOG_E(TAG, "Unsupported Key type");
|
||||
string_reset(context->notification_msg);
|
||||
string_set_str(context->notification_msg, "Unsupported Key type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Data
|
||||
@@ -53,15 +63,24 @@ bool flipfrid_load(FlipFridState* context, const char* file_path) {
|
||||
} else {
|
||||
FURI_LOG_I(TAG, "Key: %s", string_get_cstr(context->data_str));
|
||||
|
||||
// Check data size
|
||||
if(context->proto == EM4100) {
|
||||
if(string_size(context->data_str) != 14) {
|
||||
FURI_LOG_E(TAG, "Incorrect Key length");
|
||||
string_reset(context->notification_msg);
|
||||
string_set_str(context->notification_msg, "Incorrect Key length");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if(string_size(context->data_str) != 17) {
|
||||
FURI_LOG_E(TAG, "Incorrect Key length");
|
||||
string_reset(context->notification_msg);
|
||||
string_set_str(context->notification_msg, "Incorrect Key length");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// String to uint8_t
|
||||
for(uint8_t i = 0; i < 5; i++) {
|
||||
for(uint8_t i = 0; i < 6; i++) {
|
||||
char temp_str2[3];
|
||||
temp_str2[0] = string_get_cstr(context->data_str)[i * 3];
|
||||
temp_str2[1] = string_get_cstr(context->data_str)[i * 3 + 1];
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
#include <gui/elements.h>
|
||||
|
||||
uint8_t counter = 0;
|
||||
#define TIME_BETWEEN_CARDS 5
|
||||
uint8_t id_list[16][5] = {
|
||||
#define TIME_BETWEEN_CARDS 6
|
||||
uint8_t id_list[17][5] = {
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00}, // Null bytes
|
||||
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, // Only FF
|
||||
{0x11, 0x11, 0x11, 0x11, 0x11}, // Only 11
|
||||
@@ -16,18 +16,40 @@ uint8_t id_list[16][5] = {
|
||||
{0x88, 0x88, 0x88, 0x88, 0x88}, // Only 88
|
||||
{0x99, 0x99, 0x99, 0x99, 0x99}, // Only 99
|
||||
{0x12, 0x34, 0x56, 0x78, 0x9A}, // Incremental UID
|
||||
{0x9A, 0x78, 0x56, 0x34, 0x12}, // Decremental UID
|
||||
{0x04, 0xd0, 0x9b, 0x0d, 0x6a}, // From arha
|
||||
{0x34, 0x00, 0x29, 0x3d, 0x9e}, // From arha
|
||||
{0x04, 0xdf, 0x00, 0x00, 0x01}, // From arha
|
||||
{0xCA, 0xCA, 0xCA, 0xCA, 0xCA}, // From arha
|
||||
};
|
||||
|
||||
uint8_t id_list_hid[14][6] = {
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Null bytes
|
||||
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, // Only FF
|
||||
{0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, // Only 11
|
||||
{0x22, 0x22, 0x22, 0x22, 0x22, 0x22}, // Only 22
|
||||
{0x33, 0x33, 0x33, 0x33, 0x33, 0x33}, // Only 33
|
||||
{0x44, 0x44, 0x44, 0x44, 0x44, 0x44}, // Only 44
|
||||
{0x55, 0x55, 0x55, 0x55, 0x55, 0x55}, // Only 55
|
||||
{0x66, 0x66, 0x66, 0x66, 0x66, 0x66}, // Only 66
|
||||
{0x77, 0x77, 0x77, 0x77, 0x77, 0x77}, // Only 77
|
||||
{0x88, 0x88, 0x88, 0x88, 0x88, 0x88}, // Only 88
|
||||
{0x99, 0x99, 0x99, 0x99, 0x99, 0x99}, // Only 99
|
||||
{0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC}, // Incremental UID
|
||||
{0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12}, // Decremental UID
|
||||
{0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA}, // From arha
|
||||
};
|
||||
|
||||
void flipfrid_scene_run_attack_on_enter(FlipFridState* context) {
|
||||
context->attack_step = 0;
|
||||
context->dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
|
||||
context->worker = lfrfid_worker_alloc(context->dict);
|
||||
if(context->proto == HIDProx) {
|
||||
context->protocol = protocol_dict_get_protocol_by_name(context->dict, "HIDProx");
|
||||
} else {
|
||||
context->protocol = protocol_dict_get_protocol_by_name(context->dict, "EM4100");
|
||||
}
|
||||
}
|
||||
|
||||
void flipfrid_scene_run_attack_on_exit(FlipFridState* context) {
|
||||
lfrfid_worker_stop(context->worker);
|
||||
@@ -40,7 +62,7 @@ void flipfrid_scene_run_attack_on_exit(FlipFridState* context) {
|
||||
void flipfrid_scene_run_attack_on_tick(FlipFridState* context) {
|
||||
if(context->is_attacking) {
|
||||
if(1 == counter) {
|
||||
protocol_dict_set_data(context->dict, context->protocol, context->payload, 5);
|
||||
protocol_dict_set_data(context->dict, context->protocol, context->payload, 6);
|
||||
lfrfid_worker_free(context->worker);
|
||||
context->worker = lfrfid_worker_alloc(context->dict);
|
||||
lfrfid_worker_start_thread(context->worker);
|
||||
@@ -50,12 +72,31 @@ void flipfrid_scene_run_attack_on_tick(FlipFridState* context) {
|
||||
lfrfid_worker_stop_thread(context->worker);
|
||||
switch(context->attack) {
|
||||
case FlipFridAttackDefaultValues:
|
||||
if(context->proto == EM4100) {
|
||||
context->payload[0] = id_list[context->attack_step][0];
|
||||
context->payload[1] = id_list[context->attack_step][1];
|
||||
context->payload[2] = id_list[context->attack_step][2];
|
||||
context->payload[3] = id_list[context->attack_step][3];
|
||||
context->payload[4] = id_list[context->attack_step][4];
|
||||
|
||||
if(context->attack_step == 15) {
|
||||
context->attack_step = 0;
|
||||
counter = 0;
|
||||
context->is_attacking = false;
|
||||
notification_message(context->notify, &sequence_blink_stop);
|
||||
notification_message(context->notify, &sequence_single_vibro);
|
||||
} else {
|
||||
context->attack_step++;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
context->payload[0] = id_list_hid[context->attack_step][0];
|
||||
context->payload[1] = id_list_hid[context->attack_step][1];
|
||||
context->payload[2] = id_list_hid[context->attack_step][2];
|
||||
context->payload[3] = id_list_hid[context->attack_step][3];
|
||||
context->payload[4] = id_list_hid[context->attack_step][4];
|
||||
context->payload[5] = id_list_hid[context->attack_step][5];
|
||||
|
||||
if(context->attack_step == 15) {
|
||||
context->attack_step = 0;
|
||||
counter = 0;
|
||||
@@ -67,8 +108,10 @@ void flipfrid_scene_run_attack_on_tick(FlipFridState* context) {
|
||||
context->attack_step++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case FlipFridAttackBfCustomerId:
|
||||
if(context->proto == EM4100) {
|
||||
context->payload[0] = context->attack_step;
|
||||
context->payload[1] = 0x00;
|
||||
context->payload[2] = 0x00;
|
||||
@@ -85,7 +128,28 @@ void flipfrid_scene_run_attack_on_tick(FlipFridState* context) {
|
||||
context->attack_step++;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
context->payload[0] = context->attack_step;
|
||||
context->payload[1] = 0x00;
|
||||
context->payload[2] = 0x00;
|
||||
context->payload[3] = 0x00;
|
||||
context->payload[4] = 0x00;
|
||||
context->payload[5] = 0x00;
|
||||
|
||||
if(context->attack_step == 255) {
|
||||
context->attack_step = 0;
|
||||
counter = 0;
|
||||
context->is_attacking = false;
|
||||
notification_message(context->notify, &sequence_blink_stop);
|
||||
notification_message(context->notify, &sequence_single_vibro);
|
||||
} else {
|
||||
context->attack_step++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case FlipFridAttackLoadFile:
|
||||
if(context->proto == EM4100) {
|
||||
context->payload[0] = context->data[0];
|
||||
context->payload[1] = context->data[1];
|
||||
context->payload[2] = context->data[2];
|
||||
@@ -105,7 +169,31 @@ void flipfrid_scene_run_attack_on_tick(FlipFridState* context) {
|
||||
context->attack_step++;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
context->payload[0] = context->data[0];
|
||||
context->payload[1] = context->data[1];
|
||||
context->payload[2] = context->data[2];
|
||||
context->payload[3] = context->data[3];
|
||||
context->payload[4] = context->data[4];
|
||||
context->payload[5] = context->data[5];
|
||||
|
||||
context->payload[context->key_index] = context->attack_step;
|
||||
|
||||
if(context->attack_step == 255) {
|
||||
context->attack_step = 0;
|
||||
counter = 0;
|
||||
context->is_attacking = false;
|
||||
notification_message(context->notify, &sequence_blink_stop);
|
||||
notification_message(context->notify, &sequence_single_vibro);
|
||||
break;
|
||||
} else {
|
||||
context->attack_step++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case FlipFridAttackLoadFileCustomUids:
|
||||
if(context->proto == EM4100) {
|
||||
while(true) {
|
||||
string_reset(context->data_str);
|
||||
if(!stream_read_line(context->uids_stream, context->data_str)) {
|
||||
@@ -131,6 +219,33 @@ void flipfrid_scene_run_attack_on_tick(FlipFridState* context) {
|
||||
context->payload[i] = (uint8_t)strtol(temp_str, NULL, 16);
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
while(true) {
|
||||
string_reset(context->data_str);
|
||||
if(!stream_read_line(context->uids_stream, context->data_str)) {
|
||||
context->attack_step = 0;
|
||||
counter = 0;
|
||||
context->is_attacking = false;
|
||||
notification_message(context->notify, &sequence_blink_stop);
|
||||
notification_message(context->notify, &sequence_single_vibro);
|
||||
break;
|
||||
};
|
||||
if(string_get_char(context->data_str, 0) == '#') continue;
|
||||
if(string_size(context->data_str) != 13) continue;
|
||||
break;
|
||||
}
|
||||
FURI_LOG_D(TAG, string_get_cstr(context->data_str));
|
||||
|
||||
// string is valid, parse it in context->payload
|
||||
for(uint8_t i = 0; i < 6; i++) {
|
||||
char temp_str[3];
|
||||
temp_str[0] = string_get_cstr(context->data_str)[i * 2];
|
||||
temp_str[1] = string_get_cstr(context->data_str)[i * 2 + 1];
|
||||
temp_str[2] = '\0';
|
||||
context->payload[i] = (uint8_t)strtol(temp_str, NULL, 16);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,7 +305,19 @@ void flipfrid_scene_run_attack_on_draw(Canvas* canvas, FlipFridState* context) {
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 64, 8, AlignCenter, AlignTop, string_get_cstr(context->attack_name));
|
||||
|
||||
char uid[16];
|
||||
char uid[18];
|
||||
if(context->proto == HIDProx) {
|
||||
snprintf(
|
||||
uid,
|
||||
sizeof(uid),
|
||||
"%02X:%02X:%02X:%02X:%02X:%02X",
|
||||
context->payload[0],
|
||||
context->payload[1],
|
||||
context->payload[2],
|
||||
context->payload[3],
|
||||
context->payload[4],
|
||||
context->payload[5]);
|
||||
} else {
|
||||
snprintf(
|
||||
uid,
|
||||
sizeof(uid),
|
||||
@@ -200,6 +327,8 @@ void flipfrid_scene_run_attack_on_draw(Canvas* canvas, FlipFridState* context) {
|
||||
context->payload[2],
|
||||
context->payload[3],
|
||||
context->payload[4]);
|
||||
}
|
||||
|
||||
canvas_draw_str_aligned(canvas, 64, 24, AlignCenter, AlignTop, uid);
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
|
||||
@@ -47,7 +47,8 @@ static void render_callback(Canvas* const canvas, void* ctx) {
|
||||
canvas_draw_frame(canvas, 0, 0, 128, 64);
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
if(!plugin_state->addr_err && !plugin_state->ducky_err && !plugin_state->is_thread_running) {
|
||||
if(!plugin_state->addr_err && !plugin_state->ducky_err && !plugin_state->is_thread_running &&
|
||||
!plugin_state->is_ducky_running) {
|
||||
snprintf(target_text, sizeof(target_text), target_fmt_text, target_address_str);
|
||||
canvas_draw_str_aligned(canvas, 7, 10, AlignLeft, AlignBottom, target_text);
|
||||
canvas_draw_str_aligned(canvas, 22, 20, AlignLeft, AlignBottom, "<- select address ->");
|
||||
@@ -66,7 +67,10 @@ static void render_callback(Canvas* const canvas, void* ctx) {
|
||||
canvas, 3, 10, AlignLeft, AlignBottom, "Error: No mousejacker folder");
|
||||
canvas_draw_str_aligned(canvas, 3, 20, AlignLeft, AlignBottom, "or duckyscript file");
|
||||
canvas_draw_str_aligned(canvas, 3, 30, AlignLeft, AlignBottom, "loading error");
|
||||
} else if(plugin_state->is_thread_running) {
|
||||
} else if(plugin_state->is_thread_running && !plugin_state->is_ducky_running) {
|
||||
canvas_draw_str_aligned(canvas, 3, 10, AlignLeft, AlignBottom, "Loading...");
|
||||
canvas_draw_str_aligned(canvas, 3, 20, AlignLeft, AlignBottom, "Please wait!");
|
||||
} else if(plugin_state->is_thread_running && plugin_state->is_ducky_running) {
|
||||
canvas_draw_str_aligned(canvas, 3, 10, AlignLeft, AlignBottom, "Running duckyscript");
|
||||
canvas_draw_str_aligned(canvas, 3, 20, AlignLeft, AlignBottom, "Please wait!");
|
||||
canvas_draw_str_aligned(
|
||||
@@ -97,7 +101,7 @@ static void hexlify(uint8_t* in, uint8_t size, char* out) {
|
||||
snprintf(out + strlen(out), sizeof(out + strlen(out)), "%02X", in[i]);
|
||||
}
|
||||
|
||||
static bool open_ducky_script(Stream* stream) {
|
||||
static bool open_ducky_script(Stream* stream, PluginState* plugin_state) {
|
||||
DialogsApp* dialogs = furi_record_open("dialogs");
|
||||
bool result = false;
|
||||
string_t path;
|
||||
@@ -120,6 +124,9 @@ static bool open_ducky_script(Stream* stream) {
|
||||
}
|
||||
}
|
||||
string_clear(path);
|
||||
|
||||
plugin_state->is_ducky_running = true;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -160,10 +167,11 @@ static bool process_ducky_file(
|
||||
uint8_t* file_buf;
|
||||
bool loaded = false;
|
||||
FURI_LOG_D(TAG, "opening ducky script");
|
||||
if(open_ducky_script(file_stream)) {
|
||||
if(open_ducky_script(file_stream, plugin_state)) {
|
||||
file_size = stream_size(file_stream);
|
||||
if(file_size == (size_t)0) {
|
||||
FURI_LOG_D(TAG, "load failed. file_size: %d", file_size);
|
||||
plugin_state->is_ducky_running = false;
|
||||
return loaded;
|
||||
}
|
||||
file_buf = malloc(file_size);
|
||||
@@ -180,6 +188,7 @@ static bool process_ducky_file(
|
||||
}
|
||||
free(file_buf);
|
||||
}
|
||||
plugin_state->is_ducky_running = false;
|
||||
return loaded;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ typedef struct {
|
||||
bool ducky_err;
|
||||
bool addr_err;
|
||||
bool is_thread_running;
|
||||
bool is_ducky_running;
|
||||
bool close_thread_please;
|
||||
Storage* storage;
|
||||
FuriThread* mjthread;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
App(
|
||||
appid="picopass",
|
||||
name="PicoPass Reader",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="picopass_app",
|
||||
requires=[
|
||||
"storage",
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
App(
|
||||
appid="spectrum_analyzer",
|
||||
name="Spectrum Analyzer",
|
||||
apptype=FlipperAppType.APP,
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="spectrum_analyzer_app",
|
||||
cdefines=["APP_SPECTRUM_ANALYZER"],
|
||||
requires=["gui"],
|
||||
icon="A_SpectrumAnalyzer_14",
|
||||
stack_size=2 * 1024,
|
||||
order=12,
|
||||
fap_icon="spectrum_10px.png",
|
||||
fap_category="Tools",
|
||||
)
|
||||
BIN
applications/plugins/spectrum_analyzer/spectrum_10px.png
Normal file
BIN
applications/plugins/spectrum_analyzer/spectrum_10px.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 135 B |
@@ -2,7 +2,7 @@ App(
|
||||
appid="subbrute",
|
||||
name="Sub-GHz Bruteforcer",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="subbrute_start",
|
||||
entry_point="subbrute_app",
|
||||
cdefines=["APP_SUB_BRUTE"],
|
||||
requires=["gui","dialogs"],
|
||||
stack_size=2 * 1024,
|
||||
|
||||
370
applications/plugins/subbrute/helpers/subbrute_worker.c
Normal file
370
applications/plugins/subbrute/helpers/subbrute_worker.c
Normal file
@@ -0,0 +1,370 @@
|
||||
#include "subbrute_worker.h"
|
||||
|
||||
#include <subghz/environment.h>
|
||||
#include <subghz/transmitter.h>
|
||||
#include <flipper_format_i.h>
|
||||
|
||||
#define TAG "SubBruteWorker"
|
||||
|
||||
struct SubBruteWorker {
|
||||
FuriThread* thread;
|
||||
volatile bool worker_running;
|
||||
volatile bool worker_manual_mode;
|
||||
bool is_manual_init;
|
||||
|
||||
SubGhzEnvironment* environment;
|
||||
SubGhzTransmitter* transmitter;
|
||||
FlipperFormat* flipper_format;
|
||||
|
||||
uint32_t last_time_tx_data;
|
||||
|
||||
// Preset and frequency needed
|
||||
FuriHalSubGhzPreset preset;
|
||||
uint32_t frequency;
|
||||
string_t protocol_name;
|
||||
|
||||
//SubBruteWorkerCallback callback;
|
||||
//void* context;
|
||||
};
|
||||
|
||||
/** Taken from subghz_tx_rx_worker.c */
|
||||
#define SUBBRUTE_TXRX_WORKER_BUF_SIZE 2048
|
||||
#define SUBBRUTE_TXRX_WORKER_MAX_TXRX_SIZE 60
|
||||
#define SUBBRUTE_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF 40
|
||||
#define SUBBRUTE_TX_TIMEOUT 50
|
||||
#define SUBBRUTE_SEND_DELAY 260
|
||||
|
||||
/**
|
||||
* Entrypoint for worker
|
||||
*
|
||||
* @param context SubBruteWorker*
|
||||
* @return 0 if ok
|
||||
*/
|
||||
int32_t subbrute_worker_thread(void* context) {
|
||||
furi_assert(context);
|
||||
SubBruteWorker* instance = (SubBruteWorker*)context;
|
||||
|
||||
if(!instance->worker_running) {
|
||||
FURI_LOG_W(TAG, "Worker is not set to running state!");
|
||||
return -1;
|
||||
}
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_I(TAG, "Worker start");
|
||||
#endif
|
||||
|
||||
instance->environment = subghz_environment_alloc();
|
||||
instance->transmitter = subghz_transmitter_alloc_init(
|
||||
instance->environment, string_get_cstr(instance->protocol_name));
|
||||
|
||||
furi_hal_subghz_reset();
|
||||
furi_hal_subghz_load_preset(instance->preset);
|
||||
instance->frequency = furi_hal_subghz_set_frequency_and_path(instance->frequency);
|
||||
|
||||
furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
|
||||
furi_hal_gpio_write(&gpio_cc1101_g0, true);
|
||||
|
||||
// Set ready to transmit value
|
||||
instance->last_time_tx_data = furi_get_tick() - SUBBRUTE_SEND_DELAY;
|
||||
|
||||
while(instance->worker_running) {
|
||||
// Transmit
|
||||
if(!furi_hal_subghz_tx()) {
|
||||
FURI_LOG_E(TAG, "Cannot transmit!");
|
||||
break;
|
||||
}
|
||||
furi_delay_ms(SUBBRUTE_TX_TIMEOUT);
|
||||
}
|
||||
|
||||
furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate);
|
||||
furi_hal_subghz_sleep();
|
||||
|
||||
subghz_transmitter_free(instance->transmitter);
|
||||
instance->transmitter = NULL;
|
||||
subghz_environment_free(instance->environment);
|
||||
instance->environment = NULL;
|
||||
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_I(TAG, "Worker stop");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
SubBruteWorker* subbrute_worker_alloc() {
|
||||
SubBruteWorker* instance = malloc(sizeof(SubBruteWorker));
|
||||
|
||||
instance->thread = furi_thread_alloc();
|
||||
furi_thread_set_name(instance->thread, "SubBruteAttackWorker");
|
||||
furi_thread_set_stack_size(instance->thread, 2048);
|
||||
furi_thread_set_context(instance->thread, instance);
|
||||
furi_thread_set_callback(instance->thread, subbrute_worker_thread);
|
||||
|
||||
//instance->status = SubBruteWorkerStatusIDLE;
|
||||
instance->worker_running = false;
|
||||
instance->worker_manual_mode = false;
|
||||
|
||||
instance->flipper_format = flipper_format_string_alloc();
|
||||
string_init(instance->protocol_name);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subbrute_worker_free(SubBruteWorker* instance) {
|
||||
furi_assert(instance);
|
||||
furi_assert(!instance->worker_running);
|
||||
|
||||
if(instance->transmitter != NULL) {
|
||||
subghz_transmitter_free(instance->transmitter);
|
||||
instance->transmitter = NULL;
|
||||
}
|
||||
|
||||
if(instance->environment != NULL) {
|
||||
subghz_environment_free(instance->environment);
|
||||
instance->environment = NULL;
|
||||
}
|
||||
|
||||
furi_thread_free(instance->thread);
|
||||
flipper_format_free(instance->flipper_format);
|
||||
|
||||
string_clear(instance->protocol_name);
|
||||
|
||||
free(instance);
|
||||
}
|
||||
|
||||
bool subbrute_worker_start(
|
||||
SubBruteWorker* instance,
|
||||
uint32_t frequency,
|
||||
FuriHalSubGhzPreset preset,
|
||||
const char* protocol_name) {
|
||||
furi_assert(instance);
|
||||
|
||||
if(instance->worker_manual_mode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
instance->frequency = frequency;
|
||||
instance->preset = preset;
|
||||
|
||||
string_clear(instance->protocol_name);
|
||||
string_init_printf(instance->protocol_name, "%s", protocol_name);
|
||||
|
||||
bool res = false;
|
||||
|
||||
furi_hal_subghz_reset();
|
||||
furi_hal_subghz_idle();
|
||||
furi_hal_subghz_load_preset(instance->preset);
|
||||
|
||||
furi_hal_subghz_set_frequency_and_path(instance->frequency);
|
||||
furi_hal_subghz_flush_rx();
|
||||
|
||||
if(furi_hal_subghz_is_tx_allowed(frequency)) {
|
||||
instance->frequency = frequency;
|
||||
res = true;
|
||||
}
|
||||
instance->worker_running = res;
|
||||
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_I(TAG, "Frequency: %d", frequency);
|
||||
#endif
|
||||
instance->preset = preset;
|
||||
|
||||
furi_thread_start(instance->thread);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void subbrute_worker_stop(SubBruteWorker* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
instance->worker_running = false;
|
||||
|
||||
furi_thread_join(instance->thread);
|
||||
|
||||
furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate);
|
||||
furi_hal_subghz_sleep();
|
||||
}
|
||||
|
||||
bool subbrute_worker_is_running(SubBruteWorker* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
return instance->worker_running;
|
||||
}
|
||||
|
||||
bool subbrute_worker_can_transmit(SubBruteWorker* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
return (furi_get_tick() - instance->last_time_tx_data) > SUBBRUTE_SEND_DELAY;
|
||||
}
|
||||
|
||||
bool subbrute_worker_transmit(SubBruteWorker* instance, const char* payload) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->worker_running);
|
||||
|
||||
if(!subbrute_worker_can_transmit(instance)) {
|
||||
FURI_LOG_E(TAG, "Too early to transmit");
|
||||
|
||||
return false;
|
||||
}
|
||||
instance->last_time_tx_data = furi_get_tick();
|
||||
|
||||
#ifdef FURI_DEBUG
|
||||
//FURI_LOG_D(TAG, "payload: %s", payload);
|
||||
#endif
|
||||
|
||||
Stream* stream = flipper_format_get_raw_stream(instance->flipper_format);
|
||||
stream_clean(stream);
|
||||
stream_write_cstring(stream, payload);
|
||||
subghz_transmitter_deserialize(instance->transmitter, instance->flipper_format);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool subbrute_worker_init_manual_transmit(
|
||||
SubBruteWorker* instance,
|
||||
uint32_t frequency,
|
||||
FuriHalSubGhzPreset preset,
|
||||
const char* protocol_name) {
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"subbrute_worker_init_manual_transmit. frequency: %d, protocol: %s",
|
||||
frequency,
|
||||
protocol_name);
|
||||
#endif
|
||||
if(instance->worker_manual_mode || !subbrute_worker_can_transmit(instance)) {
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "cannot transmit");
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
if(instance->worker_running) {
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "subbrute_worker_stop");
|
||||
#endif
|
||||
subbrute_worker_stop(instance);
|
||||
}
|
||||
|
||||
// Not transmit at this period
|
||||
instance->worker_manual_mode = true;
|
||||
|
||||
if(instance->is_manual_init) {
|
||||
FURI_LOG_E(TAG, "Trying to setup without normally shutdown prev transmit session!");
|
||||
subbrute_worker_manual_transmit_stop(instance);
|
||||
}
|
||||
|
||||
instance->preset = preset;
|
||||
instance->frequency = frequency;
|
||||
|
||||
string_clear(instance->protocol_name);
|
||||
string_init_printf(instance->protocol_name, "%s", protocol_name);
|
||||
|
||||
furi_hal_subghz_reset();
|
||||
furi_hal_subghz_idle();
|
||||
furi_hal_subghz_load_preset(instance->preset);
|
||||
|
||||
furi_hal_subghz_set_frequency_and_path(instance->frequency);
|
||||
furi_hal_subghz_flush_rx();
|
||||
|
||||
if(!furi_hal_subghz_is_tx_allowed(frequency)) {
|
||||
FURI_LOG_E(TAG, "Frequency: %d invalid!", frequency);
|
||||
|
||||
instance->frequency = frequency;
|
||||
instance->worker_manual_mode = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_I(TAG, "Frequency: %d", frequency);
|
||||
#endif
|
||||
|
||||
instance->environment = subghz_environment_alloc();
|
||||
instance->transmitter = subghz_transmitter_alloc_init(
|
||||
instance->environment, string_get_cstr(instance->protocol_name));
|
||||
|
||||
furi_hal_subghz_reset();
|
||||
furi_hal_subghz_load_preset(instance->preset);
|
||||
instance->frequency = furi_hal_subghz_set_frequency_and_path(frequency);
|
||||
|
||||
furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate);
|
||||
furi_hal_subghz_sleep();
|
||||
subghz_transmitter_free(instance->transmitter);
|
||||
instance->transmitter = NULL;
|
||||
|
||||
instance->worker_manual_mode = false;
|
||||
instance->is_manual_init = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void subbrute_worker_manual_transmit_stop(SubBruteWorker* instance) {
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "subbrute_worker_manual_transmit_stop");
|
||||
#endif
|
||||
if(!instance->is_manual_init) {
|
||||
return;
|
||||
}
|
||||
|
||||
furi_hal_subghz_idle();
|
||||
furi_hal_subghz_sleep();
|
||||
|
||||
if(instance->transmitter != NULL) {
|
||||
subghz_transmitter_free(instance->transmitter);
|
||||
instance->transmitter = NULL;
|
||||
}
|
||||
subghz_environment_free(instance->environment);
|
||||
instance->environment = NULL;
|
||||
|
||||
instance->is_manual_init = false;
|
||||
}
|
||||
|
||||
bool subbrute_worker_manual_transmit(SubBruteWorker* instance, const char* payload) {
|
||||
furi_assert(instance);
|
||||
|
||||
if(instance->worker_manual_mode || !subbrute_worker_can_transmit(instance)) {
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "cannot transmit");
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
if(instance->worker_running) {
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "subbrute_worker_stop");
|
||||
#endif
|
||||
subbrute_worker_stop(instance);
|
||||
}
|
||||
if(!instance->is_manual_init) {
|
||||
FURI_LOG_E(TAG, "Manually transmit doesn't set!");
|
||||
return false;
|
||||
}
|
||||
|
||||
instance->last_time_tx_data = furi_get_tick();
|
||||
instance->worker_manual_mode = true;
|
||||
|
||||
Stream* stream = flipper_format_get_raw_stream(instance->flipper_format);
|
||||
stream_clean(stream);
|
||||
stream_write_cstring(stream, payload);
|
||||
|
||||
instance->transmitter = subghz_transmitter_alloc_init(
|
||||
instance->environment, string_get_cstr(instance->protocol_name));
|
||||
subghz_transmitter_deserialize(instance->transmitter, instance->flipper_format);
|
||||
furi_hal_subghz_reset();
|
||||
furi_hal_subghz_load_preset(instance->preset);
|
||||
instance->frequency = furi_hal_subghz_set_frequency_and_path(instance->frequency);
|
||||
|
||||
furi_hal_subghz_start_async_tx(subghz_transmitter_yield, instance->transmitter);
|
||||
|
||||
while(!furi_hal_subghz_is_async_tx_complete()) {
|
||||
furi_delay_ms(SUBBRUTE_TX_TIMEOUT);
|
||||
}
|
||||
furi_hal_subghz_stop_async_tx();
|
||||
|
||||
furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate);
|
||||
furi_hal_subghz_sleep();
|
||||
subghz_transmitter_free(instance->transmitter);
|
||||
instance->transmitter = NULL;
|
||||
|
||||
stream_clean(stream);
|
||||
|
||||
instance->worker_manual_mode = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
35
applications/plugins/subbrute/helpers/subbrute_worker.h
Normal file
35
applications/plugins/subbrute/helpers/subbrute_worker.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi_hal_subghz.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,
|
||||
const char* protocol_name);
|
||||
void subbrute_worker_stop(SubBruteWorker* instance);
|
||||
//bool subbrute_worker_write(SubBruteWorker* instance, uint8_t* data, size_t size);
|
||||
bool subbrute_worker_is_running(SubBruteWorker* instance);
|
||||
bool subbrute_worker_can_transmit(SubBruteWorker* instance);
|
||||
bool subbrute_worker_transmit(SubBruteWorker* instance, const char* payload);
|
||||
bool subbrute_worker_init_manual_transmit(SubBruteWorker* instance,
|
||||
uint32_t frequency,
|
||||
FuriHalSubGhzPreset preset,
|
||||
const char* protocol_name);
|
||||
bool subbrute_worker_manual_transmit(SubBruteWorker* instance, const char* payload);
|
||||
void subbrute_worker_manual_transmit_stop(SubBruteWorker* instance);
|
||||
@@ -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,7 @@
|
||||
ADD_SCENE(subbrute, load_file, LoadFile)
|
||||
ADD_SCENE(subbrute, load_select, LoadSelect)
|
||||
ADD_SCENE(subbrute, run_attack, RunAttack)
|
||||
ADD_SCENE(subbrute, save_name, SaveName)
|
||||
ADD_SCENE(subbrute, save_success, SaveSuccess)
|
||||
ADD_SCENE(subbrute, setup_attack, SetupAttack)
|
||||
ADD_SCENE(subbrute, start, Start)
|
||||
@@ -0,0 +1,77 @@
|
||||
#include "../subbrute_i.h"
|
||||
#include "../subbrute_custom_event.h"
|
||||
#include <lib/subghz/protocols/registry.h>
|
||||
|
||||
#define TAG "SubBruteSceneLoadFile"
|
||||
|
||||
//void subbrute_scene_load_file_callback(SubBruteCustomEvent event, void* context) {
|
||||
//// furi_assert(context);
|
||||
////
|
||||
//// SubBruteState* instance = (SubBruteState*)context;
|
||||
//// view_dispatcher_send_custom_event(instance->view_dispatcher, event);
|
||||
//}
|
||||
|
||||
void subbrute_scene_load_file_on_enter(void* context) {
|
||||
furi_assert(context);
|
||||
SubBruteState* instance = (SubBruteState*)context;
|
||||
|
||||
// Input events and views are managed by file_browser
|
||||
string_t app_folder;
|
||||
string_t load_path;
|
||||
string_init(load_path);
|
||||
string_init_set_str(app_folder, SUBBRUTE_PATH);
|
||||
|
||||
DialogsFileBrowserOptions browser_options;
|
||||
dialog_file_browser_set_basic_options(&browser_options, SUBBRUTE_FILE_EXT, &I_sub1_10px);
|
||||
|
||||
SubBruteFileResult load_result = SubBruteFileResultUnknown;
|
||||
bool res =
|
||||
dialog_file_browser_show(instance->dialogs, load_path, app_folder, &browser_options);
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"load_path: %s, app_folder: %s",
|
||||
string_get_cstr(load_path),
|
||||
string_get_cstr(app_folder));
|
||||
#endif
|
||||
if(res) {
|
||||
load_result = subbrute_device_load_from_file(instance->device, load_path);
|
||||
if(load_result == SubBruteFileResultOk) {
|
||||
load_result = subbrute_device_attack_set(instance->device, SubBruteAttackLoadFile);
|
||||
if(load_result == SubBruteFileResultOk) {
|
||||
// Ready to run!
|
||||
instance->device->state = SubBruteDeviceStateReady;
|
||||
FURI_LOG_I(TAG, "Ready to run");
|
||||
res = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(load_result == SubBruteFileResultOk) {
|
||||
scene_manager_next_scene(instance->scene_manager, SubBruteSceneLoadSelect);
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Returned error: %d", load_result);
|
||||
|
||||
string_t dialog_msg;
|
||||
string_init(dialog_msg);
|
||||
string_cat_printf(
|
||||
dialog_msg, "Cannot parse\nfile: %s", subbrute_device_error_get_desc(load_result));
|
||||
dialog_message_show_storage_error(instance->dialogs, string_get_cstr(dialog_msg));
|
||||
string_clear(dialog_msg);
|
||||
scene_manager_search_and_switch_to_previous_scene(
|
||||
instance->scene_manager, SubBruteSceneStart);
|
||||
}
|
||||
|
||||
string_clear(app_folder);
|
||||
string_clear(load_path);
|
||||
}
|
||||
|
||||
void subbrute_scene_load_file_on_exit(void* context) {
|
||||
UNUSED(context);
|
||||
}
|
||||
|
||||
bool subbrute_scene_load_file_on_event(void* context, SceneManagerEvent event) {
|
||||
UNUSED(context);
|
||||
UNUSED(event);
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
#include "../subbrute_i.h"
|
||||
#include "../subbrute_custom_event.h"
|
||||
#include "../views/subbrute_main_view.h"
|
||||
|
||||
#define TAG "SubBruteSceneStart"
|
||||
|
||||
void subbrute_scene_load_select_callback(SubBruteCustomEvent event, void* context) {
|
||||
furi_assert(context);
|
||||
|
||||
SubBruteState* instance = (SubBruteState*)context;
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "subbrute_scene_load_select_callback");
|
||||
#endif
|
||||
view_dispatcher_send_custom_event(instance->view_dispatcher, event);
|
||||
}
|
||||
|
||||
void subbrute_scene_load_select_on_enter(void* context) {
|
||||
furi_assert(context);
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_I(TAG, "subbrute_scene_load_select_on_enter");
|
||||
#endif
|
||||
SubBruteState* instance = (SubBruteState*)context;
|
||||
SubBruteMainView* view = instance->view_main;
|
||||
|
||||
instance->current_view = SubBruteViewMain;
|
||||
subbrute_main_view_set_callback(view, subbrute_scene_load_select_callback, instance);
|
||||
subbrute_main_view_set_index(view, 7, true, instance->device->file_key);
|
||||
|
||||
view_dispatcher_switch_to_view(instance->view_dispatcher, instance->current_view);
|
||||
}
|
||||
|
||||
void subbrute_scene_load_select_on_exit(void* context) {
|
||||
UNUSED(context);
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_I(TAG, "subbrute_scene_load_select_on_exit");
|
||||
#endif
|
||||
}
|
||||
|
||||
bool subbrute_scene_load_select_on_event(void* context, SceneManagerEvent event) {
|
||||
SubBruteState* instance = (SubBruteState*)context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == SubBruteCustomEventTypeIndexSelected) {
|
||||
instance->device->load_index = subbrute_main_view_get_index(instance->view_main);
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "load_index: %d", instance->device->load_index);
|
||||
#endif
|
||||
scene_manager_next_scene(instance->scene_manager, SubBruteSceneSetupAttack);
|
||||
consumed = true;
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeBack) {
|
||||
if(!scene_manager_search_and_switch_to_previous_scene(
|
||||
instance->scene_manager, SubBruteSceneStart)) {
|
||||
scene_manager_next_scene(instance->scene_manager, SubBruteSceneStart);
|
||||
}
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
#include "../subbrute_i.h"
|
||||
#include "../subbrute_custom_event.h"
|
||||
#include "../views/subbrute_attack_view.h"
|
||||
#include "../helpers/subbrute_worker.h"
|
||||
|
||||
static void subbrute_scene_run_attack_callback(SubBruteCustomEvent event, void* context) {
|
||||
furi_assert(context);
|
||||
|
||||
SubBruteState* instance = (SubBruteState*)context;
|
||||
view_dispatcher_send_custom_event(instance->view_dispatcher, event);
|
||||
}
|
||||
|
||||
void subbrute_scene_run_attack_on_exit(void* context) {
|
||||
furi_assert(context);
|
||||
|
||||
SubBruteState* instance = (SubBruteState*)context;
|
||||
notification_message(instance->notifications, &sequence_blink_stop);
|
||||
}
|
||||
|
||||
void subbrute_scene_run_attack_on_enter(void* context) {
|
||||
furi_assert(context);
|
||||
SubBruteState* instance = (SubBruteState*)context;
|
||||
SubBruteAttackView* view = instance->view_attack;
|
||||
|
||||
instance->current_view = SubBruteViewAttack;
|
||||
subbrute_attack_view_set_callback(view, subbrute_scene_run_attack_callback, instance);
|
||||
view_dispatcher_switch_to_view(instance->view_dispatcher, instance->current_view);
|
||||
|
||||
subbrute_attack_view_init_values(
|
||||
view,
|
||||
(uint8_t)instance->device->attack,
|
||||
instance->device->max_value,
|
||||
instance->device->key_index,
|
||||
true);
|
||||
|
||||
// Start worker if not started
|
||||
subbrute_worker_init_manual_transmit(
|
||||
instance->worker,
|
||||
instance->device->frequency,
|
||||
instance->device->preset,
|
||||
string_get_cstr(instance->device->protocol_name));
|
||||
}
|
||||
|
||||
bool subbrute_scene_run_attack_on_event(void* context, SceneManagerEvent event) {
|
||||
SubBruteState* instance = (SubBruteState*)context;
|
||||
SubBruteAttackView* view = instance->view_attack;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == SubBruteCustomEventTypeTransmitNotStarted ||
|
||||
event.event == SubBruteCustomEventTypeTransmitFinished ||
|
||||
event.event == SubBruteCustomEventTypeBackPressed) {
|
||||
// Stop transmit
|
||||
scene_manager_next_scene(instance->scene_manager, SubBruteSceneSetupAttack);
|
||||
consumed = true;
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeTick) {
|
||||
if(subbrute_worker_can_transmit(instance->worker)) {
|
||||
// Blink
|
||||
notification_message(instance->notifications, &sequence_blink_yellow_100);
|
||||
|
||||
if(subbrute_worker_manual_transmit(instance->worker, instance->device->payload)) {
|
||||
// Make payload for new iteration or exit
|
||||
if(instance->device->key_index + 1 > instance->device->max_value) {
|
||||
// End of list
|
||||
scene_manager_next_scene(instance->scene_manager, SubBruteSceneSetupAttack);
|
||||
} else {
|
||||
instance->device->key_index++;
|
||||
subbrute_attack_view_set_current_step(view, instance->device->key_index);
|
||||
subbrute_device_create_packet_parsed(
|
||||
instance->device, instance->device->key_index);
|
||||
}
|
||||
}
|
||||
|
||||
// Stop
|
||||
notification_message(instance->notifications, &sequence_blink_stop);
|
||||
}
|
||||
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
#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"
|
||||
|
||||
#define TAG "SubBruteSceneSaveFile"
|
||||
|
||||
void subbrute_scene_save_name_on_enter(void* context) {
|
||||
SubBruteState* instance = (SubBruteState*)context;
|
||||
SubBruteDevice* device = instance->device;
|
||||
|
||||
// Setup view
|
||||
TextInput* text_input = instance->text_input;
|
||||
set_random_name(device->text_store, sizeof(device->text_store));
|
||||
|
||||
text_input_set_header_text(text_input, "Name of file");
|
||||
text_input_set_result_callback(
|
||||
text_input,
|
||||
subbrute_text_input_callback,
|
||||
instance,
|
||||
device->text_store,
|
||||
SUBBRUTE_MAX_LEN_NAME,
|
||||
true);
|
||||
|
||||
string_set_str(device->load_path, SUBBRUTE_PATH);
|
||||
|
||||
ValidatorIsFile* validator_is_file =
|
||||
validator_is_file_alloc_init(string_get_cstr(device->load_path), SUBBRUTE_FILE_EXT, "");
|
||||
text_input_set_validator(text_input, validator_is_file_callback, validator_is_file);
|
||||
|
||||
view_dispatcher_switch_to_view(instance->view_dispatcher, SubBruteViewTextInput);
|
||||
}
|
||||
|
||||
bool subbrute_scene_save_name_on_event(void* context, SceneManagerEvent event) {
|
||||
SubBruteState* instance = (SubBruteState*)context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeBack) {
|
||||
scene_manager_previous_scene(instance->scene_manager);
|
||||
return true;
|
||||
} else if(
|
||||
event.type == SceneManagerEventTypeCustom &&
|
||||
event.event == SubBruteCustomEventTypeTextEditDone) {
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "Saving: %s", instance->device->text_store);
|
||||
#endif
|
||||
bool success = false;
|
||||
if(strcmp(instance->device->text_store, "")) {
|
||||
string_cat_printf(
|
||||
instance->device->load_path,
|
||||
"/%s%s",
|
||||
instance->device->text_store,
|
||||
SUBBRUTE_FILE_EXT);
|
||||
|
||||
if(subbrute_device_save_file(
|
||||
instance->device, string_get_cstr(instance->device->load_path))) {
|
||||
scene_manager_next_scene(instance->scene_manager, SubBruteSceneSaveSuccess);
|
||||
success = true;
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!success) {
|
||||
dialog_message_show_storage_error(instance->dialogs, "Error during saving!");
|
||||
consumed = scene_manager_search_and_switch_to_previous_scene(
|
||||
instance->scene_manager, SubBruteSceneSetupAttack);
|
||||
}
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void subbrute_scene_save_name_on_exit(void* context) {
|
||||
SubBruteState* instance = (SubBruteState*)context;
|
||||
|
||||
// Clear view
|
||||
void* validator_context = text_input_get_validator_callback_context(instance->text_input);
|
||||
text_input_set_validator(instance->text_input, NULL, NULL);
|
||||
validator_is_file_free(validator_context);
|
||||
|
||||
text_input_reset(instance->text_input);
|
||||
|
||||
string_reset(instance->device->load_path);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
#include "../subbrute_i.h"
|
||||
#include "../subbrute_custom_event.h"
|
||||
|
||||
void subbrute_scene_save_success_on_enter(void* context) {
|
||||
furi_assert(context);
|
||||
SubBruteState* instance = context;
|
||||
|
||||
// Setup view
|
||||
Popup* popup = instance->popup;
|
||||
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
|
||||
popup_set_header(popup, "Saved!", 13, 22, AlignLeft, AlignBottom);
|
||||
popup_set_timeout(popup, 1500);
|
||||
popup_set_context(popup, instance);
|
||||
popup_set_callback(popup, subbrute_popup_closed_callback);
|
||||
popup_enable_timeout(popup);
|
||||
view_dispatcher_switch_to_view(instance->view_dispatcher, SubBruteViewPopup);
|
||||
}
|
||||
|
||||
bool subbrute_scene_save_success_on_event(void* context, SceneManagerEvent event) {
|
||||
furi_assert(context);
|
||||
|
||||
SubBruteState* instance = (SubBruteState*)context;
|
||||
//SubBruteMainView* view = instance->view_main;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == SubBruteCustomEventTypePopupClosed) {
|
||||
if(!scene_manager_search_and_switch_to_previous_scene(
|
||||
instance->scene_manager, SubBruteSceneSetupAttack)) {
|
||||
scene_manager_next_scene(instance->scene_manager, SubBruteSceneStart);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void subbrute_scene_save_success_on_exit(void* context) {
|
||||
furi_assert(context);
|
||||
|
||||
SubBruteState* instance = (SubBruteState*)context;
|
||||
|
||||
// Clear view
|
||||
Popup* popup = instance->popup;
|
||||
popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom);
|
||||
popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);
|
||||
popup_set_icon(popup, 0, 0, NULL);
|
||||
popup_set_callback(popup, NULL);
|
||||
popup_set_context(popup, NULL);
|
||||
popup_set_timeout(popup, 0);
|
||||
popup_disable_timeout(popup);
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
#include "../subbrute_i.h"
|
||||
#include "../subbrute_custom_event.h"
|
||||
#include "../views/subbrute_attack_view.h"
|
||||
|
||||
#define TAG "SubBruteSceneSetupAttack"
|
||||
|
||||
static void subbrute_scene_setup_attack_callback(SubBruteCustomEvent event, void* context) {
|
||||
furi_assert(context);
|
||||
|
||||
SubBruteState* instance = (SubBruteState*)context;
|
||||
view_dispatcher_send_custom_event(instance->view_dispatcher, event);
|
||||
}
|
||||
|
||||
void subbrute_scene_setup_attack_on_enter(void* context) {
|
||||
furi_assert(context);
|
||||
SubBruteState* instance = (SubBruteState*)context;
|
||||
SubBruteAttackView* view = instance->view_attack;
|
||||
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "Enter Attack: %d", instance->device->attack);
|
||||
#endif
|
||||
|
||||
subbrute_attack_view_init_values(
|
||||
view,
|
||||
instance->device->attack,
|
||||
instance->device->max_value,
|
||||
instance->device->key_index,
|
||||
false);
|
||||
|
||||
subbrute_worker_init_manual_transmit(
|
||||
instance->worker,
|
||||
instance->device->frequency,
|
||||
instance->device->preset,
|
||||
string_get_cstr(instance->device->protocol_name));
|
||||
|
||||
instance->current_view = SubBruteViewAttack;
|
||||
subbrute_attack_view_set_callback(view, subbrute_scene_setup_attack_callback, instance);
|
||||
view_dispatcher_switch_to_view(instance->view_dispatcher, instance->current_view);
|
||||
}
|
||||
|
||||
void subbrute_scene_setup_attack_on_exit(void* context) {
|
||||
furi_assert(context);
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "subbrute_scene_setup_attack_on_exit");
|
||||
#endif
|
||||
SubBruteState* instance = (SubBruteState*)context;
|
||||
subbrute_worker_manual_transmit_stop(instance->worker);
|
||||
notification_message(instance->notifications, &sequence_blink_stop);
|
||||
}
|
||||
|
||||
bool subbrute_scene_setup_attack_on_event(void* context, SceneManagerEvent event) {
|
||||
SubBruteState* instance = (SubBruteState*)context;
|
||||
SubBruteAttackView* view = instance->view_attack;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == SubBruteCustomEventTypeTransmitStarted) {
|
||||
subbrute_device_create_packet_parsed(instance->device, instance->device->key_index);
|
||||
scene_manager_next_scene(instance->scene_manager, SubBruteSceneRunAttack);
|
||||
} else if(event.event == SubBruteCustomEventTypeSaveFile) {
|
||||
subbrute_worker_manual_transmit_stop(instance->worker);
|
||||
|
||||
subbrute_attack_view_init_values(
|
||||
view,
|
||||
instance->device->attack,
|
||||
instance->device->max_value,
|
||||
instance->device->key_index,
|
||||
false);
|
||||
scene_manager_next_scene(instance->scene_manager, SubBruteSceneSaveName);
|
||||
} else if(event.event == SubBruteCustomEventTypeBackPressed) {
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "SubBruteCustomEventTypeBackPressed");
|
||||
#endif
|
||||
instance->device->key_index = 0x00;
|
||||
//subbrute_attack_view_stop_worker(view);
|
||||
subbrute_attack_view_init_values(
|
||||
view,
|
||||
instance->device->attack,
|
||||
instance->device->max_value,
|
||||
instance->device->key_index,
|
||||
false);
|
||||
scene_manager_next_scene(instance->scene_manager, SubBruteSceneStart);
|
||||
} else if(event.event == SubBruteCustomEventTypeChangeStepUp) {
|
||||
// +1
|
||||
if((instance->device->key_index + 1) - instance->device->max_value == 1) {
|
||||
instance->device->key_index = 0x00;
|
||||
} else {
|
||||
uint64_t value = instance->device->key_index + 1;
|
||||
if(value == instance->device->max_value) {
|
||||
instance->device->key_index = value;
|
||||
} else {
|
||||
instance->device->key_index = value % instance->device->max_value;
|
||||
}
|
||||
}
|
||||
subbrute_attack_view_set_current_step(view, instance->device->key_index);
|
||||
} else if(event.event == SubBruteCustomEventTypeChangeStepUpMore) {
|
||||
// +100
|
||||
uint64_t value = instance->device->key_index + 100;
|
||||
if(value == instance->device->max_value) {
|
||||
instance->device->key_index += value;
|
||||
} else {
|
||||
instance->device->key_index = value % instance->device->max_value;
|
||||
}
|
||||
subbrute_attack_view_set_current_step(view, instance->device->key_index);
|
||||
} else if(event.event == SubBruteCustomEventTypeChangeStepDown) {
|
||||
// -1
|
||||
if(instance->device->key_index - 1 == 0) {
|
||||
instance->device->key_index = 0x00;
|
||||
} else if(instance->device->key_index == 0) {
|
||||
instance->device->key_index = instance->device->max_value;
|
||||
} else {
|
||||
uint64_t value = ((instance->device->key_index - 1) + instance->device->max_value);
|
||||
if(value == instance->device->max_value) {
|
||||
instance->device->key_index = value;
|
||||
} else {
|
||||
instance->device->key_index = value % instance->device->max_value;
|
||||
}
|
||||
}
|
||||
subbrute_attack_view_set_current_step(view, instance->device->key_index);
|
||||
} else if(event.event == SubBruteCustomEventTypeChangeStepDownMore) {
|
||||
// -100
|
||||
uint64_t value = ((instance->device->key_index - 100) + instance->device->max_value);
|
||||
if(value == instance->device->max_value) {
|
||||
instance->device->key_index = value;
|
||||
} else {
|
||||
instance->device->key_index = value % instance->device->max_value;
|
||||
}
|
||||
subbrute_attack_view_set_current_step(view, instance->device->key_index);
|
||||
} else if(event.event == SubBruteCustomEventTypeTransmitCustom) {
|
||||
if(subbrute_worker_can_transmit(instance->worker)) {
|
||||
// Blink
|
||||
notification_message(instance->notifications, &sequence_blink_green_100);
|
||||
|
||||
// if(!subbrute_attack_view_is_worker_running(view)) {
|
||||
// subbrute_attack_view_start_worker(
|
||||
// view,
|
||||
// instance->device->frequency,
|
||||
// instance->device->preset,
|
||||
// string_get_cstr(instance->device->protocol_name));
|
||||
// }
|
||||
subbrute_device_create_packet_parsed(
|
||||
instance->device, instance->device->key_index);
|
||||
subbrute_worker_manual_transmit(instance->worker, instance->device->payload);
|
||||
|
||||
// Stop
|
||||
notification_message(instance->notifications, &sequence_blink_stop);
|
||||
}
|
||||
}
|
||||
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
// if(event.type == SceneManagerEventTypeCustom) {
|
||||
// switch(event.event) {
|
||||
// case SubBruteCustomEventTypeMenuSelected:
|
||||
// with_view_model(
|
||||
// view, (SubBruteMainViewModel * model) {
|
||||
// instance->menu_index = model->index;
|
||||
// return false;
|
||||
// });
|
||||
// scene_manager_next_scene(instance->scene_manager, SubBruteSceneLoadFile);
|
||||
// consumed = true;
|
||||
// break;
|
||||
// case SubBruteCustomEventTypeLoadFile:
|
||||
// with_view_model(
|
||||
// view, (SubBruteMainViewModel * model) {
|
||||
// instance->menu_index = model->index;
|
||||
// return false;
|
||||
// });
|
||||
// scene_manager_next_scene(instance->scene_manager, SubBruteSceneSetupAttack);
|
||||
// consumed = true;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
return consumed;
|
||||
}
|
||||
66
applications/plugins/subbrute/scenes/subbrute_scene_start.c
Normal file
66
applications/plugins/subbrute/scenes/subbrute_scene_start.c
Normal file
@@ -0,0 +1,66 @@
|
||||
#include "../subbrute_i.h"
|
||||
#include "../subbrute_custom_event.h"
|
||||
#include "../views/subbrute_main_view.h"
|
||||
|
||||
#define TAG "SubBruteSceneStart"
|
||||
|
||||
void subbrute_scene_start_callback(SubBruteCustomEvent event, void* context) {
|
||||
furi_assert(context);
|
||||
|
||||
SubBruteState* instance = (SubBruteState*)context;
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "subbrute_scene_start_callback");
|
||||
#endif
|
||||
view_dispatcher_send_custom_event(instance->view_dispatcher, event);
|
||||
}
|
||||
|
||||
void subbrute_scene_start_on_enter(void* context) {
|
||||
furi_assert(context);
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_I(TAG, "subbrute_scene_start_on_enter");
|
||||
#endif
|
||||
SubBruteState* instance = (SubBruteState*)context;
|
||||
SubBruteMainView* view = instance->view_main;
|
||||
|
||||
instance->current_view = SubBruteViewMain;
|
||||
subbrute_main_view_set_callback(view, subbrute_scene_start_callback, instance);
|
||||
subbrute_main_view_set_index(view, instance->device->attack, false, NULL);
|
||||
|
||||
view_dispatcher_switch_to_view(instance->view_dispatcher, instance->current_view);
|
||||
}
|
||||
|
||||
void subbrute_scene_start_on_exit(void* context) {
|
||||
UNUSED(context);
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_I(TAG, "subbrute_scene_start_on_exit");
|
||||
#endif
|
||||
}
|
||||
|
||||
bool subbrute_scene_start_on_event(void* context, SceneManagerEvent event) {
|
||||
SubBruteState* instance = (SubBruteState*)context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "Event: %d", event.event);
|
||||
#endif
|
||||
if(event.event == SubBruteCustomEventTypeMenuSelected) {
|
||||
SubBruteAttacks attack = subbrute_main_view_get_index(instance->view_main);
|
||||
|
||||
subbrute_device_attack_set(instance->device, attack);
|
||||
scene_manager_next_scene(instance->scene_manager, SubBruteSceneSetupAttack);
|
||||
|
||||
consumed = true;
|
||||
} else if(event.event == SubBruteCustomEventTypeLoadFile) {
|
||||
scene_manager_next_scene(instance->scene_manager, SubBruteSceneLoadFile);
|
||||
consumed = true;
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeBack) {
|
||||
//exit app
|
||||
scene_manager_stop(instance->scene_manager);
|
||||
view_dispatcher_stop(instance->view_dispatcher);
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
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,305 @@
|
||||
#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_i.h"
|
||||
#include "subbrute_custom_event.h"
|
||||
|
||||
#include "scene/subbrute_scene_load_file.h"
|
||||
#include "scene/subbrute_scene_select_field.h"
|
||||
#include "scene/subbrute_scene_run_attack.h"
|
||||
#include "scene/subbrute_scene_entrypoint.h"
|
||||
#include "scene/subbrute_scene_save_name.h"
|
||||
#define TAG "SubBruteApp"
|
||||
|
||||
static void draw_callback(Canvas* const canvas, void* ctx) {
|
||||
SubBruteState* subbrute_state = (SubBruteState*)acquire_mutex((ValueMutex*)ctx, 100);
|
||||
static const char* subbrute_menu_names[] = {
|
||||
[SubBruteAttackCAME12bit307] = "CAME 12bit 307mhz",
|
||||
[SubBruteAttackCAME12bit433] = "CAME 12bit 433mhz",
|
||||
[SubBruteAttackCAME12bit868] = "CAME 12bit 868mhz",
|
||||
[SubBruteAttackChamberlain9bit315] = "Chamberlain 9bit 315mhz",
|
||||
[SubBruteAttackChamberlain9bit390] = "Chamberlain 9bit 390mhz",
|
||||
[SubBruteAttackLinear10bit300] = "Linear 10bit 300mhz",
|
||||
[SubBruteAttackLinear10bit310] = "Linear 10bit 310mhz",
|
||||
[SubBruteAttackNICE12bit433] = "NICE 12bit 433mhz",
|
||||
[SubBruteAttackNICE12bit868] = "NICE 12bit 868mhz",
|
||||
[SubBruteAttackLoadFile] = "BF existing dump",
|
||||
[SubBruteAttackTotalCount] = "Total Count",
|
||||
};
|
||||
|
||||
if(subbrute_state == NULL) {
|
||||
return;
|
||||
static const char* subbrute_menu_names_small[] = {
|
||||
[SubBruteAttackCAME12bit307] = "CAME 307mhz",
|
||||
[SubBruteAttackCAME12bit433] = "CAME 433mhz",
|
||||
[SubBruteAttackCAME12bit868] = "CAME 868mhz",
|
||||
[SubBruteAttackChamberlain9bit315] = "Cham 315mhz",
|
||||
[SubBruteAttackChamberlain9bit390] = "Cham 390mhz",
|
||||
[SubBruteAttackLinear10bit300] = "Linear 300mhz",
|
||||
[SubBruteAttackLinear10bit310] = "Linear 310mhz",
|
||||
[SubBruteAttackNICE12bit433] = "NICE 433mhz",
|
||||
[SubBruteAttackNICE12bit868] = "NICE 868mhz",
|
||||
[SubBruteAttackLoadFile] = "Existing",
|
||||
[SubBruteAttackTotalCount] = "Total Count",
|
||||
};
|
||||
|
||||
static bool subbrute_custom_event_callback(void* context, uint32_t event) {
|
||||
furi_assert(context);
|
||||
SubBruteState* instance = context;
|
||||
return scene_manager_handle_custom_event(instance->scene_manager, event);
|
||||
}
|
||||
|
||||
// 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;
|
||||
static bool subbrute_back_event_callback(void* context) {
|
||||
furi_assert(context);
|
||||
SubBruteState* instance = context;
|
||||
return scene_manager_handle_back_event(instance->scene_manager);
|
||||
}
|
||||
|
||||
release_mutex((ValueMutex*)ctx, subbrute_state);
|
||||
}
|
||||
|
||||
void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
|
||||
furi_assert(event_queue);
|
||||
|
||||
SubBruteEvent event = {
|
||||
.evt_type = EventTypeKey, .key = input_event->key, .input_type = input_event->type};
|
||||
furi_message_queue_put(event_queue, &event, 100);
|
||||
}
|
||||
|
||||
static void timer_callback(FuriMessageQueue* event_queue) {
|
||||
furi_assert(event_queue);
|
||||
SubBruteEvent event = {
|
||||
.evt_type = EventTypeTick, .key = InputKeyUp, .input_type = InputTypeRelease};
|
||||
furi_message_queue_put(event_queue, &event, 100);
|
||||
static void subbrute_tick_event_callback(void* context) {
|
||||
furi_assert(context);
|
||||
SubBruteState* instance = context;
|
||||
scene_manager_handle_tick_event(instance->scene_manager);
|
||||
}
|
||||
|
||||
SubBruteState* subbrute_alloc() {
|
||||
SubBruteState* subbrute = malloc(sizeof(SubBruteState));
|
||||
SubBruteState* instance = malloc(sizeof(SubBruteState));
|
||||
|
||||
string_init(subbrute->protocol);
|
||||
string_init(subbrute->preset);
|
||||
string_init(subbrute->file_path);
|
||||
string_init(subbrute->file_path_tmp);
|
||||
string_init_set(subbrute->notification_msg, "");
|
||||
string_init(subbrute->candidate);
|
||||
string_init(subbrute->flipper_format_string);
|
||||
instance->scene_manager = scene_manager_alloc(&subbrute_scene_handlers, instance);
|
||||
instance->view_dispatcher = view_dispatcher_alloc();
|
||||
|
||||
subbrute->previous_scene = NoneScene;
|
||||
subbrute->current_scene = SceneSelectFile;
|
||||
subbrute->is_running = true;
|
||||
subbrute->is_attacking = false;
|
||||
subbrute->key_index = 7;
|
||||
subbrute->notify = furi_record_open(RECORD_NOTIFICATION);
|
||||
instance->gui = furi_record_open(RECORD_GUI);
|
||||
|
||||
subbrute->view_dispatcher = view_dispatcher_alloc();
|
||||
view_dispatcher_enable_queue(instance->view_dispatcher);
|
||||
view_dispatcher_set_event_callback_context(instance->view_dispatcher, instance);
|
||||
view_dispatcher_set_custom_event_callback(
|
||||
instance->view_dispatcher, subbrute_custom_event_callback);
|
||||
view_dispatcher_set_navigation_event_callback(
|
||||
instance->view_dispatcher, subbrute_back_event_callback);
|
||||
view_dispatcher_set_tick_event_callback(
|
||||
instance->view_dispatcher, subbrute_tick_event_callback, 100);
|
||||
|
||||
//Dialog
|
||||
subbrute->dialogs = furi_record_open(RECORD_DIALOGS);
|
||||
instance->dialogs = furi_record_open(RECORD_DIALOGS);
|
||||
|
||||
subbrute->preset_def = malloc(sizeof(SubGhzPresetDefinition));
|
||||
// Notifications
|
||||
instance->notifications = furi_record_open(RECORD_NOTIFICATION);
|
||||
|
||||
//subbrute->flipper_format = flipper_format_string_alloc();
|
||||
//subbrute->environment = subghz_environment_alloc();
|
||||
// Devices
|
||||
instance->device = subbrute_device_alloc();
|
||||
|
||||
return subbrute;
|
||||
// Worker
|
||||
instance->worker = subbrute_worker_alloc();
|
||||
|
||||
// TextInput
|
||||
instance->text_input = text_input_alloc();
|
||||
view_dispatcher_add_view(
|
||||
instance->view_dispatcher,
|
||||
SubBruteViewTextInput,
|
||||
text_input_get_view(instance->text_input));
|
||||
|
||||
// Custom Widget
|
||||
instance->widget = widget_alloc();
|
||||
view_dispatcher_add_view(
|
||||
instance->view_dispatcher, SubBruteViewWidget, widget_get_view(instance->widget));
|
||||
|
||||
// Popup
|
||||
instance->popup = popup_alloc();
|
||||
view_dispatcher_add_view(
|
||||
instance->view_dispatcher, SubBruteViewPopup, popup_get_view(instance->popup));
|
||||
|
||||
// ViewStack
|
||||
instance->view_stack = view_stack_alloc();
|
||||
view_dispatcher_add_view(
|
||||
instance->view_dispatcher, SubBruteViewStack, view_stack_get_view(instance->view_stack));
|
||||
|
||||
// SubBruteMainView
|
||||
instance->view_main = subbrute_main_view_alloc();
|
||||
view_dispatcher_add_view(
|
||||
instance->view_dispatcher,
|
||||
SubBruteViewMain,
|
||||
subbrute_main_view_get_view(instance->view_main));
|
||||
|
||||
// SubBruteAttackView
|
||||
instance->view_attack = subbrute_attack_view_alloc();
|
||||
view_dispatcher_add_view(
|
||||
instance->view_dispatcher,
|
||||
SubBruteViewAttack,
|
||||
subbrute_attack_view_get_view(instance->view_attack));
|
||||
|
||||
// Loading
|
||||
instance->loading = loading_alloc();
|
||||
//instance->flipper_format = flipper_format_string_alloc();
|
||||
//instance->environment = subghz_environment_alloc();
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subbrute_free(SubBruteState* subbrute) {
|
||||
//Dialog
|
||||
furi_record_close(RECORD_DIALOGS);
|
||||
void subbrute_free(SubBruteState* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
notification_message(subbrute->notify, &sequence_blink_stop);
|
||||
// SubBruteDevice
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "free SubBruteDevice");
|
||||
#endif
|
||||
subbrute_device_free(instance->device);
|
||||
|
||||
// SubBruteWorker
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "free SubBruteDevice");
|
||||
#endif
|
||||
subbrute_worker_stop(instance->worker);
|
||||
subbrute_worker_free(instance->worker);
|
||||
|
||||
// Notifications
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "free Notifications");
|
||||
#endif
|
||||
notification_message(instance->notifications, &sequence_blink_stop);
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
instance->notifications = NULL;
|
||||
|
||||
view_dispatcher_free(subbrute->view_dispatcher);
|
||||
// Loading
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "free loading");
|
||||
#endif
|
||||
loading_free(instance->loading);
|
||||
|
||||
string_clear(subbrute->preset);
|
||||
string_clear(subbrute->candidate);
|
||||
// View Main
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "free SubBruteViewMain");
|
||||
#endif
|
||||
view_dispatcher_remove_view(instance->view_dispatcher, SubBruteViewMain);
|
||||
subbrute_main_view_free(instance->view_main);
|
||||
|
||||
// Path strings
|
||||
string_clear(subbrute->file_path);
|
||||
string_clear(subbrute->file_path_tmp);
|
||||
string_clear(subbrute->notification_msg);
|
||||
string_clear(subbrute->candidate);
|
||||
string_clear(subbrute->flipper_format_string);
|
||||
// View Attack
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "free SubBruteViewAttack");
|
||||
#endif
|
||||
view_dispatcher_remove_view(instance->view_dispatcher, SubBruteViewAttack);
|
||||
subbrute_attack_view_free(instance->view_attack);
|
||||
|
||||
//flipper_format_free(subbrute->flipper_format);
|
||||
//subghz_environment_free(subbrute->environment);
|
||||
//subghz_receiver_free(subbrute->receiver);
|
||||
// TextInput
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "free SubBruteViewTextInput");
|
||||
#endif
|
||||
view_dispatcher_remove_view(instance->view_dispatcher, SubBruteViewTextInput);
|
||||
text_input_free(instance->text_input);
|
||||
|
||||
free(subbrute->preset_def);
|
||||
// Custom Widget
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "free SubBruteViewWidget");
|
||||
#endif
|
||||
view_dispatcher_remove_view(instance->view_dispatcher, SubBruteViewWidget);
|
||||
widget_free(instance->widget);
|
||||
|
||||
// Popup
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "free SubBruteViewPopup");
|
||||
#endif
|
||||
view_dispatcher_remove_view(instance->view_dispatcher, SubBruteViewPopup);
|
||||
popup_free(instance->popup);
|
||||
|
||||
// ViewStack
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "free SubBruteViewStack");
|
||||
#endif
|
||||
view_dispatcher_remove_view(instance->view_dispatcher, SubBruteViewStack);
|
||||
view_stack_free(instance->view_stack);
|
||||
|
||||
//Dialog
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "free RECORD_DIALOGS");
|
||||
#endif
|
||||
furi_record_close(RECORD_DIALOGS);
|
||||
instance->dialogs = NULL;
|
||||
|
||||
// Scene manager
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "free scene_manager");
|
||||
#endif
|
||||
scene_manager_free(instance->scene_manager);
|
||||
|
||||
// View Dispatcher
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "free view_dispatcher");
|
||||
#endif
|
||||
view_dispatcher_free(instance->view_dispatcher);
|
||||
|
||||
// GUI
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "free RECORD_GUI");
|
||||
#endif
|
||||
furi_record_close(RECORD_GUI);
|
||||
instance->gui = NULL;
|
||||
|
||||
// The rest
|
||||
free(subbrute);
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "free instance");
|
||||
#endif
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subbrute_show_loading_popup(void* context, bool show) {
|
||||
TaskHandle_t timer_task = xTaskGetHandle(configTIMER_SERVICE_TASK_NAME);
|
||||
SubBruteState* instance = context;
|
||||
ViewStack* view_stack = instance->view_stack;
|
||||
Loading* loading = instance->loading;
|
||||
|
||||
if(show) {
|
||||
// Raise timer priority so that animations can play
|
||||
vTaskPrioritySet(timer_task, configMAX_PRIORITIES - 1);
|
||||
view_stack_add_view(view_stack, loading_get_view(loading));
|
||||
} else {
|
||||
view_stack_remove_view(view_stack, loading_get_view(loading));
|
||||
// Restore default timer priority
|
||||
vTaskPrioritySet(timer_task, configTIMER_TASK_PRIORITY);
|
||||
}
|
||||
}
|
||||
|
||||
void subbrute_text_input_callback(void* context) {
|
||||
furi_assert(context);
|
||||
SubBruteState* instance = context;
|
||||
view_dispatcher_send_custom_event(
|
||||
instance->view_dispatcher, SubBruteCustomEventTypeTextEditDone);
|
||||
}
|
||||
|
||||
void subbrute_popup_closed_callback(void* context) {
|
||||
furi_assert(context);
|
||||
SubBruteState* instance = context;
|
||||
view_dispatcher_send_custom_event(
|
||||
instance->view_dispatcher, SubBruteCustomEventTypePopupClosed);
|
||||
}
|
||||
|
||||
const char* subbrute_get_menu_name(SubBruteAttacks index) {
|
||||
furi_assert(index < SubBruteAttackTotalCount);
|
||||
|
||||
return subbrute_menu_names[index];
|
||||
}
|
||||
|
||||
const char* subbrute_get_small_menu_name(SubBruteAttacks index) {
|
||||
furi_assert(index < SubBruteAttackTotalCount);
|
||||
|
||||
return subbrute_menu_names_small[index];
|
||||
}
|
||||
|
||||
// ENTRYPOINT
|
||||
int32_t subbrute_start(void* p) {
|
||||
int32_t subbrute_app(void* p) {
|
||||
UNUSED(p);
|
||||
// Input
|
||||
FURI_LOG_I(TAG, "Initializing input");
|
||||
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(SubBruteEvent));
|
||||
SubBruteState* subbrute_state = subbrute_alloc();
|
||||
ValueMutex subbrute_state_mutex;
|
||||
|
||||
// Mutex
|
||||
FURI_LOG_I(TAG, "Initializing flipfrid mutex");
|
||||
if(!init_mutex(&subbrute_state_mutex, subbrute_state, sizeof(SubBruteState))) {
|
||||
FURI_LOG_E(TAG, "cannot create mutex\r\n");
|
||||
furi_message_queue_free(event_queue);
|
||||
subbrute_free(subbrute_state);
|
||||
return 255;
|
||||
}
|
||||
SubBruteState* instance = subbrute_alloc();
|
||||
view_dispatcher_attach_to_gui(
|
||||
instance->view_dispatcher, instance->gui, ViewDispatcherTypeFullscreen);
|
||||
scene_manager_next_scene(instance->scene_manager, SubBruteSceneStart);
|
||||
|
||||
furi_hal_power_suppress_charge_enter();
|
||||
|
||||
// Configure view port
|
||||
FURI_LOG_I(TAG, "Initializing viewport");
|
||||
ViewPort* view_port = view_port_alloc();
|
||||
view_port_draw_callback_set(view_port, draw_callback, &subbrute_state_mutex);
|
||||
view_port_input_callback_set(view_port, input_callback, event_queue);
|
||||
|
||||
// Configure timer
|
||||
FURI_LOG_I(TAG, "Initializing timer");
|
||||
FuriTimer* timer = furi_timer_alloc(timer_callback, FuriTimerTypePeriodic, event_queue);
|
||||
furi_timer_start(timer, furi_kernel_get_tick_frequency() / 10); // 10 times per second
|
||||
|
||||
// Register view port in GUI
|
||||
FURI_LOG_I(TAG, "Initializing gui");
|
||||
subbrute_state->gui = furi_record_open(RECORD_GUI);
|
||||
gui_add_view_port(subbrute_state->gui, view_port, GuiLayerFullscreen);
|
||||
|
||||
view_dispatcher_attach_to_gui(
|
||||
subbrute_state->view_dispatcher, subbrute_state->gui, ViewDispatcherTypeFullscreen);
|
||||
|
||||
subbrute_state->current_scene = SceneEntryPoint;
|
||||
|
||||
// Init values
|
||||
SubBruteEvent event;
|
||||
while(subbrute_state->is_running) {
|
||||
// Get next event
|
||||
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 25);
|
||||
if(event_status == FuriStatusOk) {
|
||||
if(event.evt_type == EventTypeKey) {
|
||||
//Handle event key
|
||||
FURI_LOG_D(TAG, "EVENT ###");
|
||||
switch(subbrute_state->current_scene) {
|
||||
case SceneSelectFile:
|
||||
subbrute_scene_load_file_on_event(event, subbrute_state);
|
||||
break;
|
||||
case SceneSelectField:
|
||||
subbrute_scene_select_field_on_event(event, subbrute_state);
|
||||
break;
|
||||
case SceneSaveName:
|
||||
subbrute_scene_save_name_on_event(event, subbrute_state);
|
||||
break;
|
||||
case SceneAttack:
|
||||
subbrute_scene_run_attack_on_event(event, subbrute_state);
|
||||
break;
|
||||
case NoneScene:
|
||||
case SceneEntryPoint:
|
||||
subbrute_scene_entrypoint_on_event(event, subbrute_state);
|
||||
break;
|
||||
}
|
||||
|
||||
} else if(event.evt_type == EventTypeTick) {
|
||||
//Handle event tick
|
||||
if(subbrute_state->current_scene != subbrute_state->previous_scene) {
|
||||
// Trigger Exit Scene
|
||||
switch(subbrute_state->previous_scene) {
|
||||
case SceneSelectFile:
|
||||
subbrute_scene_load_file_on_exit(subbrute_state);
|
||||
break;
|
||||
case SceneSelectField:
|
||||
subbrute_scene_select_field_on_exit(subbrute_state);
|
||||
break;
|
||||
case SceneAttack:
|
||||
subbrute_scene_run_attack_on_exit(subbrute_state);
|
||||
break;
|
||||
case SceneEntryPoint:
|
||||
subbrute_scene_entrypoint_on_exit(subbrute_state);
|
||||
break;
|
||||
case SceneSaveName:
|
||||
subbrute_scene_save_name_on_exit(subbrute_state);
|
||||
break;
|
||||
case NoneScene:
|
||||
break;
|
||||
}
|
||||
|
||||
// Trigger Entry Scene
|
||||
switch(subbrute_state->current_scene) {
|
||||
case NoneScene:
|
||||
case SceneSelectFile:
|
||||
subbrute_scene_load_file_on_enter(subbrute_state);
|
||||
break;
|
||||
case SceneSelectField:
|
||||
subbrute_scene_select_field_on_enter(subbrute_state);
|
||||
break;
|
||||
case SceneAttack:
|
||||
subbrute_scene_run_attack_on_enter(subbrute_state);
|
||||
break;
|
||||
case SceneSaveName:
|
||||
subbrute_scene_save_name_on_enter(subbrute_state);
|
||||
break;
|
||||
case SceneEntryPoint:
|
||||
subbrute_scene_entrypoint_on_enter(subbrute_state);
|
||||
break;
|
||||
}
|
||||
subbrute_state->previous_scene = subbrute_state->current_scene;
|
||||
}
|
||||
|
||||
// Trigger Tick Scene
|
||||
switch(subbrute_state->current_scene) {
|
||||
case NoneScene:
|
||||
case SceneSelectFile:
|
||||
subbrute_scene_load_file_on_tick(subbrute_state);
|
||||
break;
|
||||
case SceneSelectField:
|
||||
subbrute_scene_select_field_on_tick(subbrute_state);
|
||||
break;
|
||||
case SceneAttack:
|
||||
//subbrute_scene_run_attack_on_tick(subbrute_state);
|
||||
break;
|
||||
case SceneEntryPoint:
|
||||
subbrute_scene_entrypoint_on_tick(subbrute_state);
|
||||
break;
|
||||
case SceneSaveName:
|
||||
subbrute_scene_save_name_on_tick(subbrute_state);
|
||||
break;
|
||||
}
|
||||
view_port_update(view_port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
furi_timer_stop(timer);
|
||||
furi_timer_free(timer);
|
||||
|
||||
view_dispatcher_run(instance->view_dispatcher);
|
||||
furi_hal_power_suppress_charge_exit();
|
||||
|
||||
FURI_LOG_I(TAG, "Cleaning up");
|
||||
gui_remove_view_port(subbrute_state->gui, view_port);
|
||||
view_port_free(view_port);
|
||||
furi_message_queue_free(event_queue);
|
||||
furi_record_close(RECORD_GUI);
|
||||
subbrute_free(subbrute_state);
|
||||
subbrute_free(instance);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,110 +1,3 @@
|
||||
#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>
|
||||
#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;
|
||||
typedef struct SubBruteState SubBruteState;
|
||||
28
applications/plugins/subbrute/subbrute_custom_event.h
Normal file
28
applications/plugins/subbrute/subbrute_custom_event.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#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,
|
||||
SubBruteCustomEventTypeIndexSelected,
|
||||
SubBruteCustomEventTypeTransmitStarted,
|
||||
SubBruteCustomEventTypeTransmitFinished,
|
||||
SubBruteCustomEventTypeTransmitNotStarted,
|
||||
SubBruteCustomEventTypeTransmitCustom,
|
||||
SubBruteCustomEventTypeSaveFile,
|
||||
SubBruteCustomEventTypeSaveSuccess,
|
||||
SubBruteCustomEventTypeChangeStepUp,
|
||||
SubBruteCustomEventTypeChangeStepDown,
|
||||
SubBruteCustomEventTypeChangeStepUpMore,
|
||||
SubBruteCustomEventTypeChangeStepDownMore,
|
||||
|
||||
SubBruteCustomEventTypeMenuSelected,
|
||||
SubBruteCustomEventTypeTextEditDone,
|
||||
SubBruteCustomEventTypePopupClosed,
|
||||
|
||||
SubBruteCustomEventTypeLoadFile,
|
||||
} SubBruteCustomEvent;
|
||||
643
applications/plugins/subbrute/subbrute_device.c
Normal file
643
applications/plugins/subbrute/subbrute_device.c
Normal file
@@ -0,0 +1,643 @@
|
||||
#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";
|
||||
static const char* subbrute_key_file_key = "%s\nKey: %s\n";
|
||||
static const char* subbrute_key_file_princeton_end = "%s\nKey: %s\nTE: %d\n";
|
||||
|
||||
// Why nobody set in as const in all codebase?
|
||||
static const char* preset_ook270_async = "FuriHalSubGhzPresetOok270Async";
|
||||
static const char* preset_ook650_async = "FuriHalSubGhzPresetOok650Async";
|
||||
static const char* preset_2fsk_dev238_async = "FuriHalSubGhzPreset2FSKDev238Async";
|
||||
static const char* preset_2fsk_dev476_async = "FuriHalSubGhzPreset2FSKDev476Async";
|
||||
static const char* preset_msk99_97_kb_async = "FuriHalSubGhzPresetMSK99_97KbAsync";
|
||||
static const char* preset_gfs99_97_kb_async = "FuriHalSubGhzPresetGFS99_97KbAsync";
|
||||
|
||||
SubBruteDevice* subbrute_device_alloc() {
|
||||
SubBruteDevice* instance = malloc(sizeof(SubBruteDevice));
|
||||
|
||||
instance->state = SubBruteDeviceStateIDLE;
|
||||
instance->key_index = 0;
|
||||
|
||||
string_init(instance->load_path);
|
||||
string_init(instance->preset_name);
|
||||
string_init(instance->protocol_name);
|
||||
|
||||
instance->decoder_result = NULL;
|
||||
instance->receiver = NULL;
|
||||
instance->environment = NULL;
|
||||
|
||||
subbrute_device_attack_set_default_values(instance, SubBruteAttackCAME12bit307);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subbrute_device_free(SubBruteDevice* instance) {
|
||||
furi_assert(instance);
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "subbrute_device_free");
|
||||
#endif
|
||||
|
||||
// I don't know how to free this
|
||||
instance->decoder_result = NULL;
|
||||
|
||||
if(instance->receiver != NULL) {
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "subghz_receiver_free");
|
||||
#endif
|
||||
subghz_receiver_free(instance->receiver);
|
||||
instance->receiver = NULL;
|
||||
}
|
||||
|
||||
if(instance->environment != NULL) {
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "subghz_environment_free");
|
||||
#endif
|
||||
subghz_environment_free(instance->environment);
|
||||
instance->environment = NULL;
|
||||
}
|
||||
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "before free");
|
||||
#endif
|
||||
|
||||
string_clear(instance->load_path);
|
||||
string_clear(instance->preset_name);
|
||||
string_clear(instance->protocol_name);
|
||||
|
||||
free(instance);
|
||||
}
|
||||
|
||||
bool subbrute_device_save_file(SubBruteDevice* instance, const char* dev_file_name) {
|
||||
furi_assert(instance);
|
||||
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "subbrute_device_save_file: %s", dev_file_name);
|
||||
#endif
|
||||
bool result = subbrute_device_create_packet_parsed(instance, instance->key_index);
|
||||
|
||||
if(!result) {
|
||||
FURI_LOG_E(TAG, "subbrute_device_create_packet_parsed failed!");
|
||||
//subbrute_device_notification_message(instance, &sequence_error);
|
||||
return false;
|
||||
}
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
Stream* stream = buffered_file_stream_alloc(storage);
|
||||
|
||||
result = false;
|
||||
do {
|
||||
if(!buffered_file_stream_open(stream, dev_file_name, FSAM_READ_WRITE, FSOM_OPEN_ALWAYS)) {
|
||||
buffered_file_stream_close(stream);
|
||||
break;
|
||||
}
|
||||
stream_write_cstring(stream, instance->payload);
|
||||
|
||||
result = true;
|
||||
} while(false);
|
||||
|
||||
buffered_file_stream_close(stream);
|
||||
stream_free(stream);
|
||||
if(!result) {
|
||||
FURI_LOG_E(TAG, "stream_write_string failed!");
|
||||
//subbrute_device_notification_message(instance, &sequence_error);
|
||||
}
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const char* subbrute_device_error_get_desc(SubBruteFileResult error_id) {
|
||||
const char* result;
|
||||
switch(error_id) {
|
||||
case(SubBruteFileResultOk):
|
||||
result = "OK";
|
||||
break;
|
||||
case(SubBruteFileResultErrorOpenFile):
|
||||
result = "invalid name/path";
|
||||
break;
|
||||
case(SubBruteFileResultMissingOrIncorrectHeader):
|
||||
result = "Missing or incorrect header";
|
||||
break;
|
||||
case(SubBruteFileResultFrequencyNotAllowed):
|
||||
result = "Invalid frequency!";
|
||||
break;
|
||||
case(SubBruteFileResultMissingOrIncorrectFrequency):
|
||||
result = "Missing or incorrect Frequency";
|
||||
break;
|
||||
case(SubBruteFileResultPresetInvalid):
|
||||
result = "Preset FAIL";
|
||||
break;
|
||||
case(SubBruteFileResultMissingProtocol):
|
||||
result = "Missing Protocol";
|
||||
break;
|
||||
case(SubBruteFileResultProtocolNotSupported):
|
||||
result = "RAW unsupported";
|
||||
break;
|
||||
case(SubBruteFileResultDynamicProtocolNotValid):
|
||||
result = "Dynamic protocol unsupported";
|
||||
break;
|
||||
case(SubBruteFileResultProtocolNotFound):
|
||||
result = "Protocol not found";
|
||||
break;
|
||||
case(SubBruteFileResultMissingOrIncorrectBit):
|
||||
result = "Missing or incorrect Bit";
|
||||
break;
|
||||
case(SubBruteFileResultMissingOrIncorrectKey):
|
||||
result = "Missing or incorrect Key";
|
||||
break;
|
||||
case(SubBruteFileResultMissingOrIncorrectTe):
|
||||
result = "Missing or incorrect TE";
|
||||
break;
|
||||
case SubBruteFileResultUnknown:
|
||||
default:
|
||||
result = "Unknown error";
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool subbrute_device_create_packet_parsed(SubBruteDevice* instance, uint64_t step) {
|
||||
furi_assert(instance);
|
||||
|
||||
//char step_payload[32];
|
||||
//memset(step_payload, '0', sizeof(step_payload));
|
||||
memset(instance->payload, 0, sizeof(instance->payload));
|
||||
string_t candidate;
|
||||
string_init(candidate);
|
||||
|
||||
if(instance->attack == SubBruteAttackLoadFile) {
|
||||
if(step >= sizeof(instance->file_key)) {
|
||||
return false;
|
||||
}
|
||||
char subbrute_payload_byte[4];
|
||||
string_set_str(candidate, instance->file_key);
|
||||
snprintf(subbrute_payload_byte, 4, "%02X ", (uint8_t)step);
|
||||
string_replace_at(candidate, instance->load_index * 3, 3, subbrute_payload_byte);
|
||||
//snprintf(step_payload, sizeof(step_payload), "%02X", (uint8_t)instance->file_key[step]);
|
||||
} else {
|
||||
//snprintf(step_payload, sizeof(step_payload), "%16X", step);
|
||||
//snprintf(step_payload, sizeof(step_payload), "%016llX", step);
|
||||
string_t buffer;
|
||||
string_init(buffer);
|
||||
string_init_printf(buffer, "%16X", step);
|
||||
int j = 0;
|
||||
string_set_str(candidate, " ");
|
||||
for(uint8_t i = 0; i < 16; i++) {
|
||||
if(string_get_char(buffer, i) != ' ') {
|
||||
string_set_char(candidate, i + j, string_get_char(buffer, i));
|
||||
} else {
|
||||
string_set_char(candidate, i + j, '0');
|
||||
}
|
||||
if(i % 2 != 0) {
|
||||
j++;
|
||||
}
|
||||
}
|
||||
string_clear(buffer);
|
||||
}
|
||||
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "candidate: %s, step: %d", string_get_cstr(candidate), step);
|
||||
#endif
|
||||
|
||||
if(instance->has_tail) {
|
||||
snprintf(
|
||||
instance->payload,
|
||||
sizeof(instance->payload),
|
||||
subbrute_key_file_princeton_end,
|
||||
instance->file_template,
|
||||
string_get_cstr(candidate),
|
||||
instance->te);
|
||||
} else {
|
||||
snprintf(
|
||||
instance->payload,
|
||||
sizeof(instance->payload),
|
||||
subbrute_key_file_key,
|
||||
instance->file_template,
|
||||
string_get_cstr(candidate));
|
||||
}
|
||||
|
||||
#ifdef FURI_DEBUG
|
||||
//FURI_LOG_D(TAG, "payload: %s", instance->payload);
|
||||
#endif
|
||||
|
||||
string_clear(candidate);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
SubBruteFileResult subbrute_device_attack_set(SubBruteDevice* instance, SubBruteAttacks type) {
|
||||
furi_assert(instance);
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "subbrute_device_attack_set: %d", type);
|
||||
#endif
|
||||
subbrute_device_attack_set_default_values(instance, type);
|
||||
switch(type) {
|
||||
case SubBruteAttackLoadFile:
|
||||
// In this case values must be already set
|
||||
// file_result =
|
||||
// subbrute_device_load_from_file(instance, string_get_cstr(instance->load_path));
|
||||
// if(file_result != SubBruteFileResultOk) {
|
||||
// // Failed load file so failed to set attack type
|
||||
// return file_result; // RETURN
|
||||
// }
|
||||
break;
|
||||
case SubBruteAttackCAME12bit307:
|
||||
case SubBruteAttackCAME12bit433:
|
||||
case SubBruteAttackCAME12bit868:
|
||||
if(type == SubBruteAttackCAME12bit307) {
|
||||
instance->frequency = 307800000;
|
||||
} else if(type == SubBruteAttackCAME12bit433) {
|
||||
instance->frequency = 433920000;
|
||||
} else /* ALWAYS TRUE if(type == SubBruteAttackCAME12bit868) */ {
|
||||
instance->frequency = 868350000;
|
||||
}
|
||||
instance->bit = 12;
|
||||
string_set_str(instance->protocol_name, protocol_came);
|
||||
string_set_str(instance->preset_name, preset_ook650_async);
|
||||
break;
|
||||
case SubBruteAttackChamberlain9bit315:
|
||||
instance->frequency = 315000000;
|
||||
instance->bit = 9;
|
||||
string_set_str(instance->protocol_name, protocol_cham_code);
|
||||
string_set_str(instance->preset_name, preset_ook650_async);
|
||||
break;
|
||||
case SubBruteAttackChamberlain9bit390:
|
||||
instance->frequency = 390000000;
|
||||
instance->bit = 9;
|
||||
string_set_str(instance->protocol_name, protocol_cham_code);
|
||||
string_set_str(instance->preset_name, preset_ook650_async);
|
||||
break;
|
||||
case SubBruteAttackLinear10bit300:
|
||||
instance->frequency = 300000000;
|
||||
instance->bit = 10;
|
||||
string_set_str(instance->protocol_name, protocol_linear);
|
||||
string_set_str(instance->preset_name, preset_ook650_async);
|
||||
break;
|
||||
case SubBruteAttackLinear10bit310:
|
||||
instance->frequency = 310000000;
|
||||
instance->bit = 10;
|
||||
string_set_str(instance->protocol_name, protocol_linear);
|
||||
string_set_str(instance->preset_name, preset_ook650_async);
|
||||
break;
|
||||
case SubBruteAttackNICE12bit433:
|
||||
instance->frequency = 433920000;
|
||||
instance->bit = 12;
|
||||
string_set_str(instance->protocol_name, protocol_nice_flo);
|
||||
string_set_str(instance->preset_name, preset_ook650_async);
|
||||
break;
|
||||
case SubBruteAttackNICE12bit868:
|
||||
instance->frequency = 868350000;
|
||||
instance->bit = 12;
|
||||
string_set_str(instance->protocol_name, protocol_nice_flo);
|
||||
string_set_str(instance->preset_name, preset_ook650_async);
|
||||
break;
|
||||
default:
|
||||
FURI_LOG_E(TAG, "Unknown attack type: %d", type);
|
||||
return SubBruteFileResultProtocolNotFound; // RETURN
|
||||
}
|
||||
|
||||
if(!furi_hal_subghz_is_tx_allowed(instance->frequency)) {
|
||||
FURI_LOG_E(TAG, "Frequency invalid: %d", instance->frequency);
|
||||
return SubBruteFileResultMissingOrIncorrectFrequency; // RETURN
|
||||
}
|
||||
|
||||
// For non-file types we didn't set SubGhzProtocolDecoderBase
|
||||
instance->environment = subghz_environment_alloc();
|
||||
instance->receiver = subghz_receiver_alloc_init(instance->environment);
|
||||
subghz_receiver_set_filter(instance->receiver, SubGhzProtocolFlag_Decodable);
|
||||
furi_hal_subghz_reset();
|
||||
|
||||
uint8_t protocol_check_result = SubBruteFileResultProtocolNotFound;
|
||||
if(type != SubBruteAttackLoadFile) {
|
||||
instance->decoder_result = subghz_receiver_search_decoder_base_by_name(
|
||||
instance->receiver, string_get_cstr(instance->protocol_name));
|
||||
|
||||
if(!instance->decoder_result ||
|
||||
instance->decoder_result->protocol->type == SubGhzProtocolTypeDynamic) {
|
||||
FURI_LOG_E(TAG, "Can't load SubGhzProtocolDecoderBase in phase non-file decoder set");
|
||||
} else {
|
||||
protocol_check_result = SubBruteFileResultOk;
|
||||
}
|
||||
} else {
|
||||
// And here we need to set preset enum
|
||||
instance->preset = subbrute_device_convert_preset(string_get_cstr(instance->preset_name));
|
||||
protocol_check_result = SubBruteFileResultOk;
|
||||
}
|
||||
|
||||
subghz_environment_free(instance->environment);
|
||||
subghz_receiver_free(instance->receiver);
|
||||
instance->receiver = NULL;
|
||||
instance->environment = NULL;
|
||||
|
||||
if(protocol_check_result != SubBruteFileResultOk) {
|
||||
return SubBruteFileResultProtocolNotFound;
|
||||
}
|
||||
|
||||
instance->has_tail =
|
||||
(strcmp(string_get_cstr(instance->protocol_name), protocol_princeton) == 0);
|
||||
|
||||
// Calc max value
|
||||
if(instance->attack == SubBruteAttackLoadFile) {
|
||||
instance->max_value = 0xFF;
|
||||
} else {
|
||||
string_t max_value_s;
|
||||
string_init(max_value_s);
|
||||
for(uint8_t i = 0; i < instance->bit; i++) {
|
||||
string_cat_printf(max_value_s, "1");
|
||||
}
|
||||
instance->max_value = (uint64_t)strtol(string_get_cstr(max_value_s), NULL, 2);
|
||||
string_clear(max_value_s);
|
||||
}
|
||||
|
||||
// Now we are ready to set file template for using in the future with snprintf
|
||||
// for sending attack payload
|
||||
snprintf(
|
||||
instance->file_template,
|
||||
sizeof(instance->file_template),
|
||||
subbrute_key_file_start,
|
||||
instance->frequency,
|
||||
string_get_cstr(instance->preset_name),
|
||||
string_get_cstr(instance->protocol_name),
|
||||
instance->bit);
|
||||
// strncat(instance->file_template, "\n", sizeof(instance->file_template));
|
||||
// strncat(instance->file_template, subbrute_key_file_key, sizeof(instance->file_template));
|
||||
// if(instance->has_tail) {
|
||||
// strncat(
|
||||
// instance->file_template,
|
||||
// subbrute_key_file_princeton_end,
|
||||
// sizeof(instance->file_template));
|
||||
// }
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "tail: %d, file_template: %s", instance->has_tail, instance->file_template);
|
||||
#endif
|
||||
|
||||
// Init payload
|
||||
subbrute_device_create_packet_parsed(instance, instance->key_index);
|
||||
|
||||
return SubBruteFileResultOk;
|
||||
}
|
||||
|
||||
uint8_t subbrute_device_load_from_file(SubBruteDevice* instance, string_t file_path) {
|
||||
furi_assert(instance);
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "subbrute_device_load_from_file: %s", string_get_cstr(file_path));
|
||||
#endif
|
||||
SubBruteFileResult result = SubBruteFileResultUnknown;
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);
|
||||
|
||||
string_t temp_str;
|
||||
string_init(temp_str);
|
||||
uint32_t temp_data32;
|
||||
|
||||
instance->environment = subghz_environment_alloc();
|
||||
instance->receiver = subghz_receiver_alloc_init(instance->environment);
|
||||
subghz_receiver_set_filter(instance->receiver, SubGhzProtocolFlag_Decodable);
|
||||
furi_hal_subghz_reset();
|
||||
|
||||
do {
|
||||
if(!flipper_format_file_open_existing(fff_data_file, string_get_cstr(file_path))) {
|
||||
FURI_LOG_E(TAG, "Error open file %s", string_get_cstr(file_path));
|
||||
result = SubBruteFileResultErrorOpenFile;
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) {
|
||||
FURI_LOG_E(TAG, "Missing or incorrect header");
|
||||
result = SubBruteFileResultMissingOrIncorrectHeader;
|
||||
break;
|
||||
}
|
||||
|
||||
// Frequency
|
||||
if(flipper_format_read_uint32(fff_data_file, "Frequency", &temp_data32, 1)) {
|
||||
instance->frequency = temp_data32;
|
||||
if(!furi_hal_subghz_is_tx_allowed(instance->frequency)) {
|
||||
result = SubBruteFileResultFrequencyNotAllowed;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Missing or incorrect Frequency");
|
||||
result = SubBruteFileResultMissingOrIncorrectFrequency;
|
||||
break;
|
||||
}
|
||||
// Preset
|
||||
if(!flipper_format_read_string(fff_data_file, "Preset", temp_str)) {
|
||||
FURI_LOG_E(TAG, "Preset FAIL");
|
||||
result = SubBruteFileResultPresetInvalid;
|
||||
} else {
|
||||
string_init_set_str(instance->preset_name, string_get_cstr(temp_str));
|
||||
}
|
||||
|
||||
// Protocol
|
||||
if(!flipper_format_read_string(fff_data_file, "Protocol", temp_str)) {
|
||||
FURI_LOG_E(TAG, "Missing Protocol");
|
||||
result = SubBruteFileResultMissingProtocol;
|
||||
break;
|
||||
} else {
|
||||
string_init_set_str(instance->protocol_name, string_get_cstr(temp_str));
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "Protocol: %s", string_get_cstr(instance->protocol_name));
|
||||
#endif
|
||||
}
|
||||
|
||||
instance->decoder_result = subghz_receiver_search_decoder_base_by_name(
|
||||
instance->receiver, string_get_cstr(instance->protocol_name));
|
||||
|
||||
if(!instance->decoder_result ||
|
||||
strcmp(string_get_cstr(instance->protocol_name), "RAW") == 0) {
|
||||
FURI_LOG_E(TAG, "RAW unsupported");
|
||||
result = SubBruteFileResultProtocolNotSupported;
|
||||
break;
|
||||
}
|
||||
|
||||
if(instance->decoder_result->protocol->type == SubGhzProtocolTypeDynamic) {
|
||||
FURI_LOG_E(TAG, "Protocol is dynamic - not supported");
|
||||
result = SubBruteFileResultDynamicProtocolNotValid;
|
||||
break;
|
||||
}
|
||||
#ifdef FURI_DEBUG
|
||||
else {
|
||||
FURI_LOG_D(TAG, "Decoder: %s", instance->decoder_result->protocol->name);
|
||||
}
|
||||
#endif
|
||||
|
||||
// instance->decoder_result = subghz_receiver_search_decoder_base_by_name(
|
||||
// instance->receiver, string_get_cstr(instance->protocol_name));
|
||||
//
|
||||
// if(!instance->decoder_result) {
|
||||
// FURI_LOG_E(TAG, "Protocol not found");
|
||||
// result = SubBruteFileResultProtocolNotFound;
|
||||
// break;
|
||||
// }
|
||||
|
||||
// Bit
|
||||
if(!flipper_format_read_uint32(fff_data_file, "Bit", &temp_data32, 1)) {
|
||||
FURI_LOG_E(TAG, "Missing or incorrect Bit");
|
||||
result = SubBruteFileResultMissingOrIncorrectBit;
|
||||
break;
|
||||
} else {
|
||||
instance->bit = temp_data32;
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "Bit: %d", instance->bit);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Key
|
||||
if(!flipper_format_read_string(fff_data_file, "Key", temp_str)) {
|
||||
FURI_LOG_E(TAG, "Missing or incorrect Key");
|
||||
result = SubBruteFileResultMissingOrIncorrectKey;
|
||||
break;
|
||||
} else {
|
||||
snprintf(
|
||||
instance->file_key, sizeof(instance->file_key), "%s", string_get_cstr(temp_str));
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "Key: %s", instance->file_key);
|
||||
#endif
|
||||
}
|
||||
|
||||
// TE
|
||||
if(!flipper_format_read_uint32(fff_data_file, "TE", &temp_data32, 1)) {
|
||||
FURI_LOG_E(TAG, "Missing or incorrect TE");
|
||||
//result = SubBruteFileResultMissingOrIncorrectTe;
|
||||
//break;
|
||||
} else {
|
||||
instance->te = temp_data32;
|
||||
instance->has_tail = true;
|
||||
}
|
||||
|
||||
// Repeat
|
||||
if(flipper_format_read_uint32(fff_data_file, "Repeat", &temp_data32, 1)) {
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "Repeat: %d", temp_data32);
|
||||
#endif
|
||||
instance->repeat = temp_data32;
|
||||
} else {
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "Repeat: 3 (default)");
|
||||
#endif
|
||||
instance->repeat = 3;
|
||||
}
|
||||
|
||||
result = SubBruteFileResultOk;
|
||||
} while(0);
|
||||
|
||||
string_clear(temp_str);
|
||||
flipper_format_file_close(fff_data_file);
|
||||
flipper_format_free(fff_data_file);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
subghz_environment_free(instance->environment);
|
||||
subghz_receiver_free(instance->receiver);
|
||||
|
||||
instance->decoder_result = NULL;
|
||||
instance->receiver = NULL;
|
||||
instance->environment = NULL;
|
||||
|
||||
if(result == SubBruteFileResultOk) {
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "Loaded successfully");
|
||||
#endif
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void subbrute_device_attack_set_default_values(
|
||||
SubBruteDevice* instance,
|
||||
SubBruteAttacks default_attack) {
|
||||
furi_assert(instance);
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "subbrute_device_attack_set_default_values");
|
||||
#endif
|
||||
instance->attack = default_attack;
|
||||
instance->key_index = 0x00;
|
||||
instance->load_index = 0x00;
|
||||
memset(instance->file_template, 0, sizeof(instance->file_template));
|
||||
memset(instance->current_key, 0, sizeof(instance->current_key));
|
||||
memset(instance->text_store, 0, sizeof(instance->text_store));
|
||||
memset(instance->payload, 0, sizeof(instance->payload));
|
||||
|
||||
if(default_attack != SubBruteAttackLoadFile) {
|
||||
memset(instance->file_key, 0, sizeof(instance->file_key));
|
||||
|
||||
instance->max_value = (uint64_t)0x00;
|
||||
|
||||
string_clear(instance->protocol_name);
|
||||
string_clear(instance->preset_name);
|
||||
|
||||
string_clear(instance->load_path);
|
||||
string_init(instance->load_path);
|
||||
|
||||
string_init_set_str(instance->protocol_name, protocol_raw);
|
||||
string_init_set_str(instance->preset_name, preset_ook650_async);
|
||||
instance->preset = FuriHalSubGhzPresetOok650Async;
|
||||
|
||||
instance->repeat = 5;
|
||||
instance->te = 0;
|
||||
instance->has_tail = false;
|
||||
}
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(
|
||||
TAG, "subbrute_device_attack_set_default_values done. has_tail: %d", instance->has_tail);
|
||||
//furi_delay_ms(250);
|
||||
#endif
|
||||
}
|
||||
|
||||
FuriHalSubGhzPreset subbrute_device_convert_preset(const char* preset_name) {
|
||||
string_t preset;
|
||||
string_init_set_str(preset, preset_name);
|
||||
FuriHalSubGhzPreset preset_value;
|
||||
if(string_cmp_str(preset, preset_ook270_async) == 0) {
|
||||
preset_value = FuriHalSubGhzPresetOok270Async;
|
||||
} else if(string_cmp_str(preset, preset_ook650_async) == 0) {
|
||||
preset_value = FuriHalSubGhzPresetOok650Async;
|
||||
} else if(string_cmp_str(preset, preset_2fsk_dev238_async) == 0) {
|
||||
preset_value = FuriHalSubGhzPreset2FSKDev238Async;
|
||||
} else if(string_cmp_str(preset, preset_2fsk_dev476_async) == 0) {
|
||||
preset_value = FuriHalSubGhzPreset2FSKDev476Async;
|
||||
} else if(string_cmp_str(preset, preset_msk99_97_kb_async) == 0) {
|
||||
preset_value = FuriHalSubGhzPresetMSK99_97KbAsync;
|
||||
} else if(string_cmp_str(preset, preset_gfs99_97_kb_async) == 0) {
|
||||
preset_value = FuriHalSubGhzPresetMSK99_97KbAsync;
|
||||
} else {
|
||||
preset_value = FuriHalSubGhzPresetCustom;
|
||||
}
|
||||
|
||||
string_clear(preset);
|
||||
return preset_value;
|
||||
}
|
||||
99
applications/plugins/subbrute/subbrute_device.h
Normal file
99
applications/plugins/subbrute/subbrute_device.h
Normal file
@@ -0,0 +1,99 @@
|
||||
#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 256
|
||||
|
||||
#define SUBBRUTE_MAX_LEN_NAME 64
|
||||
#define SUBBRUTE_PATH EXT_PATH("subghz")
|
||||
#define SUBBRUTE_FILE_EXT ".sub"
|
||||
|
||||
#define SUBBRUTE_PAYLOAD_SIZE 16
|
||||
|
||||
typedef enum {
|
||||
SubBruteAttackCAME12bit307,
|
||||
SubBruteAttackCAME12bit433,
|
||||
SubBruteAttackCAME12bit868,
|
||||
SubBruteAttackChamberlain9bit315,
|
||||
SubBruteAttackChamberlain9bit390,
|
||||
SubBruteAttackLinear10bit300,
|
||||
SubBruteAttackLinear10bit310,
|
||||
SubBruteAttackNICE12bit433,
|
||||
SubBruteAttackNICE12bit868,
|
||||
SubBruteAttackLoadFile,
|
||||
SubBruteAttackTotalCount,
|
||||
} SubBruteAttacks;
|
||||
|
||||
typedef enum {
|
||||
SubBruteFileResultUnknown,
|
||||
SubBruteFileResultOk,
|
||||
SubBruteFileResultErrorOpenFile,
|
||||
SubBruteFileResultMissingOrIncorrectHeader,
|
||||
SubBruteFileResultFrequencyNotAllowed,
|
||||
SubBruteFileResultMissingOrIncorrectFrequency,
|
||||
SubBruteFileResultPresetInvalid,
|
||||
SubBruteFileResultMissingProtocol,
|
||||
SubBruteFileResultProtocolNotSupported,
|
||||
SubBruteFileResultDynamicProtocolNotValid,
|
||||
SubBruteFileResultProtocolNotFound,
|
||||
SubBruteFileResultMissingOrIncorrectBit,
|
||||
SubBruteFileResultMissingOrIncorrectKey,
|
||||
SubBruteFileResultMissingOrIncorrectTe,
|
||||
} SubBruteFileResult;
|
||||
|
||||
typedef enum {
|
||||
SubBruteDeviceStateIDLE,
|
||||
SubBruteDeviceStateReady,
|
||||
SubBruteDeviceStateTx,
|
||||
SubBruteDeviceStateFinished,
|
||||
} SubBruteDeviceState;
|
||||
|
||||
typedef struct {
|
||||
SubBruteDeviceState state;
|
||||
|
||||
// Current step
|
||||
uint64_t key_index;
|
||||
string_t load_path;
|
||||
// Index of group to bruteforce in loaded file
|
||||
uint8_t load_index;
|
||||
|
||||
SubGhzReceiver* receiver;
|
||||
SubGhzProtocolDecoderBase* decoder_result;
|
||||
SubGhzEnvironment* environment;
|
||||
|
||||
// Attack state
|
||||
SubBruteAttacks attack;
|
||||
char file_template[SUBBRUTE_TEXT_STORE_SIZE];
|
||||
bool has_tail;
|
||||
char payload[SUBBRUTE_TEXT_STORE_SIZE * 2];
|
||||
uint64_t max_value;
|
||||
|
||||
// Loaded info for attack type
|
||||
FuriHalSubGhzPreset preset;
|
||||
string_t preset_name;
|
||||
string_t protocol_name;
|
||||
uint32_t frequency;
|
||||
uint32_t repeat;
|
||||
uint32_t bit;
|
||||
char current_key[SUBBRUTE_PAYLOAD_SIZE];
|
||||
uint32_t te;
|
||||
|
||||
char file_key[SUBBRUTE_MAX_LEN_NAME];
|
||||
char text_store[SUBBRUTE_PAYLOAD_SIZE];
|
||||
} SubBruteDevice;
|
||||
|
||||
SubBruteDevice* subbrute_device_alloc();
|
||||
void subbrute_device_free(SubBruteDevice* instance);
|
||||
bool subbrute_device_save_file(SubBruteDevice* instance, const char* key_name);
|
||||
const char* subbrute_device_error_get_desc(SubBruteFileResult error_id);
|
||||
bool subbrute_device_create_packet_parsed(SubBruteDevice* context, uint64_t step);
|
||||
SubBruteFileResult subbrute_device_attack_set(SubBruteDevice* context, SubBruteAttacks type);
|
||||
uint8_t subbrute_device_load_from_file(SubBruteDevice* context, string_t file_path);
|
||||
FuriHalSubGhzPreset subbrute_device_convert_preset(const char* preset);
|
||||
void subbrute_device_attack_set_default_values(SubBruteDevice* context, SubBruteAttacks default_attack);
|
||||
83
applications/plugins/subbrute/subbrute_i.h
Normal file
83
applications/plugins/subbrute/subbrute_i.h
Normal file
@@ -0,0 +1,83 @@
|
||||
#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 "helpers/subbrute_worker.h"
|
||||
#include "subbrute.h"
|
||||
#include "scenes/subbrute_scene.h"
|
||||
#include "views/subbrute_attack_view.h"
|
||||
#include "views/subbrute_main_view.h"
|
||||
|
||||
typedef enum {
|
||||
SubBruteViewNone,
|
||||
SubBruteViewMain,
|
||||
SubBruteViewAttack,
|
||||
SubBruteViewTextInput,
|
||||
SubBruteViewDialogEx,
|
||||
SubBruteViewPopup,
|
||||
SubBruteViewWidget,
|
||||
SubBruteViewStack,
|
||||
} SubBruteView;
|
||||
|
||||
struct SubBruteState {
|
||||
// GUI elements
|
||||
NotificationApp* notifications;
|
||||
Gui* gui;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
ViewStack* view_stack;
|
||||
TextInput* text_input;
|
||||
Popup* popup;
|
||||
Widget* widget;
|
||||
DialogsApp* dialogs;
|
||||
Loading* loading;
|
||||
|
||||
// Views
|
||||
SubBruteMainView* view_main;
|
||||
SubBruteAttackView* view_attack;
|
||||
SubBruteView current_view;
|
||||
|
||||
// Scene
|
||||
SceneManager* scene_manager;
|
||||
|
||||
SubBruteDevice* device;
|
||||
SubBruteWorker* worker;
|
||||
|
||||
//Menu stuff
|
||||
// TODO: Do we need it?
|
||||
uint8_t menu_index;
|
||||
};
|
||||
|
||||
void subbrute_show_loading_popup(void* context, bool show);
|
||||
void subbrute_text_input_callback(void* context);
|
||||
void subbrute_popup_closed_callback(void* context);
|
||||
const char* subbrute_get_menu_name(uint8_t index);
|
||||
const char* subbrute_get_small_menu_name(uint8_t index);
|
||||
@@ -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);
|
||||
374
applications/plugins/subbrute/views/subbrute_attack_view.c
Normal file
374
applications/plugins/subbrute/views/subbrute_attack_view.c
Normal file
@@ -0,0 +1,374 @@
|
||||
#include "subbrute_attack_view.h"
|
||||
#include "../subbrute_i.h"
|
||||
|
||||
#include "assets_icons.h"
|
||||
#include <input/input.h>
|
||||
#include <gui/elements.h>
|
||||
#include <gui/icon_i.h>
|
||||
#include <gui/icon_animation_i.h>
|
||||
|
||||
#define TAG "SubBruteAttackView"
|
||||
|
||||
struct SubBruteAttackView {
|
||||
View* view;
|
||||
SubBruteAttackViewCallback callback;
|
||||
void* context;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
SubBruteAttacks index;
|
||||
uint64_t max_value;
|
||||
uint64_t current_step;
|
||||
bool is_attacking;
|
||||
IconAnimation* icon;
|
||||
} SubBruteAttackViewModel;
|
||||
|
||||
void subbrute_attack_view_set_callback(
|
||||
SubBruteAttackView* instance,
|
||||
SubBruteAttackViewCallback callback,
|
||||
void* context) {
|
||||
furi_assert(instance);
|
||||
furi_assert(callback);
|
||||
|
||||
instance->callback = callback;
|
||||
instance->context = context;
|
||||
}
|
||||
|
||||
bool subbrute_attack_view_input(InputEvent* event, void* context) {
|
||||
furi_assert(event);
|
||||
furi_assert(context);
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "InputKey: %d", event->key);
|
||||
#endif
|
||||
SubBruteAttackView* instance = context;
|
||||
|
||||
if(event->key == InputKeyBack && event->type == InputTypeShort) {
|
||||
instance->callback(SubBruteCustomEventTypeBackPressed, instance->context);
|
||||
with_view_model(
|
||||
instance->view, (SubBruteAttackViewModel * model) {
|
||||
model->is_attacking = false;
|
||||
return true;
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
bool is_attacking = false;
|
||||
|
||||
with_view_model(
|
||||
instance->view, (SubBruteAttackViewModel * model) {
|
||||
is_attacking = model->is_attacking;
|
||||
return false;
|
||||
});
|
||||
|
||||
// if(!is_attacking) {
|
||||
// instance->callback(SubBruteCustomEventTypeTransmitNotStarted, instance->context);
|
||||
// } else {
|
||||
// instance->callback(SubBruteCustomEventTypeTransmitStarted, instance->context);
|
||||
// }
|
||||
|
||||
if(!is_attacking) {
|
||||
if((event->type == InputTypeShort || event->type == InputTypeRepeat) &&
|
||||
event->key == InputKeyOk) {
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "InputKey: %d OK", event->key);
|
||||
#endif
|
||||
with_view_model(
|
||||
instance->view, (SubBruteAttackViewModel * model) {
|
||||
model->is_attacking = true;
|
||||
icon_animation_stop(model->icon);
|
||||
icon_animation_start(model->icon);
|
||||
return true;
|
||||
});
|
||||
instance->callback(SubBruteCustomEventTypeTransmitStarted, instance->context);
|
||||
// } else if(event->key == InputKeyBack) {
|
||||
// if(previous_scene == SubBruteSceneLoadFile) {
|
||||
// instance->callback(SubBruteCustomEventTypeLoadFile, instance->context);
|
||||
// } else {
|
||||
// instance->callback(SubBruteCustomEventTypeBackPressed, instance->context);
|
||||
// }
|
||||
} else if(event->key == InputKeyUp) {
|
||||
instance->callback(SubBruteCustomEventTypeSaveFile, instance->context);
|
||||
} else if(event->key == InputKeyDown) {
|
||||
instance->callback(SubBruteCustomEventTypeTransmitCustom, instance->context);
|
||||
} else if(event->type == InputTypeShort) {
|
||||
if(event->key == InputKeyLeft) {
|
||||
instance->callback(SubBruteCustomEventTypeChangeStepDown, instance->context);
|
||||
} else if(event->key == InputKeyRight) {
|
||||
instance->callback(SubBruteCustomEventTypeChangeStepUp, instance->context);
|
||||
}
|
||||
// with_view_model(
|
||||
// instance->view, (SubBruteAttackViewModel * model) {
|
||||
// if(event->key == InputKeyLeft) {
|
||||
// model->current_step =
|
||||
// ((model->current_step - 1) + model->max_value) % model->max_value;
|
||||
// } else if(event->key == InputKeyRight) {
|
||||
// model->current_step = (model->current_step + 1) % model->max_value;
|
||||
// }
|
||||
// return true;
|
||||
// });
|
||||
// instance->callback(SubBruteCustomEventTypeChangeStep, instance->context);
|
||||
} else if(event->type == InputTypeRepeat) {
|
||||
if(event->key == InputKeyLeft) {
|
||||
instance->callback(SubBruteCustomEventTypeChangeStepDownMore, instance->context);
|
||||
} else if(event->key == InputKeyRight) {
|
||||
instance->callback(SubBruteCustomEventTypeChangeStepUpMore, instance->context);
|
||||
}
|
||||
/*with_view_model(
|
||||
instance->view, (SubBruteAttackViewModel * model) {
|
||||
if(event->key == InputKeyLeft) {
|
||||
model->current_step =
|
||||
((model->current_step - 100) + model->max_value) % model->max_value;
|
||||
} else if(event->key == InputKeyRight) {
|
||||
model->current_step = (model->current_step + 100) % model->max_value;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
instance->callback(SubBruteCustomEventTypeChangeStep, instance->context);*/
|
||||
}
|
||||
} else {
|
||||
if((event->type == InputTypeShort || event->type == InputTypeRepeat) &&
|
||||
(event->key == InputKeyOk || event->key == InputKeyBack)) {
|
||||
with_view_model(
|
||||
instance->view, (SubBruteAttackViewModel * model) {
|
||||
model->is_attacking = false;
|
||||
icon_animation_stop(model->icon);
|
||||
icon_animation_start(model->icon);
|
||||
return true;
|
||||
});
|
||||
instance->callback(SubBruteCustomEventTypeTransmitNotStarted, instance->context);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
SubBruteAttackView* subbrute_attack_view_alloc() {
|
||||
SubBruteAttackView* instance = malloc(sizeof(SubBruteAttackView));
|
||||
|
||||
instance->view = view_alloc();
|
||||
view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(SubBruteAttackViewModel));
|
||||
view_set_context(instance->view, instance);
|
||||
|
||||
with_view_model(
|
||||
instance->view, (SubBruteAttackViewModel * model) {
|
||||
model->icon = icon_animation_alloc(&A_Sub1ghz_14);
|
||||
view_tie_icon_animation(instance->view, model->icon);
|
||||
return false;
|
||||
});
|
||||
|
||||
view_set_draw_callback(instance->view, (ViewDrawCallback)subbrute_attack_view_draw);
|
||||
view_set_input_callback(instance->view, subbrute_attack_view_input);
|
||||
view_set_enter_callback(instance->view, subbrute_attack_view_enter);
|
||||
view_set_exit_callback(instance->view, subbrute_attack_view_exit);
|
||||
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subbrute_attack_view_enter(void* context) {
|
||||
furi_assert(context);
|
||||
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "subbrute_attack_view_enter");
|
||||
#endif
|
||||
}
|
||||
|
||||
void subbrute_attack_view_free(SubBruteAttackView* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "subbrute_attack_view_free");
|
||||
#endif
|
||||
|
||||
with_view_model(
|
||||
instance->view, (SubBruteAttackViewModel * model) {
|
||||
icon_animation_free(model->icon);
|
||||
return false;
|
||||
});
|
||||
|
||||
view_free(instance->view);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
View* subbrute_attack_view_get_view(SubBruteAttackView* instance) {
|
||||
furi_assert(instance);
|
||||
return instance->view;
|
||||
}
|
||||
|
||||
void subbrute_attack_view_set_current_step(SubBruteAttackView* instance, uint64_t current_step) {
|
||||
furi_assert(instance);
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "Set step: %d", current_step);
|
||||
#endif
|
||||
with_view_model(
|
||||
instance->view, (SubBruteAttackViewModel * model) {
|
||||
model->current_step = current_step;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
uint64_t subbrute_attack_view_get_current_step(SubBruteAttackView* instance) {
|
||||
uint64_t current_step;
|
||||
with_view_model(
|
||||
instance->view, (SubBruteAttackViewModel * model) {
|
||||
current_step = model->current_step;
|
||||
return false;
|
||||
});
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "Get step: %d", current_step);
|
||||
#endif
|
||||
return current_step;
|
||||
}
|
||||
|
||||
// We need to call init every time, because not every time we calls enter
|
||||
// normally, call enter only once
|
||||
void subbrute_attack_view_init_values(
|
||||
SubBruteAttackView* instance,
|
||||
uint8_t index,
|
||||
uint64_t max_value,
|
||||
uint64_t current_step,
|
||||
bool is_attacking) {
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(
|
||||
TAG, "init, index: %d, max_value: %d, current_step: %d", index, max_value, current_step);
|
||||
#endif
|
||||
with_view_model(
|
||||
instance->view, (SubBruteAttackViewModel * model) {
|
||||
model->max_value = max_value;
|
||||
model->index = index;
|
||||
model->current_step = current_step;
|
||||
model->is_attacking = is_attacking;
|
||||
if(is_attacking) {
|
||||
icon_animation_start(model->icon);
|
||||
} else {
|
||||
icon_animation_stop(model->icon);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void subbrute_attack_view_exit(void* context) {
|
||||
furi_assert(context);
|
||||
SubBruteAttackView* instance = context;
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "subbrute_attack_view_exit");
|
||||
#endif
|
||||
with_view_model(
|
||||
instance->view, (SubBruteAttackViewModel * model) {
|
||||
icon_animation_stop(model->icon);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
void elements_button_top_left(Canvas* canvas, const char* str) {
|
||||
const Icon* icon = &I_ButtonUp_7x4;
|
||||
|
||||
const uint8_t button_height = 12;
|
||||
const uint8_t vertical_offset = 9; //
|
||||
const uint8_t horizontal_offset = 3;
|
||||
const uint8_t string_width = canvas_string_width(canvas, str);
|
||||
const uint8_t icon_h_offset = 3;
|
||||
const uint8_t icon_width_with_offset = icon->width + icon_h_offset;
|
||||
const uint8_t icon_v_offset = icon->height; //
|
||||
const uint8_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset + 1;
|
||||
|
||||
const uint8_t x = 0;
|
||||
const uint8_t y = 0;
|
||||
|
||||
canvas_draw_box(canvas, x, y, button_width, button_height);
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(
|
||||
TAG, "lbox, x: %d, y: %d, width: %d, height: %d", x, y, button_width, button_height);
|
||||
#endif
|
||||
// canvas_draw_line(canvas, x + button_width + 0, y, x + button_width + 0, y + button_height - 0); //
|
||||
// canvas_draw_line(canvas, x + button_width + 1, y, x + button_width + 1, y + button_height - 1);
|
||||
// canvas_draw_line(canvas, x + button_width + 2, y, x + button_width + 2, y + button_height - 2);
|
||||
|
||||
canvas_invert_color(canvas);
|
||||
canvas_draw_icon(canvas, x + horizontal_offset, y + icon_v_offset, icon);
|
||||
canvas_draw_str(
|
||||
canvas, x + horizontal_offset + icon_width_with_offset, y + vertical_offset, str);
|
||||
canvas_invert_color(canvas);
|
||||
}
|
||||
|
||||
void elements_button_top_right(Canvas* canvas, const char* str) {
|
||||
const Icon* icon = &I_ButtonDown_7x4;
|
||||
|
||||
const uint8_t button_height = 12;
|
||||
const uint8_t vertical_offset = 9;
|
||||
const uint8_t horizontal_offset = 3;
|
||||
const uint8_t string_width = canvas_string_width(canvas, str);
|
||||
const uint8_t icon_h_offset = 3;
|
||||
const uint8_t icon_width_with_offset = icon->width + icon_h_offset;
|
||||
const uint8_t icon_v_offset = icon->height; // + vertical_offset;
|
||||
const uint8_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset + 1;
|
||||
|
||||
const uint8_t x = canvas_width(canvas);
|
||||
const uint8_t y = 0;
|
||||
|
||||
canvas_draw_box(canvas, x - button_width, y, button_width, button_height);
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"rbox, x: %d, y: %d, width: %d, height: %d",
|
||||
x - button_width,
|
||||
y,
|
||||
button_width,
|
||||
button_height);
|
||||
#endif
|
||||
// canvas_draw_line(canvas, x - button_width - 1, y, x + button_width - 1, y + button_height - 0);
|
||||
// canvas_draw_line(canvas, x - button_width - 2, y, x + button_width - 2, y + button_height - 1);
|
||||
// canvas_draw_line(canvas, x - button_width - 3, y, x + button_width - 3, y + button_height - 2);
|
||||
|
||||
canvas_invert_color(canvas);
|
||||
canvas_draw_str(canvas, x - button_width + horizontal_offset, y + vertical_offset, str);
|
||||
canvas_draw_icon(canvas, x - horizontal_offset - icon->width, y + icon_v_offset, icon);
|
||||
canvas_invert_color(canvas);
|
||||
}
|
||||
|
||||
void subbrute_attack_view_draw(Canvas* canvas, void* context) {
|
||||
furi_assert(context);
|
||||
SubBruteAttackViewModel* model = (SubBruteAttackViewModel*)context;
|
||||
char buffer[26];
|
||||
|
||||
const char* attack_name = NULL;
|
||||
attack_name = subbrute_get_menu_name(model->index);
|
||||
// Title
|
||||
if(model->is_attacking) {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, attack_name);
|
||||
}
|
||||
// Value
|
||||
canvas_set_font(canvas, FontBigNumbers);
|
||||
snprintf(buffer, sizeof(buffer), "%04d/%04d", (int)model->current_step, (int)model->max_value);
|
||||
canvas_draw_str_aligned(canvas, 64, 17, AlignCenter, AlignTop, buffer);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
|
||||
if(!model->is_attacking) {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str_aligned(canvas, 64, 44, AlignCenter, AlignBottom, attack_name);
|
||||
|
||||
elements_button_left(canvas, "-1");
|
||||
elements_button_right(canvas, "+1");
|
||||
elements_button_center(canvas, "Start");
|
||||
elements_button_top_left(canvas, "Save");
|
||||
elements_button_top_right(canvas, "Resend");
|
||||
} else {
|
||||
// canvas_draw_icon_animation
|
||||
const uint8_t icon_h_offset = 0;
|
||||
const uint8_t icon_width_with_offset = model->icon->icon->width + icon_h_offset;
|
||||
const uint8_t icon_v_offset = model->icon->icon->height; // + vertical_offset;
|
||||
const uint8_t x = canvas_width(canvas);
|
||||
const uint8_t y = canvas_height(canvas);
|
||||
canvas_draw_icon_animation(
|
||||
canvas, x - icon_width_with_offset, y - icon_v_offset, model->icon);
|
||||
// Progress bar
|
||||
// Resolution: 128x64 px
|
||||
float progress_value = (float)model->current_step / model->max_value;
|
||||
elements_progress_bar(canvas, 8, 37, 110, progress_value > 1 ? 1 : progress_value);
|
||||
|
||||
elements_button_center(canvas, "Stop");
|
||||
}
|
||||
}
|
||||
28
applications/plugins/subbrute/views/subbrute_attack_view.h
Normal file
28
applications/plugins/subbrute/views/subbrute_attack_view.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#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, uint64_t current_step);
|
||||
uint64_t subbrute_attack_view_get_current_step(SubBruteAttackView* instance);
|
||||
void subbrute_attack_view_init_values(
|
||||
SubBruteAttackView* instance,
|
||||
uint8_t index,
|
||||
uint64_t max_value,
|
||||
uint64_t current_step,
|
||||
bool is_attacking);
|
||||
381
applications/plugins/subbrute/views/subbrute_main_view.c
Normal file
381
applications/plugins/subbrute/views/subbrute_main_view.c
Normal file
@@ -0,0 +1,381 @@
|
||||
#include "subbrute_main_view.h"
|
||||
#include "../subbrute_i.h"
|
||||
|
||||
#include <input/input.h>
|
||||
#include <gui/elements.h>
|
||||
#include "assets_icons.h"
|
||||
#include <gui/icon.h>
|
||||
|
||||
#define STATUS_BAR_Y_SHIFT 14
|
||||
#define TAG "SubBruteMainView"
|
||||
|
||||
struct SubBruteMainView {
|
||||
View* view;
|
||||
SubBruteMainViewCallback callback;
|
||||
void* context;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint8_t index;
|
||||
uint8_t window_position;
|
||||
bool is_select_byte;
|
||||
const char* key_field;
|
||||
} SubBruteMainViewModel;
|
||||
|
||||
void subbrute_main_view_set_callback(
|
||||
SubBruteMainView* instance,
|
||||
SubBruteMainViewCallback callback,
|
||||
void* context) {
|
||||
furi_assert(instance);
|
||||
furi_assert(callback);
|
||||
|
||||
instance->callback = callback;
|
||||
instance->context = context;
|
||||
}
|
||||
|
||||
void center_displayed_key(string_t result, const char* key_cstr, uint8_t index) {
|
||||
uint8_t str_index = (index * 3);
|
||||
|
||||
char display_menu[] = {
|
||||
'X', 'X', ' ', 'X', 'X', ' ', '<', 'X', 'X', '>', ' ', 'X', 'X', ' ', 'X', 'X', '\0'};
|
||||
|
||||
if(key_cstr != NULL) {
|
||||
if(index > 1) {
|
||||
display_menu[0] = key_cstr[str_index - 6];
|
||||
display_menu[1] = key_cstr[str_index - 5];
|
||||
} else {
|
||||
display_menu[0] = ' ';
|
||||
display_menu[1] = ' ';
|
||||
}
|
||||
|
||||
if(index > 0) {
|
||||
display_menu[3] = key_cstr[str_index - 3];
|
||||
display_menu[4] = key_cstr[str_index - 2];
|
||||
} else {
|
||||
display_menu[3] = ' ';
|
||||
display_menu[4] = ' ';
|
||||
}
|
||||
|
||||
display_menu[7] = key_cstr[str_index];
|
||||
display_menu[8] = key_cstr[str_index + 1];
|
||||
|
||||
if((str_index + 4) <= (uint8_t)strlen(key_cstr)) {
|
||||
display_menu[11] = key_cstr[str_index + 3];
|
||||
display_menu[12] = key_cstr[str_index + 4];
|
||||
} else {
|
||||
display_menu[11] = ' ';
|
||||
display_menu[12] = ' ';
|
||||
}
|
||||
|
||||
if((str_index + 8) <= (uint8_t)strlen(key_cstr)) {
|
||||
display_menu[14] = key_cstr[str_index + 6];
|
||||
display_menu[15] = key_cstr[str_index + 7];
|
||||
} else {
|
||||
display_menu[14] = ' ';
|
||||
display_menu[15] = ' ';
|
||||
}
|
||||
}
|
||||
string_init_set_str(result, display_menu);
|
||||
}
|
||||
|
||||
void subbrute_main_view_draw(Canvas* canvas, SubBruteMainViewModel* model) {
|
||||
SubBruteMainViewModel* m = model;
|
||||
|
||||
// Title
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_box(canvas, 0, 0, canvas_width(canvas), STATUS_BAR_Y_SHIFT);
|
||||
canvas_invert_color(canvas);
|
||||
canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, "Sub-GHz Bruteforcer");
|
||||
canvas_invert_color(canvas);
|
||||
|
||||
if(m->is_select_byte) {
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "key_field: %s", m->key_field);
|
||||
#endif
|
||||
char msg_index[18];
|
||||
snprintf(msg_index, sizeof(msg_index), "Field index : %d", m->index);
|
||||
canvas_draw_str_aligned(canvas, 64, 26, AlignCenter, AlignTop, msg_index);
|
||||
|
||||
string_t menu_items;
|
||||
string_init(menu_items);
|
||||
|
||||
center_displayed_key(menu_items, m->key_field, m->index);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 64, 40, AlignCenter, AlignTop, string_get_cstr(menu_items));
|
||||
|
||||
elements_button_center(canvas, "Select");
|
||||
elements_button_left(canvas, "<");
|
||||
elements_button_right(canvas, ">");
|
||||
|
||||
string_reset(menu_items);
|
||||
} else {
|
||||
// Menu
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
uint8_t items_on_screen = 3;
|
||||
const uint8_t item_height = 16;
|
||||
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "window_position: %d, index: %d", model->window_position, m->index);
|
||||
#endif
|
||||
for(uint8_t position = 0; position < SubBruteAttackTotalCount; ++position) {
|
||||
uint8_t item_position = position - model->window_position;
|
||||
|
||||
if(item_position < items_on_screen) {
|
||||
const char* str = subbrute_get_menu_name(position);
|
||||
if(m->index == position) {
|
||||
canvas_draw_str_aligned(
|
||||
canvas,
|
||||
64,
|
||||
9 + (item_position * item_height) + STATUS_BAR_Y_SHIFT,
|
||||
AlignCenter,
|
||||
AlignCenter,
|
||||
str);
|
||||
elements_frame(
|
||||
canvas, 1, 1 + (item_position * item_height) + STATUS_BAR_Y_SHIFT, 124, 15);
|
||||
} else {
|
||||
canvas_draw_str_aligned(
|
||||
canvas,
|
||||
64,
|
||||
9 + (item_position * item_height) + STATUS_BAR_Y_SHIFT,
|
||||
AlignCenter,
|
||||
AlignCenter,
|
||||
str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
elements_scrollbar_pos(
|
||||
canvas,
|
||||
canvas_width(canvas),
|
||||
STATUS_BAR_Y_SHIFT + 2,
|
||||
canvas_height(canvas) - STATUS_BAR_Y_SHIFT,
|
||||
m->index,
|
||||
SubBruteAttackTotalCount);
|
||||
}
|
||||
}
|
||||
|
||||
bool subbrute_main_view_input(InputEvent* event, void* context) {
|
||||
furi_assert(event);
|
||||
furi_assert(context);
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "InputKey: %d", event->key);
|
||||
#endif
|
||||
|
||||
if(event->key == InputKeyBack && event->type == InputTypeShort) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SubBruteMainView* instance = context;
|
||||
const uint8_t min_value = 0;
|
||||
const uint8_t correct_total = SubBruteAttackTotalCount - 1;
|
||||
uint8_t index = 0;
|
||||
bool is_select_byte = false;
|
||||
with_view_model(
|
||||
instance->view, (SubBruteMainViewModel * model) {
|
||||
is_select_byte = model->is_select_byte;
|
||||
return false;
|
||||
});
|
||||
|
||||
bool consumed = false;
|
||||
if(!is_select_byte) {
|
||||
if((event->type == InputTypeShort) || (event->type == InputTypeRepeat)) {
|
||||
with_view_model(
|
||||
instance->view, (SubBruteMainViewModel * model) {
|
||||
bool ret = false;
|
||||
uint8_t items_on_screen = 3;
|
||||
if(event->key == InputKeyUp) {
|
||||
if(model->index == min_value) {
|
||||
model->index = correct_total;
|
||||
} else {
|
||||
model->index = CLAMP(model->index - 1, correct_total, min_value);
|
||||
}
|
||||
ret = true;
|
||||
consumed = true;
|
||||
} else if(event->key == InputKeyDown) {
|
||||
if(model->index == correct_total) {
|
||||
model->index = min_value;
|
||||
} else {
|
||||
model->index = CLAMP(model->index + 1, correct_total, min_value);
|
||||
}
|
||||
ret = true;
|
||||
consumed = true;
|
||||
}
|
||||
if(ret) {
|
||||
model->window_position = model->index;
|
||||
if(model->window_position > 0) {
|
||||
model->window_position -= 1;
|
||||
}
|
||||
|
||||
if(SubBruteAttackTotalCount <= items_on_screen) {
|
||||
model->window_position = 0;
|
||||
} else {
|
||||
if(model->window_position >=
|
||||
(SubBruteAttackTotalCount - items_on_screen)) {
|
||||
model->window_position =
|
||||
(SubBruteAttackTotalCount - items_on_screen);
|
||||
}
|
||||
}
|
||||
}
|
||||
index = model->index;
|
||||
return ret;
|
||||
});
|
||||
}
|
||||
|
||||
#ifdef FURI_DEBUG
|
||||
with_view_model(
|
||||
instance->view, (SubBruteMainViewModel * model) {
|
||||
index = model->index;
|
||||
return false;
|
||||
});
|
||||
FURI_LOG_I(TAG, "Index: %d", index);
|
||||
#endif
|
||||
|
||||
if(event->key == InputKeyOk && event->type == InputTypeShort) {
|
||||
if(index == SubBruteAttackLoadFile) {
|
||||
instance->callback(SubBruteCustomEventTypeLoadFile, instance->context);
|
||||
} else {
|
||||
instance->callback(SubBruteCustomEventTypeMenuSelected, instance->context);
|
||||
}
|
||||
consumed = true;
|
||||
}
|
||||
} else {
|
||||
if((event->type == InputTypeShort) || (event->type == InputTypeRepeat)) {
|
||||
with_view_model(
|
||||
instance->view, (SubBruteMainViewModel * model) {
|
||||
if(event->key == InputKeyLeft) {
|
||||
if(model->index > 0) {
|
||||
model->index--;
|
||||
}
|
||||
} else if(event->key == InputKeyRight) {
|
||||
if(model->index < 7) {
|
||||
model->index++;
|
||||
}
|
||||
}
|
||||
|
||||
index = model->index;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
#ifdef FURI_DEBUG
|
||||
with_view_model(
|
||||
instance->view, (SubBruteMainViewModel * model) {
|
||||
index = model->index;
|
||||
return false;
|
||||
});
|
||||
FURI_LOG_I(TAG, "Index: %d", index);
|
||||
#endif
|
||||
|
||||
if(event->key == InputKeyOk && event->type == InputTypeShort) {
|
||||
instance->callback(SubBruteCustomEventTypeIndexSelected, instance->context);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void subbrute_main_view_enter(void* context) {
|
||||
furi_assert(context);
|
||||
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "subbrute_main_view_enter");
|
||||
#endif
|
||||
}
|
||||
|
||||
void subbrute_main_view_exit(void* context) {
|
||||
furi_assert(context);
|
||||
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "subbrute_main_view_exit");
|
||||
#endif
|
||||
}
|
||||
|
||||
SubBruteMainView* subbrute_main_view_alloc() {
|
||||
SubBruteMainView* instance = malloc(sizeof(SubBruteMainView));
|
||||
instance->view = view_alloc();
|
||||
view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(SubBruteMainViewModel));
|
||||
view_set_context(instance->view, instance);
|
||||
view_set_draw_callback(instance->view, (ViewDrawCallback)subbrute_main_view_draw);
|
||||
view_set_input_callback(instance->view, subbrute_main_view_input);
|
||||
view_set_enter_callback(instance->view, subbrute_main_view_enter);
|
||||
view_set_exit_callback(instance->view, subbrute_main_view_exit);
|
||||
|
||||
with_view_model(
|
||||
instance->view, (SubBruteMainViewModel * model) {
|
||||
model->index = 0;
|
||||
model->window_position = 0;
|
||||
model->key_field = NULL;
|
||||
model->is_select_byte = false;
|
||||
return true;
|
||||
});
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subbrute_main_view_free(SubBruteMainView* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
view_free(instance->view);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
View* subbrute_main_view_get_view(SubBruteMainView* instance) {
|
||||
furi_assert(instance);
|
||||
return instance->view;
|
||||
}
|
||||
|
||||
void subbrute_main_view_set_index(
|
||||
SubBruteMainView* instance,
|
||||
uint8_t idx,
|
||||
bool is_select_byte,
|
||||
const char* key_field) {
|
||||
furi_assert(instance);
|
||||
furi_assert(idx < SubBruteAttackTotalCount);
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_I(TAG, "Set index: %d", idx);
|
||||
#endif
|
||||
with_view_model(
|
||||
instance->view, (SubBruteMainViewModel * model) {
|
||||
model->is_select_byte = is_select_byte;
|
||||
model->key_field = key_field;
|
||||
model->index = idx;
|
||||
model->window_position = idx;
|
||||
|
||||
if(!is_select_byte) {
|
||||
uint8_t items_on_screen = 3;
|
||||
|
||||
if(model->window_position > 0) {
|
||||
model->window_position -= 1;
|
||||
}
|
||||
|
||||
if(SubBruteAttackTotalCount <= items_on_screen) {
|
||||
model->window_position = 0;
|
||||
} else {
|
||||
if(model->window_position >= (SubBruteAttackTotalCount - items_on_screen)) {
|
||||
model->window_position = (SubBruteAttackTotalCount - items_on_screen);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
SubBruteAttacks subbrute_main_view_get_index(SubBruteMainView* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
uint8_t idx = 0;
|
||||
with_view_model(
|
||||
instance->view, (SubBruteMainViewModel * model) {
|
||||
idx = model->index;
|
||||
return false;
|
||||
});
|
||||
|
||||
#ifdef FURI_DEBUG
|
||||
FURI_LOG_D(TAG, "Get index: %d", idx);
|
||||
#endif
|
||||
|
||||
return idx;
|
||||
}
|
||||
30
applications/plugins/subbrute/views/subbrute_main_view.h
Normal file
30
applications/plugins/subbrute/views/subbrute_main_view.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#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,
|
||||
bool is_select_byte,
|
||||
const char* key_field);
|
||||
uint8_t subbrute_main_view_get_index(SubBruteMainView* instance);
|
||||
void subbrute_attack_view_enter(void* context);
|
||||
void subbrute_attack_view_exit(void* context);
|
||||
bool subbrute_attack_view_input(InputEvent* event, void* context);
|
||||
void subbrute_attack_view_draw(Canvas* canvas, void* context);
|
||||
@@ -22,15 +22,11 @@ void desktop_scene_slideshow_on_enter(void* context) {
|
||||
bool desktop_scene_slideshow_on_event(void* context, SceneManagerEvent event) {
|
||||
Desktop* desktop = (Desktop*)context;
|
||||
bool consumed = false;
|
||||
Storage* storage = NULL;
|
||||
Power* power = NULL;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
switch(event.event) {
|
||||
case DesktopSlideshowCompleted:
|
||||
storage = furi_record_open(RECORD_STORAGE);
|
||||
storage_common_remove(storage, SLIDESHOW_FS_PATH);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
scene_manager_previous_scene(desktop->scene_manager);
|
||||
consumed = true;
|
||||
break;
|
||||
@@ -50,4 +46,8 @@ bool desktop_scene_slideshow_on_event(void* context, SceneManagerEvent event) {
|
||||
|
||||
void desktop_scene_slideshow_on_exit(void* context) {
|
||||
UNUSED(context);
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
storage_common_remove(storage, SLIDESHOW_FS_PATH);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ static void desktop_view_pin_input_draw_cells(Canvas* canvas, DesktopViewPinInpu
|
||||
canvas_draw_icon(canvas, x + 3, y + 2, &I_Pin_arrow_down_7x9);
|
||||
break;
|
||||
case InputKeyUp:
|
||||
canvas_draw_icon(canvas, x + 3, y + 2, &I_Pin_arrow_up7x9);
|
||||
canvas_draw_icon(canvas, x + 3, y + 2, &I_Pin_arrow_up_7x9);
|
||||
break;
|
||||
case InputKeyLeft:
|
||||
canvas_draw_icon(canvas, x + 2, y + 3, &I_Pin_arrow_left_9x7);
|
||||
|
||||
@@ -142,6 +142,8 @@ void button_panel_reset(ButtonPanel* button_panel) {
|
||||
}
|
||||
model->reserve_x = 0;
|
||||
model->reserve_y = 0;
|
||||
model->selected_item_x = 0;
|
||||
model->selected_item_y = 0;
|
||||
LabelList_reset(model->labels);
|
||||
ButtonMatrix_reset(model->button_matrix);
|
||||
return true;
|
||||
|
||||
@@ -318,16 +318,18 @@ static void text_input_handle_ok(TextInput* text_input, TextInputModel* model, b
|
||||
}
|
||||
} else if(selected == BACKSPACE_KEY) {
|
||||
text_input_backspace_cb(model);
|
||||
} else if(text_length < (model->text_buffer_size - 1)) {
|
||||
} else {
|
||||
if(model->clear_default_text) {
|
||||
text_length = 0;
|
||||
}
|
||||
if(text_length < (model->text_buffer_size - 1)) {
|
||||
if(text_length == 0 && char_is_lowercase(selected)) {
|
||||
selected = char_to_uppercase(selected);
|
||||
}
|
||||
model->text_buffer[text_length] = selected;
|
||||
model->text_buffer[text_length + 1] = 0;
|
||||
}
|
||||
}
|
||||
model->clear_default_text = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
#include "power_i.h"
|
||||
#include "views/power_off.h"
|
||||
#include "desktop/desktop_settings.h"
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <gui/view_port.h>
|
||||
#include <gui/view.h>
|
||||
|
||||
#define POWER_OFF_TIMEOUT 90
|
||||
|
||||
|
||||
@@ -60,19 +60,8 @@ const char* const delay_text[DELAY_COUNT] = {
|
||||
"10min",
|
||||
"30min",
|
||||
};
|
||||
const uint32_t delay_value[DELAY_COUNT] = {
|
||||
1000,
|
||||
5000,
|
||||
10000,
|
||||
15000,
|
||||
30000,
|
||||
60000,
|
||||
90000,
|
||||
120000,
|
||||
300000,
|
||||
600000,
|
||||
1800000
|
||||
};
|
||||
const uint32_t delay_value[DELAY_COUNT] =
|
||||
{1000, 5000, 10000, 15000, 30000, 60000, 90000, 120000, 300000, 600000, 1800000};
|
||||
|
||||
#define VIBRO_COUNT 2
|
||||
const char* const vibro_text[VIBRO_COUNT] = {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user