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

Compare commits

..

95 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
MX
8d68bf62a5 update changelog 2022-09-19 23:34:31 +03:00
MX
2c85adb270 remove unused icon, update api symbols and unirf 2022-09-19 21:21:31 +03:00
MX
e2123c55bb Merge branch 'fz-dev' into dev 2022-09-19 21:15:04 +03:00
David
f5ff6438d1 NFC user dict list, delete, and de-duplication. (#1533)
* Add MFC user keys list
* Leakey submenu fix
* Set next target for Save/Delete success scenes
* Delete individual user keys
* Update count of total keys
* Fix memory leak
* Check for duplicate keys
* Remove a submodule that I never added?
* Swap and position icons
* Revamp according to design doc
* Rename icons to include size and replace keychain icon with smaller variant
* Fix typos
* Final fixes
* Fufill requested changes
* Cleanup comments
* Merge dev after SD app loading
* Fixing icon names
* Revert merge mistakes and API version
* Scene switching adjustments
* F7: add/change/remove some nfc icons in api_symbols.csv

Co-authored-by: あく <alleteam@gmail.com>
2022-09-20 01:43:53 +09:00
Astra
9f3b80e606 Add new card parsers (#1503)
* Add the "Two cities" parser
* Add plantain and plantain4k parsers
* Add new parsers to the supported list
* United card PoC
* Fix nfc device not sleeping
* Completely read the 4K troika variants
* Correct naming
* Update to reflect upstream changes
* Add support for MfUl info
* Fix parsers
* Card type detection fixes
* Remove debug info
* Fixes for the verification of cards
* nfc: fix verification for supported cards
* nfc: remove unused vars
* Improve card reading reliability and fix plantain
* plantain: change log level

Co-authored-by: gornekich <n.gorbadey@gmail.com>
2022-09-20 01:05:04 +09:00
MX
111656d2c1 Merge branch 'fz-dev' into dev 2022-09-19 18:50:20 +03:00
MX
2045a29d3f lower frame rate in custom anim to save a bit of battery charge 2022-09-19 18:40:50 +03:00
MX
26e46f9267 Merge pull request #71 from TasyDevilsky/patch-1
Update setting_user
2022-09-19 17:47:12 +03:00
Max Lapan
d003db0404 SubGhz: Oregon v2.1 decoder (#1678)
* Oregon v2.1 decoder
* Refactor FSM to switch
* Refactor headers
* Format strings
* Unit tests of oregon2
* Cleanups
* Add oregon2 raw data to random_test_raw.sub
* Adjust count of packets detected on random test
* Format sources

Co-authored-by: あく <alleteam@gmail.com>
2022-09-19 23:24:24 +09:00
MX
5a31e35dc2 Merge branch 'fz-dev' into dev 2022-09-19 17:21:31 +03:00
Patrick Cunningham
c7cd5721ed Picopass: detect and show SE / SIO (#1701)
* detect and show SE / SIO
* fix fault
* remove bad read check

Co-authored-by: あく <alleteam@gmail.com>
2022-09-19 22:37:12 +09:00
Nikolay Minaylov
fb476c29e6 RFID: fix read info screen (#1723)
* RFID: fix read info screen
* Fix line break for long data string
* Protocol data redecoding before write

Co-authored-by: SG <who.just.the.doctor@gmail.com>
Co-authored-by: あく <alleteam@gmail.com>
2022-09-19 22:21:40 +09:00
Nikolay Minaylov
d80329b323 [FL-2815, FL-2821] Dummy mode (#1739)
* Dummy mode implementation
* dumb -> dummy
* F7: Add new api_symbols: game icon
* Starting snake game from dummy mode

Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
2022-09-19 22:03:42 +09:00
Astra
3d3c422751 [FL-2674] Show error popup when NFC chip is not init/disconnected (#1722)
* Show error popup when NFC chip is not init/disconnected
* Move to dialogs for the error message
* Fix a memory leak and wrap the hal check
* F7: update api_symbols.csv, add furi_hal_nfc_is_init

Co-authored-by: SG <who.just.the.doctor@gmail.com>
Co-authored-by: あく <alleteam@gmail.com>
2022-09-19 21:46:56 +09:00
hedger
ed385594a3 faploader: more subsystem headers in API table (#1742)
* faploader: more subsystem headers in API table; not counting header entries for SDK version change
* subghz: removed dead function
* Adjusted API version
* hal: removed furi_hal_power_get_system_voltage
* lib: mbedtls: additional flags for .fap linkage
* fbt: rebuilding assets when git commit id changes
* fbt: removed assets rebuild on git commit id change; added explicit dependency for SDK source on compiled assets parts; removed unneeded sdk regeneration runs
* fbt: changed stock plugins to EXTERNAL apps; restored building app as a PLUGIN as a part of main fw as well as a .fap; readme fixes
* fbt: restored certain apps to PLUGIN type
* fbt: app manifests: renamed version->fap_version, added extra fields
* fbt: fixed version processing after rename

Co-authored-by: あく <alleteam@gmail.com>
2022-09-19 21:39:00 +09:00
Astra
787df44c79 [FL-2800] Fix Mifare Classic 4K reading of the last 8 sectors (#1712)
* Fix FURI_BIT_SET

Co-authored-by: gornekich <n.gorbadey@gmail.com>
Co-authored-by: SG <who.just.the.doctor@gmail.com>
2022-09-19 21:30:18 +09:00
TasmanDevil
f0eedc3243 Update setting_user
Adding the 868.95 frequency for the Sommer device, the `Sommer(fsk476)` protocol was recently added but Flipper could not read it until now.
2022-09-19 08:59:13 +02:00
MX
365d055dc5 update changelog again 2022-09-19 05:55:54 +03:00
MX
650ba8a91f Update HowToInstall.md 2022-09-19 05:53:23 +03:00
MX
654d6dc6ec update changelog 2022-09-19 05:51:10 +03:00
MX
18b70ac6b5 update universal ir assets
by @amec0e
2022-09-19 05:45:58 +03:00
hedger
51369d6219 fbt: removed assets rebuild on git commit id change; added explicit dependency for SDK source on compiled assets parts; removed unneeded sdk regeneration runs 2022-09-19 05:27:21 +03:00
MX
181533df1b fixed bug with power suppress in unirf, removed icon from api 2022-09-19 05:22:50 +03:00
MX
d85731636f move spectrum analyzer into plugins, fix debug builds 2022-09-19 04:59:28 +03:00
MX
6f66f87fab add new frequency to user config 2022-09-18 21:10:08 +03:00
216 changed files with 7184 additions and 2827 deletions

1
.github/CODEOWNERS vendored Normal file
View File

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

View File

@@ -9,6 +9,10 @@
"type": "command", "type": "command",
"command": "shellCommand.execute", "command": "shellCommand.execute",
"args": { "args": {
"useSingleResult": true,
"env": {
"PATH": "${workspaceFolder};${env:PATH}"
},
"command": "./fbt get_blackmagic", "command": "./fbt get_blackmagic",
"description": "Get Blackmagic device", "description": "Get Blackmagic device",
} }

View File

@@ -22,4 +22,4 @@
"SConstruct": "python", "SConstruct": "python",
"*.fam": "python", "*.fam": "python",
} }
} }

View File

@@ -1,15 +1,16 @@
### New changes ### New changes
* Fixed all known issues with SubGHz settings (broken menu when you select last item in read and go into read raw config, issues with hopper and detect raw modes, bugs with frequency selector) (@xMasterX) * PR: SubGHz bruteforcer plugin - deep refactoring (huge thanks to @derskythe ! | PR #75)
* Removed subghz last settings saving feature (may be returned in future) (caused issues with freq selector, hopper mode) * OFW: Preliminary Rust support
* Updated universal IR assets (by @Amec0e)
#### **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) [- 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!] - Keeloq [Not ALL systems supported yet!]
- Nice Flor S - Nice Flor S
- Security+ v1 & v2 - Security+ v1 & v2
- Star Line - Star Line (saving only)
## Support us so we can buy equipment and develop new features ## Support us so we can buy equipment and develop new features
* ETH/BSC/ERC20-Tokens: `0xFebF1bBc8229418FF2408C07AF6Afa49152fEc6a` * 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) - 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 -> 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 -> 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 # Instructions
## [- How to install firmware](https://github.com/Eng1n33r/flipperzero-firmware/blob/dev/documentation/HowToInstall.md) ## [- How to install firmware](https://github.com/Eng1n33r/flipperzero-firmware/blob/dev/documentation/HowToInstall.md)
@@ -167,4 +166,4 @@ Games:
- `site_scons` - Build helpers - `site_scons` - Build helpers
- `scripts` - Supplementary scripts and python libraries home - `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}", "target extended-remote ${GDBREMOTE}",
"-ex", "-ex",
"set confirm off", "set confirm off",
"-ex",
"set pagination off",
], ],
GDBOPTS_BLACKMAGIC=[ GDBOPTS_BLACKMAGIC=[
"-ex", "-ex",
@@ -234,10 +236,19 @@ distenv.PhonyTarget(
distenv.PhonyTarget( distenv.PhonyTarget(
"debug_other", "debug_other",
"${GDBPYCOM}", "${GDBPYCOM}",
GDBPYOPTS='-ex "source debug/PyCortexMDebug/PyCortexMDebug.py" ', GDBOPTS="${GDBOPTS_BASE}",
GDBREMOTE="${OPENOCD_GDB_PIPE}", 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 # Just start OpenOCD
distenv.PhonyTarget( distenv.PhonyTarget(
"openocd", "openocd",

View File

@@ -3,6 +3,7 @@
#include <storage/storage.h> #include <storage/storage.h>
#include <lib/flipper_format/flipper_format.h> #include <lib/flipper_format/flipper_format.h>
#include <lib/nfc/protocols/nfca.h> #include <lib/nfc/protocols/nfca.h>
#include <lib/nfc/helpers/mf_classic_dict.h>
#include <lib/digital_signal/digital_signal.h> #include <lib/digital_signal/digital_signal.h>
#include <lib/flipper_format/flipper_format_i.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"); "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) { MU_TEST_SUITE(nfc) {
nfc_test_alloc(); nfc_test_alloc();
MU_RUN_TEST(nfc_digital_signal_test); MU_RUN_TEST(nfc_digital_signal_test);
MU_RUN_TEST(mf_classic_dict_test);
nfc_test_free(); nfc_test_free();
} }

View File

@@ -13,7 +13,7 @@
#define CAME_ATOMO_DIR_NAME EXT_PATH("subghz/assets/came_atomo") #define CAME_ATOMO_DIR_NAME EXT_PATH("subghz/assets/came_atomo")
#define NICE_FLOR_S_DIR_NAME EXT_PATH("subghz/assets/nice_flor_s") #define NICE_FLOR_S_DIR_NAME EXT_PATH("subghz/assets/nice_flor_s")
#define TEST_RANDOM_DIR_NAME EXT_PATH("unit_tests/subghz/test_random_raw.sub") #define TEST_RANDOM_DIR_NAME EXT_PATH("unit_tests/subghz/test_random_raw.sub")
#define TEST_RANDOM_COUNT_PARSE 232 #define TEST_RANDOM_COUNT_PARSE 233
#define TEST_TIMEOUT 10000 #define TEST_TIMEOUT 10000
static SubGhzEnvironment* environment_handler; static SubGhzEnvironment* environment_handler;
@@ -434,6 +434,13 @@ MU_TEST(subghz_decoder_clemsa_test) {
"Test decoder " SUBGHZ_PROTOCOL_CLEMSA_NAME " error\r\n"); "Test decoder " SUBGHZ_PROTOCOL_CLEMSA_NAME " error\r\n");
} }
MU_TEST(subghz_decoder_oregon2_test) {
mu_assert(
subghz_decoder_test(
EXT_PATH("unit_tests/subghz/oregon2_raw.sub"), SUBGHZ_PROTOCOL_OREGON2_NAME),
"Test decoder " SUBGHZ_PROTOCOL_OREGON2_NAME " error\r\n");
}
//test encoders //test encoders
MU_TEST(subghz_encoder_princeton_test) { MU_TEST(subghz_encoder_princeton_test) {
mu_assert( mu_assert(
@@ -595,6 +602,7 @@ MU_TEST_SUITE(subghz) {
MU_RUN_TEST(subghz_decoder_magellen_test); MU_RUN_TEST(subghz_decoder_magellen_test);
MU_RUN_TEST(subghz_decoder_intertechno_v3_test); MU_RUN_TEST(subghz_decoder_intertechno_v3_test);
MU_RUN_TEST(subghz_decoder_clemsa_test); MU_RUN_TEST(subghz_decoder_clemsa_test);
MU_RUN_TEST(subghz_decoder_oregon2_test);
MU_RUN_TEST(subghz_encoder_princeton_test); MU_RUN_TEST(subghz_encoder_princeton_test);
MU_RUN_TEST(subghz_encoder_came_test); MU_RUN_TEST(subghz_encoder_came_test);

View File

@@ -15,6 +15,23 @@ App(
"archive", "archive",
"clock", "clock",
"unirfremix", "unirfremix",
"spectrum_analyzer", ],
)
App(
appid="main_apps_default",
name="Basic applications for main menu",
apptype=FlipperAppType.METAPACKAGE,
provides=[
"gpio",
#"ibutton",
"infrared",
"lfrfid",
"nfc",
"subghz",
#"bad_usb",
#"u2f",
"fap_loader",
"archive",
], ],
) )

View File

@@ -16,6 +16,7 @@ static const char* tab_default_paths[] = {
[ArchiveTabInfrared] = ANY_PATH("infrared"), [ArchiveTabInfrared] = ANY_PATH("infrared"),
[ArchiveTabBadUsb] = ANY_PATH("badusb"), [ArchiveTabBadUsb] = ANY_PATH("badusb"),
[ArchiveTabU2f] = "/app:u2f", [ArchiveTabU2f] = "/app:u2f",
[ArchiveTabApps] = ANY_PATH("apps"),
[ArchiveTabBrowser] = STORAGE_ANY_PATH_PREFIX, [ArchiveTabBrowser] = STORAGE_ANY_PATH_PREFIX,
}; };
@@ -27,6 +28,7 @@ static const char* known_ext[] = {
[ArchiveFileTypeInfrared] = ".ir", [ArchiveFileTypeInfrared] = ".ir",
[ArchiveFileTypeBadUsb] = ".txt", [ArchiveFileTypeBadUsb] = ".txt",
[ArchiveFileTypeU2f] = "?", [ArchiveFileTypeU2f] = "?",
[ArchiveFileTypeApps] = ".fap",
[ArchiveFileTypeUpdateManifest] = ".fuf", [ArchiveFileTypeUpdateManifest] = ".fuf",
[ArchiveFileTypeFolder] = "?", [ArchiveFileTypeFolder] = "?",
[ArchiveFileTypeUnknown] = "*", [ArchiveFileTypeUnknown] = "*",
@@ -41,6 +43,7 @@ static const ArchiveFileTypeEnum known_type[] = {
[ArchiveTabInfrared] = ArchiveFileTypeInfrared, [ArchiveTabInfrared] = ArchiveFileTypeInfrared,
[ArchiveTabBadUsb] = ArchiveFileTypeBadUsb, [ArchiveTabBadUsb] = ArchiveFileTypeBadUsb,
[ArchiveTabU2f] = ArchiveFileTypeU2f, [ArchiveTabU2f] = ArchiveFileTypeU2f,
[ArchiveTabApps] = ArchiveFileTypeApps,
[ArchiveTabBrowser] = ArchiveFileTypeUnknown, [ArchiveTabBrowser] = ArchiveFileTypeUnknown,
}; };

View File

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

View File

@@ -42,10 +42,7 @@ ARRAY_DEF(
#pragma GCC diagnostic push #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function" #pragma GCC diagnostic ignored "-Wunused-function"
// Using in applications/archive/views/archive_browser_view.c // Using in applications/archive/views/archive_browser_view.c
static void archive_menu_add_item( static void archive_menu_add_item(ArchiveContextMenuItem_t* obj, string_t text, uint32_t event) {
ArchiveContextMenuItem_t* obj,
string_t text,
uint32_t event) {
string_init_move(obj->text, text); string_init_move(obj->text, text);
obj->event = event; obj->event = event;
} }

View File

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

View File

@@ -37,7 +37,11 @@ void archive_scene_info_on_enter(void* context) {
// Directory path // Directory path
path_extract_dirname(string_get_cstr(current->path), dirname); 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 // File size
FileInfo fileinfo; FileInfo fileinfo;
@@ -60,7 +64,7 @@ void archive_scene_info_on_enter(void* context) {
string_get_cstr(dirname)); string_get_cstr(dirname));
} }
widget_add_text_box_element( 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 // This one to return and cursor select this file
path_extract_filename_no_ext(string_get_cstr(current->path), filename); path_extract_filename_no_ext(string_get_cstr(current->path), filename);

View File

@@ -16,6 +16,7 @@ static const char* ArchiveTabNames[] = {
[ArchiveTabInfrared] = "Infrared", [ArchiveTabInfrared] = "Infrared",
[ArchiveTabBadUsb] = "Bad USB", [ArchiveTabBadUsb] = "Bad USB",
[ArchiveTabU2f] = "U2F", [ArchiveTabU2f] = "U2F",
[ArchiveTabApps] = "Apps",
[ArchiveTabBrowser] = "Browser", [ArchiveTabBrowser] = "Browser",
}; };
@@ -27,6 +28,7 @@ static const Icon* ArchiveItemIcons[] = {
[ArchiveFileTypeInfrared] = &I_ir_10px, [ArchiveFileTypeInfrared] = &I_ir_10px,
[ArchiveFileTypeBadUsb] = &I_badusb_10px, [ArchiveFileTypeBadUsb] = &I_badusb_10px,
[ArchiveFileTypeU2f] = &I_u2f_10px, [ArchiveFileTypeU2f] = &I_u2f_10px,
[ArchiveFileTypeApps] = &I_Apps_10px,
[ArchiveFileTypeUpdateManifest] = &I_update_10px, [ArchiveFileTypeUpdateManifest] = &I_update_10px,
[ArchiveFileTypeFolder] = &I_dir_10px, [ArchiveFileTypeFolder] = &I_dir_10px,
[ArchiveFileTypeUnknown] = &I_unknown_10px, [ArchiveFileTypeUnknown] = &I_unknown_10px,

View File

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

View File

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

View File

@@ -25,7 +25,7 @@ static bool
FlipperApplication* app = flipper_application_alloc(loader->storage, &hashtable_api_interface); FlipperApplication* app = flipper_application_alloc(loader->storage, &hashtable_api_interface);
FlipperApplicationPreloadStatus preload_res = FlipperApplicationPreloadStatus preload_res =
flipper_application_preload(app, string_get_cstr(path)); flipper_application_preload_manifest(app, string_get_cstr(path));
bool load_success = false; 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) { void infrared_scene_universal_common_on_enter(void* context) {
Infrared* infrared = 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)); 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; Infrared* infrared = context;
ButtonPanel* button_panel = infrared->button_panel; ButtonPanel* button_panel = infrared->button_panel;
view_stack_remove_view(infrared->view_stack, button_panel_get_view(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); button_panel_reset(button_panel);
} }

View File

@@ -16,7 +16,7 @@ static void lfrfid_view_read_draw_callback(Canvas* canvas, void* _model) {
LfRfidReadViewModel* model = _model; LfRfidReadViewModel* model = _model;
canvas_set_color(canvas, ColorBlack); canvas_set_color(canvas, ColorBlack);
canvas_draw_icon(canvas, 0, 8, &I_NFC_manual); canvas_draw_icon(canvas, 0, 8, &I_NFC_manual_60x50);
canvas_set_font(canvas, FontPrimary); canvas_set_font(canvas, FontPrimary);

View File

@@ -231,7 +231,30 @@ void nfc_show_loading_popup(void* context, bool show) {
} }
} }
static bool nfc_is_hal_ready() {
if(!furi_hal_nfc_is_init()) {
// No connection to the chip, show an error screen
DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
DialogMessage* message = dialog_message_alloc();
dialog_message_set_text(
message,
"Error!\nNFC chip failed to start\n\n\nSend a photo of this to:\nsupport@flipperzero.one",
0,
0,
AlignLeft,
AlignTop);
dialog_message_show(dialogs, message);
dialog_message_free(message);
furi_record_close(RECORD_DIALOGS);
return false;
} else {
return true;
}
}
int32_t nfc_app(void* p) { int32_t nfc_app(void* p) {
if(!nfc_is_hal_ready()) return 0;
Nfc* nfc = nfc_alloc(); Nfc* nfc = nfc_alloc();
char* args = p; char* args = p;

View File

@@ -33,8 +33,14 @@
#include <nfc/scenes/nfc_scene.h> #include <nfc/scenes/nfc_scene.h>
#include <nfc/helpers/nfc_custom_event.h> #include <nfc/helpers/nfc_custom_event.h>
#include <dialogs/dialogs.h>
#include "rpc/rpc_app.h" #include "rpc/rpc_app.h"
#include <m-array.h>
ARRAY_DEF(MfClassicUserKeys, char*, M_PTR_OPLIST);
#define NFC_TEXT_STORE_SIZE 128 #define NFC_TEXT_STORE_SIZE 128
typedef enum { typedef enum {
@@ -58,6 +64,7 @@ struct Nfc {
char text_store[NFC_TEXT_STORE_SIZE + 1]; char text_store[NFC_TEXT_STORE_SIZE + 1];
string_t text_box_store; string_t text_box_store;
uint8_t byte_input_store[6]; uint8_t byte_input_store[6];
MfClassicUserKeys_t mfc_key_strs; // Used in MFC key listing
void* rpc_ctx; void* rpc_ctx;
NfcRpcState rpc_state; NfcRpcState rpc_state;

View File

@@ -32,6 +32,9 @@ ADD_SCENE(nfc, mf_classic_menu, MfClassicMenu)
ADD_SCENE(nfc, mf_classic_emulate, MfClassicEmulate) ADD_SCENE(nfc, mf_classic_emulate, MfClassicEmulate)
ADD_SCENE(nfc, mf_classic_keys, MfClassicKeys) ADD_SCENE(nfc, mf_classic_keys, MfClassicKeys)
ADD_SCENE(nfc, mf_classic_keys_add, MfClassicKeysAdd) ADD_SCENE(nfc, mf_classic_keys_add, MfClassicKeysAdd)
ADD_SCENE(nfc, mf_classic_keys_list, MfClassicKeysList)
ADD_SCENE(nfc, mf_classic_keys_delete, MfClassicKeysDelete)
ADD_SCENE(nfc, mf_classic_keys_warn_duplicate, MfClassicKeysWarnDuplicate)
ADD_SCENE(nfc, mf_classic_dict_attack, MfClassicDictAttack) ADD_SCENE(nfc, mf_classic_dict_attack, MfClassicDictAttack)
ADD_SCENE(nfc, emv_read_success, EmvReadSuccess) ADD_SCENE(nfc, emv_read_success, EmvReadSuccess)
ADD_SCENE(nfc, emv_menu, EmvMenu) ADD_SCENE(nfc, emv_menu, EmvMenu)

View File

@@ -25,8 +25,13 @@ bool nfc_scene_delete_success_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcCustomEventViewExit) { if(event.event == NfcCustomEventViewExit) {
consumed = scene_manager_search_and_switch_to_previous_scene( if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicKeys)) {
nfc->scene_manager, NfcSceneFileSelect); consumed = scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneMfClassicKeys);
} else {
consumed = scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneStart);
}
} }
} }
return consumed; return consumed;

View File

@@ -47,7 +47,9 @@ void nfc_scene_device_info_on_enter(void* context) {
} }
string_clear(country_name); string_clear(country_name);
} }
} else if(dev_data->protocol == NfcDeviceProtocolMifareClassic) { } else if(
dev_data->protocol == NfcDeviceProtocolMifareClassic ||
dev_data->protocol == NfcDeviceProtocolMifareUl) {
string_set(temp_str, nfc->dev->dev_data.parsed_data); string_set(temp_str, nfc->dev->dev_data.parsed_data);
} }

View File

@@ -31,7 +31,10 @@ bool nfc_scene_dict_not_found_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcCustomEventViewExit) { if(event.event == NfcCustomEventViewExit) {
if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneExtraActions)) { if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicKeys)) {
consumed = scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneMfClassicKeys);
} else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneExtraActions)) {
consumed = scene_manager_search_and_switch_to_previous_scene( consumed = scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneExtraActions); nfc->scene_manager, NfcSceneExtraActions);
} else { } else {

View File

@@ -17,7 +17,7 @@ void nfc_scene_extra_actions_on_enter(void* context) {
submenu_add_item( submenu_add_item(
submenu, submenu,
"Mf Classic Keys", "Mifare Classic Keys",
SubmenuIndexMfClassicKeys, SubmenuIndexMfClassicKeys,
nfc_scene_extra_actions_submenu_callback, nfc_scene_extra_actions_submenu_callback,
nfc); nfc);

View File

@@ -26,15 +26,25 @@ void nfc_scene_mf_classic_keys_on_enter(void* context) {
} }
widget_add_string_element( widget_add_string_element(
nfc->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "MF Classic Keys"); nfc->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "Mifare Classic Keys");
char temp_str[32]; char temp_str[32];
snprintf(temp_str, sizeof(temp_str), "Flipper dict: %ld", flipper_dict_keys_total); snprintf(temp_str, sizeof(temp_str), "Flipper list: %ld", flipper_dict_keys_total);
widget_add_string_element(nfc->widget, 0, 20, AlignLeft, AlignTop, FontSecondary, temp_str); widget_add_string_element(nfc->widget, 0, 20, AlignLeft, AlignTop, FontSecondary, temp_str);
snprintf(temp_str, sizeof(temp_str), "User dict: %ld", user_dict_keys_total); snprintf(temp_str, sizeof(temp_str), "User list: %ld", user_dict_keys_total);
widget_add_string_element(nfc->widget, 0, 32, AlignLeft, AlignTop, FontSecondary, temp_str); widget_add_string_element(nfc->widget, 0, 32, AlignLeft, AlignTop, FontSecondary, temp_str);
widget_add_button_element( widget_add_button_element(
nfc->widget, GuiButtonTypeCenter, "Add", nfc_scene_mf_classic_keys_widget_callback, nfc); nfc->widget, GuiButtonTypeCenter, "Add", nfc_scene_mf_classic_keys_widget_callback, nfc);
widget_add_icon_element(nfc->widget, 90, 12, &I_Keychain); widget_add_button_element(
nfc->widget, GuiButtonTypeLeft, "Back", nfc_scene_mf_classic_keys_widget_callback, nfc);
widget_add_icon_element(nfc->widget, 87, 13, &I_Keychain_39x36);
if(user_dict_keys_total > 0) {
widget_add_button_element(
nfc->widget,
GuiButtonTypeRight,
"List",
nfc_scene_mf_classic_keys_widget_callback,
nfc);
}
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
} }
@@ -47,6 +57,12 @@ bool nfc_scene_mf_classic_keys_on_event(void* context, SceneManagerEvent event)
if(event.event == GuiButtonTypeCenter) { if(event.event == GuiButtonTypeCenter) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeysAdd); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeysAdd);
consumed = true; consumed = true;
} else if(event.event == GuiButtonTypeLeft) {
scene_manager_previous_scene(nfc->scene_manager);
consumed = true;
} else if(event.event == GuiButtonTypeRight) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeysList);
consumed = true;
} }
} }

View File

@@ -29,15 +29,16 @@ bool nfc_scene_mf_classic_keys_add_on_event(void* context, SceneManagerEvent eve
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcCustomEventByteInputDone) { if(event.event == NfcCustomEventByteInputDone) {
// Add key to dict // Add key to dict
bool key_added = false;
MfClassicDict* dict = mf_classic_dict_alloc(MfClassicDictTypeUser); MfClassicDict* dict = mf_classic_dict_alloc(MfClassicDictTypeUser);
if(dict) { if(dict) {
if(mf_classic_dict_add_key(dict, nfc->byte_input_store)) { if(mf_classic_dict_is_key_present(dict, nfc->byte_input_store)) {
key_added = true; scene_manager_next_scene(
nfc->scene_manager, NfcSceneMfClassicKeysWarnDuplicate);
} else if(mf_classic_dict_add_key(dict, nfc->byte_input_store)) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess);
} else {
scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound);
} }
}
if(key_added) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess);
} else { } else {
scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound);
} }

View File

@@ -0,0 +1,77 @@
#include "../nfc_i.h"
void nfc_scene_mf_classic_keys_delete_widget_callback(
GuiButtonType result,
InputType type,
void* context) {
Nfc* nfc = context;
if(type == InputTypeShort) {
view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
}
}
void nfc_scene_mf_classic_keys_delete_on_enter(void* context) {
Nfc* nfc = context;
MfClassicDict* dict = mf_classic_dict_alloc(MfClassicDictTypeUser);
uint32_t key_index =
scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfClassicKeysDelete);
// Setup Custom Widget view
string_t key_str;
string_init(key_str);
widget_add_string_element(
nfc->widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "Delete this key?");
widget_add_button_element(
nfc->widget,
GuiButtonTypeLeft,
"Cancel",
nfc_scene_mf_classic_keys_delete_widget_callback,
nfc);
widget_add_button_element(
nfc->widget,
GuiButtonTypeRight,
"Delete",
nfc_scene_mf_classic_keys_delete_widget_callback,
nfc);
mf_classic_dict_get_key_at_index_str(dict, key_str, key_index);
widget_add_string_element(
nfc->widget, 64, 32, AlignCenter, AlignCenter, FontSecondary, string_get_cstr(key_str));
string_clear(key_str);
mf_classic_dict_free(dict);
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
}
bool nfc_scene_mf_classic_keys_delete_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = context;
bool consumed = false;
uint32_t key_index =
scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfClassicKeysDelete);
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == GuiButtonTypeLeft) {
consumed = scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneMfClassicKeys);
} else if(event.event == GuiButtonTypeRight) {
MfClassicDict* dict = mf_classic_dict_alloc(MfClassicDictTypeUser);
if(mf_classic_dict_delete_index(dict, key_index)) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneDeleteSuccess);
} else {
scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneMfClassicKeys);
}
mf_classic_dict_free(dict);
consumed = true;
}
}
return consumed;
}
void nfc_scene_mf_classic_keys_delete_on_exit(void* context) {
Nfc* nfc = context;
widget_reset(nfc->widget);
}

View File

@@ -0,0 +1,100 @@
#include "../nfc_i.h"
#define NFC_SCENE_MF_CLASSIC_KEYS_LIST_MAX (100)
void nfc_scene_mf_classic_keys_list_submenu_callback(void* context, uint32_t index) {
furi_assert(context);
Nfc* nfc = context;
view_dispatcher_send_custom_event(nfc->view_dispatcher, index);
}
void nfc_scene_mf_classic_keys_list_popup_callback(void* context) {
furi_assert(context);
Nfc* nfc = context;
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit);
}
void nfc_scene_mf_classic_keys_list_prepare(Nfc* nfc, MfClassicDict* dict) {
Submenu* submenu = nfc->submenu;
uint32_t index = 0;
string_t temp_key;
string_init(temp_key);
submenu_set_header(submenu, "Select key to delete:");
while(mf_classic_dict_get_next_key_str(dict, temp_key)) {
char* current_key = (char*)malloc(sizeof(char) * 13);
strncpy(current_key, string_get_cstr(temp_key), 12);
MfClassicUserKeys_push_back(nfc->mfc_key_strs, current_key);
FURI_LOG_D("ListKeys", "Key %d: %s", index, current_key);
submenu_add_item(
submenu, current_key, index++, nfc_scene_mf_classic_keys_list_submenu_callback, nfc);
}
string_clear(temp_key);
}
void nfc_scene_mf_classic_keys_list_on_enter(void* context) {
Nfc* nfc = context;
MfClassicDict* dict = mf_classic_dict_alloc(MfClassicDictTypeUser);
MfClassicUserKeys_init(nfc->mfc_key_strs);
if(dict) {
uint32_t total_user_keys = mf_classic_dict_get_total_keys(dict);
if(total_user_keys < NFC_SCENE_MF_CLASSIC_KEYS_LIST_MAX) {
nfc_scene_mf_classic_keys_list_prepare(nfc, dict);
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
} else {
popup_set_header(nfc->popup, "Too many keys!", 64, 0, AlignCenter, AlignTop);
popup_set_text(
nfc->popup,
"Edit user dictionary\nwith file browser",
64,
12,
AlignCenter,
AlignTop);
popup_set_callback(nfc->popup, nfc_scene_mf_classic_keys_list_popup_callback);
popup_set_context(nfc->popup, nfc);
popup_set_timeout(nfc->popup, 3000);
popup_enable_timeout(nfc->popup);
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
}
mf_classic_dict_free(dict);
} else {
popup_set_header(
nfc->popup, "Failed to load dictionary", 64, 32, AlignCenter, AlignCenter);
popup_set_callback(nfc->popup, nfc_scene_mf_classic_keys_list_popup_callback);
popup_set_context(nfc->popup, nfc);
popup_set_timeout(nfc->popup, 3000);
popup_enable_timeout(nfc->popup);
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
}
}
bool nfc_scene_mf_classic_keys_list_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcCustomEventViewExit) {
consumed = scene_manager_previous_scene(nfc->scene_manager);
} else {
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneMfClassicKeysDelete, event.event);
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeysDelete);
consumed = true;
}
}
return consumed;
}
void nfc_scene_mf_classic_keys_list_on_exit(void* context) {
Nfc* nfc = context;
MfClassicUserKeys_it_t it;
for(MfClassicUserKeys_it(it, nfc->mfc_key_strs); !MfClassicUserKeys_end_p(it);
MfClassicUserKeys_next(it)) {
free(*MfClassicUserKeys_ref(it));
}
MfClassicUserKeys_clear(nfc->mfc_key_strs);
submenu_reset(nfc->submenu);
popup_reset(nfc->popup);
}

View File

@@ -0,0 +1,47 @@
#include "../nfc_i.h"
void nfc_scene_mf_classic_keys_warn_duplicate_popup_callback(void* context) {
Nfc* nfc = context;
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit);
}
void nfc_scene_mf_classic_keys_warn_duplicate_on_enter(void* context) {
Nfc* nfc = context;
// Setup view
Popup* popup = nfc->popup;
popup_set_icon(popup, 72, 16, &I_DolphinCommon_56x48);
popup_set_header(popup, "Key already exists!", 64, 3, AlignCenter, AlignTop);
popup_set_text(
popup,
"Please enter a\n"
"different key.",
4,
24,
AlignLeft,
AlignTop);
popup_set_timeout(popup, 5000);
popup_set_context(popup, nfc);
popup_set_callback(popup, nfc_scene_mf_classic_keys_warn_duplicate_popup_callback);
popup_enable_timeout(popup);
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
}
bool nfc_scene_mf_classic_keys_warn_duplicate_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcCustomEventViewExit) {
consumed = scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneMfClassicKeysAdd);
}
}
return consumed;
}
void nfc_scene_mf_classic_keys_warn_duplicate_on_exit(void* context) {
Nfc* nfc = context;
popup_reset(nfc->popup);
}

View File

@@ -7,6 +7,13 @@ enum SubmenuIndex {
SubmenuIndexDynamic, // dynamic indexes start here 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) { MifareDesfireApplication* nfc_scene_mf_desfire_app_get_app(Nfc* nfc) {
uint32_t app_idx = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp) >> uint32_t app_idx = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp) >>
1; 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) { void nfc_scene_mf_desfire_app_on_enter(void* context) {
Nfc* nfc = context; Nfc* nfc = context;
Submenu* submenu = nfc->submenu;
MifareDesfireApplication* app = nfc_scene_mf_desfire_app_get_app(nfc); MifareDesfireApplication* app = nfc_scene_mf_desfire_app_get_app(nfc);
if(!app) { if(!app) {
popup_set_icon(nfc->popup, 5, 5, &I_WarningDolphin_45x42); popup_set_icon(nfc->popup, 5, 5, &I_WarningDolphin_45x42);
popup_set_header(nfc->popup, "Internal Error!", 55, 12, AlignLeft, AlignBottom); popup_set_header(nfc->popup, "Empty card!", 55, 12, AlignLeft, AlignBottom);
popup_set_text( popup_set_callback(nfc->popup, nfc_scene_mf_desfire_popup_callback);
nfc->popup, popup_set_context(nfc->popup, nfc);
"No app selected.\nThis should\nnever happen,\nplease file a bug.", popup_set_timeout(nfc->popup, 3000);
55, popup_enable_timeout(nfc->popup);
15, popup_set_text(nfc->popup, "No application\nfound.", 55, 15, AlignLeft, AlignTop);
AlignLeft,
AlignTop);
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
FURI_LOG_E(TAG, "Bad state. No app selected?"); } else {
return; 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); uint16_t cap = NFC_TEXT_STORE_SIZE;
char* buf = nfc->text_store;
submenu_add_item( int idx = SubmenuIndexDynamic;
submenu, "App info", SubmenuIndexAppInfo, nfc_scene_mf_desfire_app_submenu_callback, nfc); for(MifareDesfireFile* file = app->file_head; file; file = file->next) {
int size = snprintf(buf, cap, "File %d", file->id);
uint16_t cap = NFC_TEXT_STORE_SIZE; if(size < 0 || size >= cap) {
char* buf = nfc->text_store; FURI_LOG_W(
int idx = SubmenuIndexDynamic; TAG,
for(MifareDesfireFile* file = app->file_head; file; file = file->next) { "Exceeded NFC_TEXT_STORE_SIZE when preparing file id strings; menu truncated");
int size = snprintf(buf, cap, "File %d", file->id); break;
if(size < 0 || size >= cap) { }
FURI_LOG_W( char* label = buf;
TAG, cap -= size + 1;
"Exceeded NFC_TEXT_STORE_SIZE when preparing file id strings; menu truncated"); buf += size + 1;
break; 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) { 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); uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp);
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
MifareDesfireApplication* app = nfc_scene_mf_desfire_app_get_app(nfc); if(event.event == NfcCustomEventViewExit) {
TextBox* text_box = nfc->text_box; consumed = scene_manager_previous_scene(nfc->scene_manager);
string_reset(nfc->text_box_store);
if(event.event == SubmenuIndexAppInfo) {
mf_df_cat_application_info(app, nfc->text_box_store);
} else { } else {
uint16_t index = event.event - SubmenuIndexDynamic; MifareDesfireApplication* app = nfc_scene_mf_desfire_app_get_app(nfc);
MifareDesfireFile* file = app->file_head; TextBox* text_box = nfc->text_box;
for(int i = 0; file && i < index; i++) { string_reset(nfc->text_box_store);
file = file->next; 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) { text_box_set_text(text_box, string_get_cstr(nfc->text_box_store));
return false; scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp, state | 1);
} view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox);
mf_df_cat_file(file, nfc->text_box_store); 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) { } else if(event.type == SceneManagerEventTypeBack) {
if(state & 1) { if(state & 1) {
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); 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; Nfc* nfc = context;
// Clear views // Clear views
popup_reset(nfc->popup);
text_box_reset(nfc->text_box); text_box_reset(nfc->text_box);
string_reset(nfc->text_box_store); string_reset(nfc->text_box_store);
submenu_reset(nfc->submenu); submenu_reset(nfc->submenu);

View File

@@ -27,7 +27,7 @@ void nfc_scene_mf_ultralight_read_auth_set_state(Nfc* nfc, NfcSceneMfUlReadState
popup_reset(nfc->popup); popup_reset(nfc->popup);
popup_set_text( popup_set_text(
nfc->popup, "Apply card to\nFlipper's back", 97, 24, AlignCenter, AlignTop); nfc->popup, "Apply card to\nFlipper's back", 97, 24, AlignCenter, AlignTop);
popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual); popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual_60x50);
} else if(state == NfcSceneMfUlReadStateReading) { } else if(state == NfcSceneMfUlReadStateReading) {
popup_reset(nfc->popup); popup_reset(nfc->popup);
popup_set_header( popup_set_header(

View File

@@ -34,15 +34,19 @@ void nfc_scene_mf_ultralight_read_success_on_enter(void* context) {
nfc); nfc);
string_t temp_str; string_t temp_str;
string_init_printf(temp_str, "\e#%s\n", nfc_mf_ul_type(mf_ul_data->type, true)); if(string_size(nfc->dev->dev_data.parsed_data)) {
string_cat_printf(temp_str, "UID:"); string_init_set(temp_str, nfc->dev->dev_data.parsed_data);
for(size_t i = 0; i < data->uid_len; i++) { } else {
string_cat_printf(temp_str, " %02X", data->uid[i]); string_init_printf(temp_str, "\e#%s\n", nfc_mf_ul_type(mf_ul_data->type, true));
} string_cat_printf(temp_str, "UID:");
string_cat_printf( for(size_t i = 0; i < data->uid_len; i++) {
temp_str, "\nPages Read: %d/%d", mf_ul_data->data_read / 4, mf_ul_data->data_size / 4); string_cat_printf(temp_str, " %02X", data->uid[i]);
if(mf_ul_data->data_read != mf_ul_data->data_size) { }
string_cat_printf(temp_str, "\nPassword-protected pages!"); string_cat_printf(
temp_str, "\nPages Read: %d/%d", mf_ul_data->data_read / 4, mf_ul_data->data_size / 4);
if(mf_ul_data->data_read != mf_ul_data->data_size) {
string_cat_printf(temp_str, "\nPassword-protected pages!");
}
} }
widget_add_text_scroll_element(widget, 0, 0, 128, 52, string_get_cstr(temp_str)); widget_add_text_scroll_element(widget, 0, 0, 128, 52, string_get_cstr(temp_str));
string_clear(temp_str); string_clear(temp_str);

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); scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu);
submenu_add_item( submenu_add_item(
submenu, submenu,
"Enter Password Manually", "Enter PWD Manually",
SubmenuIndexMfUlUnlockMenuManual, SubmenuIndexMfUlUnlockMenuManual,
nfc_scene_mf_ultralight_unlock_menu_submenu_callback, nfc_scene_mf_ultralight_unlock_menu_submenu_callback,
nfc); nfc);

View File

@@ -26,7 +26,7 @@ void nfc_scene_read_set_state(Nfc* nfc, NfcSceneReadState state) {
popup_reset(nfc->popup); popup_reset(nfc->popup);
popup_set_text( popup_set_text(
nfc->popup, "Apply card to\nFlipper's back", 97, 24, AlignCenter, AlignTop); nfc->popup, "Apply card to\nFlipper's back", 97, 24, AlignCenter, AlignTop);
popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual); popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual_60x50);
} else if(state == NfcSceneReadStateReading) { } else if(state == NfcSceneReadStateReading) {
popup_reset(nfc->popup); popup_reset(nfc->popup);
popup_set_header( popup_set_header(

View File

@@ -11,7 +11,7 @@ void nfc_scene_restore_original_confirm_on_enter(void* context) {
DialogEx* dialog_ex = nfc->dialog_ex; DialogEx* dialog_ex = nfc->dialog_ex;
dialog_ex_set_header(dialog_ex, "Restore Card Data?", 64, 0, AlignCenter, AlignTop); dialog_ex_set_header(dialog_ex, "Restore Card Data?", 64, 0, AlignCenter, AlignTop);
dialog_ex_set_icon(dialog_ex, 5, 15, &I_Restoring); dialog_ex_set_icon(dialog_ex, 5, 15, &I_Restoring_38x32);
dialog_ex_set_text( dialog_ex_set_text(
dialog_ex, "It will be returned\nto its original state.", 47, 21, AlignLeft, AlignTop); dialog_ex, "It will be returned\nto its original state.", 47, 21, AlignLeft, AlignTop);
dialog_ex_set_left_button_text(dialog_ex, "Cancel"); dialog_ex_set_left_button_text(dialog_ex, "Cancel");

View File

@@ -27,7 +27,10 @@ bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcCustomEventViewExit) { if(event.event == NfcCustomEventViewExit) {
if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) { if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicKeys)) {
consumed = scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneMfClassicKeys);
} else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) {
consumed = scene_manager_search_and_switch_to_previous_scene( consumed = scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneSavedMenu); nfc->scene_manager, NfcSceneSavedMenu);
} else { } else {

View File

@@ -91,7 +91,9 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) {
bool application_info_present = false; bool application_info_present = false;
if(dev_data->protocol == NfcDeviceProtocolEMV) { if(dev_data->protocol == NfcDeviceProtocolEMV) {
application_info_present = true; application_info_present = true;
} else if(dev_data->protocol == NfcDeviceProtocolMifareClassic) { } else if(
dev_data->protocol == NfcDeviceProtocolMifareClassic ||
dev_data->protocol == NfcDeviceProtocolMifareUl) {
application_info_present = nfc_supported_card_verify_and_parse(dev_data); application_info_present = nfc_supported_card_verify_and_parse(dev_data);
} }

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); 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) { void subghz_scene_save_name_on_enter(void* context) {
SubGhz* subghz = 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) == if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) ==
SubGhzCustomEventManagerSetRAW) { SubGhzCustomEventManagerSetRAW) {
dev_name_empty = true; 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); 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) { 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 vertical_offset = 3;
const uint8_t horizontal_offset = 1; const uint8_t horizontal_offset = 1;
const uint8_t string_width = canvas_string_width(canvas, str); 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_invert_color(canvas);
canvas_draw_icon( 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_draw_str(
canvas, x + horizontal_offset + icon_width_with_offset, y - vertical_offset, str); canvas, x + horizontal_offset + icon_width_with_offset, y - vertical_offset, str);
canvas_invert_color(canvas); canvas_invert_color(canvas);

View File

@@ -790,7 +790,7 @@ static void render_callback(Canvas* canvas, void* ctx) {
break; break;
case 1: case 1:
canvas_draw_icon(canvas, 113, 15, &I_Pin_cell_13x13); canvas_draw_icon(canvas, 113, 15, &I_Pin_cell_13x13);
canvas_draw_icon(canvas, 116, 17, &I_Pin_arrow_up7x9); canvas_draw_icon(canvas, 116, 17, &I_Pin_arrow_up_7x9);
break; break;
case 2: case 2:
canvas_draw_icon(canvas, 113, 15, &I_Pin_cell_13x13); canvas_draw_icon(canvas, 113, 15, &I_Pin_cell_13x13);
@@ -845,6 +845,8 @@ void unirfremix_subghz_alloc(UniRFRemix* app) {
UniRFRemix* unirfremix_alloc(void) { UniRFRemix* unirfremix_alloc(void) {
UniRFRemix* app = malloc(sizeof(UniRFRemix)); UniRFRemix* app = malloc(sizeof(UniRFRemix));
furi_hal_power_suppress_charge_enter();
app->model_mutex = furi_mutex_alloc(FuriMutexTypeNormal); app->model_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
app->input_queue = furi_message_queue_alloc(32, sizeof(InputEvent)); app->input_queue = furi_message_queue_alloc(32, sizeof(InputEvent));
@@ -862,7 +864,9 @@ UniRFRemix* unirfremix_alloc(void) {
return app; return app;
} }
void unirfremix_free(UniRFRemix* app) { void unirfremix_free(UniRFRemix* app, bool with_subghz) {
furi_hal_power_suppress_charge_exit();
string_clear(app->up_file); string_clear(app->up_file);
string_clear(app->down_file); string_clear(app->down_file);
string_clear(app->left_file); string_clear(app->left_file);
@@ -888,10 +892,12 @@ void unirfremix_free(UniRFRemix* app) {
furi_mutex_free(app->model_mutex); furi_mutex_free(app->model_mutex);
furi_hal_subghz_sleep(); if(with_subghz) {
subghz_setting_free(app->setting); furi_hal_subghz_sleep();
subghz_receiver_free(app->subghz_receiver); subghz_setting_free(app->setting);
subghz_environment_free(app->environment); subghz_receiver_free(app->subghz_receiver);
subghz_environment_free(app->environment);
}
furi_record_close(RECORD_NOTIFICATION); furi_record_close(RECORD_NOTIFICATION);
app->notification = NULL; app->notification = NULL;
@@ -939,6 +945,8 @@ int32_t unirfremix_app(void* p) {
furi_record_close(RECORD_DIALOGS); furi_record_close(RECORD_DIALOGS);
if(!res) { if(!res) {
FURI_LOG_E(TAG, "No file selected"); FURI_LOG_E(TAG, "No file selected");
unirfremix_free(app, false);
return 255;
} else { } else {
//check map and population variables //check map and population variables
unirfremix_cfg_set_check(app, app->file_path); unirfremix_cfg_set_check(app, app->file_path);
@@ -970,8 +978,6 @@ int32_t unirfremix_app(void* p) {
furi_mutex_release(app->model_mutex); furi_mutex_release(app->model_mutex);
view_port_update(app->view_port); view_port_update(app->view_port);
furi_hal_power_suppress_charge_enter();
//input detect loop start //input detect loop start
InputEvent input; InputEvent input;
while(1) { while(1) {
@@ -1164,9 +1170,7 @@ int32_t unirfremix_app(void* p) {
} }
// remove & free all stuff created by app // remove & free all stuff created by app
unirfremix_free(app); unirfremix_free(app, true);
furi_hal_power_suppress_charge_exit();
return 0; return 0;
} }

View File

@@ -109,7 +109,7 @@ const BtHidKeyboardKey bt_hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = {
{.width = 1, .icon = NULL, .key = "-", .shift_key = "_", .value = HID_KEYBOARD_MINUS}, {.width = 1, .icon = NULL, .key = "-", .shift_key = "_", .value = HID_KEYBOARD_MINUS},
}, },
{ {
{.width = 1, .icon = &I_Pin_arrow_up7x9, .value = HID_KEYBOARD_L_SHIFT}, {.width = 1, .icon = &I_Pin_arrow_up_7x9, .value = HID_KEYBOARD_L_SHIFT},
{.width = 1, .icon = NULL, .key = ",", .shift_key = "<", .value = HID_KEYPAD_COMMA}, {.width = 1, .icon = NULL, .key = ",", .shift_key = "<", .value = HID_KEYPAD_COMMA},
{.width = 1, .icon = NULL, .key = ".", .shift_key = ">", .value = HID_KEYBOARD_DOT}, {.width = 1, .icon = NULL, .key = ".", .shift_key = ">", .value = HID_KEYBOARD_DOT},
{.width = 4, .icon = NULL, .key = " ", .value = HID_KEYBOARD_SPACEBAR}, {.width = 4, .icon = NULL, .key = " ", .value = HID_KEYBOARD_SPACEBAR},

View File

@@ -53,7 +53,7 @@ static void bt_hid_mouse_draw_callback(Canvas* canvas, void* context) {
canvas_set_bitmap_mode(canvas, 0); canvas_set_bitmap_mode(canvas, 0);
canvas_set_color(canvas, ColorWhite); canvas_set_color(canvas, ColorWhite);
} }
canvas_draw_icon(canvas, 84, 10, &I_Pin_arrow_up7x9); canvas_draw_icon(canvas, 84, 10, &I_Pin_arrow_up_7x9);
canvas_set_color(canvas, ColorBlack); canvas_set_color(canvas, ColorBlack);
// Down // Down

View File

@@ -1,21 +1,35 @@
# Flipfrid # Flipfrid
Basic EM4100 Fuzzer Basic EM4100 and HIDProx Fuzzer.
## Why ## Why
Flipfrid is a simple Rfid fuzzer using EM4100 protocol (125khz). 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. 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 ## How
There is 4 modes : 1) Select the Protocol with the left and right arrows
- Default key loop over 16 factory/default keys and emulate each one after one ; 2) Select the Mode with the up and down arrows
- 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; ### Info
- Uids list: loop over a text file (one uid per line)
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 : TODO :
- blank screen on back press - 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->is_attacking = false;
flipfrid->key_index = 0; flipfrid->key_index = 0;
flipfrid->menu_index = 0; flipfrid->menu_index = 0;
flipfrid->menu_proto_index = 0;
flipfrid->attack = FlipFridAttackDefaultValues; flipfrid->attack = FlipFridAttackDefaultValues;
flipfrid->notify = furi_record_open(RECORD_NOTIFICATION); flipfrid->notify = furi_record_open(RECORD_NOTIFICATION);
@@ -73,12 +74,14 @@ FlipFridState* flipfrid_alloc() {
flipfrid->data[2] = 0x00; flipfrid->data[2] = 0x00;
flipfrid->data[3] = 0x00; flipfrid->data[3] = 0x00;
flipfrid->data[4] = 0x00; flipfrid->data[4] = 0x00;
flipfrid->data[5] = 0x00;
flipfrid->payload[0] = 0x00; flipfrid->payload[0] = 0x00;
flipfrid->payload[1] = 0x00; flipfrid->payload[1] = 0x00;
flipfrid->payload[2] = 0x00; flipfrid->payload[2] = 0x00;
flipfrid->payload[3] = 0x00; flipfrid->payload[3] = 0x00;
flipfrid->payload[4] = 0x00; flipfrid->payload[4] = 0x00;
flipfrid->payload[5] = 0x00;
//Dialog //Dialog
flipfrid->dialogs = furi_record_open(RECORD_DIALOGS); flipfrid->dialogs = furi_record_open(RECORD_DIALOGS);

View File

@@ -28,6 +28,11 @@ typedef enum {
FlipFridAttackLoadFileCustomUids, FlipFridAttackLoadFileCustomUids,
} FlipFridAttacks; } FlipFridAttacks;
typedef enum {
EM4100,
HIDProx,
} FlipFridProtos;
typedef enum { typedef enum {
NoneScene, NoneScene,
SceneEntryPoint, SceneEntryPoint,
@@ -56,13 +61,16 @@ typedef struct {
FlipFridScene previous_scene; FlipFridScene previous_scene;
NotificationApp* notify; NotificationApp* notify;
u_int8_t menu_index; u_int8_t menu_index;
u_int8_t menu_proto_index;
string_t data_str; string_t data_str;
uint8_t data[5]; uint8_t data[6];
uint8_t payload[5]; uint8_t payload[6];
uint8_t attack_step; uint8_t attack_step;
FlipFridAttacks attack; FlipFridAttacks attack;
FlipFridProtos proto;
string_t attack_name; string_t attack_name;
string_t proto_name;
DialogsApp* dialogs; DialogsApp* dialogs;
string_t notification_msg; string_t notification_msg;

View File

@@ -1,8 +1,12 @@
#include "flipfrid_scene_entrypoint.h" #include "flipfrid_scene_entrypoint.h"
string_t menu_items[4]; 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) { switch(index) {
case FlipFridAttackDefaultValues: case FlipFridAttackDefaultValues:
context->attack = FlipFridAttackDefaultValues; context->attack = FlipFridAttackDefaultValues;
@@ -27,6 +31,19 @@ void flipfrid_scene_entrypoint_menu_callback(FlipFridState* context, uint32_t in
default: default:
break; 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) { 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[2] = 0x00;
context->payload[3] = 0x00; context->payload[3] = 0x00;
context->payload[4] = 0x00; context->payload[4] = 0x00;
context->payload[5] = 0x00;
context->menu_index = 0; context->menu_index = 0;
for(uint32_t i = 0; i < 4; i++) { 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[1], "BF Customer ID");
string_set(menu_items[2], "Load File"); string_set(menu_items[2], "Load File");
string_set(menu_items[3], "Load uids from 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) { 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++) { for(uint32_t i = 0; i < 4; i++) {
string_clear(menu_items[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) { void flipfrid_scene_entrypoint_on_tick(FlipFridState* context) {
@@ -74,10 +104,18 @@ void flipfrid_scene_entrypoint_on_event(FlipFridEvent event, FlipFridState* cont
} }
break; break;
case InputKeyLeft: case InputKeyLeft:
if(context->menu_proto_index > EM4100) {
context->menu_proto_index--;
}
break;
case InputKeyRight: case InputKeyRight:
if(context->menu_proto_index < HIDProx) {
context->menu_proto_index++;
}
break; break;
case InputKeyOk: 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; break;
case InputKeyBack: case InputKeyBack:
context->is_running = false; context->is_running = false;
@@ -91,10 +129,6 @@ void flipfrid_scene_entrypoint_on_draw(Canvas* canvas, FlipFridState* context) {
canvas_clear(canvas); canvas_clear(canvas);
canvas_set_color(canvas, ColorBlack); 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) { if(context->menu_index > FlipFridAttackDefaultValues) {
canvas_set_font(canvas, FontSecondary); canvas_set_font(canvas, FontSecondary);
canvas_draw_str_aligned( canvas_draw_str_aligned(
@@ -120,4 +154,41 @@ void flipfrid_scene_entrypoint_on_draw(Canvas* canvas, FlipFridState* context) {
AlignTop, AlignTop,
string_get_cstr(menu_items[context->menu_index + 1])); 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; break;
} else { } else {
FURI_LOG_I(TAG, "Key type: %s", string_get_cstr(temp_str)); 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"); if(context->proto == EM4100) {
string_reset(context->notification_msg); if(strcmp(string_get_cstr(temp_str), "EM4100") != 0) {
string_set_str(context->notification_msg, "Unsupported Key type"); FURI_LOG_E(TAG, "Unsupported Key type");
break; 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 { } else {
FURI_LOG_I(TAG, "Key: %s", string_get_cstr(context->data_str)); FURI_LOG_I(TAG, "Key: %s", string_get_cstr(context->data_str));
// Check data size if(context->proto == EM4100) {
if(string_size(context->data_str) != 14) { if(string_size(context->data_str) != 14) {
FURI_LOG_E(TAG, "Incorrect Key length"); FURI_LOG_E(TAG, "Incorrect Key length");
string_reset(context->notification_msg); string_reset(context->notification_msg);
string_set_str(context->notification_msg, "Incorrect Key length"); string_set_str(context->notification_msg, "Incorrect Key length");
break; 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 // String to uint8_t
for(uint8_t i = 0; i < 5; i++) { for(uint8_t i = 0; i < 6; i++) {
char temp_str2[3]; char temp_str2[3];
temp_str2[0] = string_get_cstr(context->data_str)[i * 3]; temp_str2[0] = string_get_cstr(context->data_str)[i * 3];
temp_str2[1] = string_get_cstr(context->data_str)[i * 3 + 1]; temp_str2[1] = string_get_cstr(context->data_str)[i * 3 + 1];

View File

@@ -2,8 +2,8 @@
#include <gui/elements.h> #include <gui/elements.h>
uint8_t counter = 0; uint8_t counter = 0;
#define TIME_BETWEEN_CARDS 5 #define TIME_BETWEEN_CARDS 6
uint8_t id_list[16][5] = { uint8_t id_list[17][5] = {
{0x00, 0x00, 0x00, 0x00, 0x00}, // Null bytes {0x00, 0x00, 0x00, 0x00, 0x00}, // Null bytes
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, // Only FF {0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, // Only FF
{0x11, 0x11, 0x11, 0x11, 0x11}, // Only 11 {0x11, 0x11, 0x11, 0x11, 0x11}, // Only 11
@@ -16,17 +16,39 @@ uint8_t id_list[16][5] = {
{0x88, 0x88, 0x88, 0x88, 0x88}, // Only 88 {0x88, 0x88, 0x88, 0x88, 0x88}, // Only 88
{0x99, 0x99, 0x99, 0x99, 0x99}, // Only 99 {0x99, 0x99, 0x99, 0x99, 0x99}, // Only 99
{0x12, 0x34, 0x56, 0x78, 0x9A}, // Incremental UID {0x12, 0x34, 0x56, 0x78, 0x9A}, // Incremental UID
{0x9A, 0x78, 0x56, 0x34, 0x12}, // Decremental UID
{0x04, 0xd0, 0x9b, 0x0d, 0x6a}, // From arha {0x04, 0xd0, 0x9b, 0x0d, 0x6a}, // From arha
{0x34, 0x00, 0x29, 0x3d, 0x9e}, // From arha {0x34, 0x00, 0x29, 0x3d, 0x9e}, // From arha
{0x04, 0xdf, 0x00, 0x00, 0x01}, // From arha {0x04, 0xdf, 0x00, 0x00, 0x01}, // From arha
{0xCA, 0xCA, 0xCA, 0xCA, 0xCA}, // 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) { void flipfrid_scene_run_attack_on_enter(FlipFridState* context) {
context->attack_step = 0; context->attack_step = 0;
context->dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); context->dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
context->worker = lfrfid_worker_alloc(context->dict); 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) { 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) { void flipfrid_scene_run_attack_on_tick(FlipFridState* context) {
if(context->is_attacking) { if(context->is_attacking) {
if(1 == counter) { 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); lfrfid_worker_free(context->worker);
context->worker = lfrfid_worker_alloc(context->dict); context->worker = lfrfid_worker_alloc(context->dict);
lfrfid_worker_start_thread(context->worker); 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); lfrfid_worker_stop_thread(context->worker);
switch(context->attack) { switch(context->attack) {
case FlipFridAttackDefaultValues: case FlipFridAttackDefaultValues:
context->payload[0] = id_list[context->attack_step][0]; if(context->proto == EM4100) {
context->payload[1] = id_list[context->attack_step][1]; context->payload[0] = id_list[context->attack_step][0];
context->payload[2] = id_list[context->attack_step][2]; context->payload[1] = id_list[context->attack_step][1];
context->payload[3] = id_list[context->attack_step][3]; context->payload[2] = id_list[context->attack_step][2];
context->payload[4] = id_list[context->attack_step][4]; context->payload[3] = id_list[context->attack_step][3];
context->payload[4] = id_list[context->attack_step][4];
if(context->attack_step == 15) { if(context->attack_step == 15) {
context->attack_step = 0; context->attack_step = 0;
counter = 0; counter = 0;
context->is_attacking = false; context->is_attacking = false;
notification_message(context->notify, &sequence_blink_stop); notification_message(context->notify, &sequence_blink_stop);
notification_message(context->notify, &sequence_single_vibro); notification_message(context->notify, &sequence_single_vibro);
} else {
} else { context->attack_step++;
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);
break; break;
} else { } 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: case FlipFridAttackBfCustomerId:
while(true) { if(context->proto == EM4100) {
string_reset(context->data_str); context->payload[0] = context->attack_step;
if(!stream_read_line(context->uids_stream, context->data_str)) { 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; context->attack_step = 0;
counter = 0; counter = 0;
context->is_attacking = false; context->is_attacking = false;
notification_message(context->notify, &sequence_blink_stop); notification_message(context->notify, &sequence_blink_stop);
notification_message(context->notify, &sequence_single_vibro); notification_message(context->notify, &sequence_single_vibro);
break; break;
}; } else {
if(string_get_char(context->data_str, 0) == '#') continue; context->attack_step++;
if(string_size(context->data_str) != 11) continue; }
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; break;
} }
FURI_LOG_D(TAG, string_get_cstr(context->data_str));
// string is valid, parse it in context->payload case FlipFridAttackLoadFileCustomUids:
for(uint8_t i = 0; i < 5; i++) { if(context->proto == EM4100) {
char temp_str[3]; while(true) {
temp_str[0] = string_get_cstr(context->data_str)[i * 2]; string_reset(context->data_str);
temp_str[1] = string_get_cstr(context->data_str)[i * 2 + 1]; if(!stream_read_line(context->uids_stream, context->data_str)) {
temp_str[2] = '\0'; context->attack_step = 0;
context->payload[i] = (uint8_t)strtol(temp_str, NULL, 16); 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_draw_str_aligned(
canvas, 64, 8, AlignCenter, AlignTop, string_get_cstr(context->attack_name)); canvas, 64, 8, AlignCenter, AlignTop, string_get_cstr(context->attack_name));
char uid[16]; char uid[18];
snprintf( if(context->proto == HIDProx) {
uid, snprintf(
sizeof(uid), uid,
"%02X:%02X:%02X:%02X:%02X", sizeof(uid),
context->payload[0], "%02X:%02X:%02X:%02X:%02X:%02X",
context->payload[1], context->payload[0],
context->payload[2], context->payload[1],
context->payload[3], context->payload[2],
context->payload[4]); 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_draw_str_aligned(canvas, 64, 24, AlignCenter, AlignTop, uid);
canvas_set_font(canvas, FontSecondary); 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_draw_frame(canvas, 0, 0, 128, 64);
canvas_set_font(canvas, FontSecondary); 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); 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, 7, 10, AlignLeft, AlignBottom, target_text);
canvas_draw_str_aligned(canvas, 22, 20, AlignLeft, AlignBottom, "<- select address ->"); 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, 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, 20, AlignLeft, AlignBottom, "or duckyscript file");
canvas_draw_str_aligned(canvas, 3, 30, AlignLeft, AlignBottom, "loading error"); 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, 10, AlignLeft, AlignBottom, "Running duckyscript");
canvas_draw_str_aligned(canvas, 3, 20, AlignLeft, AlignBottom, "Please wait!"); canvas_draw_str_aligned(canvas, 3, 20, AlignLeft, AlignBottom, "Please wait!");
canvas_draw_str_aligned( 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]); 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"); DialogsApp* dialogs = furi_record_open("dialogs");
bool result = false; bool result = false;
string_t path; string_t path;
@@ -120,6 +124,9 @@ static bool open_ducky_script(Stream* stream) {
} }
} }
string_clear(path); string_clear(path);
plugin_state->is_ducky_running = true;
return result; return result;
} }
@@ -160,10 +167,11 @@ static bool process_ducky_file(
uint8_t* file_buf; uint8_t* file_buf;
bool loaded = false; bool loaded = false;
FURI_LOG_D(TAG, "opening ducky script"); 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); file_size = stream_size(file_stream);
if(file_size == (size_t)0) { if(file_size == (size_t)0) {
FURI_LOG_D(TAG, "load failed. file_size: %d", file_size); FURI_LOG_D(TAG, "load failed. file_size: %d", file_size);
plugin_state->is_ducky_running = false;
return loaded; return loaded;
} }
file_buf = malloc(file_size); file_buf = malloc(file_size);
@@ -180,6 +188,7 @@ static bool process_ducky_file(
} }
free(file_buf); free(file_buf);
} }
plugin_state->is_ducky_running = false;
return loaded; return loaded;
} }

View File

@@ -24,6 +24,7 @@ typedef struct {
bool ducky_err; bool ducky_err;
bool addr_err; bool addr_err;
bool is_thread_running; bool is_thread_running;
bool is_ducky_running;
bool close_thread_please; bool close_thread_please;
Storage* storage; Storage* storage;
FuriThread* mjthread; FuriThread* mjthread;

View File

@@ -1,7 +1,7 @@
App( App(
appid="picopass", appid="picopass",
name="PicoPass Reader", name="PicoPass Reader",
apptype=FlipperAppType.PLUGIN, apptype=FlipperAppType.EXTERNAL,
entry_point="picopass_app", entry_point="picopass_app",
requires=[ requires=[
"storage", "storage",

View File

@@ -1,11 +1,12 @@
App( App(
appid="spectrum_analyzer", appid="spectrum_analyzer",
name="Spectrum Analyzer", name="Spectrum Analyzer",
apptype=FlipperAppType.APP, apptype=FlipperAppType.EXTERNAL,
entry_point="spectrum_analyzer_app", entry_point="spectrum_analyzer_app",
cdefines=["APP_SPECTRUM_ANALYZER"], cdefines=["APP_SPECTRUM_ANALYZER"],
requires=["gui"], requires=["gui"],
icon="A_SpectrumAnalyzer_14",
stack_size=2 * 1024, stack_size=2 * 1024,
order=12, order=12,
fap_icon="spectrum_10px.png",
fap_category="Tools",
) )

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 B

View File

@@ -2,7 +2,7 @@ App(
appid="subbrute", appid="subbrute",
name="Sub-GHz Bruteforcer", name="Sub-GHz Bruteforcer",
apptype=FlipperAppType.EXTERNAL, apptype=FlipperAppType.EXTERNAL,
entry_point="subbrute_start", entry_point="subbrute_app",
cdefines=["APP_SUB_BRUTE"], cdefines=["APP_SUB_BRUTE"],
requires=["gui","dialogs"], requires=["gui","dialogs"],
stack_size=2 * 1024, stack_size=2 * 1024,

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.h"
#include "subbrute_i.h"
#include "subbrute_custom_event.h"
#include "scene/subbrute_scene_load_file.h" #define TAG "SubBruteApp"
#include "scene/subbrute_scene_select_field.h"
#include "scene/subbrute_scene_run_attack.h"
#include "scene/subbrute_scene_entrypoint.h"
#include "scene/subbrute_scene_save_name.h"
static void draw_callback(Canvas* const canvas, void* ctx) { static const char* subbrute_menu_names[] = {
SubBruteState* subbrute_state = (SubBruteState*)acquire_mutex((ValueMutex*)ctx, 100); [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) { static const char* subbrute_menu_names_small[] = {
return; [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 static bool subbrute_custom_event_callback(void* context, uint32_t event) {
switch(subbrute_state->current_scene) { furi_assert(context);
case NoneScene: SubBruteState* instance = context;
case SceneSelectFile: return scene_manager_handle_custom_event(instance->scene_manager, event);
subbrute_scene_load_file_on_draw(canvas, subbrute_state);
break;
case SceneSelectField:
subbrute_scene_select_field_on_draw(canvas, subbrute_state);
break;
case SceneAttack:
subbrute_scene_run_attack_on_draw(canvas, subbrute_state);
break;
case SceneEntryPoint:
subbrute_scene_entrypoint_on_draw(canvas, subbrute_state);
break;
case SceneSaveName:
break;
}
release_mutex((ValueMutex*)ctx, subbrute_state);
} }
void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { static bool subbrute_back_event_callback(void* context) {
furi_assert(event_queue); furi_assert(context);
SubBruteState* instance = context;
SubBruteEvent event = { return scene_manager_handle_back_event(instance->scene_manager);
.evt_type = EventTypeKey, .key = input_event->key, .input_type = input_event->type};
furi_message_queue_put(event_queue, &event, 100);
} }
static void timer_callback(FuriMessageQueue* event_queue) { static void subbrute_tick_event_callback(void* context) {
furi_assert(event_queue); furi_assert(context);
SubBruteEvent event = { SubBruteState* instance = context;
.evt_type = EventTypeTick, .key = InputKeyUp, .input_type = InputTypeRelease}; scene_manager_handle_tick_event(instance->scene_manager);
furi_message_queue_put(event_queue, &event, 100);
} }
SubBruteState* subbrute_alloc() { SubBruteState* subbrute_alloc() {
SubBruteState* subbrute = malloc(sizeof(SubBruteState)); SubBruteState* instance = malloc(sizeof(SubBruteState));
string_init(subbrute->protocol); instance->scene_manager = scene_manager_alloc(&subbrute_scene_handlers, instance);
string_init(subbrute->preset); instance->view_dispatcher = view_dispatcher_alloc();
string_init(subbrute->file_path);
string_init(subbrute->file_path_tmp);
string_init_set(subbrute->notification_msg, "");
string_init(subbrute->candidate);
string_init(subbrute->flipper_format_string);
subbrute->previous_scene = NoneScene; instance->gui = furi_record_open(RECORD_GUI);
subbrute->current_scene = SceneSelectFile;
subbrute->is_running = true;
subbrute->is_attacking = false;
subbrute->key_index = 7;
subbrute->notify = furi_record_open(RECORD_NOTIFICATION);
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 //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(); // Devices
//subbrute->environment = subghz_environment_alloc(); 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) { void subbrute_free(SubBruteState* instance) {
//Dialog furi_assert(instance);
furi_record_close(RECORD_DIALOGS);
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); 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); // View Main
string_clear(subbrute->candidate); #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 // View Attack
string_clear(subbrute->file_path); #ifdef FURI_DEBUG
string_clear(subbrute->file_path_tmp); FURI_LOG_D(TAG, "free SubBruteViewAttack");
string_clear(subbrute->notification_msg); #endif
string_clear(subbrute->candidate); view_dispatcher_remove_view(instance->view_dispatcher, SubBruteViewAttack);
string_clear(subbrute->flipper_format_string); subbrute_attack_view_free(instance->view_attack);
//flipper_format_free(subbrute->flipper_format); // TextInput
//subghz_environment_free(subbrute->environment); #ifdef FURI_DEBUG
//subghz_receiver_free(subbrute->receiver); 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 // 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 // ENTRYPOINT
int32_t subbrute_start(void* p) { int32_t subbrute_app(void* p) {
UNUSED(p); UNUSED(p);
// Input
FURI_LOG_I(TAG, "Initializing input");
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(SubBruteEvent));
SubBruteState* subbrute_state = subbrute_alloc();
ValueMutex subbrute_state_mutex;
// Mutex SubBruteState* instance = subbrute_alloc();
FURI_LOG_I(TAG, "Initializing flipfrid mutex"); view_dispatcher_attach_to_gui(
if(!init_mutex(&subbrute_state_mutex, subbrute_state, sizeof(SubBruteState))) { instance->view_dispatcher, instance->gui, ViewDispatcherTypeFullscreen);
FURI_LOG_E(TAG, "cannot create mutex\r\n"); scene_manager_next_scene(instance->scene_manager, SubBruteSceneStart);
furi_message_queue_free(event_queue);
subbrute_free(subbrute_state);
return 255;
}
furi_hal_power_suppress_charge_enter(); furi_hal_power_suppress_charge_enter();
view_dispatcher_run(instance->view_dispatcher);
// 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);
furi_hal_power_suppress_charge_exit(); furi_hal_power_suppress_charge_exit();
subbrute_free(instance);
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);
return 0; return 0;
} }

View File

@@ -1,110 +1,3 @@
#pragma once #pragma once
#include <furi.h>
#include <furi_hal.h>
#include <input/input.h>
#include <gui/gui.h>
#include "m-string.h"
#include <toolbox/stream/stream.h> typedef struct SubBruteState SubBruteState;
#include <lib/subghz/transmitter.h>
#include <lib/subghz/receiver.h>
#include <flipper_format/flipper_format_i.h>
#include <dialogs/dialogs.h>
#include <notification/notification.h>
#include <notification/notification_messages.h>
#include <gui/view_dispatcher.h>
#include <gui/modules/text_input.h>
#include <gui/modules/popup.h>
#define TAG "SUBBRUTE"
typedef enum {
NoneScene,
SceneSelectFile,
SceneSelectField,
SceneAttack,
SceneEntryPoint,
SceneSaveName
} SubBruteScene;
typedef enum {
SubBruteAttackLoadFile,
SubBruteAttackCAME12bit307,
SubBruteAttackCAME12bit433,
SubBruteAttackCAME12bit868,
SubBruteAttackChamberlain9bit315,
SubBruteAttackChamberlain9bit390,
SubBruteAttackLinear10bit300,
SubBruteAttackLinear10bit310,
SubBruteAttackNICE12bit433,
SubBruteAttackNICE12bit868,
} SubBruteAttacks;
typedef enum {
EventTypeTick,
EventTypeKey,
EventTypeCustom,
} EventType;
typedef struct {
EventType evt_type;
InputKey key;
InputType input_type;
} SubBruteEvent;
// STRUCTS
typedef struct {
// Application stuff
bool is_running;
bool is_attacking;
bool is_thread_running;
bool close_thread_please;
SubBruteScene current_scene;
SubBruteScene previous_scene;
NotificationApp* notify;
Gui* gui;
ViewDispatcher* view_dispatcher;
TextInput* text_input;
Popup* popup;
// SubGhz Stuff
FuriThread* bruthread;
FlipperFormat* flipper_format;
SubGhzEnvironment* environment;
SubGhzTransmitter* transmitter;
SubGhzReceiver* receiver;
SubGhzProtocolDecoderBase* decoder_result;
SubGhzPresetDefinition* preset_def;
string_t preset;
Stream* stream;
string_t protocol;
uint32_t frequency;
uint32_t frequency_cal;
uint32_t repeat;
uint32_t bit;
string_t key;
uint32_t te;
// Context Stuff
DialogsApp* dialogs;
char file_name_tmp[64];
string_t file_path;
string_t file_path_tmp;
string_t notification_msg;
uint8_t key_index;
uint64_t payload;
string_t candidate;
uint8_t str_index;
string_t flipper_format_string;
SubBruteAttacks attack;
//Menu stuff
uint8_t menu_index;
// RAW stuff
string_t subbrute_raw_one;
string_t subbrute_raw_zero;
string_t subbrute_raw_stop;
} SubBruteState;

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) { bool desktop_scene_slideshow_on_event(void* context, SceneManagerEvent event) {
Desktop* desktop = (Desktop*)context; Desktop* desktop = (Desktop*)context;
bool consumed = false; bool consumed = false;
Storage* storage = NULL;
Power* power = NULL; Power* power = NULL;
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) { switch(event.event) {
case DesktopSlideshowCompleted: 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); scene_manager_previous_scene(desktop->scene_manager);
consumed = true; consumed = true;
break; break;
@@ -50,4 +46,8 @@ bool desktop_scene_slideshow_on_event(void* context, SceneManagerEvent event) {
void desktop_scene_slideshow_on_exit(void* context) { void desktop_scene_slideshow_on_exit(void* context) {
UNUSED(context); UNUSED(context);
Storage* storage = furi_record_open(RECORD_STORAGE);
storage_common_remove(storage, SLIDESHOW_FS_PATH);
furi_record_close(RECORD_STORAGE);
} }

View File

@@ -117,7 +117,7 @@ static void desktop_view_pin_input_draw_cells(Canvas* canvas, DesktopViewPinInpu
canvas_draw_icon(canvas, x + 3, y + 2, &I_Pin_arrow_down_7x9); canvas_draw_icon(canvas, x + 3, y + 2, &I_Pin_arrow_down_7x9);
break; break;
case InputKeyUp: case InputKeyUp:
canvas_draw_icon(canvas, x + 3, y + 2, &I_Pin_arrow_up7x9); canvas_draw_icon(canvas, x + 3, y + 2, &I_Pin_arrow_up_7x9);
break; break;
case InputKeyLeft: case InputKeyLeft:
canvas_draw_icon(canvas, x + 2, y + 3, &I_Pin_arrow_left_9x7); canvas_draw_icon(canvas, x + 2, y + 3, &I_Pin_arrow_left_9x7);

View File

@@ -142,6 +142,8 @@ void button_panel_reset(ButtonPanel* button_panel) {
} }
model->reserve_x = 0; model->reserve_x = 0;
model->reserve_y = 0; model->reserve_y = 0;
model->selected_item_x = 0;
model->selected_item_y = 0;
LabelList_reset(model->labels); LabelList_reset(model->labels);
ButtonMatrix_reset(model->button_matrix); ButtonMatrix_reset(model->button_matrix);
return true; 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) { } else if(selected == BACKSPACE_KEY) {
text_input_backspace_cb(model); text_input_backspace_cb(model);
} else if(text_length < (model->text_buffer_size - 1)) { } else {
if(model->clear_default_text) { if(model->clear_default_text) {
text_length = 0; text_length = 0;
} }
if(text_length == 0 && char_is_lowercase(selected)) { if(text_length < (model->text_buffer_size - 1)) {
selected = char_to_uppercase(selected); 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; model->clear_default_text = false;
} }

View File

@@ -1,11 +1,8 @@
#include "power_i.h" #include "power_i.h"
#include "views/power_off.h"
#include "desktop/desktop_settings.h" #include "desktop/desktop_settings.h"
#include <furi.h> #include <furi.h>
#include <furi_hal.h> #include <furi_hal.h>
#include <gui/view_port.h>
#include <gui/view.h>
#define POWER_OFF_TIMEOUT 90 #define POWER_OFF_TIMEOUT 90

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