1
mirror of https://github.com/DarkFlippers/unleashed-firmware.git synced 2025-12-13 13:09:49 +04:00

Compare commits

...

70 Commits

Author SHA1 Message Date
MX
5e30b14d90 update changelog 2022-09-26 02:12:11 +03:00
MX
c07e3a34dd Merge pull request #75 from derskythe/subbrute-deep-refactor
SubBrute deep refactor
2022-09-26 02:08:45 +03:00
MX
be7e11e60f Merge branch 'dev' into subbrute-deep-refactor 2022-09-26 02:06:50 +03:00
MX
e96e414561 Merge branch 'fz-dev' into dev 2022-09-26 02:06:09 +03:00
derskythe
0c99cb52ec free transmitter during subbrute_worker_init_manual_transmit 2022-09-26 02:45:09 +04:00
derskythe
ad9e1ce4df set furi_hal_subghz_set_path to FuriHalSubGhzPathIsolate on each manual iteration 2022-09-26 02:42:39 +04:00
derskythe
22dc5190d1 remove furi_hal_power_suppress_charge_enter/exit from other places 2022-09-26 02:39:17 +04:00
derskythe
f2fd97d9c5 fix memory leaks 2022-09-26 02:36:38 +04:00
derskythe
08084d5763 fix first send signal equals last transferred or 0x00 2022-09-26 02:03:36 +04:00
derskythe
add1ad6949 fix manual select key on max and min values 2022-09-26 01:48:51 +04:00
DerSkythe
87654e60b8 Merge remote-tracking branch 'origin/subbrute-deep-refactor' into subbrute-deep-refactor 2022-09-26 01:10:21 +04:00
DerSkythe
6f92cd645e fixed frame width to scroll 2022-09-26 01:09:00 +04:00
DerSkythe
23f6ea2e05 refactor worker moved it to SubBruteState 2022-09-26 01:07:16 +04:00
David Coles
a6b98ccbbe Preliminary Rust support (#1781)
* Add support for R_ARM_THM_MOVW_ABS_NC/THM_MOVT_ABS

These are sometimes emitted by the Rust LLVM compiler.

Ref: https://github.com/ARM-software/abi-aa/blob/main/aaelf32/aaelf32.rst#56relocation

* Discard LLVM bitcode from extension applications

LLVM-based compilers may include uncompressed bitcode in object files
to help with link-time optimization. However this can bloat binary sizes
from KB to MB.

* Expose alligned_malloc/free functions to applications

This is required to implement a global allocator in Rust.
2022-09-26 07:06:46 +10:00
DerSkythe
ba5f590dab switched to manual transmit 2022-09-26 00:07:14 +04:00
Der Skythe
f1048733d2 Merge branch 'Eng1n33r:dev' into subbrute-deep-refactor 2022-09-25 23:16:27 +04:00
DerSkythe
ea7f68fcab fixed load existing dump 2022-09-25 23:12:31 +04:00
MX
8013aacd94 oops 2022-09-25 21:03:32 +03:00
MX
be8f409098 update changelog 2022-09-25 21:01:12 +03:00
MX
97e6fe8f4e update universal remote assets
by @amec0e
2022-09-25 20:39:22 +03:00
DerSkythe
54757428e6 fix bug with return to main menu when choice file 2022-09-25 21:31:33 +04:00
MX
bd39d81324 Merge branch 'fz-dev' into dev 2022-09-25 19:55:35 +03:00
Chris van Marle
2a2078d9b5 Text input overwrite max size template (#1687) 2022-09-26 01:17:09 +09:00
MX
01ca588488 Merge branch 'fz-dev' into dev 2022-09-25 18:27:58 +03:00
Kowalski Dragon
f86eada292 Remove unused headers (#1751) 2022-09-25 23:39:06 +09:00
Skorpionm
bc777b2eff SubGhz: fix config menu (#1748)
* SubGhz: fix config menu
* SubGhz: fix gui Magellen protocol
* SubGhz: fix gui Transmit SubGhz
* SubGhz: keeloq, new gen manufacture code
* SubGhz: Update keeloq_mfcodes

Co-authored-by: あく <alleteam@gmail.com>
2022-09-25 23:34:52 +09:00
DerSkythe
6f91fa42f0 Added additional graphic decorations 2022-09-25 18:26:10 +04:00
Sergey Gavrilov
e6d22ed147 ELF-Loader: C++ plugin support, loader overhaul. (#1744)
* fap-loader: load all code and data sections
* fap-loader: relocate all code and data sections
* fap-loader: remove old elf loader
* fap-loader: new jmp call relocation
* openocd: resume on detach
* fap-loader: trampoline for big jumps
* fap-loader: rename cache
* fap-loader: init_array support
* fap-loader: untangled flipper_application into separate entities
* fap-loader: fix debug
* fap-loader: optimize section container
* fap-loader: optimize key for section container
* fap-loader: disable debug log
* documentation
* F7: bump api symbols version
* Lib: cleanup elf_file.c

Co-authored-by: あく <alleteam@gmail.com>
2022-09-25 23:11:29 +09:00
MX
436f70b69b Merge branch 'fz-dev' into dev 2022-09-25 16:46:23 +03:00
DerSkythe
ec9ce0cad7 Working prototype, but not yet tested on a real device 2022-09-25 17:05:52 +04:00
Jauder Ho
7e2008095e Bump protobuf from 3.20.1 to 3.20.2 in /scripts (#1774) 2022-09-25 20:56:53 +09:00
Sergey Gavrilov
92e440c77d Core: simplify record container (#1776)
Co-authored-by: あく <alleteam@gmail.com>
2022-09-25 20:48:57 +09:00
DerSkythe
666821e9ce SubBruteMainView is ready 2022-09-25 00:46:43 +04:00
MX
1bca477a43 update install instructions
thanks to @Svaarich !
2022-09-24 22:20:13 +03:00
MX
41571ce9ad SubGHz RAW - datetime in default names (+ format changed)
OFW PR 1772 by Skorpionm / printf text format changed by me
2022-09-24 22:15:06 +03:00
MX
038d098c85 Merge branch 'fz-dev' into dev 2022-09-24 21:56:17 +03:00
DerSkythe
b03cc8ddc3 trying to fix load failure 2022-09-24 22:30:08 +04:00
DerSkythe
c8e3d9b040 fix repeat call of view_dispatcher_alloc 2022-09-24 22:15:09 +04:00
DerSkythe
aeb02500de Deep refactor of SubBrute was made, but it doesn't start. Debug device needed 2022-09-24 21:47:21 +04:00
ghettorce
eadd7801af fbt: exclude user site-packages directory from sys.path (#1778)
* fbt: exclude user site-packages directory from sys.path
* fbt: python path fixes for *nix
* fbt: fixed cli target on Windows

Co-authored-by: hedger <hedger@users.noreply.github.com>
2022-09-24 15:30:19 +04:00
Yoanndp
6d2b0a3b6c Update ReadMe.md (#1766) 2022-09-24 19:36:11 +09:00
MX
32a7642761 remove duplicate function, update changelog 2022-09-22 21:51:47 +03:00
MX
e8bb45496d Merge branch 'fz-dev' into dev 2022-09-22 21:45:26 +03:00
Andrea Sacchi
3846852f2b NFC Fix Mifare Classic (#1769)
* Fix Mifare Classic key str to int conversion: Wrong cast lead to unexpected behavior converting key from str to int.
* Nfc: fix type cast in mf_classic_dict and add basic unit tests

Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
2022-09-23 02:35:28 +09:00
Georgii Surkov
17d01f5c29 [FL-2848] Universal Remote fix (#1770)
* Reset BruteForce on exit from Universal Remote
* Reset current button in ButtonPanel
2022-09-23 01:13:00 +09:00
MX
e6bcba6959 update changelog, minor fixes 2022-09-22 18:23:32 +03:00
MX
e13edc2f70 keeeloq update 2022-09-22 18:01:54 +03:00
MX
de6ff1d9c9 update changelog 2022-09-21 23:07:39 +03:00
MX
bea15134ba fix mousejacker gui 2022-09-21 22:29:42 +03:00
MX
28a55bf576 Merge branch 'fz-dev' into dev 2022-09-21 22:02:02 +03:00
gornekich
e70121e20f [FL-2843] NFC fixes (#1764)
* nfc: fix empty desfire card message
* nfc: limit total user keys to list
* nfc: increase popup timeout

Co-authored-by: あく <alleteam@gmail.com>
2022-09-22 00:53:25 +09:00
hedger
432ff41d6a [FL-2844] desktop: removing slideshow file when leaving slideshow view (#1762)
* [FL-2844] desktop: removing slideshow file when leaving slideshow view; vscode: fix for BM port fetcher; fap api: more symbols for LL
* desktop: actually removing slideshow file
* desktop: moved slideshow removal to scene code; fbt: better blackmagic device handling
* fbt: disabled pagination for gdb
* vscode: restored blackmagic command line
* fbt: fixed debug_other target; added debug_other_blackmagic
* furi: added furi_thread_suspend API group; fixed null-pointer deref for thread name; cleaned up RTOS config
* furi: changed thread state check to eTaskGetState
2022-09-21 23:42:59 +09:00
MX
87393a086c fix rfid fuzzer crashes, some new random names 2022-09-21 08:43:07 +03:00
MX
6000d47a0f allow saving only for protocols without encoder 2022-09-21 07:12:09 +03:00
MX
d986ef4104 fix nice flor s crash, fix debug pack for debug builds 2022-09-21 06:52:34 +03:00
MX
f85dc1675d update changelog, rm unused var from clock 2022-09-21 01:00:56 +03:00
MX
7c7ac07e6a Merge pull request #74 from mvanzanten/adding-support-for-hidprox
adding support for HIDProx, updating the UI to switch between protocols
2022-09-20 23:25:45 +03:00
MX
ca02826cfd set time between cards to 6, run fbt format 2022-09-20 23:24:34 +03:00
MX
96ad7f3cef fix nfc list crash, fix magellen gui, fix transmitter gui 2022-09-20 23:13:15 +03:00
Matt Van Zanten
c213ff596a adding support for HIDProx, updating the UI to switch between protocols 2022-09-20 11:45:16 -07:00
MX
b2589698ff Merge branch 'fz-dev' into dev 2022-09-20 21:39:22 +03:00
Max Lapan
3360f818a1 Subghz: Adding checks for get_upload functions (#1704)
* Adding checks for get_upload functions
  Almost in every protocol, function which generates upload might fail and return false.
  But we don't check this result, which might end up sending random memory contents to the air.
* Format sources and fix crash on ivalid bit count in chamberlain

Co-authored-by: あく <alleteam@gmail.com>
2022-09-20 14:29:10 +09:00
Georgii Surkov
066da4080b [FL-2792] AC Universal Remote (#1725)
* Add Universal AC Remote scene
* Implement AC gui
* Basic working implemetation
* Another Universal AC Remote implementation
* Update icons
* Adjust button positions
* Revert old ButtonPanel class
* Update resource manifest
* [FL-2627] Flipper applications: SDK, build and debug system (#1387)
* Update api definitions
* Add UniversalRemotes documentation
* Use more Flipper-friendly signal names

Co-authored-by: SG <who.just.the.doctor@gmail.com>
2022-09-20 14:09:37 +09:00
MX
b2c118f267 fix null pointer dereference in archive -> Info
and fix long path display
2022-09-20 05:52:13 +03:00
MX
a8db46124e update docs & changelog 2022-09-20 04:09:14 +03:00
MX
672e27f258 fix icon name 2022-09-20 03:23:22 +03:00
MX
e762a68265 Merge pull request #72 from RogueMaster/ApplicationsFromArchive
Applications from archive
2022-09-20 03:20:25 +03:00
MX
8659becc9d fix tab name and add new icon
icon by @Svaarich
2022-09-20 03:19:31 +03:00
RogueMaster
82e1e8af6a Enable fap support on Archive app 2022-09-19 18:47:22 -04:00
RogueMaster
a71d05a114 Update archive_browser.h 2022-09-19 18:39:38 -04:00
137 changed files with 5280 additions and 2589 deletions

1
.github/CODEOWNERS vendored Normal file
View File

@@ -0,0 +1 @@
* @xMasterX

View File

@@ -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",
}

View File

@@ -1,19 +1,16 @@
### New changes
* PR: Added 868.95 MHz into subghz user config (PR 71 by TasyDevilsky)
* Lower framerate in custom anim to save a bit of battery charge
* Removed unused icon from OFW PR 1533, fixed unirf icon name, updated api symbols (version is 3.0)
* OFW: NFC user dict list, delete, and de-duplication. (OFW PR 1533)
* OFW: Add new russian transport card parsers (OFW PR 1503)
* OFW: SubGhz: Oregon v2.1 decoder (OFR PR 1678)
* OFW: Show error popup when NFC chip is not init/disconnected (OFW PR 1722)
* 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.

View File

@@ -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.

View File

@@ -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",

View File

@@ -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();
}

View File

@@ -24,13 +24,13 @@ App(
apptype=FlipperAppType.METAPACKAGE,
provides=[
"gpio",
"ibutton",
#"ibutton",
"infrared",
"lfrfid",
"nfc",
"subghz",
"bad_usb",
"u2f",
#"bad_usb",
#"u2f",
"fap_loader",
"archive",
],

View File

@@ -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,
};

View File

@@ -13,6 +13,7 @@ typedef enum {
ArchiveFileTypeInfrared,
ArchiveFileTypeBadUsb,
ArchiveFileTypeU2f,
ArchiveFileTypeApps,
ArchiveFileTypeUpdateManifest,
ArchiveFileTypeFolder,
ArchiveFileTypeUnknown,

View File

@@ -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;
}

View File

@@ -19,6 +19,7 @@ static const char* flipper_app_name[] = {
[ArchiveFileTypeInfrared] = "Infrared",
[ArchiveFileTypeBadUsb] = "Bad USB",
[ArchiveFileTypeU2f] = "U2F",
[ArchiveFileTypeApps] = "Applications",
[ArchiveFileTypeUpdateManifest] = "UpdaterApp",
};
@@ -132,7 +133,7 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) {
case ArchiveBrowserEventFileMenuRename:
if(favorites) {
browser->callback(ArchiveBrowserEventEnterFavMove, browser->context);
//} else if((archive_is_known_app(selected->type)) && (selected->is_app == false)) {
//} else if((archive_is_known_app(selected->type)) && (selected->is_app == false)) {
} else {
// Added ability to rename files and folders
archive_show_file_menu(browser, false);

View File

@@ -37,7 +37,11 @@ void archive_scene_info_on_enter(void* context) {
// Directory path
path_extract_dirname(string_get_cstr(current->path), dirname);
string_replace_str(dirname, STORAGE_ANY_PATH_PREFIX, "");
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);

View File

@@ -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,

View File

@@ -27,6 +27,7 @@ typedef enum {
ArchiveTabIButton,
ArchiveTabBadUsb,
ArchiveTabU2f,
ArchiveTabApps,
ArchiveTabBrowser,
ArchiveTabTotal,
} ArchiveTabEnum;

View File

@@ -33,5 +33,4 @@ typedef enum {
typedef struct {
TimeFormat time_format;
DateFormat date_format;
uint8_t increment_precision;
} ClockSettings;

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -1,48 +1,87 @@
#include "../nfc_i.h"
void nfc_scene_mf_classic_keys_list_submenu_callback(void* context, uint32_t index) {
Nfc* nfc = context;
#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;
Submenu* submenu = nfc->submenu;
MfClassicDict* dict = mf_classic_dict_alloc(MfClassicDictTypeUser);
uint32_t index = 0;
string_t temp_key;
MfClassicUserKeys_init(nfc->mfc_key_strs);
string_init(temp_key);
if(dict) {
mf_classic_dict_rewind(dict);
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);
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);
}
submenu_set_header(submenu, "Select key to delete:");
mf_classic_dict_free(dict);
string_clear(temp_key);
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
}
bool nfc_scene_mf_classic_keys_list_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneMfClassicKeysDelete, event.event);
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeysDelete);
consumed = true;
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;
}
@@ -57,4 +96,5 @@ void nfc_scene_mf_classic_keys_list_on_exit(void* context) {
}
MfClassicUserKeys_clear(nfc->mfc_key_strs);
submenu_reset(nfc->submenu);
popup_reset(nfc->popup);
}

View File

@@ -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,46 +32,45 @@ 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(
nfc->submenu,
"App info",
SubmenuIndexAppInfo,
nfc_scene_mf_desfire_app_submenu_callback,
nfc);
text_box_set_font(nfc->text_box, TextBoxFontHex);
submenu_add_item(
submenu, "App info", SubmenuIndexAppInfo, nfc_scene_mf_desfire_app_submenu_callback, nfc);
uint16_t cap = NFC_TEXT_STORE_SIZE;
char* buf = nfc->text_store;
int idx = SubmenuIndexDynamic;
for(MifareDesfireFile* file = app->file_head; file; file = file->next) {
int size = snprintf(buf, cap, "File %d", file->id);
if(size < 0 || size >= cap) {
FURI_LOG_W(
TAG,
"Exceeded NFC_TEXT_STORE_SIZE when preparing file id strings; menu truncated");
break;
uint16_t cap = NFC_TEXT_STORE_SIZE;
char* buf = nfc->text_store;
int idx = SubmenuIndexDynamic;
for(MifareDesfireFile* file = app->file_head; file; file = file->next) {
int size = snprintf(buf, cap, "File %d", file->id);
if(size < 0 || size >= cap) {
FURI_LOG_W(
TAG,
"Exceeded NFC_TEXT_STORE_SIZE when preparing file id strings; menu truncated");
break;
}
char* label = buf;
cap -= size + 1;
buf += size + 1;
submenu_add_item(
nfc->submenu, label, idx++, nfc_scene_mf_desfire_app_submenu_callback, nfc);
}
char* label = buf;
cap -= size + 1;
buf += size + 1;
submenu_add_item(submenu, label, idx++, nfc_scene_mf_desfire_app_submenu_callback, nfc);
}
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
}
}
bool nfc_scene_mf_desfire_app_on_event(void* context, SceneManagerEvent event) {
@@ -73,26 +79,30 @@ 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) {
MifareDesfireApplication* app = nfc_scene_mf_desfire_app_get_app(nfc);
TextBox* text_box = nfc->text_box;
string_reset(nfc->text_box_store);
if(event.event == SubmenuIndexAppInfo) {
mf_df_cat_application_info(app, nfc->text_box_store);
if(event.event == NfcCustomEventViewExit) {
consumed = scene_manager_previous_scene(nfc->scene_manager);
} else {
uint16_t index = event.event - SubmenuIndexDynamic;
MifareDesfireFile* file = app->file_head;
for(int i = 0; file && i < index; i++) {
file = file->next;
MifareDesfireApplication* app = nfc_scene_mf_desfire_app_get_app(nfc);
TextBox* text_box = nfc->text_box;
string_reset(nfc->text_box_store);
if(event.event == SubmenuIndexAppInfo) {
mf_df_cat_application_info(app, nfc->text_box_store);
} else {
uint16_t index = event.event - SubmenuIndexDynamic;
MifareDesfireFile* file = app->file_head;
for(int i = 0; file && i < index; i++) {
file = file->next;
}
if(!file) {
return false;
}
mf_df_cat_file(file, nfc->text_box_store);
}
if(!file) {
return false;
}
mf_df_cat_file(file, nfc->text_box_store);
text_box_set_text(text_box, string_get_cstr(nfc->text_box_store));
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp, state | 1);
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox);
consumed = true;
}
text_box_set_text(text_box, string_get_cstr(nfc->text_box_store));
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);

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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`

View File

@@ -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);

View File

@@ -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;

View File

@@ -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]));
}
}

View File

@@ -36,11 +36,21 @@ bool flipfrid_load(FlipFridState* context, const char* file_path) {
break;
} else {
FURI_LOG_I(TAG, "Key type: %s", string_get_cstr(temp_str));
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;
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;
}
}
}
@@ -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(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;
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];

View File

@@ -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,17 +16,39 @@ 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);
context->protocol = protocol_dict_get_protocol_by_name(context->dict, "EM4100");
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) {
@@ -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,87 +72,180 @@ void flipfrid_scene_run_attack_on_tick(FlipFridState* context) {
lfrfid_worker_stop_thread(context->worker);
switch(context->attack) {
case FlipFridAttackDefaultValues:
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->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;
case FlipFridAttackBfCustomerId:
context->payload[0] = context->attack_step;
context->payload[1] = 0x00;
context->payload[2] = 0x00;
context->payload[3] = 0x00;
context->payload[4] = 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:
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[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);
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->attack_step++;
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;
context->is_attacking = false;
notification_message(context->notify, &sequence_blink_stop);
notification_message(context->notify, &sequence_single_vibro);
} else {
context->attack_step++;
}
break;
}
break;
case FlipFridAttackLoadFileCustomUids:
while(true) {
string_reset(context->data_str);
if(!stream_read_line(context->uids_stream, context->data_str)) {
case FlipFridAttackBfCustomerId:
if(context->proto == EM4100) {
context->payload[0] = context->attack_step;
context->payload[1] = 0x00;
context->payload[2] = 0x00;
context->payload[3] = 0x00;
context->payload[4] = 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;
} 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];
context->payload[3] = context->data[3];
context->payload[4] = context->data[4];
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;
};
if(string_get_char(context->data_str, 0) == '#') continue;
if(string_size(context->data_str) != 11) continue;
} else {
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;
}
FURI_LOG_D(TAG, string_get_cstr(context->data_str));
// string is valid, parse it in context->payload
for(uint8_t i = 0; i < 5; 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);
case FlipFridAttackLoadFileCustomUids:
if(context->proto == EM4100) {
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) != 11) 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 < 5; 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;
} 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;
}
break;
}
}
@@ -190,16 +305,30 @@ 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];
snprintf(
uid,
sizeof(uid),
"%02X:%02X:%02X:%02X:%02X",
context->payload[0],
context->payload[1],
context->payload[2],
context->payload[3],
context->payload[4]);
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),
"%02X:%02X:%02X:%02X:%02X",
context->payload[0],
context->payload[1],
context->payload[2],
context->payload[3],
context->payload[4]);
}
canvas_draw_str_aligned(canvas, 64, 24, AlignCenter, AlignTop, uid);
canvas_set_font(canvas, FontSecondary);

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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,

View 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;
}

View 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);

View File

@@ -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]));
}
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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();

View File

@@ -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;
}
}

View File

@@ -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);

View File

@@ -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));
}

View File

@@ -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);

View 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

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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;
}

View 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;
}

View 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,
};

View File

@@ -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",
};
// Draw correct Canvas
switch(subbrute_state->current_scene) {
case NoneScene:
case SceneSelectFile:
subbrute_scene_load_file_on_draw(canvas, subbrute_state);
break;
case SceneSelectField:
subbrute_scene_select_field_on_draw(canvas, subbrute_state);
break;
case SceneAttack:
subbrute_scene_run_attack_on_draw(canvas, subbrute_state);
break;
case SceneEntryPoint:
subbrute_scene_entrypoint_on_draw(canvas, subbrute_state);
break;
case SceneSaveName:
break;
}
release_mutex((ValueMutex*)ctx, subbrute_state);
static bool subbrute_custom_event_callback(void* context, uint32_t event) {
furi_assert(context);
SubBruteState* instance = context;
return scene_manager_handle_custom_event(instance->scene_manager, event);
}
void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
furi_assert(event_queue);
SubBruteEvent event = {
.evt_type = EventTypeKey, .key = input_event->key, .input_type = input_event->type};
furi_message_queue_put(event_queue, &event, 100);
static bool subbrute_back_event_callback(void* context) {
furi_assert(context);
SubBruteState* instance = context;
return scene_manager_handle_back_event(instance->scene_manager);
}
static void timer_callback(FuriMessageQueue* event_queue) {
furi_assert(event_queue);
SubBruteEvent event = {
.evt_type = EventTypeTick, .key = InputKeyUp, .input_type = InputTypeRelease};
furi_message_queue_put(event_queue, &event, 100);
static void subbrute_tick_event_callback(void* context) {
furi_assert(context);
SubBruteState* instance = context;
scene_manager_handle_tick_event(instance->scene_manager);
}
SubBruteState* subbrute_alloc() {
SubBruteState* subbrute = malloc(sizeof(SubBruteState));
SubBruteState* instance = malloc(sizeof(SubBruteState));
string_init(subbrute->protocol);
string_init(subbrute->preset);
string_init(subbrute->file_path);
string_init(subbrute->file_path_tmp);
string_init_set(subbrute->notification_msg, "");
string_init(subbrute->candidate);
string_init(subbrute->flipper_format_string);
instance->scene_manager = scene_manager_alloc(&subbrute_scene_handlers, instance);
instance->view_dispatcher = view_dispatcher_alloc();
subbrute->previous_scene = NoneScene;
subbrute->current_scene = SceneSelectFile;
subbrute->is_running = true;
subbrute->is_attacking = false;
subbrute->key_index = 7;
subbrute->notify = furi_record_open(RECORD_NOTIFICATION);
instance->gui = furi_record_open(RECORD_GUI);
subbrute->view_dispatcher = view_dispatcher_alloc();
view_dispatcher_enable_queue(instance->view_dispatcher);
view_dispatcher_set_event_callback_context(instance->view_dispatcher, instance);
view_dispatcher_set_custom_event_callback(
instance->view_dispatcher, subbrute_custom_event_callback);
view_dispatcher_set_navigation_event_callback(
instance->view_dispatcher, subbrute_back_event_callback);
view_dispatcher_set_tick_event_callback(
instance->view_dispatcher, subbrute_tick_event_callback, 100);
//Dialog
subbrute->dialogs = furi_record_open(RECORD_DIALOGS);
instance->dialogs = furi_record_open(RECORD_DIALOGS);
subbrute->preset_def = malloc(sizeof(SubGhzPresetDefinition));
// Notifications
instance->notifications = furi_record_open(RECORD_NOTIFICATION);
//subbrute->flipper_format = flipper_format_string_alloc();
//subbrute->environment = subghz_environment_alloc();
// Devices
instance->device = subbrute_device_alloc();
return subbrute;
// Worker
instance->worker = subbrute_worker_alloc();
// TextInput
instance->text_input = text_input_alloc();
view_dispatcher_add_view(
instance->view_dispatcher,
SubBruteViewTextInput,
text_input_get_view(instance->text_input));
// Custom Widget
instance->widget = widget_alloc();
view_dispatcher_add_view(
instance->view_dispatcher, SubBruteViewWidget, widget_get_view(instance->widget));
// Popup
instance->popup = popup_alloc();
view_dispatcher_add_view(
instance->view_dispatcher, SubBruteViewPopup, popup_get_view(instance->popup));
// ViewStack
instance->view_stack = view_stack_alloc();
view_dispatcher_add_view(
instance->view_dispatcher, SubBruteViewStack, view_stack_get_view(instance->view_stack));
// SubBruteMainView
instance->view_main = subbrute_main_view_alloc();
view_dispatcher_add_view(
instance->view_dispatcher,
SubBruteViewMain,
subbrute_main_view_get_view(instance->view_main));
// SubBruteAttackView
instance->view_attack = subbrute_attack_view_alloc();
view_dispatcher_add_view(
instance->view_dispatcher,
SubBruteViewAttack,
subbrute_attack_view_get_view(instance->view_attack));
// Loading
instance->loading = loading_alloc();
//instance->flipper_format = flipper_format_string_alloc();
//instance->environment = subghz_environment_alloc();
return instance;
}
void subbrute_free(SubBruteState* subbrute) {
//Dialog
furi_record_close(RECORD_DIALOGS);
void subbrute_free(SubBruteState* instance) {
furi_assert(instance);
notification_message(subbrute->notify, &sequence_blink_stop);
// SubBruteDevice
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "free SubBruteDevice");
#endif
subbrute_device_free(instance->device);
// SubBruteWorker
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "free SubBruteDevice");
#endif
subbrute_worker_stop(instance->worker);
subbrute_worker_free(instance->worker);
// Notifications
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "free Notifications");
#endif
notification_message(instance->notifications, &sequence_blink_stop);
furi_record_close(RECORD_NOTIFICATION);
instance->notifications = NULL;
view_dispatcher_free(subbrute->view_dispatcher);
// Loading
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "free loading");
#endif
loading_free(instance->loading);
string_clear(subbrute->preset);
string_clear(subbrute->candidate);
// View Main
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "free SubBruteViewMain");
#endif
view_dispatcher_remove_view(instance->view_dispatcher, SubBruteViewMain);
subbrute_main_view_free(instance->view_main);
// Path strings
string_clear(subbrute->file_path);
string_clear(subbrute->file_path_tmp);
string_clear(subbrute->notification_msg);
string_clear(subbrute->candidate);
string_clear(subbrute->flipper_format_string);
// View Attack
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "free SubBruteViewAttack");
#endif
view_dispatcher_remove_view(instance->view_dispatcher, SubBruteViewAttack);
subbrute_attack_view_free(instance->view_attack);
//flipper_format_free(subbrute->flipper_format);
//subghz_environment_free(subbrute->environment);
//subghz_receiver_free(subbrute->receiver);
// TextInput
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "free SubBruteViewTextInput");
#endif
view_dispatcher_remove_view(instance->view_dispatcher, SubBruteViewTextInput);
text_input_free(instance->text_input);
free(subbrute->preset_def);
// Custom Widget
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "free SubBruteViewWidget");
#endif
view_dispatcher_remove_view(instance->view_dispatcher, SubBruteViewWidget);
widget_free(instance->widget);
// Popup
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "free SubBruteViewPopup");
#endif
view_dispatcher_remove_view(instance->view_dispatcher, SubBruteViewPopup);
popup_free(instance->popup);
// ViewStack
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "free SubBruteViewStack");
#endif
view_dispatcher_remove_view(instance->view_dispatcher, SubBruteViewStack);
view_stack_free(instance->view_stack);
//Dialog
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "free RECORD_DIALOGS");
#endif
furi_record_close(RECORD_DIALOGS);
instance->dialogs = NULL;
// Scene manager
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "free scene_manager");
#endif
scene_manager_free(instance->scene_manager);
// View Dispatcher
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "free view_dispatcher");
#endif
view_dispatcher_free(instance->view_dispatcher);
// GUI
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "free RECORD_GUI");
#endif
furi_record_close(RECORD_GUI);
instance->gui = NULL;
// The rest
free(subbrute);
#ifdef FURI_DEBUG
FURI_LOG_D(TAG, "free instance");
#endif
free(instance);
}
void subbrute_show_loading_popup(void* context, bool show) {
TaskHandle_t timer_task = xTaskGetHandle(configTIMER_SERVICE_TASK_NAME);
SubBruteState* instance = context;
ViewStack* view_stack = instance->view_stack;
Loading* loading = instance->loading;
if(show) {
// Raise timer priority so that animations can play
vTaskPrioritySet(timer_task, configMAX_PRIORITIES - 1);
view_stack_add_view(view_stack, loading_get_view(loading));
} else {
view_stack_remove_view(view_stack, loading_get_view(loading));
// Restore default timer priority
vTaskPrioritySet(timer_task, configTIMER_TASK_PRIORITY);
}
}
void subbrute_text_input_callback(void* context) {
furi_assert(context);
SubBruteState* instance = context;
view_dispatcher_send_custom_event(
instance->view_dispatcher, SubBruteCustomEventTypeTextEditDone);
}
void subbrute_popup_closed_callback(void* context) {
furi_assert(context);
SubBruteState* instance = context;
view_dispatcher_send_custom_event(
instance->view_dispatcher, SubBruteCustomEventTypePopupClosed);
}
const char* subbrute_get_menu_name(SubBruteAttacks index) {
furi_assert(index < SubBruteAttackTotalCount);
return subbrute_menu_names[index];
}
const char* subbrute_get_small_menu_name(SubBruteAttacks index) {
furi_assert(index < SubBruteAttackTotalCount);
return subbrute_menu_names_small[index];
}
// ENTRYPOINT
int32_t subbrute_start(void* p) {
int32_t subbrute_app(void* p) {
UNUSED(p);
// Input
FURI_LOG_I(TAG, "Initializing input");
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(SubBruteEvent));
SubBruteState* subbrute_state = subbrute_alloc();
ValueMutex subbrute_state_mutex;
// Mutex
FURI_LOG_I(TAG, "Initializing flipfrid mutex");
if(!init_mutex(&subbrute_state_mutex, subbrute_state, sizeof(SubBruteState))) {
FURI_LOG_E(TAG, "cannot create mutex\r\n");
furi_message_queue_free(event_queue);
subbrute_free(subbrute_state);
return 255;
}
SubBruteState* instance = subbrute_alloc();
view_dispatcher_attach_to_gui(
instance->view_dispatcher, instance->gui, ViewDispatcherTypeFullscreen);
scene_manager_next_scene(instance->scene_manager, SubBruteSceneStart);
furi_hal_power_suppress_charge_enter();
// Configure view port
FURI_LOG_I(TAG, "Initializing viewport");
ViewPort* view_port = view_port_alloc();
view_port_draw_callback_set(view_port, draw_callback, &subbrute_state_mutex);
view_port_input_callback_set(view_port, input_callback, event_queue);
// Configure timer
FURI_LOG_I(TAG, "Initializing timer");
FuriTimer* timer = furi_timer_alloc(timer_callback, FuriTimerTypePeriodic, event_queue);
furi_timer_start(timer, furi_kernel_get_tick_frequency() / 10); // 10 times per second
// Register view port in GUI
FURI_LOG_I(TAG, "Initializing gui");
subbrute_state->gui = furi_record_open(RECORD_GUI);
gui_add_view_port(subbrute_state->gui, view_port, GuiLayerFullscreen);
view_dispatcher_attach_to_gui(
subbrute_state->view_dispatcher, subbrute_state->gui, ViewDispatcherTypeFullscreen);
subbrute_state->current_scene = SceneEntryPoint;
// Init values
SubBruteEvent event;
while(subbrute_state->is_running) {
// Get next event
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 25);
if(event_status == FuriStatusOk) {
if(event.evt_type == EventTypeKey) {
//Handle event key
FURI_LOG_D(TAG, "EVENT ###");
switch(subbrute_state->current_scene) {
case SceneSelectFile:
subbrute_scene_load_file_on_event(event, subbrute_state);
break;
case SceneSelectField:
subbrute_scene_select_field_on_event(event, subbrute_state);
break;
case SceneSaveName:
subbrute_scene_save_name_on_event(event, subbrute_state);
break;
case SceneAttack:
subbrute_scene_run_attack_on_event(event, subbrute_state);
break;
case NoneScene:
case SceneEntryPoint:
subbrute_scene_entrypoint_on_event(event, subbrute_state);
break;
}
} else if(event.evt_type == EventTypeTick) {
//Handle event tick
if(subbrute_state->current_scene != subbrute_state->previous_scene) {
// Trigger Exit Scene
switch(subbrute_state->previous_scene) {
case SceneSelectFile:
subbrute_scene_load_file_on_exit(subbrute_state);
break;
case SceneSelectField:
subbrute_scene_select_field_on_exit(subbrute_state);
break;
case SceneAttack:
subbrute_scene_run_attack_on_exit(subbrute_state);
break;
case SceneEntryPoint:
subbrute_scene_entrypoint_on_exit(subbrute_state);
break;
case SceneSaveName:
subbrute_scene_save_name_on_exit(subbrute_state);
break;
case NoneScene:
break;
}
// Trigger Entry Scene
switch(subbrute_state->current_scene) {
case NoneScene:
case SceneSelectFile:
subbrute_scene_load_file_on_enter(subbrute_state);
break;
case SceneSelectField:
subbrute_scene_select_field_on_enter(subbrute_state);
break;
case SceneAttack:
subbrute_scene_run_attack_on_enter(subbrute_state);
break;
case SceneSaveName:
subbrute_scene_save_name_on_enter(subbrute_state);
break;
case SceneEntryPoint:
subbrute_scene_entrypoint_on_enter(subbrute_state);
break;
}
subbrute_state->previous_scene = subbrute_state->current_scene;
}
// Trigger Tick Scene
switch(subbrute_state->current_scene) {
case NoneScene:
case SceneSelectFile:
subbrute_scene_load_file_on_tick(subbrute_state);
break;
case SceneSelectField:
subbrute_scene_select_field_on_tick(subbrute_state);
break;
case SceneAttack:
//subbrute_scene_run_attack_on_tick(subbrute_state);
break;
case SceneEntryPoint:
subbrute_scene_entrypoint_on_tick(subbrute_state);
break;
case SceneSaveName:
subbrute_scene_save_name_on_tick(subbrute_state);
break;
}
view_port_update(view_port);
}
}
}
// Cleanup
furi_timer_stop(timer);
furi_timer_free(timer);
view_dispatcher_run(instance->view_dispatcher);
furi_hal_power_suppress_charge_exit();
FURI_LOG_I(TAG, "Cleaning up");
gui_remove_view_port(subbrute_state->gui, view_port);
view_port_free(view_port);
furi_message_queue_free(event_queue);
furi_record_close(RECORD_GUI);
subbrute_free(subbrute_state);
subbrute_free(instance);
return 0;
}

View File

@@ -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;

View 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;

View 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;
}

View 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);

View 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);

View File

@@ -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;
}

View File

@@ -1,4 +0,0 @@
#pragma once
#include "subbrute.h"
bool subbrute_is_frequency_allowed(SubBruteState* context);

View 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");
}
}

View 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);

View 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;
}

View 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);

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -318,15 +318,17 @@ 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 == 0 && char_is_lowercase(selected)) {
selected = char_to_uppercase(selected);
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->text_buffer[text_length] = selected;
model->text_buffer[text_length + 1] = 0;
}
model->clear_default_text = false;
}

View File

@@ -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

View File

@@ -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] = {

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -1,6 +1,6 @@
Filetype: IR library file
Version: 1
# Last Updated 17th Sept, 2022
# Last Updated 20th Sept, 2022
#
# ON
name: POWER
@@ -1227,3 +1227,9 @@ frequency: 38000
duty_cycle: 0.330000
data: 3120 1593 488 1180 489 1177 492 342 492 342 492 367 467 1174 485 349 485 349 485 1181 488 1179 490 343 491 1177 492 341 493 366 458 1183 486 1181 488 346 488 1179 490 1177 492 342 492 341 493 1174 485 349 485 347 487 1180 489 345 489 344 490 370 464 368 466 341 493 341 483 376 458 375 459 348 486 374 460 372 462 345 489 370 464 369 465 368 466 341 493 367 457 348 486 374 460 348 486 1179 490 343 491 343 491 1176 493 1174 485 349 485 349 485 374 460 373 461 373 461 346 488 371 463 371 463 369 465 1175 484 350 484 350 484 348 486 374 460 346 488 372 462 345 489 371 463 370 464 368 466 340 484 376 458 376 458 375 459 348 486 347 487 345 489 370 464 370 464 369 465 342 492 368 466 367 457 375 459 348 486 374 460 347 487 372 462 345 489 371 463 343 491 368 466 341 493 367 457 376 458 375 459 1181 488 346 488 345 489 1178 491 342 492 342 492 1175 494 1173 486 1182 487 346 488 346 488 1179 490 343 491 342 492 368 466 367 457
#
name: SWING
type: parsed
protocol: NEC
address: 80 00 00 00
command: 92 00 00 00
#

View File

@@ -1,6 +1,24 @@
Filetype: IR library file
Version: 1
# Last Updated 17th Sept, 2022
# Last Updated 25th Sept, 2022
#
name: POWER
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 9150 4435 643 1608 643 468 644 469 642 364 749 468 643 447 665 449 663 469 643 452 660 470 642 450 662 442 670 449 662 469 643 1579 672 1608 642 1580 671 1609 641 1607 643 1578 672 1607 643 1608 642 1606 644 1606 644 1606 644 1607 643 1576 675 1579 671 1605 674 438 645 466 673 438 646 466 674 437 673 439 672 439 673 438 646 1604 673 1577 673 1578 673 1577 674 1577 673 23799 9095 4485 616
#
name: VOL+
type: parsed
protocol: NEC42
address: 01 00 00 00
command: 0C 00 00 00
#
name: VOL-
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 9151 4434 644 1608 643 376 737 379 733 446 666 449 663 468 644 469 643 468 644 468 644 468 644 447 665 448 664 468 644 450 662 1608 643 1607 644 1576 676 1607 644 1608 643 1578 674 1608 643 1577 674 1579 672 1607 643 1608 643 1607 644 1607 644 1608 643 448 664 1608 643 448 664 468 644 469 643 380 732 468 644 469 643 1607 644 468 644 1608 643 1608 644 1609 643 1608 643 23837 9152 4434 642
#
name: POWER
type: raw
@@ -1430,3 +1448,45 @@ protocol: NEC
address: 4D 00 00 00
command: 00 00 00 00
#
name: VOL+
type: parsed
protocol: NEC
address: 00 00 00 00
command: 19 00 00 00
#
name: VOL-
type: parsed
protocol: NEC
address: 00 00 00 00
command: 16 00 00 00
#
name: MUTE
type: parsed
protocol: NEC
address: 00 00 00 00
command: 44 00 00 00
#
name: VOL+
type: parsed
protocol: NECext
address: 12 36 00 00
command: 0A F5 00 00
#
name: VOL-
type: parsed
protocol: NECext
address: 12 36 00 00
command: 0B F4 00 00
#
name: MUTE
type: parsed
protocol: NECext
address: 12 36 00 00
command: 09 F6 00 00
#
name: POWER
type: parsed
protocol: NECext
address: 12 36 00 00
command: 01 FE 00 00
#

View File

@@ -1,6 +1,6 @@
Filetype: IR library file
Version: 1
# Last Updated 18th Sept, 2022
# Last Updated 22th Sept, 2022
#
# TIMER UP
name: TIMER
@@ -956,3 +956,27 @@ protocol: NEC
address: 80 00 00 00
command: 03 00 00 00
#
name: POWER
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1397 357 1370 357 500 1188 1427 331 1343 383 473 1240 476 1240 476 1240 475 1240 475 1240 475 1240 1370 7358 1367 361 1366 361 496 1220 1366 361 1366 361 496 1220 495 1220 495 1220 495 1221 494 1220 495 1220 1366 7361 1365 361 1366 361 495 1221 1365 362 1365 362 494 1221 494 1221 494 1220 495 1220 495 1221 494 1220 1365 7361 1364 361 1366 362 494 1221 1365 362 1365 362 494 1221 494 1221 494 1221 494 1221 494 1221 494 1221 1365 7361 1364 362 1364 362 494 1221 1364 362 1365 362 495 1221 494 1221 494 1221 494 1221 494 1221 494 1221 1364 7361 1364 363 1364 363 493 1222 1364 363 1363 363 493 1223 492 1223 492 1223 492 1247 468 1223 492 1247 1339 7386 1338 388 1338 388 468 1247 1339 388 1338 388 468 1248 467 1247 468 1247 468 1247 468 1248 467 1247 1338 7387 1337 389 1338 389 467 1248 1338 389 1337 389 467 1248 467 1248 467 1248 467 1248 467 1248 467 1248 1337 7388 1336 389 1337 389 467 1249 1336 390 1337 390 466 1249 466 1249 466 1249 466 1249 466 1249 466 1248 1337 7388 1312 414 1312 414 465 1251 1335 391 1337 390 441 1274 441 1274 465 1249 464 1250 442 1274 441 1273 1337 7388 1311 414 1312 415 441 1274 1311 415 1311 415 441 1274 441 1274 441 1274 441 1274 441 1274 441 1274 1311 7413 1311 415 1312 415 441 1274 1311 415 1311 416 440 1275 440 1275 440 1275 440 1275 440 1275 439 1275 1310 7414 1309 416 1310 417 439 1276 1310 417 1309 417 438 1277 438 1277 438 1277 438 1301 413 1301 414 1301 1285 7439 1284 442 1284 442 414 1301 1284 443 1283 443 413 1302 413 1302 413 1302 413 1302 412 1302 413 1302 1283 7441 1283 443 1284 443 412 1303 1283 444 1282 444 412 1303 411 1303 412 1303 412 1303 412 1303 412 1303 1283 7441 1282 445 1281 445 411 1304 1282 470 1256 471 385 1330 385 1330 385 1330 385 1330 385 1330 385 1330 1256 7468 1255 471 1256 471 384 1331 1255 471 1255 472 383 1331 384 1331 383 1332 383 1332 383 1331 383 1332 1254 7470 1253 498 1228 499 356 1358 1228 499 1227 499 356 1359 356 1359 356 1359 356 1359 355 1360 355 1359 1227 7497 1226 526 1200 527 327 1387 1200 527 1199 554 300 1414 301 1415 299 1415 299 1415 300 1416 299 1415 1172 7553 1170 609 1117 583 270 1471 1117 637 1089 692 118 10334 871
# Osc
name: ROTATE
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1395 358 1369 358 499 1215 1399 331 1341 384 472 1241 474 1240 475 1240 1345 383 473 1240 474 1240 474 8239 1366 387 1339 387 469 1246 1339 388 1339 387 469 1246 469 1246 469 1246 1339 388 469 1246 469 1246 469 8242 1339 387 1339 387 469 1246 1339 387 1339 387 469 1246 469 1246 469 1246 1339 387 469 1246 469 1246 468 8243 1338 388 1338 388 469 1246 1339 388 1338 388 468 1246 468 1246 469 1247 1338 388 468 1246 468 1247 468 8243 1338 388 1338 388 468 1246 1338 388 1338 388 468 1246 469 1246 468 1246 1339 387 469 1246 468 1246 468 8218 1363 363 1363 363 493 1222 1363 363 1363 363 493 1222 493 1222 492 1222 1363 363 493 1221 493 1222 493 8217 1363 363 1363 363 493 1222 1363 363 1363 363 493 1246 468 1223 492 1223 1361 375 481 1247 467 1247 467 8243 1337 388 1338 388 468 1247 1337 389 1337 388 468 1247 468 1247 468 1247 1337 389 467 1248 466 1248 466 8243 1337 389 1337 389 467 1248 1336 389 1337 390 466 1248 466 1248 466 1248 1336 390 466 1248 466 1248 466 8244 1336 390 1311 415 465 1249 1336 390 1336 391 465 1250 465 1249 465 1250 1310 415 465 1250 464 1250 439 8270 1310 416 1310 416 440 1275 1310 417 1309 417 438 1300 414 1301 413 1301 1284 442 414 1301 413 1301 413 8297 1284 442 1284 442 413 1301 1284 443 1283 443 413 1302 412 1302 412 1302 1282 443 413 1302 412 1302 412 8298 1282 443 1283 443 413 1302 1283 443 1283 444 412 1303 411 1303 411 1303 1282 444 412 1303 411 1303 411 8299 1280 445 1281 470 385 1329 1255 470 1256 471 384 1330 384 1329 385 1329 1255 471 385 1330 384 1330 384 8326 1253 472 1254 472 384 1331 1253 473 1253 499 356 1357 357 1358 356 1358 1226 499 356 1358 356 1359 355 8355 1224 502 1224 501 355 1385 1199 527 1199 527 328 1386 328 1386 328 1386 1199 527 328 1387 327 1387 327 8409 1171 554 1172 555 300 1414 1172 555 1171 555 300 1416 299 1416 298 1415 1171 556 298 1442 272 1442 272 8438 1143 583 1143 583 271 1471 1115 611 1115 664 179 1536 178 1563 122 1619 1006
#
name: SPEED+
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1395 359 1367 358 499 1213 1368 359 1366 359 473 1239 474 1239 474 1238 475 1238 1368 360 497 1215 497 8209 1362 364 1361 366 492 1221 1360 366 1359 366 491 1222 492 1222 491 1221 493 1245 1336 366 492 1222 492 8213 1360 366 1359 389 468 1222 1359 367 1358 390 467 1246 468 1246 468 1245 468 1246 1335 390 467 1246 467 8238 1335 390 1335 390 468 1246 1335 390 1335 390 467 1246 467 1246 468 1246 467 1246 1335 390 467 1246 467 8238 1334 390 1335 390 467 1247 1334 390 1335 391 466 1247 466 1247 466 1247 466 1247 1334 390 467 1247 466 8239 1334 391 1334 391 466 1247 1334 391 1334 391 467 1247 466 1247 466 1247 466 1248 1333 391 466 1248 465 8239 1333 392 1333 392 466 1248 1333 392 1333 392 465 1248 465 1248 465 1248 465 1248 1333 392 465 1248 465 8240 1332 392 1333 392 465 1248 1333 393 1332 393 464 1249 464 1249 464 1249 464 1249 1331 393 465 1249 464 8241 1331 393 1332 393 464 1250 1331 394 1331 394 463 1250 463 1251 462 1250 463 1251 1329 396 462 1251 462 8243 1305 420 1305 444 436 1277 1280 444 1281 445 435 1277 412 1301 436 1277 435 1277 1280 445 436 1277 436 8268 1280 445 1279 445 412 1301 1280 445 1280 445 411 1302 411 1302 411 1302 411 1302 1279 446 411 1302 411 8293 1278 446 1279 446 411 1303 1278 447 1277 447 410 1303 410 1304 409 1329 384 1329 1252 472 385 1329 384 8320 1252 473 1251 473 383 1330 1251 473 1252 473 384 1330 383 1330 383 1330 383 1330 1251 474 382 1330 383 8321 1250 474 1251 475 381 1331 1250 500 1224 500 356 1358 355 1358 355 1358 355 1358 1223 501 355 1358 355 8349 1223 502 1222 528 327 1386 1196 528 1196 530 326 1386 327 1387 326 1386 327 1413 1169 556 299 1414 299 8404 1169 556 1168 584 271 1441 1141 611 1113 637 216 1498 215 1524 178 1562 122 1564 1058 11171 970
#
name: TIMER
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 1366 359 1366 360 496 1214 1367 360 1395 331 524 1185 474 1239 474 1238 475 1238 475 1238 1343 383 498 8205 1366 386 1338 386 470 1244 1338 386 1339 386 470 1243 470 1244 469 1244 469 1243 470 1244 1338 386 470 8234 1338 386 1339 386 469 1244 1338 386 1339 386 470 1243 470 1220 493 1219 494 1243 470 1244 1338 362 494 8233 1339 362 1363 386 469 1244 1338 386 1339 386 469 1244 469 1244 469 1244 469 1244 469 1244 1338 387 468 8234 1338 386 1338 387 468 1244 1338 387 1337 387 468 1244 469 1244 468 1245 469 1244 469 1244 1338 387 468 8234 1337 387 1338 387 465 1247 1338 387 1338 387 468 1245 468 1244 444 1269 468 1246 467 1245 1337 387 468 8235 1337 387 1337 388 468 1245 1336 388 1336 388 466 1246 467 1245 468 1245 467 1247 467 1245 1312 412 467 8235 1312 412 1312 412 443 1270 1335 390 1311 412 468 1246 466 1246 467 1246 467 1246 467 1246 1311 412 467 8236 1311 413 1311 413 442 1271 1311 413 1311 413 442 1271 466 1247 466 1246 442 1271 442 1271 1311 413 442 8260 1311 413 1311 413 442 1271 1311 413 1311 414 441 1271 442 1271 465 1248 464 1249 465 1247 1310 414 441 8261 1334 390 1310 414 466 1247 1335 390 1333 391 465 1248 465 1248 465 1248 465 1248 465 1248 1334 390 465 8237 1334 391 1333 390 465 1248 1309 415 1309 416 464 1249 464 1249 463 1250 462 1275 438 1274 1307 394 438 8287 1283 441 1283 441 438 1274 1283 441 1283 442 436 1276 414 1299 438 1275 413 1300 412 1300 1282 442 413 8289 1282 443 1281 443 412 1301 1281 443 1281 443 412 1301 411 1302 411 1302 410 1327 385 1327 1255 469 386 8316 1255 470 1254 470 385 1327 1255 470 1254 470 385 1327 385 1328 384 1328 385 1328 385 1328 1254 470 385 8317 1253 471 1253 470 385 1329 1253 471 1253 471 384 1329 383 1330 382 1330 382 1330 383 1330 1252 473 382 8344 1226 498 1226 498 357 1356 1226 498 1226 498 356 1356 356 1356 356 1356 356 1356 356 1356 1226 499 355 8346 1224 499 1225 525 329 1384 1198 526 1198 525 329 1384 328 1384 328 1384 328 1384 329 1384 1198 526 328 8373 1198 526 1198 527 327 1385 1197 553 1171 553 301 1412 300 1412 300 1412 300 1386 327 1412 1170 554 299 8401 1170 554 1170 555 298 1414 1169 581 1143 582 271 1440 272 1440 272 1440 272 1441 271 1441 1142 609 244 8458 1113 636 1088 663 178 1507 1088 691 1033
#

View File

@@ -1,6 +1,36 @@
Filetype: IR library file
Version: 1
# Last Updated 18th Sept, 2022
# Last Updated 25th Sept, 2022
#
name: POWER
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 3398 1660 464 419 435 1253 434 423 441 417 436 420 434 424 440 417 437 421 433 424 440 417 437 421 433 425 439 418 436 1252 435 422 432 427 437 420 434 424 440 417 436 421 433 424 440 417 437 421 433 1255 432 426 438 419 435 423 431 426 438 420 434 423 431 427 437 421 433 1228 459 425 439 1249 438 1250 437 1225 462 1226 461 422 432 426 438 1224 463 421 433 1229 458 1230 457 1257 440 1221 466 392 462 1224 463 72803 3397 1661 463 421 433 1255 432 426 438 419 435 423 431 427 437 420 434 424 440 417 437 421 433 398 466 418 436 422 432 1256 431 427 437 421 433 424 440 418 436 422 432 425 439 419 435 423 431 426 438 1250 437 421 433 425 439 418 436 422 432 426 438 419 435 423 431 428 436 1252 435 422 432 1257 440 1248 439 1249 438 1224 463 395 458 425 439 1249 438 419 435 1227 460 1255 432 1256 431 1231 466 417 437 1249 438 72803 3398 1687 437 420 434 1255 432 425 439 419 434 397 457 427 437 420 434 424 440 417 436 421 432 425 439 418 436 422 432 1257 440 417 437 422 431 426 438 393 461 423 431 427 437 420 434 424 440 417 436 1225 462 422 432 426 438 419 435 423 431 426 438 420 434 424 440 418 436 1252 435 422 432 1257 440 1247 440 1248 439 1223 464 420 434 424 440 1222 465 418 435 1253 434 1254 433 1229 458 1256 431 427 437 1248 439 72798 3403 1682 432 426 438 1250 437 420 434 424 440 417 436 421 433 425 439 418 436 422 432 425 439 419 435 422 432 426 438 1250 437 420 434 425 439 418 436 422 432 425 439 419 435 422 432 426 438 419 434 1254 433 424 440 417 437 421 432 425 439 418 436 422 432 426 438 420 434 1254 433 425 439 1222 465 1223 464 1224 463 1252 435 422 432 426 438 1250 437 421 433 1255 432 1256 431 1257 440 1221 466 419 435 1250 437 72798 3402 1683 431 426 438 1250 437 421 433 425 439 418 436 422 432 425 439 419 435 423 431 426 438 420 434 423 431 427 437 1251 436 421 433 426 438 419 435 423 431 427 437 420 434 424 440 417 436 421 433 1255 432 425 439 419 435 422 432 426 438 419 435 423 431 427 437 421 433 1255 432 426 438 1250 437 1251 436 1252 435 1254 433 424 440 418 436 1252 435 423 431 1257 440 1247 440 1249 438 1250 437 421 433 1226 461
#
name: VOL+
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 3405 1655 458 425 439 1248 438 420 433 424 440 417 436 421 433 425 439 418 435 422 431 426 438 420 433 423 441 417 436 1251 435 422 431 427 437 420 434 424 440 418 435 421 432 425 439 419 434 422 431 1257 440 417 436 421 433 424 440 417 436 421 432 425 439 418 435 423 430 426 438 420 434 424 440 417 436 421 433 1255 432 425 439 418 435 422 431 426 438 419 435 423 430 426 438 1250 437 421 432 1252 435 79433 3405 1680 434 424 440 1221 466 418 435 422 431 426 438 419 434 423 430 427 437 420 434 423 441 417 436 421 433 424 440 1248 439 419 434 423 431 427 437 420 433 424 440 417 436 421 432 424 440 418 435 1251 436 422 431 426 438 419 434 423 430 427 437 420 434 424 440 418 435 421 432 425 439 418 435 422 431 426 438 1223 464 420 434 424 440 417 436 420 434 424 440 417 436 421 432 1255 432 426 438 1247 440 79430 3397 1687 437 420 434 1254 433 425 439 419 434 422 432 426 438 420 433 423 441 417 436 420 433 424 440 417 436 421 432 1256 431 426 438 420 434 424 440 417 436 421 432 425 439 418 435 422 431 425 439 1249 438 420 433 424 440 417 436 421 433 425 439 418 435 422 432 426 438 419 434 423 441 417 436 420 433 424 440 1248 439 418 435 422 431 426 438 419 434 423 430 427 437 420 433 1254 433 425 439 1245 431 79441 3397 1687 437 420 433 1254 433 424 440 418 435 421 432 425 439 418 435 422 431 426 438 419 434 423 431 427 437 420 433 1254 433 425 439 419 434 422 431 426 438 419 434 423 430 427 437 420 433 424 440 1221 466 418 435 422 431 426 438 419 434 423 441 417 436 420 433 425 439 418 436 422 431 426 438 419 434 423 431 1257 440 417 436 421 432 425 439 418 435 422 431 426 438 419 434 1253 434 424 440 1245 431 79442 3395 1688 436 421 433 1255 432 426 438 419 434 423 430 427 437 420 433 424 440 417 436 420 434 424 440 417 436 421 432 1255 432 426 438 420 434 423 441 417 436 420 433 424 440 418 435 421 432 425 439 1249 438 419 435 423 431 427 437 420 434 424 440 417 436 421 433 425 439 418 435 422 432 426 438 419 435 423 430 1256 441 417 436 420 433 424 440 417 436 421 432 425 439 418 435 1253 434 423 441 1245 431 79441 3397 1687 437 421 432 1255 432 425 439 418 435 422 431 426 438 419 434 423 430 427 437 420 434 424 440 417 436 421 432 1255 432 425 439 419 434 423 431 426 438 420 434 423 441 417 436 420 434 424 440 1247 440 418 435 422 432 425 439 419 435 423 431 426 438 419 434 424 440 417 436 421 433 425 439 418 435 422 431 1256 431 426 438 420 433 423 441 417 436 420 434 424 440 417 436 1225 462 421 432 1253 434 79429 3399 1684 440 417 436 1225 462 421 432 425 439 418 435 422 431 425 439 418 435 422 431 426 438 419 435 422 432 426 438 1250 437 420 433 425 439 418 435 421 433 425 439 418 435 421 433 425 439 418 435 1252 435 423 431 426 438 419 434 423 431 427 437 420 434 424 440 417 436 421 432 425 439 418 436 421 432 425 439 1249 438 419 434 423 431 427 437 420 434 424 440 417 436 421 432 1255 432 425 439 1246 441
#
name: VOL-
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 3396 1688 436 421 432 1256 431 426 438 420 433 424 440 417 436 421 432 424 440 418 435 422 431 425 439 419 434 422 431 1257 440 417 436 421 432 425 439 418 435 422 431 426 438 419 434 423 430 427 437 1250 436 421 432 425 439 419 434 423 430 426 438 420 434 424 440 418 435 1252 435 423 431 427 437 420 433 424 440 1247 440 418 435 422 431 1256 431 427 437 420 433 424 440 417 436 1252 434 423 430 1254 432 77786 3404 1681 433 425 439 1248 438 419 434 423 430 427 437 420 433 424 440 417 436 421 432 424 440 417 436 421 433 425 439 1248 438 419 434 424 440 417 436 421 432 425 439 418 435 422 431 426 438 420 433 1254 433 424 440 418 435 422 431 426 438 419 434 423 430 426 438 421 432 1254 432 425 439 419 434 423 430 426 438 1250 436 421 432 425 439 1248 438 419 434 423 441 417 436 421 432 1255 432 426 438 1246 441 77777 3402 1682 432 426 438 1250 436 420 433 424 440 417 436 421 432 425 439 419 434 422 431 426 438 419 434 423 441 417 436 1251 436 422 431 426 438 419 434 423 441 417 436 420 433 424 440 417 436 421 432 1255 432 426 438 420 433 423 441 417 436 421 432 424 440 418 435 422 431 1256 441 417 436 421 432 424 440 418 435 1252 435 422 431 426 438 1250 437 421 432 424 440 418 435 421 432 1256 430 426 438 1247 440
#
name: CH+
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 3401 1683 431 426 438 1249 438 420 434 424 440 417 436 421 433 424 440 417 436 421 432 425 439 418 435 421 432 425 439 1248 439 418 435 423 431 426 438 420 434 423 431 427 437 420 434 423 431 427 437 1250 437 421 433 424 440 418 436 421 432 425 439 418 435 421 433 426 438 419 435 423 430 1256 431 427 437 1250 437 1224 463 422 432 425 439 419 435 422 431 1256 431 426 438 1249 438 1223 464 420 434 1251 436 76104 3396 1688 436 421 433 1255 432 425 439 418 435 422 432 425 439 419 435 422 431 426 438 419 434 422 432 426 438 419 434 1253 434 424 440 418 435 422 432 425 439 418 435 422 431 426 438 419 434 423 431 1257 440 417 436 421 433 424 440 417 437 421 433 424 440 417 436 422 432 425 439 418 435 1252 435 423 431 1256 441 1247 440 417 436 421 432 425 439 418 435 1252 435 422 432 1256 431 1256 441 417 437 1247 440
#
name: CH-
type: raw
frequency: 38000
duty_cycle: 0.330000
data: 3404 1652 462 422 432 1230 457 427 437 420 434 424 440 417 436 420 434 424 440 417 436 421 433 424 440 417 436 421 432 1228 459 425 439 419 434 422 432 426 438 419 434 422 431 426 438 419 435 422 431 1230 457 426 438 420 433 423 431 426 438 420 433 423 430 427 437 420 434 1228 459 424 440 1221 466 418 435 1225 462 1226 461 422 432 426 438 1224 463 420 433 1228 459 399 465 1222 465 1223 464 420 433 1225 462
#
name: POWER
type: parsed

View File

@@ -1,48 +1,51 @@
Filetype: Flipper SubGhz Keystore File
Version: 0
Encryption: 1
IV: F2 D4 F5 5A B3 CC 3F 21 28 3A AF ED D1 EB 73 DF
BBFA4D79A73C384D6E07E717F761F32A625F28AA1DB2261B8B19A18261E30AB6
CE4004AB56111B0B3D486770705FAD8BD616A80957EA2C537BAF1FD09E552DA3
F974561612C9C751237C64D978F706B41873FDBE38851306574F436CB02D9ECA
E29CAB7C2C2D9853D0F4DF69F582562E8182234E78B355540F6FE3F78C73D518
97ABE18993A700A607E37DC88E1434F84DDD1C2771693978C9D2FA4CE4F2AB7BBC7C3EB3E8545B37FBBE1C1F1CA03543
E86ABD0AAE5A4B4A9414C9CB6112CA49B3A8EC29723B14DCA85902F41B05ADDC
C1FBE921035F408C59DA6AD5E76E3887AC9BC90146619B3CAE445BED556E96AC
232C9F86915B927888352797B45F159268FE78956CF09B8D241CDC393D3B0225
3D9E2A3C701C9D4DD4D72038D4536CA6F515C547CAB0AD18BA71204BD2ABFB74
4D69A4506D2C97EF8EC68F90CF1AD1065A1EB909793EEB3AF71B0D75E55B9E76
5A7F4595DFA181C3E946EBEE4974DBD6DA85AF6FCAD0B3725FDD28667175A421D69A2122853E57927C38CCF368732476
6A946FAEDE134155B5A88EC01AA535E7A778947D360218B560381A64CAF9ACE896079D04C14718D5AD5C0D4EE3005F52
88AC0C723AAA875A1885C8392A616FA43B205119B0E8D299193979A1921FC8B3
40588AADA5E1A8BE214B2CCF32D268B48C6B783AE0DD10D88BDF3FF88E921E09
A7BE05D05DEC9B9A3AE1575D411BF7B12366AD78B726F3E3E843E7BF199961A4
79F973A155A4367F0EAA078AA0857A2A2A82FC4C8A5AE9E567E7CBF62C2A5CE2
C38296EEABDA1F95D0C401CC6DDC8656476DC19248588EEF1CB93773D94CDB02A40C902970C4FCB14FABEFFB4F8BC208
B0B7699B3C3573EE4D88D8CE65FAF3532B5A741D1F20892C0F38BAA2BCE98F2D
6E401D6BDB1B33A404DEB668F3FB353166475487BAADE4A348E3CFDEB3B1B54B
0E44B87878617559783CC6A7C65BE9F99950FE8956ED4BB04894BC53085E3A09CA19915B1E8C143A68D1B7A97F5D1ECB
AC19E55638429C65E6E567C0E96DA9648F8FB80215CF693D7FD5DD86FE7989AC7AC7BAE86BBD4FFF7161AFFB405FFA98
BCE70C69D90AD639A737813FC8FD26F40F803137BD36E47651C266A671428D6F
F053CF5255AD2E1875A5C38635F7BF203B1DAE1433B162C30AE8695AC8A5589D
B7EFC77FFA98B173E429B3566A27842C4DC5E91B0BC01F07A6A98332C4E1F42A
D7C7950FFB2C5E7D9BCDBC230BF5F1BFFC0FE6F1CF5C8C6013DD90E41AE403FE
50667B2E5909FD5F9D6385788A81DE5F72E56512EAD6BF5EACCA959CB6AF0DEF
6BDDE1EA185EA975343C161DF358E0AE71007DE13EE8CD26683C28B21470B910
8128188D151CB4D465427768BF811B1AF5345801BF6F5C937A11F0485F90CA46
9FF6331E570479788C2D61916DB48DDE534F8C049056D4E3216ADCB37C9556A6
0636731CB79FC7459BAFA37E704D58765551631F3936892FD244CF989127540A
ECB48EF5C599D4A5F1FD39A62A06EF910B1B4973BFBFDFEF61F00C81E87847BA
AAC73DC804600A04BD962F7C5B9ACA99F3769EED9F2DD7AF8338E7220271090F
3EA4D2329D0BA54E80AF71966BD74CB13C21DF8010EE795BCCCE6A580EC05758
F491DE599DA31EA9A51509B69846C6C1FB6AB5BBE3AA5915D51225F205A55EC2
54347F82D73E4B66569DAAB52B03EB5A6440F9FDFD302454B84B4E41E0E7E55C
28BBAB93FEB24CE27C47B068C065A294758CECBC3531FA691D085D2B10D189616DB074AC6F8D75BBE176A9459347968C
61260BD3C4FCEF8208CC88682032E4FE295F8D000998302AD11D8D643A30EC4F
97309829406F5CF8FA15565F8F6CF53DEA0E5C0B59DD657A2BB1F58FBCF3DF7F
EA622706F837A37D84D806F15E73D6B95658129F9E12170587200BFB8BDBAEEA
67EAFE49F7872D6EBDB4127E5107D9EB147393038DBAC4D6C4916AD7668C463D
65D7D3225DEAE1BA33F1EAB6F14E817C9D47646123E02C9D4C1BEAF46DD9DF547FE9AD432E1CD648165AE10D7D240B88
223158904F64C174A87ED14F1CACB86DA9AD7D0ED32AEB2A7E955715498969FB
9CA01D5797B93547F246CB06EDD96CABECE27E0BAB6D090681B63E83AD4F2167
AD0BF3A0C1ECDF869BF3022AAA0065941F1D354CCE346B47740D2794FD042D47
IV: 2A 34 F1 5A AF 6F F5 1A 83 A6 1E DA DE B7 3D F1
06B63DF24AE073A2F2B19C55CA9E8364FBECD26E49C551990153F6513BDE5267
6139C78C74C341EB7474085CF1D047BD6FB005F80A72AF3EF3F89D58EF5DF500
D85F11689020ECA47FBE9C2B67EE41A81E1F06DE2A35AF958965E3ECE29EA701
1AE9073A42FE0E439544FE6945F6B33CF15A7A4A279020B5E0B3BE33FD189A7E
E161F007854BB33E0056FA09A2E2DEE66789B5C87C8D6D3DE2C8C1BD2B48983EB9D1C5697CA6E95996918F7C47B761B0
59AE4644DCB3D720C38B5115F230DA58E7BE0A697907F6174BB05AB7886ACDB1
634DF0BCC185C4C1F7E1B1594B4438D051ABAE092433078963063B51D961D08C
1EBEBCB49E498B9BE977D53EC21B9A546155B627737BD0AA832D496035729346
4DFA93E639197772D57E8ACE04512CEFC045B8CC965C175A25ED525B630CBB63
C2D5235D1014A319B249EAE8A5EE350F18D5AB8A498EF222704BD4EB1435F388
F66D1937160E1392197F463A52E87FCE938A92070892113443C348D7553327A5715CF615CE2F2C96284F47759E043419
841D29E7CBE040188E2283BFBA9F26EF2F65CCB085B56C3515E8C46C3F20BD75BAA963550869435FDAF509CEEE66A2C4
7D87E24487D307635E7A17B989B8547EE11F3BF3468D055F0B44633B631BA42C
B4916043973501B95A82B329196D6EBA69FBBC3AF8FD914583104E0E18CE82F6
E4649F9C2A5465D2EA6F3E9724DD06CD6962FE2BAEB14F1453C14D1559232AE1
96E15D890DF7FD348441F5E429A875754C6BF0520A787F8E9D8C5415674783CC
CB52005EDED47B57F795BC92FB0522EAB18D23EE028B8D10ED57828C250EB285BFEC6E4A4BE8DABCE0D57ECAA20D90C3
8E5A50C7D5C374445E88752301D20F0B3D6E4988B61D90FD63779B0EDEF9C60D
49D6CB276A0E5FF134A38062503F01351F44CD6455708B50B5F07D03FC477C33
CB45B56613DF208E79E4E10A6510F07DC1AA49210C7B94E8BBAECD2C35EC6ABC99FB10FD7C96DD6BB6A6685E9FAD93FB
0743F3CC51200F763C242F1956B4D775C092ADF1A5C19ACAE96EB60C2990CF214F8FEA8FC6749286F6BDAB67657C479A
E5608B28A058787D64A145F0362DEFD98CAE0B5A0F22C6DA7C6D278C7B5F95E3
D4C113D43E7FB6D2EFA9E87471AA76A61B26872607B4AF5B87F9D72113835CE6
2DC502800BFD21B76126390CA64A08C5432A2254E822F214CDE1EA11430084C5
CA22C73010B0F1CB8009601BE2AF0B3674D83D5880E4A26C2A3FF0EA0A098CEA
E53B2B102FDB000E9BB747F957156976E5A0C0E3898AA844C13AE8A9CEE7013B
EFB27324B5661419265804ABD130C13DC9DF9CD4D2AC2011CB4FD43D56304AD1
491D75A82ACE8CE216FBE4F0D2D0133BAC7EA8F4A5304337D5E8611AD9C72523
BBFA2B00827E0BCC8AC5CE12C972BB58DFA2EA59DFEFF5A538398FCF970F58A7
6BAF9C855926B683BEDC11883543B2C0E0866FF6B06C46CC09B3C4E1200E7716
B35A4D25FF4D5CFF93B9C4C07B78FCF7E2646138DAB7C090A938C8055CCCE3D8
71CC3C0180771E6B304CE984F5A9962EB35D1965CB78EBAE6F4DDFA44E5E02A2
3DCF52C549FEFCCE2831DB74579AA2C157A4BEE70C43905664C9A6491A171F5D
F7E45AF200F7663DCCE54C14118D2CDFD1228ED0BFF7F70CFBCE15B6F9DF3D40
C44E048AD5C003E68DCF0111317D109CF2B7DD79299692CDA7DA2A12EC9A295A
2B3E6778A97B251A5FEB190991B8AE8EC48F5FC6E94C2ECB8DFADFDD9D8E21FEE5DCCEDD9A1EB8C5212DEAE36FBBDF92
1DD4756E681528CBBDBA6C7BF8833DF556D41E5EA4E4DE52CAD3544C946CD8CA
F903D388CF2016B40B492494F7475E71C50E9FDD63304558212DAF6FBC4E1BA4
9E9F24951DB27917668CD6366795052306022F6F8BA11B08A970691C6857E6C5
C88461104FA0EAA68001B0C2D0483D1A53D6AE04F4CB291C76CEF3A1A5C5DC10
8345B2B9F08B018FEAC2F74D76747FF30DBF426C9B390ACD42AD48104C12042E
087269DC66C76E1D6449831A2C3B6F0006C0F8B1861062B95708C6B222C58A9C
4D31DE05DB12E552D43B1017C68AF3F52B6FE8063E4AD82CB568CDAF22950BCC26FF21EE968FB57650111B617A84DEF7
769BAA780F84797431B6E9DD5180AF3848E03C942C67040B39CC6384E2A8F4C6
1844AAAAD6A6AFB2623DF1452DF940715E6EB92F54C49F408872EC4B2156DDB2
23EB39CD7F3921081199BA8B63D4C19F74365F8D8E71D486576DC900E5EFEEA3
45958B93A16A73CA2578ADCFB1EADAA983BB7015321B0592B67CDF573B084194

View File

@@ -4,7 +4,7 @@
# for adding manufacture keys
# AABBCCDDEEFFAABB:X:NAME\r\n
# AABBCCDDEEFFAABB - man 64 bit
# X - encryption method 1 - Simple Learning, 2 - Normal_Learning, 3 - Secure_Learning, 4 - Magic_xor_type1 Learning
# X - encryption method 1 - Simple Learning, 2 - Normal_Learning, 3 - Secure_Learning, 4 - Magic_xor_type1 Learning, 5 - FAAC, 6 - Magic Serial typ1
# 0 - iterates over both previous and man in direct and reverse byte sequence
# NAME - name (string without spaces) max 64 characters long
Filetype: Flipper SubGhz Keystore File

View File

@@ -64,7 +64,7 @@ class AppState:
def is_loaded_in_gdb(self, gdb_app) -> bool:
# Avoid constructing full app wrapper for comparison
return self.entry_address == int(gdb_app["entry"])
return self.entry_address == int(gdb_app["state"]["entry"])
@staticmethod
def parse_debug_link_data(section_data: bytes) -> Tuple[str, int]:
@@ -78,13 +78,13 @@ class AppState:
@staticmethod
def from_gdb(gdb_app: "AppState") -> "AppState":
state = AppState(str(gdb_app["manifest"]["name"].string()))
state.entry_address = int(gdb_app["entry"])
state.entry_address = int(gdb_app["state"]["entry"])
app_state = gdb_app["state"]
if debug_link_size := int(app_state["debug_link_size"]):
if debug_link_size := int(app_state["debug_link_info"]["debug_link_size"]):
debug_link_data = (
gdb.selected_inferior()
.read_memory(int(app_state["debug_link"]), debug_link_size)
.read_memory(int(app_state["debug_link_info"]["debug_link"]), debug_link_size)
.tobytes()
)
state.debug_link_elf, state.debug_link_crc = AppState.parse_debug_link_data(

View File

@@ -101,3 +101,7 @@ $_TARGETNAME configure -event trace-config {
# assignment
mmw 0xE0042004 0x00000020 0
}
$_TARGETNAME configure -event gdb-detach {
resume
}

View File

@@ -40,6 +40,26 @@ after that on web updater page - press `Connect` button
- And if all flashed successfully - you will have all needed assets pre installed
- Done
![ios](https://user-images.githubusercontent.com/10697207/192114863-75693972-31fb-4b5f-bcc4-4122abb352c2.jpg)
<br>
<br>
## With qFlipper (1.2.0)
- Download qFlipper that allows `.tgz` installation [Download qFlipper 1.2.0 (official link)](https://update.flipperzero.one/builds/qFlipper/1.2.0/)
- Be sure you updated to latest official release before(only if installing for the first time), and verify that microSD card is installed
- Open latest release page - [Releases](https://github.com/Eng1n33r/flipperzero-firmware/releases/latest)
- Download `flipper-z-f7-update-(version).tgz`
- Launch qFlipper
- Connect your device and select `Install from file`
- Select `flipper-z-f7-update-(version).tgz` that you downloaded
- Update will start
- And wait, if all flashed successfully - you will have all needed assets pre installed
- Done
![qflip](https://user-images.githubusercontent.com/10697207/192114874-4edae5f5-6bff-4674-8e3b-030ceaf17abc.png)
<br>
<br>
@@ -57,8 +77,7 @@ after that on web updater page - press `Connect` button
- Update will start, wait for all stages
- Done
![manual_install](https://user-images.githubusercontent.com/10697207/190832689-8fb50d97-2820-4501-b8b7-d8e87a235d45.gif)
![manual](https://user-images.githubusercontent.com/10697207/192114890-b9220265-1fe3-4837-8e98-ed267282e11e.png)
<br>
<br>

View File

@@ -49,7 +49,7 @@ To run cleanup (think of `make clean`) for specified targets, add `-c` option.
- `flash` - flash attached device with OpenOCD over ST-Link
- `flash_usb`, `flash_usb_full` - build, upload and install update package to device over USB. See details on `updater_package`, `updater_minpackage`
- `debug` - build and flash firmware, then attach with gdb with firmware's .elf loaded
- `debug_other` - attach gdb without loading any .elf. Allows to manually add external elf files with `add-symbol-file` in gdb
- `debug_other`, `debug_other_blackmagic` - attach gdb without loading any .elf. Allows to manually add external elf files with `add-symbol-file` in gdb
- `updater_debug` - attach gdb with updater's .elf loaded
- `blackmagic` - debug firmware with Blackmagic probe (WiFi dev board)
- `openocd` - just start OpenOCD

View File

@@ -76,19 +76,8 @@ to exclude the API function. */
#define INCLUDE_xTaskGetSchedulerState 1
#define INCLUDE_xTimerPendFunctionCall 1
/* CMSIS-RTOS V2 flags */
#define configUSE_OS2_THREAD_SUSPEND_RESUME 1
#define configUSE_OS2_THREAD_ENUMERATE 1
#define configUSE_OS2_THREAD_FLAGS 1
#define configUSE_OS2_TIMER 1
#define configUSE_OS2_MUTEX 1
// NEVER TO BE USED, because of their hard realtime nature
// #define configUSE_OS2_EVENTFLAGS_FROM_ISR 1
/* CMSIS-RTOS */
/* Furi-specific */
#define configTASK_NOTIFICATION_ARRAY_ENTRIES 2
#define CMSIS_TASK_NOTIFY_INDEX 1
extern __attribute__((__noreturn__)) void furi_thread_catch();
#define configTASK_RETURN_ADDRESS (furi_thread_catch + 2)

View File

@@ -163,29 +163,29 @@ Function,-,LL_ADC_REG_Init,ErrorStatus,"ADC_TypeDef*, LL_ADC_REG_InitTypeDef*"
Function,-,LL_ADC_REG_StructInit,void,LL_ADC_REG_InitTypeDef*
Function,-,LL_ADC_StructInit,void,LL_ADC_InitTypeDef*
Function,-,LL_COMP_DeInit,ErrorStatus,COMP_TypeDef*
Function,-,LL_COMP_Init,ErrorStatus,"COMP_TypeDef*, LL_COMP_InitTypeDef*"
Function,+,LL_COMP_Init,ErrorStatus,"COMP_TypeDef*, LL_COMP_InitTypeDef*"
Function,-,LL_COMP_StructInit,void,LL_COMP_InitTypeDef*
Function,-,LL_CRC_DeInit,ErrorStatus,CRC_TypeDef*
Function,-,LL_CRS_DeInit,ErrorStatus,
Function,-,LL_DMA_DeInit,ErrorStatus,"DMA_TypeDef*, uint32_t"
Function,-,LL_DMA_Init,ErrorStatus,"DMA_TypeDef*, uint32_t, LL_DMA_InitTypeDef*"
Function,+,LL_DMA_DeInit,ErrorStatus,"DMA_TypeDef*, uint32_t"
Function,+,LL_DMA_Init,ErrorStatus,"DMA_TypeDef*, uint32_t, LL_DMA_InitTypeDef*"
Function,-,LL_DMA_StructInit,void,LL_DMA_InitTypeDef*
Function,-,LL_EXTI_DeInit,ErrorStatus,
Function,-,LL_EXTI_Init,ErrorStatus,LL_EXTI_InitTypeDef*
Function,-,LL_EXTI_StructInit,void,LL_EXTI_InitTypeDef*
Function,-,LL_GPIO_DeInit,ErrorStatus,GPIO_TypeDef*
Function,-,LL_GPIO_Init,ErrorStatus,"GPIO_TypeDef*, LL_GPIO_InitTypeDef*"
Function,+,LL_GPIO_Init,ErrorStatus,"GPIO_TypeDef*, LL_GPIO_InitTypeDef*"
Function,-,LL_GPIO_StructInit,void,LL_GPIO_InitTypeDef*
Function,-,LL_I2C_DeInit,ErrorStatus,I2C_TypeDef*
Function,-,LL_I2C_Init,ErrorStatus,"I2C_TypeDef*, LL_I2C_InitTypeDef*"
Function,+,LL_I2C_Init,ErrorStatus,"I2C_TypeDef*, LL_I2C_InitTypeDef*"
Function,-,LL_I2C_StructInit,void,LL_I2C_InitTypeDef*
Function,-,LL_Init1msTick,void,uint32_t
Function,-,LL_LPTIM_DeInit,ErrorStatus,LPTIM_TypeDef*
Function,+,LL_LPTIM_DeInit,ErrorStatus,LPTIM_TypeDef*
Function,-,LL_LPTIM_Disable,void,LPTIM_TypeDef*
Function,-,LL_LPTIM_Init,ErrorStatus,"LPTIM_TypeDef*, LL_LPTIM_InitTypeDef*"
Function,+,LL_LPTIM_Init,ErrorStatus,"LPTIM_TypeDef*, LL_LPTIM_InitTypeDef*"
Function,-,LL_LPTIM_StructInit,void,LL_LPTIM_InitTypeDef*
Function,-,LL_LPUART_DeInit,ErrorStatus,USART_TypeDef*
Function,-,LL_LPUART_Init,ErrorStatus,"USART_TypeDef*, LL_LPUART_InitTypeDef*"
Function,+,LL_LPUART_Init,ErrorStatus,"USART_TypeDef*, LL_LPUART_InitTypeDef*"
Function,-,LL_LPUART_StructInit,void,LL_LPUART_InitTypeDef*
Function,-,LL_PKA_DeInit,ErrorStatus,PKA_TypeDef*
Function,-,LL_PKA_Init,ErrorStatus,"PKA_TypeDef*, LL_PKA_InitTypeDef*"
@@ -199,14 +199,14 @@ Function,-,LL_RCC_GetADCClockFreq,uint32_t,uint32_t
Function,-,LL_RCC_GetCLK48ClockFreq,uint32_t,uint32_t
Function,-,LL_RCC_GetI2CClockFreq,uint32_t,uint32_t
Function,-,LL_RCC_GetLPTIMClockFreq,uint32_t,uint32_t
Function,-,LL_RCC_GetLPUARTClockFreq,uint32_t,uint32_t
Function,+,LL_RCC_GetLPUARTClockFreq,uint32_t,uint32_t
Function,-,LL_RCC_GetRFWKPClockFreq,uint32_t,
Function,-,LL_RCC_GetRNGClockFreq,uint32_t,uint32_t
Function,-,LL_RCC_GetRTCClockFreq,uint32_t,
Function,-,LL_RCC_GetSAIClockFreq,uint32_t,uint32_t
Function,-,LL_RCC_GetSMPSClockFreq,uint32_t,
Function,-,LL_RCC_GetSystemClocksFreq,void,LL_RCC_ClocksTypeDef*
Function,-,LL_RCC_GetUSARTClockFreq,uint32_t,uint32_t
Function,+,LL_RCC_GetUSARTClockFreq,uint32_t,uint32_t
Function,-,LL_RCC_GetUSBClockFreq,uint32_t,uint32_t
Function,-,LL_RNG_DeInit,ErrorStatus,RNG_TypeDef*
Function,-,LL_RNG_Init,ErrorStatus,"RNG_TypeDef*, LL_RNG_InitTypeDef*"
@@ -218,21 +218,21 @@ Function,-,LL_RTC_ALMB_StructInit,void,LL_RTC_AlarmTypeDef*
Function,-,LL_RTC_DATE_Init,ErrorStatus,"RTC_TypeDef*, uint32_t, LL_RTC_DateTypeDef*"
Function,-,LL_RTC_DATE_StructInit,void,LL_RTC_DateTypeDef*
Function,-,LL_RTC_DeInit,ErrorStatus,RTC_TypeDef*
Function,-,LL_RTC_EnterInitMode,ErrorStatus,RTC_TypeDef*
Function,+,LL_RTC_EnterInitMode,ErrorStatus,RTC_TypeDef*
Function,-,LL_RTC_ExitInitMode,ErrorStatus,RTC_TypeDef*
Function,-,LL_RTC_Init,ErrorStatus,"RTC_TypeDef*, LL_RTC_InitTypeDef*"
Function,+,LL_RTC_Init,ErrorStatus,"RTC_TypeDef*, LL_RTC_InitTypeDef*"
Function,-,LL_RTC_StructInit,void,LL_RTC_InitTypeDef*
Function,-,LL_RTC_TIME_Init,ErrorStatus,"RTC_TypeDef*, uint32_t, LL_RTC_TimeTypeDef*"
Function,-,LL_RTC_TIME_StructInit,void,LL_RTC_TimeTypeDef*
Function,-,LL_RTC_WaitForSynchro,ErrorStatus,RTC_TypeDef*
Function,-,LL_SPI_DeInit,ErrorStatus,SPI_TypeDef*
Function,-,LL_SPI_Init,ErrorStatus,"SPI_TypeDef*, LL_SPI_InitTypeDef*"
Function,+,LL_SPI_Init,ErrorStatus,"SPI_TypeDef*, LL_SPI_InitTypeDef*"
Function,-,LL_SPI_StructInit,void,LL_SPI_InitTypeDef*
Function,-,LL_SetFlashLatency,ErrorStatus,uint32_t
Function,-,LL_SetSystemCoreClock,void,uint32_t
Function,+,LL_SetSystemCoreClock,void,uint32_t
Function,-,LL_TIM_BDTR_Init,ErrorStatus,"TIM_TypeDef*, LL_TIM_BDTR_InitTypeDef*"
Function,-,LL_TIM_BDTR_StructInit,void,LL_TIM_BDTR_InitTypeDef*
Function,-,LL_TIM_DeInit,ErrorStatus,TIM_TypeDef*
Function,+,LL_TIM_DeInit,ErrorStatus,TIM_TypeDef*
Function,-,LL_TIM_ENCODER_Init,ErrorStatus,"TIM_TypeDef*, LL_TIM_ENCODER_InitTypeDef*"
Function,-,LL_TIM_ENCODER_StructInit,void,LL_TIM_ENCODER_InitTypeDef*
Function,-,LL_TIM_HALLSENSOR_Init,ErrorStatus,"TIM_TypeDef*, LL_TIM_HALLSENSOR_InitTypeDef*"
@@ -246,7 +246,7 @@ Function,-,LL_TIM_StructInit,void,LL_TIM_InitTypeDef*
Function,-,LL_USART_ClockInit,ErrorStatus,"USART_TypeDef*, LL_USART_ClockInitTypeDef*"
Function,-,LL_USART_ClockStructInit,void,LL_USART_ClockInitTypeDef*
Function,-,LL_USART_DeInit,ErrorStatus,USART_TypeDef*
Function,-,LL_USART_Init,ErrorStatus,"USART_TypeDef*, LL_USART_InitTypeDef*"
Function,+,LL_USART_Init,ErrorStatus,"USART_TypeDef*, LL_USART_InitTypeDef*"
Function,-,LL_USART_StructInit,void,LL_USART_InitTypeDef*
Function,-,LL_mDelay,void,uint32_t
Function,-,SystemCoreClockUpdate,void,
@@ -439,8 +439,8 @@ Function,-,acoshl,long double,long double
Function,-,acosl,long double,long double
Function,+,acquire_mutex,void*,"ValueMutex*, uint32_t"
Function,-,aligned_alloc,void*,"size_t, size_t"
Function,-,aligned_free,void,void*
Function,-,aligned_malloc,void*,"size_t, size_t"
Function,+,aligned_free,void,void*
Function,+,aligned_malloc,void*,"size_t, size_t"
Function,-,arc4random,__uint32_t,
Function,-,arc4random_buf,void,"void*, size_t"
Function,-,arc4random_uniform,__uint32_t,__uint32_t
@@ -799,13 +799,13 @@ Function,-,fiprintf,int,"FILE*, const char*, ..."
Function,-,fiscanf,int,"FILE*, const char*, ..."
Function,+,flipper_application_alloc,FlipperApplication*,"Storage*, const ElfApiInterface*"
Function,+,flipper_application_free,void,FlipperApplication*
Function,-,flipper_application_get_entry_address,const void*,FlipperApplication*
Function,+,flipper_application_get_manifest,const FlipperApplicationManifest*,FlipperApplication*
Function,-,flipper_application_get_state,const FlipperApplicationState*,FlipperApplication*
Function,-,flipper_application_get_thread,FuriThread*,FlipperApplication*
Function,+,flipper_application_load_status_to_string,const char*,FlipperApplicationLoadStatus
Function,+,flipper_application_manifest_is_compatible,_Bool,"const FlipperApplicationManifest*, const ElfApiInterface*"
Function,+,flipper_application_manifest_is_valid,_Bool,const FlipperApplicationManifest*
Function,+,flipper_application_map_to_memory,FlipperApplicationLoadStatus,FlipperApplication*
Function,+,flipper_application_preload,FlipperApplicationPreloadStatus,"FlipperApplication*, const char*"
Function,+,flipper_application_preload_manifest,FlipperApplicationPreloadStatus,"FlipperApplication*, const char*"
Function,-,flipper_application_preload_status_to_string,const char*,FlipperApplicationPreloadStatus
Function,+,flipper_application_spawn,FuriThread*,"FlipperApplication*, void*"
Function,+,flipper_format_buffered_file_alloc,FlipperFormat*,Storage*
@@ -1369,8 +1369,10 @@ Function,+,furi_thread_get_name,const char*,FuriThreadId
Function,+,furi_thread_get_return_code,int32_t,FuriThread*
Function,+,furi_thread_get_stack_space,uint32_t,FuriThreadId
Function,+,furi_thread_get_state,FuriThreadState,FuriThread*
Function,+,furi_thread_is_suspended,_Bool,FuriThreadId
Function,+,furi_thread_join,_Bool,FuriThread*
Function,+,furi_thread_mark_as_service,void,FuriThread*
Function,+,furi_thread_resume,void,FuriThreadId
Function,+,furi_thread_set_callback,void,"FuriThread*, FuriThreadCallback"
Function,+,furi_thread_set_context,void,"FuriThread*, void*"
Function,+,furi_thread_set_name,void,"FuriThread*, const char*"
@@ -1382,6 +1384,7 @@ Function,+,furi_thread_set_stdout_callback,_Bool,FuriThreadStdoutWriteCallback
Function,+,furi_thread_start,void,FuriThread*
Function,+,furi_thread_stdout_flush,int32_t,
Function,+,furi_thread_stdout_write,size_t,"const char*, size_t"
Function,+,furi_thread_suspend,void,FuriThreadId
Function,+,furi_thread_yield,void,
Function,+,furi_timer_alloc,FuriTimer*,"FuriTimerCallback, FuriTimerType, void*"
Function,+,furi_timer_free,void,FuriTimer*
@@ -4098,6 +4101,7 @@ Variable,+,A_iButton_14,const Icon,
Variable,-,ITM_RxBuffer,volatile int32_t,
Variable,+,I_125_10px,const Icon,
Variable,+,I_ActiveConnection_50x64,const Icon,
Variable,+,I_Apps_10px,const Icon,
Variable,+,I_ArrowC_1_36x36,const Icon,
Variable,+,I_ArrowDownEmpty_14x15,const Icon,
Variable,+,I_ArrowDownFilled_14x15,const Icon,
1 entry status name type params
163 Function - LL_ADC_REG_StructInit void LL_ADC_REG_InitTypeDef*
164 Function - LL_ADC_StructInit void LL_ADC_InitTypeDef*
165 Function - LL_COMP_DeInit ErrorStatus COMP_TypeDef*
166 Function - + LL_COMP_Init ErrorStatus COMP_TypeDef*, LL_COMP_InitTypeDef*
167 Function - LL_COMP_StructInit void LL_COMP_InitTypeDef*
168 Function - LL_CRC_DeInit ErrorStatus CRC_TypeDef*
169 Function - LL_CRS_DeInit ErrorStatus
170 Function - + LL_DMA_DeInit ErrorStatus DMA_TypeDef*, uint32_t
171 Function - + LL_DMA_Init ErrorStatus DMA_TypeDef*, uint32_t, LL_DMA_InitTypeDef*
172 Function - LL_DMA_StructInit void LL_DMA_InitTypeDef*
173 Function - LL_EXTI_DeInit ErrorStatus
174 Function - LL_EXTI_Init ErrorStatus LL_EXTI_InitTypeDef*
175 Function - LL_EXTI_StructInit void LL_EXTI_InitTypeDef*
176 Function - LL_GPIO_DeInit ErrorStatus GPIO_TypeDef*
177 Function - + LL_GPIO_Init ErrorStatus GPIO_TypeDef*, LL_GPIO_InitTypeDef*
178 Function - LL_GPIO_StructInit void LL_GPIO_InitTypeDef*
179 Function - LL_I2C_DeInit ErrorStatus I2C_TypeDef*
180 Function - + LL_I2C_Init ErrorStatus I2C_TypeDef*, LL_I2C_InitTypeDef*
181 Function - LL_I2C_StructInit void LL_I2C_InitTypeDef*
182 Function - LL_Init1msTick void uint32_t
183 Function - + LL_LPTIM_DeInit ErrorStatus LPTIM_TypeDef*
184 Function - LL_LPTIM_Disable void LPTIM_TypeDef*
185 Function - + LL_LPTIM_Init ErrorStatus LPTIM_TypeDef*, LL_LPTIM_InitTypeDef*
186 Function - LL_LPTIM_StructInit void LL_LPTIM_InitTypeDef*
187 Function - LL_LPUART_DeInit ErrorStatus USART_TypeDef*
188 Function - + LL_LPUART_Init ErrorStatus USART_TypeDef*, LL_LPUART_InitTypeDef*
189 Function - LL_LPUART_StructInit void LL_LPUART_InitTypeDef*
190 Function - LL_PKA_DeInit ErrorStatus PKA_TypeDef*
191 Function - LL_PKA_Init ErrorStatus PKA_TypeDef*, LL_PKA_InitTypeDef*
199 Function - LL_RCC_GetCLK48ClockFreq uint32_t uint32_t
200 Function - LL_RCC_GetI2CClockFreq uint32_t uint32_t
201 Function - LL_RCC_GetLPTIMClockFreq uint32_t uint32_t
202 Function - + LL_RCC_GetLPUARTClockFreq uint32_t uint32_t
203 Function - LL_RCC_GetRFWKPClockFreq uint32_t
204 Function - LL_RCC_GetRNGClockFreq uint32_t uint32_t
205 Function - LL_RCC_GetRTCClockFreq uint32_t
206 Function - LL_RCC_GetSAIClockFreq uint32_t uint32_t
207 Function - LL_RCC_GetSMPSClockFreq uint32_t
208 Function - LL_RCC_GetSystemClocksFreq void LL_RCC_ClocksTypeDef*
209 Function - + LL_RCC_GetUSARTClockFreq uint32_t uint32_t
210 Function - LL_RCC_GetUSBClockFreq uint32_t uint32_t
211 Function - LL_RNG_DeInit ErrorStatus RNG_TypeDef*
212 Function - LL_RNG_Init ErrorStatus RNG_TypeDef*, LL_RNG_InitTypeDef*
218 Function - LL_RTC_DATE_Init ErrorStatus RTC_TypeDef*, uint32_t, LL_RTC_DateTypeDef*
219 Function - LL_RTC_DATE_StructInit void LL_RTC_DateTypeDef*
220 Function - LL_RTC_DeInit ErrorStatus RTC_TypeDef*
221 Function - + LL_RTC_EnterInitMode ErrorStatus RTC_TypeDef*
222 Function - LL_RTC_ExitInitMode ErrorStatus RTC_TypeDef*
223 Function - + LL_RTC_Init ErrorStatus RTC_TypeDef*, LL_RTC_InitTypeDef*
224 Function - LL_RTC_StructInit void LL_RTC_InitTypeDef*
225 Function - LL_RTC_TIME_Init ErrorStatus RTC_TypeDef*, uint32_t, LL_RTC_TimeTypeDef*
226 Function - LL_RTC_TIME_StructInit void LL_RTC_TimeTypeDef*
227 Function - LL_RTC_WaitForSynchro ErrorStatus RTC_TypeDef*
228 Function - LL_SPI_DeInit ErrorStatus SPI_TypeDef*
229 Function - + LL_SPI_Init ErrorStatus SPI_TypeDef*, LL_SPI_InitTypeDef*
230 Function - LL_SPI_StructInit void LL_SPI_InitTypeDef*
231 Function - LL_SetFlashLatency ErrorStatus uint32_t
232 Function - + LL_SetSystemCoreClock void uint32_t
233 Function - LL_TIM_BDTR_Init ErrorStatus TIM_TypeDef*, LL_TIM_BDTR_InitTypeDef*
234 Function - LL_TIM_BDTR_StructInit void LL_TIM_BDTR_InitTypeDef*
235 Function - + LL_TIM_DeInit ErrorStatus TIM_TypeDef*
236 Function - LL_TIM_ENCODER_Init ErrorStatus TIM_TypeDef*, LL_TIM_ENCODER_InitTypeDef*
237 Function - LL_TIM_ENCODER_StructInit void LL_TIM_ENCODER_InitTypeDef*
238 Function - LL_TIM_HALLSENSOR_Init ErrorStatus TIM_TypeDef*, LL_TIM_HALLSENSOR_InitTypeDef*
246 Function - LL_USART_ClockInit ErrorStatus USART_TypeDef*, LL_USART_ClockInitTypeDef*
247 Function - LL_USART_ClockStructInit void LL_USART_ClockInitTypeDef*
248 Function - LL_USART_DeInit ErrorStatus USART_TypeDef*
249 Function - + LL_USART_Init ErrorStatus USART_TypeDef*, LL_USART_InitTypeDef*
250 Function - LL_USART_StructInit void LL_USART_InitTypeDef*
251 Function - LL_mDelay void uint32_t
252 Function - SystemCoreClockUpdate void
439 Function - acosl long double long double
440 Function + acquire_mutex void* ValueMutex*, uint32_t
441 Function - aligned_alloc void* size_t, size_t
442 Function - + aligned_free void void*
443 Function - + aligned_malloc void* size_t, size_t
444 Function - arc4random __uint32_t
445 Function - arc4random_buf void void*, size_t
446 Function - arc4random_uniform __uint32_t __uint32_t
799 Function - fiscanf int FILE*, const char*, ...
800 Function + flipper_application_alloc FlipperApplication* Storage*, const ElfApiInterface*
801 Function + flipper_application_free void FlipperApplication*
Function - flipper_application_get_entry_address const void* FlipperApplication*
802 Function + flipper_application_get_manifest const FlipperApplicationManifest* FlipperApplication*
Function - flipper_application_get_state const FlipperApplicationState* FlipperApplication*
Function - flipper_application_get_thread FuriThread* FlipperApplication*
803 Function + flipper_application_load_status_to_string const char* FlipperApplicationLoadStatus
804 Function + flipper_application_manifest_is_compatible _Bool const FlipperApplicationManifest*, const ElfApiInterface*
805 Function + flipper_application_manifest_is_valid _Bool const FlipperApplicationManifest*
806 Function + flipper_application_map_to_memory FlipperApplicationLoadStatus FlipperApplication*
807 Function + flipper_application_preload FlipperApplicationPreloadStatus FlipperApplication*, const char*
808 Function + flipper_application_preload_manifest FlipperApplicationPreloadStatus FlipperApplication*, const char*
809 Function - flipper_application_preload_status_to_string const char* FlipperApplicationPreloadStatus
810 Function + flipper_application_spawn FuriThread* FlipperApplication*, void*
811 Function + flipper_format_buffered_file_alloc FlipperFormat* Storage*
1369 Function + furi_thread_get_return_code int32_t FuriThread*
1370 Function + furi_thread_get_stack_space uint32_t FuriThreadId
1371 Function + furi_thread_get_state FuriThreadState FuriThread*
1372 Function + furi_thread_is_suspended _Bool FuriThreadId
1373 Function + furi_thread_join _Bool FuriThread*
1374 Function + furi_thread_mark_as_service void FuriThread*
1375 Function + furi_thread_resume void FuriThreadId
1376 Function + furi_thread_set_callback void FuriThread*, FuriThreadCallback
1377 Function + furi_thread_set_context void FuriThread*, void*
1378 Function + furi_thread_set_name void FuriThread*, const char*
1384 Function + furi_thread_start void FuriThread*
1385 Function + furi_thread_stdout_flush int32_t
1386 Function + furi_thread_stdout_write size_t const char*, size_t
1387 Function + furi_thread_suspend void FuriThreadId
1388 Function + furi_thread_yield void
1389 Function + furi_timer_alloc FuriTimer* FuriTimerCallback, FuriTimerType, void*
1390 Function + furi_timer_free void FuriTimer*
4101 Variable - ITM_RxBuffer volatile int32_t
4102 Variable + I_125_10px const Icon
4103 Variable + I_ActiveConnection_50x64 const Icon
4104 Variable + I_Apps_10px const Icon
4105 Variable + I_ArrowC_1_36x36 const Icon
4106 Variable + I_ArrowDownEmpty_14x15 const Icon
4107 Variable + I_ArrowDownFilled_14x15 const Icon

View File

@@ -48,5 +48,7 @@ SECTIONS
{
*(.comment)
*(.comment.*)
*(.llvmbc)
*(.llvmcmd)
}
}

View File

@@ -6,6 +6,7 @@
#include <m-string.h>
#include <m-dict.h>
#include <toolbox/m_cstr_dup.h>
#define FURI_RECORD_FLAG_READY (0x1)
@@ -15,7 +16,7 @@ typedef struct {
size_t holders_count;
} FuriRecordData;
DICT_DEF2(FuriRecordDataDict, string_t, STRING_OPLIST, FuriRecordData, M_POD_OPLIST)
DICT_DEF2(FuriRecordDataDict, const char*, M_CSTR_DUP_OPLIST, FuriRecordData, M_POD_OPLIST)
typedef struct {
FuriMutex* mutex;
@@ -24,6 +25,19 @@ typedef struct {
static FuriRecord* furi_record = NULL;
static FuriRecordData* furi_record_get(const char* name) {
return FuriRecordDataDict_get(furi_record->records, name);
}
static void furi_record_put(const char* name, FuriRecordData* record_data) {
FuriRecordDataDict_set_at(furi_record->records, name, *record_data);
}
static void furi_record_erase(const char* name, FuriRecordData* record_data) {
furi_event_flag_free(record_data->flags);
FuriRecordDataDict_erase(furi_record->records, name);
}
void furi_record_init() {
furi_record = malloc(sizeof(FuriRecord));
furi_record->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
@@ -31,16 +45,16 @@ void furi_record_init() {
FuriRecordDataDict_init(furi_record->records);
}
static FuriRecordData* furi_record_data_get_or_create(string_t name_str) {
static FuriRecordData* furi_record_data_get_or_create(const char* name) {
furi_assert(furi_record);
FuriRecordData* record_data = FuriRecordDataDict_get(furi_record->records, name_str);
FuriRecordData* record_data = furi_record_get(name);
if(!record_data) {
FuriRecordData new_record;
new_record.flags = furi_event_flag_alloc();
new_record.data = NULL;
new_record.holders_count = 0;
FuriRecordDataDict_set_at(furi_record->records, name_str, new_record);
record_data = FuriRecordDataDict_get(furi_record->records, name_str);
furi_record_put(name, &new_record);
record_data = furi_record_get(name);
}
return record_data;
}
@@ -59,35 +73,25 @@ bool furi_record_exists(const char* name) {
bool ret = false;
string_t name_str;
string_init_set_str(name_str, name);
furi_record_lock();
ret = (FuriRecordDataDict_get(furi_record->records, name_str) != NULL);
ret = (furi_record_get(name) != NULL);
furi_record_unlock();
string_clear(name_str);
return ret;
}
void furi_record_create(const char* name, void* data) {
furi_assert(furi_record);
string_t name_str;
string_init_set_str(name_str, name);
furi_record_lock();
// Get record data and fill it
FuriRecordData* record_data = furi_record_data_get_or_create(name_str);
FuriRecordData* record_data = furi_record_data_get_or_create(name);
furi_assert(record_data->data == NULL);
record_data->data = data;
furi_event_flag_set(record_data->flags, FURI_RECORD_FLAG_READY);
furi_record_unlock();
string_clear(name_str);
}
bool furi_record_destroy(const char* name) {
@@ -95,35 +99,26 @@ bool furi_record_destroy(const char* name) {
bool ret = false;
string_t name_str;
string_init_set_str(name_str, name);
furi_record_lock();
FuriRecordData* record_data = FuriRecordDataDict_get(furi_record->records, name_str);
FuriRecordData* record_data = furi_record_get(name);
furi_assert(record_data);
if(record_data->holders_count == 0) {
furi_event_flag_free(record_data->flags);
FuriRecordDataDict_erase(furi_record->records, name_str);
furi_record_erase(name, record_data);
ret = true;
}
furi_record_unlock();
string_clear(name_str);
return ret;
}
void* furi_record_open(const char* name) {
furi_assert(furi_record);
string_t name_str;
string_init_set_str(name_str, name);
furi_record_lock();
FuriRecordData* record_data = furi_record_data_get_or_create(name_str);
FuriRecordData* record_data = furi_record_data_get_or_create(name);
record_data->holders_count++;
furi_record_unlock();
@@ -136,24 +131,17 @@ void* furi_record_open(const char* name) {
FuriFlagWaitAny | FuriFlagNoClear,
FuriWaitForever) == FURI_RECORD_FLAG_READY);
string_clear(name_str);
return record_data->data;
}
void furi_record_close(const char* name) {
furi_assert(furi_record);
string_t name_str;
string_init_set_str(name_str, name);
furi_record_lock();
FuriRecordData* record_data = FuriRecordDataDict_get(furi_record->records, name_str);
FuriRecordData* record_data = furi_record_get(name);
furi_assert(record_data);
record_data->holders_count--;
furi_record_unlock();
string_clear(name_str);
}

View File

@@ -89,7 +89,9 @@ static void furi_thread_body(void* context) {
if(thread->is_service) {
FURI_LOG_E(
"Service", "%s thread exited. Thread memory cannot be reclaimed.", thread->name);
"Service",
"%s thread exited. Thread memory cannot be reclaimed.",
thread->name ? thread->name : "<unknown service>");
}
// clear thread local storage
@@ -515,4 +517,23 @@ size_t furi_thread_stdout_write(const char* data, size_t size) {
int32_t furi_thread_stdout_flush() {
return __furi_thread_stdout_flush(furi_thread_get_current());
}
}
void furi_thread_suspend(FuriThreadId thread_id) {
TaskHandle_t hTask = (TaskHandle_t)thread_id;
vTaskSuspend(hTask);
}
void furi_thread_resume(FuriThreadId thread_id) {
TaskHandle_t hTask = (TaskHandle_t)thread_id;
if(FURI_IS_IRQ_MODE()) {
xTaskResumeFromISR(hTask);
} else {
vTaskResume(hTask);
}
}
bool furi_thread_is_suspended(FuriThreadId thread_id) {
TaskHandle_t hTask = (TaskHandle_t)thread_id;
return eTaskGetState(hTask) == eSuspended;
}

View File

@@ -236,6 +236,25 @@ size_t furi_thread_stdout_write(const char* data, size_t size);
*/
int32_t furi_thread_stdout_flush();
/** Suspend thread
*
* @param thread_id thread id
*/
void furi_thread_suspend(FuriThreadId thread_id);
/** Resume thread
*
* @param thread_id thread id
*/
void furi_thread_resume(FuriThreadId thread_id);
/** Get thread suspended state
*
* @param thread_id thread id
* @return true if thread is suspended
*/
bool furi_thread_is_suspended(FuriThreadId thread_id);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,21 @@
#include "application_manifest.h"
bool flipper_application_manifest_is_valid(const FlipperApplicationManifest* manifest) {
if((manifest->base.manifest_magic != FAP_MANIFEST_MAGIC) ||
(manifest->base.manifest_version != FAP_MANIFEST_SUPPORTED_VERSION)) {
return false;
}
return true;
}
bool flipper_application_manifest_is_compatible(
const FlipperApplicationManifest* manifest,
const ElfApiInterface* api_interface) {
if(manifest->base.api_version.major != api_interface->api_version_major /* ||
manifest->base.api_version.minor > app->api_interface->api_version_minor */) {
return false;
}
return true;
}

View File

@@ -1,6 +1,12 @@
/**
* @file application_manifest.h
* Flipper application manifest
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "elf/elf_api_interface.h"
#ifdef __cplusplus
extern "C" {
@@ -40,6 +46,25 @@ typedef FlipperApplicationManifestV1 FlipperApplicationManifest;
#pragma pack(pop)
/**
* @brief Check if manifest is valid
*
* @param manifest
* @return bool
*/
bool flipper_application_manifest_is_valid(const FlipperApplicationManifest* manifest);
/**
* @brief Check if manifest is compatible with current ELF API interface
*
* @param manifest
* @param api_interface
* @return bool
*/
bool flipper_application_manifest_is_compatible(
const FlipperApplicationManifest* manifest,
const ElfApiInterface* api_interface);
#ifdef __cplusplus
}
#endif

View File

@@ -1116,6 +1116,8 @@ typedef struct {
#define R_ARM_LDR_SBREL_11_0 35
#define R_ARM_ALU_SBREL_19_12 36
#define R_ARM_ALU_SBREL_27_20 37
#define R_ARM_THM_MOVW_ABS_NC 47 /* Direct 16 bit (Thumb32 MOVW) */
#define R_ARM_THM_MOVT_ABS 48 /* Direct high 16 bit */
#define R_ARM_GNU_VTENTRY 100
#define R_ARM_GNU_VTINHERIT 101
#define R_ARM_THM_PC11 102 /* thumb unconditional branch */

View File

@@ -1,6 +1,6 @@
#pragma once
#include <flipper_application/elf/elf.h>
#include <elf.h>
#include <stdbool.h>
#define ELF_INVALID_ADDRESS 0xFFFFFFFF

View File

@@ -0,0 +1,835 @@
#include <elf.h>
#include "elf_file.h"
#include "elf_file_i.h"
#include "elf_api_interface.h"
#define TAG "elf"
#define ELF_NAME_BUFFER_LEN 32
#define SECTION_OFFSET(e, n) (e->section_table + n * sizeof(Elf32_Shdr))
#define IS_FLAGS_SET(v, m) ((v & m) == m)
#define RESOLVER_THREAD_YIELD_STEP 30
// #define ELF_DEBUG_LOG 1
#ifndef ELF_DEBUG_LOG
#undef FURI_LOG_D
#define FURI_LOG_D(...)
#endif
#define TRAMPOLINE_CODE_SIZE 6
/**
ldr r12, [pc, #2]
bx r12
*/
const uint8_t trampoline_code_little_endian[TRAMPOLINE_CODE_SIZE] =
{0xdf, 0xf8, 0x02, 0xc0, 0x60, 0x47};
typedef struct {
uint8_t code[TRAMPOLINE_CODE_SIZE];
uint32_t addr;
} __attribute__((packed)) JMPTrampoline;
/**************************************************************************************************/
/********************************************* Caches *********************************************/
/**************************************************************************************************/
static bool address_cache_get(AddressCache_t cache, int symEntry, Elf32_Addr* symAddr) {
Elf32_Addr* addr = AddressCache_get(cache, symEntry);
if(addr) {
*symAddr = *addr;
return true;
} else {
return false;
}
}
static void address_cache_put(AddressCache_t cache, int symEntry, Elf32_Addr symAddr) {
AddressCache_set_at(cache, symEntry, symAddr);
}
/**************************************************************************************************/
/********************************************** ELF ***********************************************/
/**************************************************************************************************/
static ELFSection* elf_file_get_section(ELFFile* elf, const char* name) {
return ELFSectionDict_get(elf->sections, name);
}
static void elf_file_put_section(ELFFile* elf, const char* name, ELFSection* section) {
ELFSectionDict_set_at(elf->sections, strdup(name), *section);
}
static bool elf_read_string_from_offset(ELFFile* elf, off_t offset, string_t name) {
bool result = false;
off_t old = storage_file_tell(elf->fd);
do {
if(!storage_file_seek(elf->fd, offset, true)) break;
char buffer[ELF_NAME_BUFFER_LEN + 1];
buffer[ELF_NAME_BUFFER_LEN] = 0;
while(true) {
uint16_t read = storage_file_read(elf->fd, buffer, ELF_NAME_BUFFER_LEN);
string_cat_str(name, buffer);
if(strlen(buffer) < ELF_NAME_BUFFER_LEN) {
result = true;
break;
}
if(storage_file_get_error(elf->fd) != FSE_OK || read == 0) break;
}
} while(false);
storage_file_seek(elf->fd, old, true);
return result;
}
static bool elf_read_section_name(ELFFile* elf, off_t offset, string_t name) {
return elf_read_string_from_offset(elf, elf->section_table_strings + offset, name);
}
static bool elf_read_symbol_name(ELFFile* elf, off_t offset, string_t name) {
return elf_read_string_from_offset(elf, elf->symbol_table_strings + offset, name);
}
static bool elf_read_section_header(ELFFile* elf, size_t section_idx, Elf32_Shdr* section_header) {
off_t offset = SECTION_OFFSET(elf, section_idx);
return storage_file_seek(elf->fd, offset, true) &&
storage_file_read(elf->fd, section_header, sizeof(Elf32_Shdr)) == sizeof(Elf32_Shdr);
}
static bool
elf_read_section(ELFFile* elf, size_t section_idx, Elf32_Shdr* section_header, string_t name) {
if(!elf_read_section_header(elf, section_idx, section_header)) {
return false;
}
if(section_header->sh_name && !elf_read_section_name(elf, section_header->sh_name, name)) {
return false;
}
return true;
}
static bool elf_read_symbol(ELFFile* elf, int n, Elf32_Sym* sym, string_t name) {
bool success = false;
off_t old = storage_file_tell(elf->fd);
off_t pos = elf->symbol_table + n * sizeof(Elf32_Sym);
if(storage_file_seek(elf->fd, pos, true) &&
storage_file_read(elf->fd, sym, sizeof(Elf32_Sym)) == sizeof(Elf32_Sym)) {
if(sym->st_name)
success = elf_read_symbol_name(elf, sym->st_name, name);
else {
Elf32_Shdr shdr;
success = elf_read_section(elf, sym->st_shndx, &shdr, name);
}
}
storage_file_seek(elf->fd, old, true);
return success;
}
static ELFSection* elf_section_of(ELFFile* elf, int index) {
ELFSectionDict_it_t it;
for(ELFSectionDict_it(it, elf->sections); !ELFSectionDict_end_p(it); ELFSectionDict_next(it)) {
ELFSectionDict_itref_t* itref = ELFSectionDict_ref(it);
if(itref->value.sec_idx == index) {
return &itref->value;
}
}
return NULL;
}
static Elf32_Addr elf_address_of(ELFFile* elf, Elf32_Sym* sym, const char* sName) {
if(sym->st_shndx == SHN_UNDEF) {
Elf32_Addr addr = 0;
if(elf->api_interface->resolver_callback(sName, &addr)) {
return addr;
}
} else {
ELFSection* symSec = elf_section_of(elf, sym->st_shndx);
if(symSec) {
return ((Elf32_Addr)symSec->data) + sym->st_value;
}
}
FURI_LOG_D(TAG, " Can not find address for symbol %s", sName);
return ELF_INVALID_ADDRESS;
}
__attribute__((unused)) static const char* elf_reloc_type_to_str(int symt) {
#define STRCASE(name) \
case name: \
return #name;
switch(symt) {
STRCASE(R_ARM_NONE)
STRCASE(R_ARM_TARGET1)
STRCASE(R_ARM_ABS32)
STRCASE(R_ARM_THM_PC22)
STRCASE(R_ARM_THM_JUMP24)
default:
return "R_<unknow>";
}
#undef STRCASE
}
static JMPTrampoline* elf_create_trampoline(Elf32_Addr addr) {
JMPTrampoline* trampoline = malloc(sizeof(JMPTrampoline));
memcpy(trampoline->code, trampoline_code_little_endian, TRAMPOLINE_CODE_SIZE);
trampoline->addr = addr;
return trampoline;
}
static void elf_relocate_jmp_call(ELFFile* elf, Elf32_Addr relAddr, int type, Elf32_Addr symAddr) {
int offset, hi, lo, s, j1, j2, i1, i2, imm10, imm11;
int to_thumb, is_call, blx_bit = 1 << 12;
/* Get initial offset */
hi = ((uint16_t*)relAddr)[0];
lo = ((uint16_t*)relAddr)[1];
s = (hi >> 10) & 1;
j1 = (lo >> 13) & 1;
j2 = (lo >> 11) & 1;
i1 = (j1 ^ s) ^ 1;
i2 = (j2 ^ s) ^ 1;
imm10 = hi & 0x3ff;
imm11 = lo & 0x7ff;
offset = (s << 24) | (i1 << 23) | (i2 << 22) | (imm10 << 12) | (imm11 << 1);
if(offset & 0x01000000) offset -= 0x02000000;
to_thumb = symAddr & 1;
is_call = (type == R_ARM_THM_PC22);
/* Store offset */
int offset_copy = offset;
/* Compute final offset */
offset += symAddr - relAddr;
if(!to_thumb && is_call) {
blx_bit = 0; /* bl -> blx */
offset = (offset + 3) & -4; /* Compute offset from aligned PC */
}
/* Check that relocation is possible
* offset must not be out of range
* if target is to be entered in arm mode:
- bit 1 must not set
- instruction must be a call (bl) or a jump to PLT */
if(!to_thumb || offset >= 0x1000000 || offset < -0x1000000) {
if(to_thumb || (symAddr & 2) || (!is_call)) {
FURI_LOG_D(
TAG,
"can't relocate value at %x, %s, doing trampoline",
relAddr,
elf_reloc_type_to_str(type));
Elf32_Addr addr;
if(!address_cache_get(elf->trampoline_cache, symAddr, &addr)) {
addr = (Elf32_Addr)elf_create_trampoline(symAddr);
address_cache_put(elf->trampoline_cache, symAddr, addr);
}
offset = offset_copy;
offset += (int)addr - relAddr;
if(!to_thumb && is_call) {
blx_bit = 0; /* bl -> blx */
offset = (offset + 3) & -4; /* Compute offset from aligned PC */
}
}
}
/* Compute and store final offset */
s = (offset >> 24) & 1;
i1 = (offset >> 23) & 1;
i2 = (offset >> 22) & 1;
j1 = s ^ (i1 ^ 1);
j2 = s ^ (i2 ^ 1);
imm10 = (offset >> 12) & 0x3ff;
imm11 = (offset >> 1) & 0x7ff;
(*(uint16_t*)relAddr) = (uint16_t)((hi & 0xf800) | (s << 10) | imm10);
(*(uint16_t*)(relAddr + 2)) =
(uint16_t)((lo & 0xc000) | (j1 << 13) | blx_bit | (j2 << 11) | imm11);
}
static void elf_relocate_mov(Elf32_Addr relAddr, int type, Elf32_Addr symAddr) {
uint16_t upper_insn = ((uint16_t*)relAddr)[0];
uint16_t lower_insn = ((uint16_t*)relAddr)[1];
/* MOV*<C> <Rd>,#<imm16>
*
* i = upper[10]
* imm4 = upper[3:0]
* imm3 = lower[14:12]
* imm8 = lower[7:0]
*
* imm16 = imm4:i:imm3:imm8
*/
uint32_t i = (upper_insn >> 10) & 1; /* upper[10] */
uint32_t imm4 = upper_insn & 0x000F; /* upper[3:0] */
uint32_t imm3 = (lower_insn >> 12) & 0x7; /* lower[14:12] */
uint32_t imm8 = lower_insn & 0x00FF; /* lower[7:0] */
int32_t addend = (imm4 << 12) | (i << 11) | (imm3 << 8) | imm8; /* imm16 */
uint32_t addr = (symAddr + addend);
if (type == R_ARM_THM_MOVT_ABS) {
addr >>= 16; /* upper 16 bits */
} else {
addr &= 0x0000FFFF; /* lower 16 bits */
}
/* Re-encode */
((uint16_t*)relAddr)[0] = (upper_insn & 0xFBF0)
| (((addr >> 11) & 1) << 10) /* i */
| ((addr >> 12) & 0x000F); /* imm4 */
((uint16_t*)relAddr)[1] = (lower_insn & 0x8F00)
| (((addr >> 8) & 0x7) << 12) /* imm3 */
| (addr & 0x00FF); /* imm8 */
}
static bool elf_relocate_symbol(ELFFile* elf, Elf32_Addr relAddr, int type, Elf32_Addr symAddr) {
switch(type) {
case R_ARM_TARGET1:
case R_ARM_ABS32:
*((uint32_t*)relAddr) += symAddr;
FURI_LOG_D(TAG, " R_ARM_ABS32 relocated is 0x%08X", (unsigned int)*((uint32_t*)relAddr));
break;
case R_ARM_THM_PC22:
case R_ARM_THM_JUMP24:
elf_relocate_jmp_call(elf, relAddr, type, symAddr);
FURI_LOG_D(
TAG, " R_ARM_THM_CALL/JMP relocated is 0x%08X", (unsigned int)*((uint32_t*)relAddr));
break;
case R_ARM_THM_MOVW_ABS_NC:
case R_ARM_THM_MOVT_ABS:
elf_relocate_mov(relAddr, type, symAddr);
FURI_LOG_D(TAG, " R_ARM_THM_MOVW_ABS_NC/MOVT_ABS relocated is 0x%08X", (unsigned int)*((uint32_t*)relAddr));
break;
default:
FURI_LOG_E(TAG, " Undefined relocation %d", type);
return false;
}
return true;
}
static bool elf_relocate(ELFFile* elf, Elf32_Shdr* h, ELFSection* s) {
if(s->data) {
Elf32_Rel rel;
size_t relEntries = h->sh_size / sizeof(rel);
size_t relCount;
(void)storage_file_seek(elf->fd, h->sh_offset, true);
FURI_LOG_D(TAG, " Offset Info Type Name");
int relocate_result = true;
string_t symbol_name;
string_init(symbol_name);
for(relCount = 0; relCount < relEntries; relCount++) {
if(relCount % RESOLVER_THREAD_YIELD_STEP == 0) {
FURI_LOG_D(TAG, " reloc YIELD");
furi_delay_tick(1);
}
if(storage_file_read(elf->fd, &rel, sizeof(Elf32_Rel)) != sizeof(Elf32_Rel)) {
FURI_LOG_E(TAG, " reloc read fail");
string_clear(symbol_name);
return false;
}
Elf32_Addr symAddr;
int symEntry = ELF32_R_SYM(rel.r_info);
int relType = ELF32_R_TYPE(rel.r_info);
Elf32_Addr relAddr = ((Elf32_Addr)s->data) + rel.r_offset;
if(!address_cache_get(elf->relocation_cache, symEntry, &symAddr)) {
Elf32_Sym sym;
string_reset(symbol_name);
if(!elf_read_symbol(elf, symEntry, &sym, symbol_name)) {
FURI_LOG_E(TAG, " symbol read fail");
string_clear(symbol_name);
return false;
}
FURI_LOG_D(
TAG,
" %08X %08X %-16s %s",
(unsigned int)rel.r_offset,
(unsigned int)rel.r_info,
elf_reloc_type_to_str(relType),
string_get_cstr(symbol_name));
symAddr = elf_address_of(elf, &sym, string_get_cstr(symbol_name));
address_cache_put(elf->relocation_cache, symEntry, symAddr);
}
if(symAddr != ELF_INVALID_ADDRESS) {
FURI_LOG_D(
TAG,
" symAddr=%08X relAddr=%08X",
(unsigned int)symAddr,
(unsigned int)relAddr);
if(!elf_relocate_symbol(elf, relAddr, relType, symAddr)) {
relocate_result = false;
}
} else {
FURI_LOG_E(TAG, " No symbol address of %s", string_get_cstr(symbol_name));
relocate_result = false;
}
}
string_clear(symbol_name);
return relocate_result;
} else {
FURI_LOG_D(TAG, "Section not loaded");
}
return false;
}
/**************************************************************************************************/
/********************************************* MISC ***********************************************/
/**************************************************************************************************/
static bool cstr_prefix(const char* prefix, const char* string) {
return strncmp(prefix, string, strlen(prefix)) == 0;
}
/**************************************************************************************************/
/************************************ Internal FAP interfaces *************************************/
/**************************************************************************************************/
typedef enum {
SectionTypeERROR = 0,
SectionTypeUnused = 1 << 0,
SectionTypeData = 1 << 1,
SectionTypeRelData = 1 << 2,
SectionTypeSymTab = 1 << 3,
SectionTypeStrTab = 1 << 4,
SectionTypeManifest = 1 << 5,
SectionTypeDebugLink = 1 << 6,
SectionTypeValid = SectionTypeSymTab | SectionTypeStrTab | SectionTypeManifest,
} SectionType;
static bool elf_load_metadata(
ELFFile* elf,
Elf32_Shdr* section_header,
FlipperApplicationManifest* manifest) {
if(section_header->sh_size < sizeof(FlipperApplicationManifest)) {
return false;
}
if(manifest == NULL) {
return true;
}
return storage_file_seek(elf->fd, section_header->sh_offset, true) &&
storage_file_read(elf->fd, manifest, section_header->sh_size) ==
section_header->sh_size;
}
static bool elf_load_debug_link(ELFFile* elf, Elf32_Shdr* section_header) {
elf->debug_link_info.debug_link_size = section_header->sh_size;
elf->debug_link_info.debug_link = malloc(section_header->sh_size);
return storage_file_seek(elf->fd, section_header->sh_offset, true) &&
storage_file_read(elf->fd, elf->debug_link_info.debug_link, section_header->sh_size) ==
section_header->sh_size;
}
static SectionType elf_preload_section(
ELFFile* elf,
size_t section_idx,
Elf32_Shdr* section_header,
string_t name_string,
FlipperApplicationManifest* manifest) {
const char* name = string_get_cstr(name_string);
const struct {
const char* prefix;
SectionType type;
} lookup_sections[] = {
{".text", SectionTypeData},
{".rodata", SectionTypeData},
{".data", SectionTypeData},
{".bss", SectionTypeData},
{".preinit_array", SectionTypeData},
{".init_array", SectionTypeData},
{".fini_array", SectionTypeData},
{".rel.text", SectionTypeRelData},
{".rel.rodata", SectionTypeRelData},
{".rel.data", SectionTypeRelData},
{".rel.preinit_array", SectionTypeRelData},
{".rel.init_array", SectionTypeRelData},
{".rel.fini_array", SectionTypeRelData},
};
for(size_t i = 0; i < COUNT_OF(lookup_sections); i++) {
if(cstr_prefix(lookup_sections[i].prefix, name)) {
FURI_LOG_D(TAG, "Found section %s", lookup_sections[i].prefix);
if(lookup_sections[i].type == SectionTypeRelData) {
name = name + strlen(".rel");
}
ELFSection* section_p = elf_file_get_section(elf, name);
if(!section_p) {
ELFSection section = {
.data = NULL,
.sec_idx = 0,
.rel_sec_idx = 0,
.size = 0,
};
elf_file_put_section(elf, name, &section);
section_p = elf_file_get_section(elf, name);
}
if(lookup_sections[i].type == SectionTypeRelData) {
section_p->rel_sec_idx = section_idx;
} else {
section_p->sec_idx = section_idx;
}
return lookup_sections[i].type;
}
}
if(strcmp(name, ".symtab") == 0) {
FURI_LOG_D(TAG, "Found .symtab section");
elf->symbol_table = section_header->sh_offset;
elf->symbol_count = section_header->sh_size / sizeof(Elf32_Sym);
return SectionTypeSymTab;
} else if(strcmp(name, ".strtab") == 0) {
FURI_LOG_D(TAG, "Found .strtab section");
elf->symbol_table_strings = section_header->sh_offset;
return SectionTypeStrTab;
} else if(strcmp(name, ".fapmeta") == 0) {
FURI_LOG_D(TAG, "Found .fapmeta section");
if(elf_load_metadata(elf, section_header, manifest)) {
return SectionTypeManifest;
} else {
return SectionTypeERROR;
}
} else if(strcmp(name, ".gnu_debuglink") == 0) {
FURI_LOG_D(TAG, "Found .gnu_debuglink section");
if(elf_load_debug_link(elf, section_header)) {
return SectionTypeDebugLink;
} else {
return SectionTypeERROR;
}
}
return SectionTypeUnused;
}
static bool elf_load_section_data(ELFFile* elf, ELFSection* section) {
Elf32_Shdr section_header;
if(section->sec_idx == 0) {
FURI_LOG_D(TAG, "Section is not present");
return true;
}
if(!elf_read_section_header(elf, section->sec_idx, &section_header)) {
return false;
}
if(section_header.sh_size == 0) {
FURI_LOG_D(TAG, "No data for section");
return true;
}
section->data = aligned_malloc(section_header.sh_size, section_header.sh_addralign);
section->size = section_header.sh_size;
if(section_header.sh_type == SHT_NOBITS) {
/* section is empty (.bss?) */
/* no need to memset - allocator already did that */
return true;
}
if((!storage_file_seek(elf->fd, section_header.sh_offset, true)) ||
(storage_file_read(elf->fd, section->data, section_header.sh_size) !=
section_header.sh_size)) {
FURI_LOG_E(TAG, " seek/read fail");
return false;
}
FURI_LOG_D(TAG, "0x%X", section->data);
return true;
}
static bool elf_relocate_section(ELFFile* elf, ELFSection* section) {
Elf32_Shdr section_header;
if(section->rel_sec_idx) {
FURI_LOG_D(TAG, "Relocating section");
if(elf_read_section_header(elf, section->rel_sec_idx, &section_header))
return elf_relocate(elf, &section_header, section);
else {
FURI_LOG_E(TAG, "Error reading section header");
return false;
}
} else {
FURI_LOG_D(TAG, "No relocation index"); /* Not an error */
}
return true;
}
static void elf_file_call_section_list(ELFFile* elf, const char* name, bool reverse_order) {
ELFSection* section = elf_file_get_section(elf, name);
if(section && section->size) {
const uint32_t* start = section->data;
const uint32_t* end = section->data + section->size;
if(reverse_order) {
while(end > start) {
end--;
((void (*)(void))(*end))();
}
} else {
while(start < end) {
((void (*)(void))(*start))();
start++;
}
}
}
}
/**************************************************************************************************/
/********************************************* Public *********************************************/
/**************************************************************************************************/
ELFFile* elf_file_alloc(Storage* storage, const ElfApiInterface* api_interface) {
ELFFile* elf = malloc(sizeof(ELFFile));
elf->fd = storage_file_alloc(storage);
elf->api_interface = api_interface;
ELFSectionDict_init(elf->sections);
AddressCache_init(elf->trampoline_cache);
return elf;
}
void elf_file_free(ELFFile* elf) {
// free sections data
{
ELFSectionDict_it_t it;
for(ELFSectionDict_it(it, elf->sections); !ELFSectionDict_end_p(it);
ELFSectionDict_next(it)) {
const ELFSectionDict_itref_t* itref = ELFSectionDict_cref(it);
if(itref->value.data) {
aligned_free(itref->value.data);
}
free((void*)itref->key);
}
ELFSectionDict_clear(elf->sections);
}
// free trampoline data
{
AddressCache_it_t it;
for(AddressCache_it(it, elf->trampoline_cache); !AddressCache_end_p(it);
AddressCache_next(it)) {
const AddressCache_itref_t* itref = AddressCache_cref(it);
free((void*)itref->value);
}
AddressCache_clear(elf->trampoline_cache);
}
if(elf->debug_link_info.debug_link) {
free(elf->debug_link_info.debug_link);
}
storage_file_free(elf->fd);
free(elf);
}
bool elf_file_open(ELFFile* elf, const char* path) {
Elf32_Ehdr h;
Elf32_Shdr sH;
if(!storage_file_open(elf->fd, path, FSAM_READ, FSOM_OPEN_EXISTING) ||
!storage_file_seek(elf->fd, 0, true) ||
storage_file_read(elf->fd, &h, sizeof(h)) != sizeof(h) ||
!storage_file_seek(elf->fd, h.e_shoff + h.e_shstrndx * sizeof(sH), true) ||
storage_file_read(elf->fd, &sH, sizeof(Elf32_Shdr)) != sizeof(Elf32_Shdr)) {
return false;
}
elf->entry = h.e_entry;
elf->sections_count = h.e_shnum;
elf->section_table = h.e_shoff;
elf->section_table_strings = sH.sh_offset;
return true;
}
bool elf_file_load_manifest(ELFFile* elf, FlipperApplicationManifest* manifest) {
bool result = false;
string_t name;
string_init(name);
FURI_LOG_D(TAG, "Looking for manifest section");
for(size_t section_idx = 1; section_idx < elf->sections_count; section_idx++) {
Elf32_Shdr section_header;
string_reset(name);
if(!elf_read_section(elf, section_idx, &section_header, name)) {
break;
}
if(string_cmp(name, ".fapmeta") == 0) {
if(elf_load_metadata(elf, &section_header, manifest)) {
FURI_LOG_D(TAG, "Load manifest done");
result = true;
break;
} else {
break;
}
}
}
string_clear(name);
return result;
}
bool elf_file_load_section_table(ELFFile* elf, FlipperApplicationManifest* manifest) {
SectionType loaded_sections = SectionTypeERROR;
string_t name;
string_init(name);
FURI_LOG_D(TAG, "Scan ELF indexs...");
for(size_t section_idx = 1; section_idx < elf->sections_count; section_idx++) {
Elf32_Shdr section_header;
string_reset(name);
if(!elf_read_section(elf, section_idx, &section_header, name)) {
loaded_sections = SectionTypeERROR;
break;
}
FURI_LOG_D(TAG, "Preloading data for section #%d %s", section_idx, string_get_cstr(name));
SectionType section_type =
elf_preload_section(elf, section_idx, &section_header, name, manifest);
loaded_sections |= section_type;
if(section_type == SectionTypeERROR) {
loaded_sections = SectionTypeERROR;
break;
}
}
string_clear(name);
FURI_LOG_D(TAG, "Load symbols done");
return IS_FLAGS_SET(loaded_sections, SectionTypeValid);
}
ELFFileLoadStatus elf_file_load_sections(ELFFile* elf) {
ELFFileLoadStatus status = ELFFileLoadStatusSuccess;
ELFSectionDict_it_t it;
AddressCache_init(elf->relocation_cache);
size_t start = furi_get_tick();
for(ELFSectionDict_it(it, elf->sections); !ELFSectionDict_end_p(it); ELFSectionDict_next(it)) {
ELFSectionDict_itref_t* itref = ELFSectionDict_ref(it);
FURI_LOG_D(TAG, "Loading section '%s'", itref->key);
if(!elf_load_section_data(elf, &itref->value)) {
FURI_LOG_E(TAG, "Error loading section '%s'", itref->key);
status = ELFFileLoadStatusUnspecifiedError;
}
}
if(status == ELFFileLoadStatusSuccess) {
for(ELFSectionDict_it(it, elf->sections); !ELFSectionDict_end_p(it);
ELFSectionDict_next(it)) {
ELFSectionDict_itref_t* itref = ELFSectionDict_ref(it);
FURI_LOG_D(TAG, "Relocating section '%s'", itref->key);
if(!elf_relocate_section(elf, &itref->value)) {
FURI_LOG_E(TAG, "Error relocating section '%s'", itref->key);
status = ELFFileLoadStatusMissingImports;
}
}
}
/* Fixing up entry point */
if(status == ELFFileLoadStatusSuccess) {
ELFSection* text_section = elf_file_get_section(elf, ".text");
if(text_section == NULL) {
FURI_LOG_E(TAG, "No .text section found");
status = ELFFileLoadStatusUnspecifiedError;
} else {
elf->entry += (uint32_t)text_section->data;
}
}
FURI_LOG_D(TAG, "Relocation cache size: %u", AddressCache_size(elf->relocation_cache));
FURI_LOG_D(TAG, "Trampoline cache size: %u", AddressCache_size(elf->trampoline_cache));
AddressCache_clear(elf->relocation_cache);
FURI_LOG_I(TAG, "Loaded in %ums", (size_t)(furi_get_tick() - start));
return status;
}
void elf_file_pre_run(ELFFile* elf) {
elf_file_call_section_list(elf, ".preinit_array", false);
elf_file_call_section_list(elf, ".init_array", false);
}
int32_t elf_file_run(ELFFile* elf, void* args) {
int32_t result;
result = ((int32_t(*)(void*))elf->entry)(args);
return result;
}
void elf_file_post_run(ELFFile* elf) {
elf_file_call_section_list(elf, ".fini_array", true);
}
const ElfApiInterface* elf_file_get_api_interface(ELFFile* elf_file) {
return elf_file->api_interface;
}
void elf_file_init_debug_info(ELFFile* elf, ELFDebugInfo* debug_info) {
// set entry
debug_info->entry = elf->entry;
// copy debug info
memcpy(&debug_info->debug_link_info, &elf->debug_link_info, sizeof(ELFDebugLinkInfo));
// init mmap
debug_info->mmap_entry_count = ELFSectionDict_size(elf->sections);
debug_info->mmap_entries = malloc(sizeof(ELFMemoryMapEntry) * debug_info->mmap_entry_count);
uint32_t mmap_entry_idx = 0;
ELFSectionDict_it_t it;
for(ELFSectionDict_it(it, elf->sections); !ELFSectionDict_end_p(it); ELFSectionDict_next(it)) {
const ELFSectionDict_itref_t* itref = ELFSectionDict_cref(it);
const void* data_ptr = itref->value.data;
if(data_ptr) {
debug_info->mmap_entries[mmap_entry_idx].address = (uint32_t)data_ptr;
debug_info->mmap_entries[mmap_entry_idx].name = itref->key;
mmap_entry_idx++;
}
}
}
void elf_file_clear_debug_info(ELFDebugInfo* debug_info) {
// clear debug info
memset(&debug_info->debug_link_info, 0, sizeof(ELFDebugLinkInfo));
// clear mmap
if(debug_info->mmap_entries) {
free(debug_info->mmap_entries);
debug_info->mmap_entries = NULL;
}
debug_info->mmap_entry_count = 0;
}

View File

@@ -0,0 +1,127 @@
/**
* @file elf_file.h
* ELF file loader
*/
#pragma once
#include <storage/storage.h>
#include "../application_manifest.h"
#include "elf_api_interface.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct ELFFile ELFFile;
typedef struct {
const char* name;
uint32_t address;
} ELFMemoryMapEntry;
typedef struct {
uint32_t debug_link_size;
uint8_t* debug_link;
} ELFDebugLinkInfo;
typedef struct {
uint32_t mmap_entry_count;
ELFMemoryMapEntry* mmap_entries;
ELFDebugLinkInfo debug_link_info;
off_t entry;
} ELFDebugInfo;
typedef enum {
ELFFileLoadStatusSuccess = 0,
ELFFileLoadStatusUnspecifiedError,
ELFFileLoadStatusNoFreeMemory,
ELFFileLoadStatusMissingImports,
} ELFFileLoadStatus;
/**
* @brief Allocate ELFFile instance
* @param storage
* @param api_interface
* @return ELFFile*
*/
ELFFile* elf_file_alloc(Storage* storage, const ElfApiInterface* api_interface);
/**
* @brief Free ELFFile instance
* @param elf_file
*/
void elf_file_free(ELFFile* elf_file);
/**
* @brief Open ELF file
* @param elf_file
* @param path
* @return bool
*/
bool elf_file_open(ELFFile* elf_file, const char* path);
/**
* @brief Load ELF file manifest
* @param elf
* @param manifest
* @return bool
*/
bool elf_file_load_manifest(ELFFile* elf, FlipperApplicationManifest* manifest);
/**
* @brief Load ELF file section table (load stage #1)
* @param elf_file
* @param manifest
* @return bool
*/
bool elf_file_load_section_table(ELFFile* elf_file, FlipperApplicationManifest* manifest);
/**
* @brief Load and relocate ELF file sections (load stage #2)
* @param elf_file
* @return ELFFileLoadStatus
*/
ELFFileLoadStatus elf_file_load_sections(ELFFile* elf_file);
/**
* @brief Execute ELF file pre-run stage, call static constructors for example (load stage #3)
* @param elf
*/
void elf_file_pre_run(ELFFile* elf);
/**
* @brief Run ELF file (load stage #4)
* @param elf_file
* @param args
* @return int32_t
*/
int32_t elf_file_run(ELFFile* elf_file, void* args);
/**
* @brief Execute ELF file post-run stage, call static destructors for example (load stage #5)
* @param elf
*/
void elf_file_post_run(ELFFile* elf);
/**
* @brief Get ELF file API interface
* @param elf_file
* @return const ElfApiInterface*
*/
const ElfApiInterface* elf_file_get_api_interface(ELFFile* elf_file);
/**
* @brief Get ELF file debug info
* @param elf_file
* @param debug_info
*/
void elf_file_init_debug_info(ELFFile* elf_file, ELFDebugInfo* debug_info);
/**
* @brief Clear ELF file debug info generated by elf_file_init_debug_info
* @param debug_info
*/
void elf_file_clear_debug_info(ELFDebugInfo* debug_info);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,46 @@
#pragma once
#include "elf_file.h"
#include <m-dict.h>
#ifdef __cplusplus
extern "C" {
#endif
DICT_DEF2(AddressCache, int, M_DEFAULT_OPLIST, Elf32_Addr, M_DEFAULT_OPLIST)
/**
* Callable elf entry type
*/
typedef int32_t(entry_t)(void*);
typedef struct {
void* data;
uint16_t sec_idx;
uint16_t rel_sec_idx;
Elf32_Word size;
} ELFSection;
DICT_DEF2(ELFSectionDict, const char*, M_CSTR_OPLIST, ELFSection, M_POD_OPLIST)
struct ELFFile {
size_t sections_count;
off_t section_table;
off_t section_table_strings;
size_t symbol_count;
off_t symbol_table;
off_t symbol_table_strings;
off_t entry;
ELFSectionDict_t sections;
AddressCache_t relocation_cache;
AddressCache_t trampoline_cache;
File* fd;
const ElfApiInterface* api_interface;
ELFDebugLinkInfo debug_link_info;
};
#ifdef __cplusplus
}
#endif

View File

@@ -1,477 +0,0 @@
#include "flipper_application_i.h"
#include <furi.h>
#define TAG "fapp-i"
#define RESOLVER_THREAD_YIELD_STEP 30
#define IS_FLAGS_SET(v, m) ((v & m) == m)
#define SECTION_OFFSET(e, n) (e->section_table + n * sizeof(Elf32_Shdr))
#define SYMBOL_OFFSET(e, n) (e->_table + n * sizeof(Elf32_Shdr))
bool flipper_application_load_elf_headers(FlipperApplication* e, const char* path) {
Elf32_Ehdr h;
Elf32_Shdr sH;
if(!storage_file_open(e->fd, path, FSAM_READ, FSOM_OPEN_EXISTING) ||
!storage_file_seek(e->fd, 0, true) ||
storage_file_read(e->fd, &h, sizeof(h)) != sizeof(h) ||
!storage_file_seek(e->fd, h.e_shoff + h.e_shstrndx * sizeof(sH), true) ||
storage_file_read(e->fd, &sH, sizeof(Elf32_Shdr)) != sizeof(Elf32_Shdr)) {
return false;
}
e->entry = h.e_entry;
e->sections = h.e_shnum;
e->section_table = h.e_shoff;
e->section_table_strings = sH.sh_offset;
return true;
}
static bool flipper_application_load_metadata(FlipperApplication* e, Elf32_Shdr* sh) {
if(sh->sh_size < sizeof(e->manifest)) {
return false;
}
return storage_file_seek(e->fd, sh->sh_offset, true) &&
storage_file_read(e->fd, &e->manifest, sh->sh_size) == sh->sh_size;
}
static bool flipper_application_load_debug_link(FlipperApplication* e, Elf32_Shdr* sh) {
e->state.debug_link_size = sh->sh_size;
e->state.debug_link = malloc(sh->sh_size);
return storage_file_seek(e->fd, sh->sh_offset, true) &&
storage_file_read(e->fd, e->state.debug_link, sh->sh_size) == sh->sh_size;
}
static FindFlags_t flipper_application_preload_section(
FlipperApplication* e,
Elf32_Shdr* sh,
const char* name,
int n) {
FURI_LOG_D(TAG, "Processing: %s", name);
const struct {
const char* name;
uint16_t* ptr_section_idx;
FindFlags_t flags;
} lookup_sections[] = {
{".text", &e->text.sec_idx, FoundText},
{".rodata", &e->rodata.sec_idx, FoundRodata},
{".data", &e->data.sec_idx, FoundData},
{".bss", &e->bss.sec_idx, FoundBss},
{".rel.text", &e->text.rel_sec_idx, FoundRelText},
{".rel.rodata", &e->rodata.rel_sec_idx, FoundRelRodata},
{".rel.data", &e->data.rel_sec_idx, FoundRelData},
};
for(size_t i = 0; i < COUNT_OF(lookup_sections); i++) {
if(strcmp(name, lookup_sections[i].name) == 0) {
*lookup_sections[i].ptr_section_idx = n;
return lookup_sections[i].flags;
}
}
if(strcmp(name, ".symtab") == 0) {
e->symbol_table = sh->sh_offset;
e->symbol_count = sh->sh_size / sizeof(Elf32_Sym);
return FoundSymTab;
} else if(strcmp(name, ".strtab") == 0) {
e->symbol_table_strings = sh->sh_offset;
return FoundStrTab;
} else if(strcmp(name, ".fapmeta") == 0) {
// Load metadata immediately
if(flipper_application_load_metadata(e, sh)) {
return FoundFappManifest;
}
} else if(strcmp(name, ".gnu_debuglink") == 0) {
if(flipper_application_load_debug_link(e, sh)) {
return FoundDebugLink;
}
}
return FoundERROR;
}
static bool
read_string_from_offset(FlipperApplication* e, off_t offset, char* buffer, size_t buffer_size) {
bool success = false;
off_t old = storage_file_tell(e->fd);
if(storage_file_seek(e->fd, offset, true) &&
(storage_file_read(e->fd, buffer, buffer_size) == buffer_size)) {
success = true;
}
storage_file_seek(e->fd, old, true);
return success;
}
static bool read_section_name(FlipperApplication* e, off_t off, char* buf, size_t max) {
return read_string_from_offset(e, e->section_table_strings + off, buf, max);
}
static bool read_symbol_name(FlipperApplication* e, off_t off, char* buf, size_t max) {
return read_string_from_offset(e, e->symbol_table_strings + off, buf, max);
}
static bool read_section_header(FlipperApplication* e, int n, Elf32_Shdr* h) {
off_t offset = SECTION_OFFSET(e, n);
return storage_file_seek(e->fd, offset, true) &&
storage_file_read(e->fd, h, sizeof(Elf32_Shdr)) == sizeof(Elf32_Shdr);
}
static bool read_section(FlipperApplication* e, int n, Elf32_Shdr* h, char* name, size_t nlen) {
if(!read_section_header(e, n, h)) {
return false;
}
if(!h->sh_name) {
return true;
}
return read_section_name(e, h->sh_name, name, nlen);
}
bool flipper_application_load_section_table(FlipperApplication* e) {
furi_check(e->state.mmap_entry_count == 0);
size_t n;
FindFlags_t found = FoundERROR;
FURI_LOG_D(TAG, "Scan ELF indexs...");
for(n = 1; n < e->sections; n++) {
Elf32_Shdr section_header;
char name[33] = {0};
if(!read_section_header(e, n, &section_header)) {
return false;
}
if(section_header.sh_name &&
!read_section_name(e, section_header.sh_name, name, sizeof(name))) {
return false;
}
FURI_LOG_T(TAG, "Examining section %d %s", n, name);
FindFlags_t section_flags =
flipper_application_preload_section(e, &section_header, name, n);
found |= section_flags;
if((section_flags & FoundGdbSection) != 0) {
e->state.mmap_entry_count++;
}
if(IS_FLAGS_SET(found, FoundAll)) {
return true;
}
}
FURI_LOG_D(TAG, "Load symbols done");
return IS_FLAGS_SET(found, FoundValid);
}
static const char* type_to_str(int symt) {
#define STRCASE(name) \
case name: \
return #name;
switch(symt) {
STRCASE(R_ARM_NONE)
STRCASE(R_ARM_ABS32)
STRCASE(R_ARM_THM_PC22)
STRCASE(R_ARM_THM_JUMP24)
default:
return "R_<unknow>";
}
#undef STRCASE
}
static void relocate_jmp_call(Elf32_Addr relAddr, int type, Elf32_Addr symAddr) {
UNUSED(type);
uint16_t upper_insn = ((uint16_t*)relAddr)[0];
uint16_t lower_insn = ((uint16_t*)relAddr)[1];
uint32_t S = (upper_insn >> 10) & 1;
uint32_t J1 = (lower_insn >> 13) & 1;
uint32_t J2 = (lower_insn >> 11) & 1;
int32_t offset = (S << 24) | /* S -> offset[24] */
((~(J1 ^ S) & 1) << 23) | /* J1 -> offset[23] */
((~(J2 ^ S) & 1) << 22) | /* J2 -> offset[22] */
((upper_insn & 0x03ff) << 12) | /* imm10 -> offset[12:21] */
((lower_insn & 0x07ff) << 1); /* imm11 -> offset[1:11] */
if(offset & 0x01000000) offset -= 0x02000000;
offset += symAddr - relAddr;
S = (offset >> 24) & 1;
J1 = S ^ (~(offset >> 23) & 1);
J2 = S ^ (~(offset >> 22) & 1);
upper_insn = ((upper_insn & 0xf800) | (S << 10) | ((offset >> 12) & 0x03ff));
((uint16_t*)relAddr)[0] = upper_insn;
lower_insn = ((lower_insn & 0xd000) | (J1 << 13) | (J2 << 11) | ((offset >> 1) & 0x07ff));
((uint16_t*)relAddr)[1] = lower_insn;
}
static bool relocate_symbol(Elf32_Addr relAddr, int type, Elf32_Addr symAddr) {
switch(type) {
case R_ARM_ABS32:
*((uint32_t*)relAddr) += symAddr;
FURI_LOG_D(TAG, " R_ARM_ABS32 relocated is 0x%08X", (unsigned int)*((uint32_t*)relAddr));
break;
case R_ARM_THM_PC22:
case R_ARM_THM_JUMP24:
relocate_jmp_call(relAddr, type, symAddr);
FURI_LOG_D(
TAG, " R_ARM_THM_CALL/JMP relocated is 0x%08X", (unsigned int)*((uint32_t*)relAddr));
break;
default:
FURI_LOG_D(TAG, " Undefined relocation %d", type);
return false;
}
return true;
}
static ELFSection_t* section_of(FlipperApplication* e, int index) {
if(e->text.sec_idx == index) {
return &e->text;
} else if(e->data.sec_idx == index) {
return &e->data;
} else if(e->bss.sec_idx == index) {
return &e->bss;
} else if(e->rodata.sec_idx == index) {
return &e->rodata;
}
return NULL;
}
static Elf32_Addr address_of(FlipperApplication* e, Elf32_Sym* sym, const char* sName) {
if(sym->st_shndx == SHN_UNDEF) {
Elf32_Addr addr = 0;
if(e->api_interface->resolver_callback(sName, &addr)) {
return addr;
}
} else {
ELFSection_t* symSec = section_of(e, sym->st_shndx);
if(symSec) {
return ((Elf32_Addr)symSec->data) + sym->st_value;
}
}
FURI_LOG_D(TAG, " Can not find address for symbol %s", sName);
return ELF_INVALID_ADDRESS;
}
static bool read_symbol(FlipperApplication* e, int n, Elf32_Sym* sym, char* name, size_t nlen) {
bool success = false;
off_t old = storage_file_tell(e->fd);
off_t pos = e->symbol_table + n * sizeof(Elf32_Sym);
if(storage_file_seek(e->fd, pos, true) &&
storage_file_read(e->fd, sym, sizeof(Elf32_Sym)) == sizeof(Elf32_Sym)) {
if(sym->st_name)
success = read_symbol_name(e, sym->st_name, name, nlen);
else {
Elf32_Shdr shdr;
success = read_section(e, sym->st_shndx, &shdr, name, nlen);
}
}
storage_file_seek(e->fd, old, true);
return success;
}
static bool
relocation_cache_get(RelocationAddressCache_t cache, int symEntry, Elf32_Addr* symAddr) {
Elf32_Addr* addr = RelocationAddressCache_get(cache, symEntry);
if(addr) {
*symAddr = *addr;
return true;
} else {
return false;
}
}
static void
relocation_cache_put(RelocationAddressCache_t cache, int symEntry, Elf32_Addr symAddr) {
RelocationAddressCache_set_at(cache, symEntry, symAddr);
}
#define MAX_SYMBOL_NAME_LEN 128u
static bool relocate(FlipperApplication* e, Elf32_Shdr* h, ELFSection_t* s) {
if(s->data) {
Elf32_Rel rel;
size_t relEntries = h->sh_size / sizeof(rel);
size_t relCount;
(void)storage_file_seek(e->fd, h->sh_offset, true);
FURI_LOG_D(TAG, " Offset Info Type Name");
int relocate_result = true;
char symbol_name[MAX_SYMBOL_NAME_LEN + 1] = {0};
for(relCount = 0; relCount < relEntries; relCount++) {
if(relCount % RESOLVER_THREAD_YIELD_STEP == 0) {
FURI_LOG_D(TAG, " reloc YIELD");
furi_delay_tick(1);
}
if(storage_file_read(e->fd, &rel, sizeof(Elf32_Rel)) != sizeof(Elf32_Rel)) {
FURI_LOG_E(TAG, " reloc read fail");
return false;
}
Elf32_Addr symAddr;
int symEntry = ELF32_R_SYM(rel.r_info);
int relType = ELF32_R_TYPE(rel.r_info);
Elf32_Addr relAddr = ((Elf32_Addr)s->data) + rel.r_offset;
if(!relocation_cache_get(e->relocation_cache, symEntry, &symAddr)) {
Elf32_Sym sym;
if(!read_symbol(e, symEntry, &sym, symbol_name, MAX_SYMBOL_NAME_LEN)) {
FURI_LOG_E(TAG, " symbol read fail");
return false;
}
FURI_LOG_D(
TAG,
" %08X %08X %-16s %s",
(unsigned int)rel.r_offset,
(unsigned int)rel.r_info,
type_to_str(relType),
symbol_name);
symAddr = address_of(e, &sym, symbol_name);
relocation_cache_put(e->relocation_cache, symEntry, symAddr);
}
if(symAddr != ELF_INVALID_ADDRESS) {
FURI_LOG_D(
TAG,
" symAddr=%08X relAddr=%08X",
(unsigned int)symAddr,
(unsigned int)relAddr);
if(!relocate_symbol(relAddr, relType, symAddr)) {
relocate_result = false;
}
} else {
FURI_LOG_D(TAG, " No symbol address of %s", symbol_name);
relocate_result = false;
}
}
return relocate_result;
} else
FURI_LOG_I(TAG, "Section not loaded");
return false;
}
static bool flipper_application_load_section_data(FlipperApplication* e, ELFSection_t* s) {
Elf32_Shdr section_header;
if(s->sec_idx == 0) {
FURI_LOG_I(TAG, "Section is not present");
return true;
}
if(!read_section_header(e, s->sec_idx, &section_header)) {
return false;
}
if(section_header.sh_size == 0) {
FURI_LOG_I(TAG, "No data for section");
return true;
}
s->data = aligned_malloc(section_header.sh_size, section_header.sh_addralign);
// e->state.mmap_entry_count++;
if(section_header.sh_type == SHT_NOBITS) {
/* section is empty (.bss?) */
/* no need to memset - allocator already did that */
/* memset(s->data, 0, h->sh_size); */
FURI_LOG_D(TAG, "0x%X", s->data);
return true;
}
if((!storage_file_seek(e->fd, section_header.sh_offset, true)) ||
(storage_file_read(e->fd, s->data, section_header.sh_size) != section_header.sh_size)) {
FURI_LOG_E(TAG, " seek/read fail");
flipper_application_free_section(s);
return false;
}
FURI_LOG_D(TAG, "0x%X", s->data);
return true;
}
static bool flipper_application_relocate_section(FlipperApplication* e, ELFSection_t* s) {
Elf32_Shdr section_header;
if(s->rel_sec_idx) {
FURI_LOG_D(TAG, "Relocating section");
if(read_section_header(e, s->rel_sec_idx, &section_header))
return relocate(e, &section_header, s);
else {
FURI_LOG_E(TAG, "Error reading section header");
return false;
}
} else
FURI_LOG_D(TAG, "No relocation index"); /* Not an error */
return true;
}
FlipperApplicationLoadStatus flipper_application_load_sections(FlipperApplication* e) {
FlipperApplicationLoadStatus status = FlipperApplicationLoadStatusSuccess;
RelocationAddressCache_init(e->relocation_cache);
size_t start = furi_get_tick();
struct {
ELFSection_t* section;
const char* name;
} sections[] = {
{&e->text, ".text"},
{&e->rodata, ".rodata"},
{&e->data, ".data"},
{&e->bss, ".bss"},
};
for(size_t i = 0; i < COUNT_OF(sections); i++) {
if(!flipper_application_load_section_data(e, sections[i].section)) {
FURI_LOG_E(TAG, "Error loading section '%s'", sections[i].name);
status = FlipperApplicationLoadStatusUnspecifiedError;
}
}
if(status == FlipperApplicationLoadStatusSuccess) {
for(size_t i = 0; i < COUNT_OF(sections); i++) {
if(!flipper_application_relocate_section(e, sections[i].section)) {
FURI_LOG_E(TAG, "Error relocating section '%s'", sections[i].name);
status = FlipperApplicationLoadStatusMissingImports;
}
}
}
if(status == FlipperApplicationLoadStatusSuccess) {
e->state.mmap_entries =
malloc(sizeof(FlipperApplicationMemoryMapEntry) * e->state.mmap_entry_count);
uint32_t mmap_entry_idx = 0;
for(size_t i = 0; i < COUNT_OF(sections); i++) {
const void* data_ptr = sections[i].section->data;
if(data_ptr) {
FURI_LOG_I(TAG, "0x%X %s", (uint32_t)data_ptr, sections[i].name);
e->state.mmap_entries[mmap_entry_idx].address = (uint32_t)data_ptr;
e->state.mmap_entries[mmap_entry_idx].name = sections[i].name;
mmap_entry_idx++;
}
}
furi_check(mmap_entry_idx == e->state.mmap_entry_count);
/* Fixing up entry point */
e->entry += (uint32_t)e->text.data;
}
FURI_LOG_D(TAG, "Relocation cache size: %u", RelocationAddressCache_size(e->relocation_cache));
RelocationAddressCache_clear(e->relocation_cache);
FURI_LOG_I(TAG, "Loaded in %ums", (size_t)(furi_get_tick() - start));
return status;
}
void flipper_application_free_section(ELFSection_t* s) {
if(s->data) {
aligned_free(s->data);
}
s->data = NULL;
}

View File

@@ -1,16 +1,22 @@
#include "flipper_application.h"
#include "flipper_application_i.h"
#include "elf/elf_file.h"
#define TAG "fapp"
struct FlipperApplication {
ELFDebugInfo state;
FlipperApplicationManifest manifest;
ELFFile* elf;
FuriThread* thread;
};
/* For debugger access to app state */
FlipperApplication* last_loaded_app = NULL;
FlipperApplication*
flipper_application_alloc(Storage* storage, const ElfApiInterface* api_interface) {
FlipperApplication* app = malloc(sizeof(FlipperApplication));
app->api_interface = api_interface;
app->fd = storage_file_alloc(storage);
app->elf = elf_file_alloc(storage, api_interface);
app->thread = NULL;
return app;
}
@@ -25,56 +31,71 @@ void flipper_application_free(FlipperApplication* app) {
last_loaded_app = NULL;
if(app->state.debug_link_size) {
free(app->state.debug_link);
}
if(app->state.mmap_entries) {
free(app->state.mmap_entries);
}
ELFSection_t* sections[] = {&app->text, &app->rodata, &app->data, &app->bss};
for(size_t i = 0; i < COUNT_OF(sections); i++) {
flipper_application_free_section(sections[i]);
}
storage_file_free(app->fd);
elf_file_clear_debug_info(&app->state);
elf_file_free(app->elf);
free(app);
}
/* Parse headers, load manifest */
FlipperApplicationPreloadStatus
flipper_application_preload(FlipperApplication* app, const char* path) {
if(!flipper_application_load_elf_headers(app, path) ||
!flipper_application_load_section_table(app)) {
return FlipperApplicationPreloadStatusInvalidFile;
}
if((app->manifest.base.manifest_magic != FAP_MANIFEST_MAGIC) &&
(app->manifest.base.manifest_version == FAP_MANIFEST_SUPPORTED_VERSION)) {
static FlipperApplicationPreloadStatus
flipper_application_validate_manifest(FlipperApplication* app) {
if(!flipper_application_manifest_is_valid(&app->manifest)) {
return FlipperApplicationPreloadStatusInvalidManifest;
}
if(app->manifest.base.api_version.major != app->api_interface->api_version_major /* ||
app->manifest.base.api_version.minor > app->api_interface->api_version_minor */) {
if(!flipper_application_manifest_is_compatible(
&app->manifest, elf_file_get_api_interface(app->elf))) {
return FlipperApplicationPreloadStatusApiMismatch;
}
return FlipperApplicationPreloadStatusSuccess;
}
/* Parse headers, load manifest */
FlipperApplicationPreloadStatus
flipper_application_preload_manifest(FlipperApplication* app, const char* path) {
if(!elf_file_open(app->elf, path) || !elf_file_load_manifest(app->elf, &app->manifest)) {
return FlipperApplicationPreloadStatusInvalidFile;
}
return flipper_application_validate_manifest(app);
}
/* Parse headers, load full file */
FlipperApplicationPreloadStatus
flipper_application_preload(FlipperApplication* app, const char* path) {
if(!elf_file_open(app->elf, path) || !elf_file_load_section_table(app->elf, &app->manifest)) {
return FlipperApplicationPreloadStatusInvalidFile;
}
return flipper_application_validate_manifest(app);
}
const FlipperApplicationManifest* flipper_application_get_manifest(FlipperApplication* app) {
return &app->manifest;
}
FlipperApplicationLoadStatus flipper_application_map_to_memory(FlipperApplication* app) {
last_loaded_app = app;
return flipper_application_load_sections(app);
ELFFileLoadStatus status = elf_file_load_sections(app->elf);
switch(status) {
case ELFFileLoadStatusSuccess:
elf_file_init_debug_info(app->elf, &app->state);
return FlipperApplicationLoadStatusSuccess;
case ELFFileLoadStatusNoFreeMemory:
return FlipperApplicationLoadStatusNoFreeMemory;
case ELFFileLoadStatusMissingImports:
return FlipperApplicationLoadStatusMissingImports;
default:
return FlipperApplicationLoadStatusUnspecifiedError;
}
}
const FlipperApplicationState* flipper_application_get_state(FlipperApplication* app) {
return &app->state;
static int32_t flipper_application_thread(void* context) {
elf_file_pre_run(last_loaded_app->elf);
int32_t result = elf_file_run(last_loaded_app->elf, context);
elf_file_post_run(last_loaded_app->elf);
return result;
}
FuriThread* flipper_application_spawn(FlipperApplication* app, void* args) {
@@ -86,20 +107,12 @@ FuriThread* flipper_application_spawn(FlipperApplication* app, void* args) {
app->thread = furi_thread_alloc();
furi_thread_set_stack_size(app->thread, manifest->stack_size);
furi_thread_set_name(app->thread, manifest->name);
furi_thread_set_callback(app->thread, (entry_t*)app->entry);
furi_thread_set_callback(app->thread, flipper_application_thread);
furi_thread_set_context(app->thread, args);
return app->thread;
}
FuriThread* flipper_application_get_thread(FlipperApplication* app) {
return app->thread;
}
void const* flipper_application_get_entry_address(FlipperApplication* app) {
return (void*)app->entry;
}
static const char* preload_status_strings[] = {
[FlipperApplicationPreloadStatusSuccess] = "Success",
[FlipperApplicationPreloadStatusUnspecifiedError] = "Unknown error",

View File

@@ -1,3 +1,7 @@
/**
* @file flipper_application.h
* Flipper application
*/
#pragma once
#include "application_manifest.h"
@@ -79,6 +83,14 @@ void flipper_application_free(FlipperApplication* app);
FlipperApplicationPreloadStatus
flipper_application_preload(FlipperApplication* app, const char* path);
/**
* @brief Validate elf file and load application manifest
* @param app Application pointer
* @return Preload result code
*/
FlipperApplicationPreloadStatus
flipper_application_preload_manifest(FlipperApplication* app, const char* path);
/**
* @brief Get pointer to application manifest for preloaded application
* @param app Application pointer
@@ -93,13 +105,6 @@ const FlipperApplicationManifest* flipper_application_get_manifest(FlipperApplic
*/
FlipperApplicationLoadStatus flipper_application_map_to_memory(FlipperApplication* app);
/**
* @brief Get state object for loaded application
* @param app Application pointer
* @return Pointer to state object
*/
const FlipperApplicationState* flipper_application_get_state(FlipperApplication* app);
/**
* @brief Create application thread at entry point address, using app name and
* stack size from metadata. Returned thread isn't started yet.
@@ -110,20 +115,6 @@ const FlipperApplicationState* flipper_application_get_state(FlipperApplication*
*/
FuriThread* flipper_application_spawn(FlipperApplication* app, void* args);
/**
* @brief Get previously spawned thread
* @param app Application pointer
* @return Created thread
*/
FuriThread* flipper_application_get_thread(FlipperApplication* app);
/**
* @brief Return relocated and valid address of app's entry point
* @param app Application pointer
* @return Address of app's entry point
*/
void const* flipper_application_get_entry_address(FlipperApplication* app);
#ifdef __cplusplus
}
#endif

View File

@@ -1,99 +0,0 @@
#pragma once
#include "elf.h"
#include "flipper_application.h"
#include <m-dict.h>
#ifdef __cplusplus
extern "C" {
#endif
DICT_DEF2(RelocationAddressCache, int, M_DEFAULT_OPLIST, Elf32_Addr, M_DEFAULT_OPLIST)
/**
* Callable elf entry type
*/
typedef int32_t(entry_t)(void*);
typedef struct {
void* data;
uint16_t sec_idx;
uint16_t rel_sec_idx;
} ELFSection_t;
struct FlipperApplication {
const ElfApiInterface* api_interface;
File* fd;
FlipperApplicationState state;
FlipperApplicationManifest manifest;
size_t sections;
off_t section_table;
off_t section_table_strings;
size_t symbol_count;
off_t symbol_table;
off_t symbol_table_strings;
off_t entry;
ELFSection_t text;
ELFSection_t rodata;
ELFSection_t data;
ELFSection_t bss;
FuriThread* thread;
RelocationAddressCache_t relocation_cache;
};
typedef enum {
FoundERROR = 0,
FoundSymTab = (1 << 0),
FoundStrTab = (1 << 2),
FoundText = (1 << 3),
FoundRodata = (1 << 4),
FoundData = (1 << 5),
FoundBss = (1 << 6),
FoundRelText = (1 << 7),
FoundRelRodata = (1 << 8),
FoundRelData = (1 << 9),
FoundRelBss = (1 << 10),
FoundFappManifest = (1 << 11),
FoundDebugLink = (1 << 12),
FoundValid = FoundSymTab | FoundStrTab | FoundFappManifest,
FoundExec = FoundValid | FoundText,
FoundGdbSection = FoundText | FoundRodata | FoundData | FoundBss,
FoundAll = FoundSymTab | FoundStrTab | FoundText | FoundRodata | FoundData | FoundBss |
FoundRelText | FoundRelRodata | FoundRelData | FoundRelBss | FoundDebugLink,
} FindFlags_t;
/**
* @brief Load and validate basic ELF file headers
* @param e Application instance
* @param path FS path to application file
* @return true if ELF file is valid
*/
bool flipper_application_load_elf_headers(FlipperApplication* e, const char* path);
/**
* @brief Iterate over all sections and save related indexes
* @param e Application instance
* @return true if all required sections are found
*/
bool flipper_application_load_section_table(FlipperApplication* e);
/**
* @brief Load section data to memory and process relocations
* @param e Application instance
* @return Status code
*/
FlipperApplicationLoadStatus flipper_application_load_sections(FlipperApplication* e);
/**
* @brief Release section data
* @param s section pointer
*/
void flipper_application_free_section(ELFSection_t* s);
#ifdef __cplusplus
}
#endif

View File

@@ -5,6 +5,7 @@
#define MF_CLASSIC_DICT_FLIPPER_PATH EXT_PATH("nfc/assets/mf_classic_dict.nfc")
#define MF_CLASSIC_DICT_USER_PATH EXT_PATH("nfc/assets/mf_classic_dict_user.nfc")
#define MF_CLASSIC_DICT_UNIT_TEST_PATH EXT_PATH("unit_tests/mf_classic_dict.nfc")
#define TAG "MfClassicDict"
@@ -23,6 +24,9 @@ bool mf_classic_dict_check_presence(MfClassicDictType dict_type) {
dict_present = storage_common_stat(storage, MF_CLASSIC_DICT_FLIPPER_PATH, NULL) == FSE_OK;
} else if(dict_type == MfClassicDictTypeUser) {
dict_present = storage_common_stat(storage, MF_CLASSIC_DICT_USER_PATH, NULL) == FSE_OK;
} else if(dict_type == MfClassicDictTypeUnitTest) {
dict_present = storage_common_stat(storage, MF_CLASSIC_DICT_UNIT_TEST_PATH, NULL) ==
FSE_OK;
}
furi_record_close(RECORD_STORAGE);
@@ -50,6 +54,15 @@ MfClassicDict* mf_classic_dict_alloc(MfClassicDictType dict_type) {
buffered_file_stream_close(dict->stream);
break;
}
} else if(dict_type == MfClassicDictTypeUnitTest) {
if(!buffered_file_stream_open(
dict->stream,
MF_CLASSIC_DICT_UNIT_TEST_PATH,
FSAM_READ_WRITE,
FSOM_CREATE_ALWAYS)) {
buffered_file_stream_close(dict->stream);
break;
}
}
// Read total amount of keys
@@ -100,7 +113,7 @@ static void mf_classic_dict_str_to_int(string_t key_str, uint64_t* key_int) {
for(uint8_t i = 0; i < 12; i += 2) {
args_char_to_hex(
string_get_char(key_str, i), string_get_char(key_str, i + 1), &key_byte_tmp);
*key_int |= (uint8_t)key_byte_tmp << 8 * (5 - i / 2);
*key_int |= (uint64_t)key_byte_tmp << 8 * (5 - i / 2);
}
}

View File

@@ -9,18 +9,41 @@
typedef enum {
MfClassicDictTypeUser,
MfClassicDictTypeFlipper,
MfClassicDictTypeUnitTest,
} MfClassicDictType;
typedef struct MfClassicDict MfClassicDict;
bool mf_classic_dict_check_presence(MfClassicDictType dict_type);
/** Allocate MfClassicDict instance
*
* @param[in] dict_type The dictionary type
*
* @return MfClassicDict instance
*/
MfClassicDict* mf_classic_dict_alloc(MfClassicDictType dict_type);
/** Free MfClassicDict instance
*
* @param dict MfClassicDict instance
*/
void mf_classic_dict_free(MfClassicDict* dict);
/** Get total keys count
*
* @param dict MfClassicDict instance
*
* @return total keys count
*/
uint32_t mf_classic_dict_get_total_keys(MfClassicDict* dict);
/** Rewind to the beginning
*
* @param dict MfClassicDict instance
*
* @return true on success
*/
bool mf_classic_dict_rewind(MfClassicDict* dict);
bool mf_classic_dict_is_key_present(MfClassicDict* dict, uint8_t* key);
@@ -31,16 +54,46 @@ bool mf_classic_dict_get_next_key(MfClassicDict* dict, uint64_t* key);
bool mf_classic_dict_get_next_key_str(MfClassicDict* dict, string_t key);
/** Get key at target offset as uint64_t
*
* @param dict MfClassicDict instance
* @param[out] key Pointer to the uint64_t key
* @param[in] target Target offset from current position
*
* @return true on success
*/
bool mf_classic_dict_get_key_at_index(MfClassicDict* dict, uint64_t* key, uint32_t target);
/** Get key at target offset as string_t
*
* @param dict MfClassicDict instance
* @param[out] key Found key destination buffer
* @param[in] target Target offset from current position
*
* @return true on success
*/
bool mf_classic_dict_get_key_at_index_str(MfClassicDict* dict, string_t key, uint32_t target);
bool mf_classic_dict_add_key(MfClassicDict* dict, uint8_t* key);
/** Add string representation of the key
*
* @param dict MfClassicDict instance
* @param[in] key String representation of the key
*
* @return true on success
*/
bool mf_classic_dict_add_key_str(MfClassicDict* dict, string_t key);
bool mf_classic_dict_find_index(MfClassicDict* dict, uint8_t* key, uint32_t* target);
bool mf_classic_dict_find_index_str(MfClassicDict* dict, string_t key, uint32_t* target);
/** Delete key at target offset
*
* @param dict MfClassicDict instance
* @param[in] target Target offset from current position
*
* @return true on success
*/
bool mf_classic_dict_delete_index(MfClassicDict* dict, uint32_t target);

Some files were not shown because too many files have changed in this diff Show More