diff --git a/.vscode/example/settings.json.tmpl b/.vscode/example/settings.json.tmpl index 5e5b5dcf4..b8e9f81cd 100644 --- a/.vscode/example/settings.json.tmpl +++ b/.vscode/example/settings.json.tmpl @@ -12,6 +12,7 @@ "SConstruct": "python", "*.fam": "python" }, + "clangd.checkUpdates": false, "clangd.path": "${workspaceFolder}/toolchain/current/bin/clangd@FBT_PLATFORM_EXECUTABLE_EXT@", "clangd.arguments": [ "--query-driver=**/arm-none-eabi-*", diff --git a/CHANGELOG.md b/CHANGELOG.md index c145fffd8..5e693c805 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,11 +5,19 @@ - OFW: JS: New `gui/widget` view, replaces old `widget` module (by @portasynthinca3) - Scripts using `widget` module will need to be updated - Check the `gui.js` example for reference usage +- BadKB: Rewritten BadKB extras on top of "new" OFW BadUSB structure (by @Willy-JL) + - Should be more reliable with BLE, will be easier to keep updated + - Previous settings and pairing will be reset, need to reconfigure and pair again ### Added: - Apps: - Games: Quadrastic (by @ivanbarsukov) -- OFW: RFID: EM4305 support (by @Astrrra) +- RFID: + - OFW: EM4305 support (by @Astrrra) + - OFW: Noralsy Format/Brand protocol (by @zinongli) +- OFW: BadKB: Mouse control (by @jetrp1) +- OFW: Infrared: Universal IR signal selection (by @portasynthinca3) +- OFW: NFC: Disney Infinity KDF plugin (by @bettse) - Desktop: - UL: Option to prevent Auto Lock when connected to USB/RPC (by @Dmitry422) - OFW: Add the Showtime animation (by @Astrrra) @@ -28,6 +36,8 @@ - Metroflip: Big refactor with plugins and assets to save RAM, RavKav moved to Calypso parser (by @luu176), unified Calypso parser (by @DocSystem) - Picopass: Added Save SR as legacy from saved menu, fix write key 'retry' when presented with new card (by @bettse) - Pinball0: Prevent tilt before ball is in play, fixed Endless table by making bottom portal extend full width (by @rdefeo) +- BadKB: Rewritten BadKB extras on top of "new" OFW BadUSB structure (by @Willy-JL) + - Additionally, can now customize MAC address when BLE Remember is enabled - NFC: - OFW: Added naming for DESFire cards + fix MF3ICD40 cards unable to be read (by @Demae) - OFW: Enable MFUL sync poller to be provided with passwords (by @GMMan) @@ -39,12 +49,14 @@ ### Fixed: - Asset Packs: Fix level-up animations not being themed (by @Willy-JL) - About: Fix missing Prev. button when invoked from Device Info keybind (by @Willy-JL) -- OFW: uFBT: Bumped action version in example github workflow for project template (by @hedger) - OFW: NFC: ST25TB poller mode check (by @RebornedBrain) - Furi: - OFW: EventLoop unsubscribe fix (by @gsurkov & @portasynthinca3) - OFW: Various bug fixes and improvements (by @skotopes) - OFW: Ensure that `furi_record_create()` is passed a non-NULL data pointer (by @dcoles) +- OFW: CLI: Fixed repeat in subghz tx_from_file command (by @Jnesselr) +- OFW: VSCode: Disabled auto-update for clangd since correct version is in the toolchain (by @hedger) +- OFW: uFBT: Bumped action version in example github workflow for project template (by @hedger) ### Removed: - JS: Removed old `widget` module, replaced by new `gui/widget` view diff --git a/ReadMe.md b/ReadMe.md index 5552bdb86..bfb54a905 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -71,7 +71,7 @@ After installing the packs to Flipper, hit the Arrow Up button on t

Bad Keyboard:

-BadUSB is a great app, but it lacks Bluetooth connectivity. Bad-KB allows you to toggle between USB and Bluetooth mode for your attacks. +BadUSB is a great app, but it lacks a lot of options. Bad-KB allows you to customize all USB and Bluetooth parameters for your attacks. In Bluetooth mode it allows you to spoof the display name and MAC address of the device to whatever you want. Showing up as a portable speaker or a wireless keyboard is easily doable, allowing you to get the attention of your target without needing a cable at hand. diff --git a/applications/ReadMe.md b/applications/ReadMe.md index be9c18a6e..aaff644b3 100644 --- a/applications/ReadMe.md +++ b/applications/ReadMe.md @@ -25,7 +25,7 @@ Applications for factory testing the Flipper. Applications for main Flipper menu. - `archive` - Archive and file manager -- `bad_kb` - Bad KB application +- `bad_usb` - Bad KB application - `gpio` - GPIO application: includes USART bridge and GPIO control - `ibutton` - iButton application, onewire keys and more - `infrared` - Infrared application, controls your IR devices diff --git a/applications/debug/file_browser_test/file_browser_app.c b/applications/debug/file_browser_test/file_browser_app.c index 89b8b9274..a502a8a90 100644 --- a/applications/debug/file_browser_test/file_browser_app.c +++ b/applications/debug/file_browser_test/file_browser_app.c @@ -47,7 +47,7 @@ FileBrowserApp* file_browser_app_alloc(char* arg) { app->file_path = furi_string_alloc(); app->file_browser = file_browser_alloc(app->file_path); - file_browser_configure(app->file_browser, "*", NULL, true, false, &I_badkb_10px, true); + file_browser_configure(app->file_browser, "*", NULL, true, false, &I_badusb_10px, true); view_dispatcher_add_view( app->view_dispatcher, FileBrowserAppViewStart, widget_get_view(app->widget)); diff --git a/applications/debug/file_browser_test/icons/badkb_10px.png b/applications/debug/file_browser_test/icons/badusb_10px.png similarity index 100% rename from applications/debug/file_browser_test/icons/badkb_10px.png rename to applications/debug/file_browser_test/icons/badusb_10px.png diff --git a/applications/external b/applications/external index ff4dbdaaf..c780ecbb0 160000 --- a/applications/external +++ b/applications/external @@ -1 +1 @@ -Subproject commit ff4dbdaafab11074484920d3e3c95776bf9511f2 +Subproject commit c780ecbb00811dae5a13adb093dbb3bd349dc26a diff --git a/applications/main/archive/helpers/archive_browser.h b/applications/main/archive/helpers/archive_browser.h index 6b05dc1ea..abdfc9743 100644 --- a/applications/main/archive/helpers/archive_browser.h +++ b/applications/main/archive/helpers/archive_browser.h @@ -14,7 +14,7 @@ static const char* tab_default_paths[] = { [ArchiveTabSubGhz] = EXT_PATH("subghz"), [ArchiveTabLFRFID] = EXT_PATH("lfrfid"), [ArchiveTabInfrared] = EXT_PATH("infrared"), - [ArchiveTabBadKb] = EXT_PATH("badusb"), + [ArchiveTabBadUsb] = EXT_PATH("badusb"), [ArchiveTabU2f] = "/app:u2f", [ArchiveTabApplications] = EXT_PATH("apps"), [ArchiveTabSearch] = "/app:search", @@ -33,7 +33,7 @@ static const char* known_ext[] = { [ArchiveFileTypeSubghzPlaylist] = ".txt", [ArchiveFileTypeSubghzRemote] = ".txt", [ArchiveFileTypeInfraredRemote] = ".txt", - [ArchiveFileTypeBadKb] = ".txt", + [ArchiveFileTypeBadUsb] = ".txt", [ArchiveFileTypeWAV] = ".wav", [ArchiveFileTypeMag] = ".mag", [ArchiveFileTypeU2f] = "?", @@ -55,7 +55,7 @@ static const ArchiveFileTypeEnum known_type[] = { [ArchiveTabSubGhz] = ArchiveFileTypeSubGhz, [ArchiveTabLFRFID] = ArchiveFileTypeLFRFID, [ArchiveTabInfrared] = ArchiveFileTypeInfrared, - [ArchiveTabBadKb] = ArchiveFileTypeBadKb, + [ArchiveTabBadUsb] = ArchiveFileTypeBadUsb, [ArchiveTabU2f] = ArchiveFileTypeU2f, [ArchiveTabApplications] = ArchiveFileTypeAppOrJs, [ArchiveTabSearch] = ArchiveFileTypeSearch, diff --git a/applications/main/archive/helpers/archive_files.c b/applications/main/archive/helpers/archive_files.c index 7c61e1b24..c71de5d08 100644 --- a/applications/main/archive/helpers/archive_files.c +++ b/applications/main/archive/helpers/archive_files.c @@ -32,8 +32,8 @@ void archive_set_file_type(ArchiveFile_t* file, const char* path, bool is_folder case ArchiveFileTypeInfraredRemote: txt_path = IR_REMOTE_PATH; break; - case ArchiveFileTypeBadKb: - txt_path = archive_get_default_path(ArchiveTabBadKb); + case ArchiveFileTypeBadUsb: + txt_path = archive_get_default_path(ArchiveTabBadUsb); break; } if(txt_path != NULL) { diff --git a/applications/main/archive/helpers/archive_files.h b/applications/main/archive/helpers/archive_files.h index 60e45a829..322cab523 100644 --- a/applications/main/archive/helpers/archive_files.h +++ b/applications/main/archive/helpers/archive_files.h @@ -18,7 +18,7 @@ typedef enum { ArchiveFileTypeSubghzPlaylist, ArchiveFileTypeSubghzRemote, ArchiveFileTypeInfraredRemote, - ArchiveFileTypeBadKb, + ArchiveFileTypeBadUsb, ArchiveFileTypeWAV, ArchiveFileTypeMag, ArchiveFileTypeU2f, diff --git a/applications/main/archive/scenes/archive_scene_browser.c b/applications/main/archive/scenes/archive_scene_browser.c index 9fd36a3c7..f00ef8317 100644 --- a/applications/main/archive/scenes/archive_scene_browser.c +++ b/applications/main/archive/scenes/archive_scene_browser.c @@ -29,7 +29,7 @@ const char* archive_get_flipper_app_name(ArchiveFileTypeEnum file_type) { return EXT_PATH("apps/Sub-Ghz/subghz_remote.fap"); case ArchiveFileTypeInfraredRemote: return EXT_PATH("apps/Infrared/ir_remote.fap"); - case ArchiveFileTypeBadKb: + case ArchiveFileTypeBadUsb: return "Bad KB"; case ArchiveFileTypeWAV: return EXT_PATH("apps/Media/wav_player.fap"); diff --git a/applications/main/archive/views/archive_browser_view.c b/applications/main/archive/views/archive_browser_view.c index 658ae1a92..b28936d5a 100644 --- a/applications/main/archive/views/archive_browser_view.c +++ b/applications/main/archive/views/archive_browser_view.c @@ -15,7 +15,7 @@ static const char* ArchiveTabNames[] = { [ArchiveTabSubGhz] = "Sub-GHz", [ArchiveTabLFRFID] = "RFID LF", [ArchiveTabInfrared] = "Infrared", - [ArchiveTabBadKb] = "Bad KB", + [ArchiveTabBadUsb] = "Bad KB", [ArchiveTabU2f] = "U2F", [ArchiveTabApplications] = "Apps", [ArchiveTabSearch] = "Search", @@ -33,7 +33,7 @@ static const Icon* ArchiveItemIcons[] = { [ArchiveFileTypeSubghzPlaylist] = &I_subplaylist_10px, [ArchiveFileTypeSubghzRemote] = &I_subrem_10px, [ArchiveFileTypeInfraredRemote] = &I_ir_scope_10px, - [ArchiveFileTypeBadKb] = &I_badkb_10px, + [ArchiveFileTypeBadUsb] = &I_badusb_10px, [ArchiveFileTypeWAV] = &I_music_10px, [ArchiveFileTypeMag] = &I_mag_card_10px, [ArchiveFileTypeU2f] = &I_u2f_10px, diff --git a/applications/main/archive/views/archive_browser_view.h b/applications/main/archive/views/archive_browser_view.h index b5db1b96d..c94d60631 100644 --- a/applications/main/archive/views/archive_browser_view.h +++ b/applications/main/archive/views/archive_browser_view.h @@ -28,7 +28,7 @@ typedef enum { ArchiveTabNFC, ArchiveTabInfrared, ArchiveTabIButton, - ArchiveTabBadKb, + ArchiveTabBadUsb, ArchiveTabU2f, ArchiveTabApplications, ArchiveTabSearch, diff --git a/applications/main/bad_kb/bad_kb_app.c b/applications/main/bad_kb/bad_kb_app.c deleted file mode 100644 index aaae95011..000000000 --- a/applications/main/bad_kb/bad_kb_app.c +++ /dev/null @@ -1,467 +0,0 @@ -#include "bad_kb_app_i.h" -#include -#include -#include -#include -#include -#include -#include "helpers/ducky_script_i.h" - -// Adjusts to serial MAC +2 in app init -uint8_t BAD_KB_BOUND_MAC[GAP_MAC_ADDR_SIZE] = {0}; - -static bool bad_kb_app_custom_event_callback(void* context, uint32_t event) { - furi_assert(context); - BadKbApp* app = context; - return scene_manager_handle_custom_event(app->scene_manager, event); -} - -static bool bad_kb_app_back_event_callback(void* context) { - furi_assert(context); - BadKbApp* app = context; - return scene_manager_handle_back_event(app->scene_manager); -} - -static void bad_kb_app_tick_event_callback(void* context) { - furi_assert(context); - BadKbApp* app = context; - scene_manager_handle_tick_event(app->scene_manager); -} - -void bad_kb_load_settings(BadKbApp* app) { - furi_string_reset(app->keyboard_layout); - BadKbConfig* cfg = &app->config; - - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* file = flipper_format_file_alloc(storage); - if(flipper_format_file_open_existing(file, BAD_KB_SETTINGS_PATH)) { - FuriString* tmp_str = furi_string_alloc(); - uint32_t tmp_uint = 0; - - if(!flipper_format_read_string(file, "Keyboard_Layout", app->keyboard_layout)) { - furi_string_reset(app->keyboard_layout); - flipper_format_rewind(file); - } - - if(!flipper_format_read_bool(file, "Is_Bt", &app->is_bt, 1)) { - app->is_bt = false; - flipper_format_rewind(file); - } - - if(!flipper_format_read_bool(file, "Bt_Remember", &cfg->ble.bonding, 1)) { - cfg->ble.bonding = false; - flipper_format_rewind(file); - } - - if(!flipper_format_read_uint32(file, "Bt_Pairing", &tmp_uint, 1)) { - tmp_uint = GapPairingNone; - flipper_format_rewind(file); - } - cfg->ble.pairing = tmp_uint; - - if(flipper_format_read_string(file, "Bt_Name", tmp_str)) { - strlcpy(cfg->ble.name, furi_string_get_cstr(tmp_str), sizeof(cfg->ble.name)); - } else { - cfg->ble.name[0] = '\0'; - flipper_format_rewind(file); - } - - if(!flipper_format_read_hex( - file, "Bt_Mac", (uint8_t*)&cfg->ble.mac, sizeof(cfg->ble.mac))) { - memset(cfg->ble.mac, 0, sizeof(cfg->ble.mac)); - flipper_format_rewind(file); - } - - if(flipper_format_read_string(file, "Usb_Manuf", tmp_str)) { - strlcpy(cfg->usb.manuf, furi_string_get_cstr(tmp_str), sizeof(cfg->usb.manuf)); - } else { - cfg->usb.manuf[0] = '\0'; - flipper_format_rewind(file); - } - - if(flipper_format_read_string(file, "Usb_Product", tmp_str)) { - strlcpy(cfg->usb.product, furi_string_get_cstr(tmp_str), sizeof(cfg->usb.product)); - } else { - cfg->usb.product[0] = '\0'; - flipper_format_rewind(file); - } - - if(!flipper_format_read_uint32(file, "Usb_Vid", &cfg->usb.vid, 1)) { - cfg->usb.vid = 0; - flipper_format_rewind(file); - } - - if(!flipper_format_read_uint32(file, "Usb_Pid", &cfg->usb.pid, 1)) { - cfg->usb.pid = 0; - flipper_format_rewind(file); - } - - furi_string_free(tmp_str); - flipper_format_file_close(file); - } - flipper_format_free(file); - - if(!furi_string_empty(app->keyboard_layout)) { - FileInfo layout_file_info; - FS_Error file_check_err = storage_common_stat( - storage, furi_string_get_cstr(app->keyboard_layout), &layout_file_info); - if(file_check_err != FSE_OK) { - furi_string_reset(app->keyboard_layout); - return; - } - if(layout_file_info.size != 256) { - furi_string_reset(app->keyboard_layout); - } - } - - furi_record_close(RECORD_STORAGE); -} - -static void bad_kb_save_settings(BadKbApp* app) { - BadKbConfig* cfg = &app->config; - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* file = flipper_format_file_alloc(storage); - if(flipper_format_file_open_always(file, BAD_KB_SETTINGS_PATH)) { - uint32_t tmp_uint = 0; - flipper_format_write_string(file, "Keyboard_Layout", app->keyboard_layout); - flipper_format_write_bool(file, "Is_Bt", &app->is_bt, 1); - flipper_format_write_bool(file, "Bt_Remember", &cfg->ble.bonding, 1); - tmp_uint = cfg->ble.pairing; - flipper_format_write_uint32(file, "Bt_Pairing", &tmp_uint, 1); - flipper_format_write_string_cstr(file, "Bt_Name", cfg->ble.name); - flipper_format_write_hex(file, "Bt_Mac", (uint8_t*)&cfg->ble.mac, sizeof(cfg->ble.mac)); - flipper_format_write_string_cstr(file, "Usb_Manuf", cfg->usb.manuf); - flipper_format_write_string_cstr(file, "Usb_Product", cfg->usb.product); - flipper_format_write_uint32(file, "Usb_Vid", &cfg->usb.vid, 1); - flipper_format_write_uint32(file, "Usb_Pid", &cfg->usb.pid, 1); - flipper_format_file_close(file); - } - flipper_format_free(file); - furi_record_close(RECORD_STORAGE); -} - -void bad_kb_app_show_loading_popup(BadKbApp* app, bool show) { - if(show) { - // Raise timer priority so that animations can play - furi_timer_set_thread_priority(FuriTimerThreadPriorityElevated); - view_dispatcher_switch_to_view(app->view_dispatcher, BadKbAppViewLoading); - } else { - // Restore default timer priority - furi_timer_set_thread_priority(FuriTimerThreadPriorityNormal); - } -} - -int32_t bad_kb_conn_apply(BadKbApp* app) { - if(app->is_bt) { - // Setup profile config - BadKbConfig* cfg = app->set_bt_id ? &app->id_config : &app->config; - memcpy(&app->cur_ble_cfg, &cfg->ble, sizeof(cfg->ble)); - if(app->cur_ble_cfg.bonding) { - // Hardcode mac for remember mode - // Change in config copy to preserve user choice for non-remember mode - memcpy(app->cur_ble_cfg.mac, BAD_KB_BOUND_MAC, sizeof(BAD_KB_BOUND_MAC)); - } - - // Prepare for new profile - bt_timeout = bt_hid_delays[LevelRssi39_0]; - bt_disconnect(app->bt); - furi_delay_ms(200); - bt_keys_storage_set_storage_path(app->bt, BAD_KB_KEYS_PATH); - - // Set profile - app->ble_hid = bt_profile_start(app->bt, ble_profile_hid, &app->cur_ble_cfg); - furi_check(app->ble_hid); - - // Advertise even if BT is off in settings - furi_hal_bt_start_advertising(); - - app->conn_mode = BadKbConnModeBt; - - } else { - // Unlock RPC connections - furi_hal_usb_unlock(); - - // Context will apply with set_config only if pointer address is different, so we use a copy - FuriHalUsbHidConfig* cur_usb_cfg = malloc(sizeof(FuriHalUsbHidConfig)); - - // Setup new config - BadKbConfig* cfg = app->set_usb_id ? &app->id_config : &app->config; - memcpy(cur_usb_cfg, &cfg->usb, sizeof(cfg->usb)); - - // Set profile - furi_check(furi_hal_usb_set_config(&usb_hid, cur_usb_cfg)); - if(app->cur_usb_cfg) free(app->cur_usb_cfg); - app->cur_usb_cfg = cur_usb_cfg; - - app->conn_mode = BadKbConnModeUsb; - } - - return 0; -} - -void bad_kb_conn_reset(BadKbApp* app) { - if(app->conn_mode == BadKbConnModeBt) { - bt_disconnect(app->bt); - furi_delay_ms(200); - bt_keys_storage_set_default_path(app->bt); - furi_check(bt_profile_restore_default(app->bt)); - } else if(app->conn_mode == BadKbConnModeUsb) { - // TODO: maybe also restore USB context? - furi_check(furi_hal_usb_set_config(app->prev_usb_mode, NULL)); - } - - app->conn_mode = BadKbConnModeNone; -} - -void bad_kb_config_adjust(BadKbConfig* cfg) { - // Avoid empty name - if(cfg->ble.name[0] == '\0') { - snprintf( - cfg->ble.name, sizeof(cfg->ble.name), "Control %s", furi_hal_version_get_name_ptr()); - } - - const uint8_t* normal_mac = furi_hal_version_get_ble_mac(); - uint8_t empty_mac[sizeof(cfg->ble.mac)] = {0}; - uint8_t default_mac[sizeof(cfg->ble.mac)] = {0x6c, 0x7a, 0xd8, 0xac, 0x57, 0x72}; //furi_hal_bt - if(memcmp(cfg->ble.mac, empty_mac, sizeof(cfg->ble.mac)) == 0 || - memcmp(cfg->ble.mac, normal_mac, sizeof(cfg->ble.mac)) == 0 || - memcmp(cfg->ble.mac, default_mac, sizeof(cfg->ble.mac)) == 0) { - memcpy(cfg->ble.mac, normal_mac, sizeof(cfg->ble.mac)); - cfg->ble.mac[2]++; - } - - // Use defaults if vid or pid are unset - if(cfg->usb.vid == 0) cfg->usb.vid = HID_VID_DEFAULT; - if(cfg->usb.pid == 0) cfg->usb.pid = HID_PID_DEFAULT; -} - -void bad_kb_config_refresh(BadKbApp* app) { - bt_set_status_changed_callback(app->bt, NULL, NULL); - furi_hal_hid_set_state_callback(NULL, NULL); - if(app->bad_kb_script) { - furi_thread_flags_set(furi_thread_get_id(app->bad_kb_script->thread), WorkerEvtDisconnect); - } - if(app->conn_init_thread) { - furi_thread_join(app->conn_init_thread); - } - - bool apply = false; - if(app->is_bt) { - BadKbConfig* cfg = app->set_bt_id ? &app->id_config : &app->config; - bad_kb_config_adjust(cfg); - - if(app->conn_mode != BadKbConnModeBt) { - apply = true; - bad_kb_conn_reset(app); - } else { - BleProfileHidParams* cur = &app->cur_ble_cfg; - apply = apply || cfg->ble.bonding != cur->bonding; - apply = apply || cfg->ble.pairing != cur->pairing; - apply = apply || strncmp(cfg->ble.name, cur->name, sizeof(cfg->ble.name)); - apply = apply || memcmp(cfg->ble.mac, cur->mac, sizeof(cfg->ble.mac)); - } - } else { - BadKbConfig* cfg = app->set_usb_id ? &app->id_config : &app->config; - bad_kb_config_adjust(cfg); - - if(app->conn_mode != BadKbConnModeUsb) { - apply = true; - bad_kb_conn_reset(app); - } else { - FuriHalUsbHidConfig* cur = app->cur_usb_cfg; - apply = apply || cfg->usb.vid != cur->vid; - apply = apply || cfg->usb.pid != cur->pid; - apply = apply || strncmp(cfg->usb.manuf, cur->manuf, sizeof(cur->manuf)); - apply = apply || strncmp(cfg->usb.product, cur->product, sizeof(cur->product)); - } - } - - if(apply) { - bad_kb_conn_apply(app); - } - - if(app->bad_kb_script) { - BadKbScript* script = app->bad_kb_script; - script->st.is_bt = app->is_bt; - script->bt = app->is_bt ? app->bt : NULL; - bool connected; - if(app->is_bt) { - bt_set_status_changed_callback(app->bt, bad_kb_bt_hid_state_callback, script); - connected = furi_hal_bt_is_connected(); - } else { - furi_hal_hid_set_state_callback(bad_kb_usb_hid_state_callback, script); - connected = furi_hal_hid_is_connected(); - } - if(connected) { - furi_thread_flags_set(furi_thread_get_id(script->thread), WorkerEvtConnect); - } - } - - // Reload config page - scene_manager_next_scene(app->scene_manager, BadKbSceneConfig); - scene_manager_previous_scene(app->scene_manager); -} - -void reverse_mac_addr(uint8_t mac_addr[GAP_MAC_ADDR_SIZE]) { - uint8_t tmp; - for(size_t i = 0; i < GAP_MAC_ADDR_SIZE / 2; i++) { - tmp = mac_addr[i]; - mac_addr[i] = mac_addr[GAP_MAC_ADDR_SIZE - 1 - i]; - mac_addr[GAP_MAC_ADDR_SIZE - 1 - i] = tmp; - } -} - -BadKbApp* bad_kb_app_alloc(char* arg) { - BadKbApp* app = malloc(sizeof(BadKbApp)); - - app->bad_kb_script = NULL; - - app->file_path = furi_string_alloc(); - app->keyboard_layout = furi_string_alloc(); - if(arg && strlen(arg)) { - furi_string_set(app->file_path, arg); - } - - bad_kb_load_settings(app); - - app->gui = furi_record_open(RECORD_GUI); - app->notifications = furi_record_open(RECORD_NOTIFICATION); - app->dialogs = furi_record_open(RECORD_DIALOGS); - - app->view_dispatcher = view_dispatcher_alloc(); - - app->scene_manager = scene_manager_alloc(&bad_kb_scene_handlers, app); - - view_dispatcher_set_event_callback_context(app->view_dispatcher, app); - view_dispatcher_set_tick_event_callback( - app->view_dispatcher, bad_kb_app_tick_event_callback, 250); - view_dispatcher_set_custom_event_callback( - app->view_dispatcher, bad_kb_app_custom_event_callback); - view_dispatcher_set_navigation_event_callback( - app->view_dispatcher, bad_kb_app_back_event_callback); - - Bt* bt = furi_record_open(RECORD_BT); - app->bt = bt; - app->bt->suppress_pin_screen = true; - bad_kb_config_adjust(&app->config); - - // Save prev config - app->prev_usb_mode = furi_hal_usb_get_config(); - - // Adjust BT remember MAC to be serial MAC +2 - memcpy(BAD_KB_BOUND_MAC, furi_hal_version_get_ble_mac(), sizeof(BAD_KB_BOUND_MAC)); - BAD_KB_BOUND_MAC[2] += 2; - - // Custom Widget - app->widget = widget_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, BadKbAppViewWidget, widget_get_view(app->widget)); - - app->var_item_list = variable_item_list_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, - BadKbAppViewVarItemList, - variable_item_list_get_view(app->var_item_list)); - - app->bad_kb_view = bad_kb_view_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, BadKbAppViewWork, bad_kb_view_get_view(app->bad_kb_view)); - - app->text_input = text_input_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, BadKbAppViewTextInput, text_input_get_view(app->text_input)); - - app->byte_input = byte_input_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, BadKbAppViewByteInput, byte_input_get_view(app->byte_input)); - - app->loading = loading_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, BadKbAppViewLoading, loading_get_view(app->loading)); - - view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); - - app->conn_mode = BadKbConnModeNone; - app->conn_init_thread = - furi_thread_alloc_ex("BadKbConnInit", 1024, (FuriThreadCallback)bad_kb_conn_apply, app); - furi_thread_start(app->conn_init_thread); - if(!furi_string_empty(app->file_path)) { - app->bad_kb_script = bad_kb_script_open(app->file_path, app->is_bt ? app->bt : NULL, app); - bad_kb_script_set_keyboard_layout(app->bad_kb_script, app->keyboard_layout); - scene_manager_next_scene(app->scene_manager, BadKbSceneWork); - } else { - furi_string_set(app->file_path, BAD_KB_APP_BASE_FOLDER); - scene_manager_next_scene(app->scene_manager, BadKbSceneFileSelect); - } - - return app; -} - -void bad_kb_app_free(BadKbApp* app) { - furi_assert(app); - - if(app->bad_kb_script) { - bad_kb_script_close(app->bad_kb_script); - app->bad_kb_script = NULL; - } - - // Views - view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewWork); - bad_kb_view_free(app->bad_kb_view); - - // Custom Widget - view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewWidget); - widget_free(app->widget); - - // Variable item list - view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewVarItemList); - variable_item_list_free(app->var_item_list); - - // Text Input - view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewTextInput); - text_input_free(app->text_input); - - // Byte Input - view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewByteInput); - byte_input_free(app->byte_input); - - // Loading - view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewLoading); - loading_free(app->loading); - - // View dispatcher - view_dispatcher_free(app->view_dispatcher); - scene_manager_free(app->scene_manager); - - // Restore connection config - app->bt->suppress_pin_screen = false; - if(app->conn_init_thread) { - furi_thread_join(app->conn_init_thread); - furi_thread_free(app->conn_init_thread); - app->conn_init_thread = NULL; - } - bad_kb_conn_reset(app); - if(app->cur_usb_cfg) free(app->cur_usb_cfg); - - // Close records - furi_record_close(RECORD_GUI); - furi_record_close(RECORD_NOTIFICATION); - furi_record_close(RECORD_DIALOGS); - furi_record_close(RECORD_BT); - - bad_kb_save_settings(app); - - furi_string_free(app->file_path); - furi_string_free(app->keyboard_layout); - - free(app); -} - -int32_t bad_kb_app(void* p) { - BadKbApp* bad_kb_app = bad_kb_app_alloc((char*)p); - - view_dispatcher_run(bad_kb_app->view_dispatcher); - - bad_kb_app_free(bad_kb_app); - return 0; -} diff --git a/applications/main/bad_kb/bad_kb_app_i.h b/applications/main/bad_kb/bad_kb_app_i.h deleted file mode 100644 index bc5f00ea4..000000000 --- a/applications/main/bad_kb/bad_kb_app_i.h +++ /dev/null @@ -1,114 +0,0 @@ -#pragma once - -#include "bad_kb_app.h" -#include "scenes/bad_kb_scene.h" -#include "helpers/ducky_script.h" -#include "helpers/ble_hid.h" -#include "bad_kb_paths.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "views/bad_kb_view.h" -#include - -#define BAD_KB_APP_SCRIPT_EXTENSION ".txt" -#define BAD_KB_APP_LAYOUT_EXTENSION ".kl" - -extern uint8_t BAD_KB_BOUND_MAC[GAP_MAC_ADDR_SIZE]; // For remember mode - -typedef enum BadKbCustomEvent { - BadKbAppCustomEventTextInputDone, - BadKbAppCustomEventByteInputDone, - BadKbCustomEventErrorBack -} BadKbCustomEvent; - -typedef enum { - BadKbAppErrorNoFiles, -} BadKbAppError; - -typedef struct { - BleProfileHidParams ble; - FuriHalUsbHidConfig usb; -} BadKbConfig; - -typedef enum { - BadKbConnModeNone, - BadKbConnModeUsb, - BadKbConnModeBt, -} BadKbConnMode; - -struct BadKbApp { - Gui* gui; - ViewDispatcher* view_dispatcher; - SceneManager* scene_manager; - NotificationApp* notifications; - DialogsApp* dialogs; - Widget* widget; - VariableItemList* var_item_list; - TextInput* text_input; - ByteInput* byte_input; - Loading* loading; - - char bt_name_buf[FURI_HAL_BT_ADV_NAME_LENGTH]; - uint8_t bt_mac_buf[GAP_MAC_ADDR_SIZE]; - char usb_name_buf[HID_MANUF_PRODUCT_NAME_LEN]; - uint16_t usb_vidpid_buf[2]; - - BadKbAppError error; - FuriString* file_path; - FuriString* keyboard_layout; - BadKb* bad_kb_view; - BadKbScript* bad_kb_script; - - Bt* bt; - bool is_bt; - BadKbConfig config; // User options - BadKbConfig id_config; // ID and BT_ID values - - bool set_bt_id; - bool set_usb_id; - bool has_bt_id; - bool has_usb_id; - - FuriHalBleProfileBase* ble_hid; - FuriHalUsbInterface* prev_usb_mode; - - BleProfileHidParams cur_ble_cfg; - FuriHalUsbHidConfig* cur_usb_cfg; - - BadKbConnMode conn_mode; - FuriThread* conn_init_thread; -}; - -typedef enum { - BadKbAppViewWidget, - BadKbAppViewWork, - BadKbAppViewVarItemList, - BadKbAppViewByteInput, - BadKbAppViewTextInput, - BadKbAppViewLoading, -} BadKbAppView; - -void bad_kb_app_show_loading_popup(BadKbApp* app, bool show); - -void bad_kb_load_settings(BadKbApp* app); - -int32_t bad_kb_conn_apply(BadKbApp* app); - -void bad_kb_conn_reset(BadKbApp* app); - -void bad_kb_config_refresh(BadKbApp* app); - -void bad_kb_config_adjust(BadKbConfig* cfg); - -void reverse_mac_addr(uint8_t mac_addr[GAP_MAC_ADDR_SIZE]); diff --git a/applications/main/bad_kb/bad_kb_paths.h b/applications/main/bad_kb/bad_kb_paths.h deleted file mode 100644 index ff569f015..000000000 --- a/applications/main/bad_kb/bad_kb_paths.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#include - -#define BAD_KB_APP_BASE_FOLDER EXT_PATH("badusb") -#define BAD_KB_KEYS_PATH BAD_KB_APP_BASE_FOLDER "/.badkb.keys" -#define BAD_KB_SETTINGS_PATH BAD_KB_APP_BASE_FOLDER "/.badkb.settings" -#define BAD_KB_APP_PATH_LAYOUT_FOLDER BAD_KB_APP_BASE_FOLDER "/assets/layouts" diff --git a/applications/main/bad_kb/helpers/ducky_script.c b/applications/main/bad_kb/helpers/ducky_script.c deleted file mode 100644 index 64b78d9eb..000000000 --- a/applications/main/bad_kb/helpers/ducky_script.c +++ /dev/null @@ -1,946 +0,0 @@ -#include "../bad_kb_app_i.h" -#include -#include -#include -#include -#include -#include -#include -#include "ble_hid.h" -#include -#include "ducky_script.h" -#include "ducky_script_i.h" -#include -#include - -#define TAG "BadKb" - -#define WORKER_TAG TAG "Worker" - -#define BADKB_ASCII_TO_KEY(script, x) \ - (((uint8_t)x < 128) ? (script->layout[(uint8_t)x]) : HID_KEYBOARD_NONE) - -// Delays for waiting between HID key press and key release -const uint8_t bt_hid_delays[LevelRssiNum] = { - 60, // LevelRssi122_100 - 55, // LevelRssi99_80 - 50, // LevelRssi79_60 - 47, // LevelRssi59_40 - 34, // LevelRssi39_0 -}; - -uint8_t bt_timeout = 0; - -static LevelRssiRange bt_remote_rssi_range(Bt* bt) { - uint8_t rssi; - - if(!bt_remote_rssi(bt, &rssi)) return LevelRssiError; - - if(rssi <= 39) - return LevelRssi39_0; - else if(rssi <= 59) - return LevelRssi59_40; - else if(rssi <= 79) - return LevelRssi79_60; - else if(rssi <= 99) - return LevelRssi99_80; - else if(rssi <= 122) - return LevelRssi122_100; - - return LevelRssiError; -} - -static inline void update_bt_timeout(Bt* bt) { - LevelRssiRange r = bt_remote_rssi_range(bt); - if(r < LevelRssiNum) { - bt_timeout = bt_hid_delays[r]; - FURI_LOG_D(WORKER_TAG, "BLE Key timeout : %u", bt_timeout); - } -} - -static const char ducky_cmd_id[] = {"ID"}; -static const char ducky_cmd_bt_id[] = {"BT_ID"}; - -static const uint8_t numpad_keys[10] = { - HID_KEYPAD_0, - HID_KEYPAD_1, - HID_KEYPAD_2, - HID_KEYPAD_3, - HID_KEYPAD_4, - HID_KEYPAD_5, - HID_KEYPAD_6, - HID_KEYPAD_7, - HID_KEYPAD_8, - HID_KEYPAD_9, -}; - -uint32_t ducky_get_command_len(const char* line) { - uint32_t len = strlen(line); - for(uint32_t i = 0; i < len; i++) { - if(line[i] == ' ') return i; - } - return 0; -} - -bool ducky_is_line_end(const char chr) { - return ((chr == ' ') || (chr == '\0') || (chr == '\r') || (chr == '\n')); -} - -uint16_t ducky_get_keycode(BadKbScript* bad_kb, const char* param, bool accept_chars) { - uint16_t keycode = ducky_get_keycode_by_name(param); - if(keycode != HID_KEYBOARD_NONE) { - return keycode; - } - - if((accept_chars) && (strlen(param) > 0)) { - return (BADKB_ASCII_TO_KEY(bad_kb, param[0]) & 0xFF); - } - return 0; -} - -bool ducky_get_number(const char* param, uint32_t* val) { - uint32_t value = 0; - if(strint_to_uint32(param, NULL, &value, 10) == StrintParseNoError) { - *val = value; - return true; - } - return false; -} - -uint8_t furi_hal_bt_hid_get_led_state() { - // FIXME - return 0; -} - -void ducky_numlock_on(BadKbScript* bad_kb) { - if(bad_kb->bt) { - if((furi_hal_bt_hid_get_led_state() & HID_KB_LED_NUM) == 0) { - ble_profile_hid_kb_press(bad_kb->app->ble_hid, HID_KEYBOARD_LOCK_NUM_LOCK); - furi_delay_ms(bt_timeout); - ble_profile_hid_kb_release(bad_kb->app->ble_hid, HID_KEYBOARD_LOCK_NUM_LOCK); - } - } else { - if((furi_hal_hid_get_led_state() & HID_KB_LED_NUM) == 0) { - furi_hal_hid_kb_press(HID_KEYBOARD_LOCK_NUM_LOCK); - furi_hal_hid_kb_release(HID_KEYBOARD_LOCK_NUM_LOCK); - } - } -} - -bool ducky_numpad_press(BadKbScript* bad_kb, const char num) { - if((num < '0') || (num > '9')) return false; - - uint16_t key = numpad_keys[num - '0']; - if(bad_kb->bt) { - ble_profile_hid_kb_press(bad_kb->app->ble_hid, key); - furi_delay_ms(bt_timeout); - ble_profile_hid_kb_release(bad_kb->app->ble_hid, key); - } else { - furi_hal_hid_kb_press(key); - furi_hal_hid_kb_release(key); - } - - return true; -} - -bool ducky_altchar(BadKbScript* bad_kb, const char* charcode) { - uint8_t i = 0; - bool state = false; - - if(bad_kb->bt) { - ble_profile_hid_kb_press(bad_kb->app->ble_hid, KEY_MOD_LEFT_ALT); - } else { - furi_hal_hid_kb_press(KEY_MOD_LEFT_ALT); - } - - while(!ducky_is_line_end(charcode[i])) { - state = ducky_numpad_press(bad_kb, charcode[i]); - if(state == false) break; - i++; - } - - if(bad_kb->bt) { - ble_profile_hid_kb_release(bad_kb->app->ble_hid, KEY_MOD_LEFT_ALT); - } else { - furi_hal_hid_kb_release(KEY_MOD_LEFT_ALT); - } - return state; -} - -bool ducky_altstring(BadKbScript* bad_kb, const char* param) { - uint32_t i = 0; - bool state = false; - - while(param[i] != '\0') { - if((param[i] < ' ') || (param[i] > '~')) { - i++; - continue; // Skip non-printable chars - } - - char temp_str[4]; - snprintf(temp_str, 4, "%u", param[i]); - - state = ducky_altchar(bad_kb, temp_str); - if(state == false) break; - i++; - } - return state; -} - -int32_t ducky_error(BadKbScript* bad_kb, const char* text, ...) { - va_list args; - va_start(args, text); - - vsnprintf(bad_kb->st.error, sizeof(bad_kb->st.error), text, args); - - va_end(args); - return SCRIPT_STATE_ERROR; -} - -bool ducky_string(BadKbScript* bad_kb, const char* param) { - uint32_t i = 0; - - while(param[i] != '\0') { - if(param[i] != '\n') { - uint16_t keycode = BADKB_ASCII_TO_KEY(bad_kb, param[i]); - if(keycode != HID_KEYBOARD_NONE) { - if(bad_kb->bt) { - ble_profile_hid_kb_press(bad_kb->app->ble_hid, keycode); - furi_delay_ms(bt_timeout); - ble_profile_hid_kb_release(bad_kb->app->ble_hid, keycode); - } else { - furi_hal_hid_kb_press(keycode); - furi_hal_hid_kb_release(keycode); - } - } - } else { - if(bad_kb->bt) { - ble_profile_hid_kb_press(bad_kb->app->ble_hid, HID_KEYBOARD_RETURN); - furi_delay_ms(bt_timeout); - ble_profile_hid_kb_release(bad_kb->app->ble_hid, HID_KEYBOARD_RETURN); - } else { - furi_hal_hid_kb_press(HID_KEYBOARD_RETURN); - furi_hal_hid_kb_release(HID_KEYBOARD_RETURN); - } - } - i++; - } - bad_kb->stringdelay = 0; - return true; -} - -static bool ducky_string_next(BadKbScript* bad_kb) { - if(bad_kb->string_print_pos >= furi_string_size(bad_kb->string_print)) { - return true; - } - - char print_char = furi_string_get_char(bad_kb->string_print, bad_kb->string_print_pos); - - if(print_char != '\n') { - uint16_t keycode = BADKB_ASCII_TO_KEY(bad_kb, print_char); - if(keycode != HID_KEYBOARD_NONE) { - if(bad_kb->bt) { - ble_profile_hid_kb_press(bad_kb->app->ble_hid, keycode); - furi_delay_ms(bt_timeout); - ble_profile_hid_kb_release(bad_kb->app->ble_hid, keycode); - } else { - furi_hal_hid_kb_press(keycode); - furi_hal_hid_kb_release(keycode); - } - } - } else { - if(bad_kb->bt) { - ble_profile_hid_kb_press(bad_kb->app->ble_hid, HID_KEYBOARD_RETURN); - furi_delay_ms(bt_timeout); - ble_profile_hid_kb_release(bad_kb->app->ble_hid, HID_KEYBOARD_RETURN); - } else { - furi_hal_hid_kb_press(HID_KEYBOARD_RETURN); - furi_hal_hid_kb_release(HID_KEYBOARD_RETURN); - } - } - - bad_kb->string_print_pos++; - - return false; -} - -static int32_t ducky_parse_line(BadKbScript* bad_kb, FuriString* line) { - uint32_t line_len = furi_string_size(line); - const char* line_tmp = furi_string_get_cstr(line); - - if(line_len == 0) { - return SCRIPT_STATE_NEXT_LINE; // Skip empty lines - } - FURI_LOG_D(WORKER_TAG, "line:%s", line_tmp); - - // Ducky Lang Functions - int32_t cmd_result = ducky_execute_cmd(bad_kb, line_tmp); - if(cmd_result != SCRIPT_STATE_CMD_UNKNOWN) { - return cmd_result; - } - - // Special keys + modifiers - uint16_t key = ducky_get_keycode(bad_kb, line_tmp, false); - if(key == HID_KEYBOARD_NONE) { - return ducky_error(bad_kb, "No keycode defined for %s", line_tmp); - } - if((key & 0xFF00) != 0) { - // It's a modifier key - uint32_t offset = ducky_get_command_len(line_tmp) + 1; - // ducky_get_command_len() returns 0 without space, so check for != 1 - if(offset != 1 && line_len > offset) { - // It's also a key combination - key |= ducky_get_keycode(bad_kb, line_tmp + offset, true); - } - } - if(bad_kb->bt) { - ble_profile_hid_kb_press(bad_kb->app->ble_hid, key); - furi_delay_ms(bt_timeout); - ble_profile_hid_kb_release(bad_kb->app->ble_hid, key); - } else { - furi_hal_hid_kb_press(key); - furi_hal_hid_kb_release(key); - } - return 0; -} - -static bool ducky_set_usb_id(BadKbScript* bad_kb, const char* line) { - FuriHalUsbHidConfig* cfg = &bad_kb->app->id_config.usb; - - if(sscanf(line, "%lX:%lX", &cfg->vid, &cfg->pid) == 2) { - cfg->manuf[0] = '\0'; - cfg->product[0] = '\0'; - - uint8_t id_len = ducky_get_command_len(line); - if(!ducky_is_line_end(line[id_len + 1])) { - sscanf(&line[id_len + 1], "%31[^\r\n:]:%31[^\r\n]", cfg->manuf, cfg->product); - } - FURI_LOG_D( - WORKER_TAG, - "set usb id: %04lX:%04lX mfr:%s product:%s", - cfg->vid, - cfg->pid, - cfg->manuf, - cfg->product); - return true; - } - return false; -} - -static bool ducky_set_bt_id(BadKbScript* bad_kb, const char* line) { - BadKbConfig* cfg = &bad_kb->app->id_config; - - size_t line_len = strlen(line); - size_t mac_len = sizeof(cfg->ble.mac) * 3; // 2 text chars + separator per byte - if(line_len < mac_len + 1) return false; // MAC + at least 1 char for name - - for(size_t i = 0; i < sizeof(cfg->ble.mac); i++) { - char a = line[i * 3]; - char b = line[i * 3 + 1]; - if((a < 'A' && a > 'F') || (a < '0' && a > '9') || (b < 'A' && b > 'F') || - (b < '0' && b > '9') || !hex_char_to_uint8(a, b, &cfg->ble.mac[i])) { - return false; - } - } - reverse_mac_addr(cfg->ble.mac); - - strlcpy(cfg->ble.name, line + mac_len, sizeof(cfg->ble.name)); - FURI_LOG_D(WORKER_TAG, "set bt id: %s", line); - - // Can't set bonding and pairing via BT_ID, sync with user choice instead - cfg->ble.bonding = bad_kb->app->config.ble.bonding; - cfg->ble.pairing = bad_kb->app->config.ble.pairing; - return true; -} - -static void ducky_script_preload(BadKbScript* bad_kb, File* script_file) { - BadKbApp* app = bad_kb->app; - uint8_t ret = 0; - uint32_t line_len = 0; - - furi_string_reset(bad_kb->line); - - do { - ret = storage_file_read(script_file, bad_kb->file_buf, FILE_BUFFER_LEN); - for(uint16_t i = 0; i < ret; i++) { - if(bad_kb->file_buf[i] == '\n' && line_len > 0) { - bad_kb->st.line_nb++; - line_len = 0; - } else { - if(bad_kb->st.line_nb == 0) { // Save first line - furi_string_push_back(bad_kb->line, bad_kb->file_buf[i]); - } - line_len++; - } - } - if(storage_file_eof(script_file)) { - if(line_len > 0) { - bad_kb->st.line_nb++; - break; - } - } - } while(ret > 0); - - // Looking for ID or BT_ID command at first line - const char* line_tmp = furi_string_get_cstr(bad_kb->line); - app->set_usb_id = false; - app->set_bt_id = false; - app->has_usb_id = strncmp(line_tmp, ducky_cmd_id, strlen(ducky_cmd_id)) == 0; - app->has_bt_id = strncmp(line_tmp, ducky_cmd_bt_id, strlen(ducky_cmd_bt_id)) == 0; - - // Auto-switch to mode chosen with ID/BT_ID, can override manually in config screen - if(app->has_usb_id) { - app->is_bt = false; - app->set_usb_id = ducky_set_usb_id(bad_kb, &line_tmp[strlen(ducky_cmd_id) + 1]); - } else if(app->has_bt_id) { - app->is_bt = true; - app->set_bt_id = ducky_set_bt_id(bad_kb, &line_tmp[strlen(ducky_cmd_bt_id) + 1]); - } - - storage_file_seek(script_file, 0, true); - furi_string_reset(bad_kb->line); -} - -static int32_t ducky_script_execute_next(BadKbScript* bad_kb, File* script_file) { - int32_t delay_val = 0; - - if(bad_kb->repeat_cnt > 0) { - bad_kb->repeat_cnt--; - delay_val = ducky_parse_line(bad_kb, bad_kb->line_prev); - if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line - return 0; - } else if(delay_val == SCRIPT_STATE_STRING_START) { // Print string with delays - return delay_val; - } else if(delay_val == SCRIPT_STATE_WAIT_FOR_BTN) { // wait for button - return delay_val; - } else if(delay_val < 0) { // Script error - bad_kb->st.error_line = bad_kb->st.line_cur - 1; - FURI_LOG_E(WORKER_TAG, "Unknown command at line %zu", bad_kb->st.line_cur - 1U); - return SCRIPT_STATE_ERROR; - } else { - return (delay_val + bad_kb->defdelay); - } - } - - furi_string_set(bad_kb->line_prev, bad_kb->line); - furi_string_reset(bad_kb->line); - - while(1) { - if(bad_kb->buf_len == 0) { - bad_kb->buf_len = storage_file_read(script_file, bad_kb->file_buf, FILE_BUFFER_LEN); - if(storage_file_eof(script_file)) { - if((bad_kb->buf_len < FILE_BUFFER_LEN) && (bad_kb->file_end == false)) { - bad_kb->file_buf[bad_kb->buf_len] = '\n'; - bad_kb->buf_len++; - bad_kb->file_end = true; - } - } - - bad_kb->buf_start = 0; - if(bad_kb->buf_len == 0) return SCRIPT_STATE_END; - } - for(uint8_t i = bad_kb->buf_start; i < (bad_kb->buf_start + bad_kb->buf_len); i++) { - if(bad_kb->file_buf[i] == '\n' && furi_string_size(bad_kb->line) > 0) { - bad_kb->st.line_cur++; - bad_kb->buf_len = bad_kb->buf_len + bad_kb->buf_start - (i + 1); - bad_kb->buf_start = i + 1; - furi_string_trim(bad_kb->line); - delay_val = ducky_parse_line(bad_kb, bad_kb->line); - if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line - return 0; - } else if(delay_val == SCRIPT_STATE_STRING_START) { // Print string with delays - return delay_val; - } else if(delay_val == SCRIPT_STATE_WAIT_FOR_BTN) { // wait for button - return delay_val; - } else if(delay_val < 0) { - bad_kb->st.error_line = bad_kb->st.line_cur; - FURI_LOG_E(WORKER_TAG, "Unknown command at line %zu", bad_kb->st.line_cur); - return SCRIPT_STATE_ERROR; - } else { - return (delay_val + bad_kb->defdelay); - } - } else { - furi_string_push_back(bad_kb->line, bad_kb->file_buf[i]); - } - } - bad_kb->buf_len = 0; - if(bad_kb->file_end) return SCRIPT_STATE_END; - } - - return 0; -} - -void bad_kb_bt_hid_state_callback(BtStatus status, void* context) { - furi_assert(context); - BadKbScript* bad_kb = context; - bool state = (status == BtStatusConnected); - - if(state == true) { - LevelRssiRange r = bt_remote_rssi_range(bad_kb->bt); - if(r != LevelRssiError) { - bt_timeout = bt_hid_delays[r]; - } - furi_thread_flags_set(furi_thread_get_id(bad_kb->thread), WorkerEvtConnect); - } else { - furi_thread_flags_set(furi_thread_get_id(bad_kb->thread), WorkerEvtDisconnect); - } -} - -void bad_kb_usb_hid_state_callback(bool state, void* context) { - furi_assert(context); - BadKbScript* bad_kb = context; - - if(state == true) { - furi_thread_flags_set(furi_thread_get_id(bad_kb->thread), WorkerEvtConnect); - } else { - furi_thread_flags_set(furi_thread_get_id(bad_kb->thread), WorkerEvtDisconnect); - } -} - -static uint32_t bad_kb_flags_get(uint32_t flags_mask, uint32_t timeout) { - uint32_t flags = furi_thread_flags_get(); - furi_check((flags & FuriFlagError) == 0); - if(flags == 0) { - flags = furi_thread_flags_wait(flags_mask, FuriFlagWaitAny, timeout); - furi_check(((flags & FuriFlagError) == 0) || (flags == (unsigned)FuriFlagErrorTimeout)); - } else { - uint32_t state = furi_thread_flags_clear(flags); - furi_check((state & FuriFlagError) == 0); - } - return flags; -} - -static int32_t bad_kb_worker(void* context) { - BadKbScript* bad_kb = context; - - BadKbWorkerState worker_state = BadKbStateInit; - BadKbWorkerState pause_state = BadKbStateRunning; - int32_t delay_val = 0; - - FURI_LOG_I(WORKER_TAG, "Init"); - File* script_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); - bad_kb->line = furi_string_alloc(); - bad_kb->line_prev = furi_string_alloc(); - bad_kb->string_print = furi_string_alloc(); - bad_kb->st.elapsed = 0; - - while(1) { - uint32_t start = furi_get_tick(); - if(worker_state == BadKbStateInit) { // State: initialization - start = 0; - FURI_LOG_D(WORKER_TAG, "init start"); - if(storage_file_open( - script_file, - furi_string_get_cstr(bad_kb->file_path), - FSAM_READ, - FSOM_OPEN_EXISTING)) { - ducky_script_preload(bad_kb, script_file); - if(bad_kb->st.line_nb > 0) { - bad_kb_config_refresh(bad_kb->app); - worker_state = BadKbStateNotConnected; // Refresh will set connected flag - } else { - worker_state = BadKbStateScriptError; // Script preload error - } - } else { - FURI_LOG_E(WORKER_TAG, "File open error"); - worker_state = BadKbStateFileError; // File open error - } - bad_kb->st.state = worker_state; - FURI_LOG_D(WORKER_TAG, "init done"); - - } else if(worker_state == BadKbStateNotConnected) { // State: Not connected - start = 0; - FURI_LOG_D(WORKER_TAG, "not connected wait"); - uint32_t flags = bad_kb_flags_get( - WorkerEvtEnd | WorkerEvtConnect | WorkerEvtDisconnect | WorkerEvtStartStop, - FuriWaitForever); - FURI_LOG_D(WORKER_TAG, "not connected flags: %lu", flags); - - if(flags & WorkerEvtEnd) { - break; - } else if(flags & WorkerEvtConnect) { - worker_state = BadKbStateIdle; // Ready to run - } else if(flags & WorkerEvtStartStop) { - worker_state = BadKbStateWillRun; // Will run when connected - } - bad_kb->st.state = worker_state; - - } else if(worker_state == BadKbStateIdle) { // State: ready to start - start = 0; - FURI_LOG_D(WORKER_TAG, "idle wait"); - uint32_t flags = bad_kb_flags_get( - WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtConnect | WorkerEvtDisconnect, - FuriWaitForever); - FURI_LOG_D(WORKER_TAG, "idle flags: %lu", flags); - - if(flags & WorkerEvtEnd) { - break; - } else if(flags & WorkerEvtStartStop) { // Start executing script - dolphin_deed(DolphinDeedBadKbPlayScript); - delay_val = 0; - bad_kb->buf_len = 0; - bad_kb->st.line_cur = 0; - bad_kb->defdelay = 0; - bad_kb->stringdelay = 0; - bad_kb->defstringdelay = 0; - bad_kb->repeat_cnt = 0; - bad_kb->key_hold_nb = 0; - bad_kb->file_end = false; - storage_file_seek(script_file, 0, true); - bad_kb_script_set_keyboard_layout(bad_kb, bad_kb->keyboard_layout); - worker_state = BadKbStateRunning; - bad_kb->st.elapsed = 0; - } else if(flags & WorkerEvtDisconnect) { - worker_state = BadKbStateNotConnected; // Disconnected - } - bad_kb->st.state = worker_state; - - } else if(worker_state == BadKbStateWillRun) { // State: start on connection - start = 0; - FURI_LOG_D(WORKER_TAG, "will run wait"); - uint32_t flags = bad_kb_flags_get( - WorkerEvtEnd | WorkerEvtConnect | WorkerEvtDisconnect | WorkerEvtStartStop, - FuriWaitForever); - FURI_LOG_D(WORKER_TAG, "will run flags: %lu", flags); - - if(flags & WorkerEvtEnd) { - break; - } else if(flags & WorkerEvtConnect) { // Start executing script - dolphin_deed(DolphinDeedBadKbPlayScript); - delay_val = 0; - bad_kb->buf_len = 0; - bad_kb->st.line_cur = 0; - bad_kb->defdelay = 0; - bad_kb->stringdelay = 0; - bad_kb->defstringdelay = 0; - bad_kb->repeat_cnt = 0; - bad_kb->file_end = false; - storage_file_seek(script_file, 0, true); - // extra time for PC to recognize Flipper as keyboard - flags = furi_thread_flags_wait( - WorkerEvtEnd | WorkerEvtDisconnect | WorkerEvtStartStop, - FuriFlagWaitAny | FuriFlagNoClear, - bad_kb->bt ? 3000 : 1500); - if(flags == (unsigned)FuriFlagErrorTimeout) { - // If nothing happened - start script execution - worker_state = BadKbStateRunning; - bad_kb->st.elapsed = 0; - } else if(flags & WorkerEvtStartStop) { - worker_state = BadKbStateIdle; - furi_thread_flags_clear(WorkerEvtStartStop); - } - if(bad_kb->bt) { - update_bt_timeout(bad_kb->bt); - } - bad_kb_script_set_keyboard_layout(bad_kb, bad_kb->keyboard_layout); - } else if(flags & WorkerEvtStartStop) { // Cancel scheduled execution - worker_state = BadKbStateNotConnected; - } - bad_kb->st.state = worker_state; - - } else if(worker_state == BadKbStateRunning) { // State: running - FURI_LOG_D(WORKER_TAG, "running"); - uint16_t delay_cur = (delay_val > 100) ? (100) : (delay_val); - uint32_t flags = furi_thread_flags_wait( - WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtConnect | - WorkerEvtDisconnect, - FuriFlagWaitAny, - delay_cur); - FURI_LOG_D(WORKER_TAG, "running flags: %lu", flags); - - delay_val -= delay_cur; - if(!(flags & FuriFlagError)) { - if(flags & WorkerEvtEnd) { - break; - } else if(flags & WorkerEvtStartStop) { - worker_state = BadKbStateIdle; // Stop executing script - if(bad_kb->bt) { - ble_profile_hid_kb_release_all(bad_kb->app->ble_hid); - } else { - furi_hal_hid_kb_release_all(); - } - } else if(flags & WorkerEvtDisconnect) { - worker_state = BadKbStateNotConnected; // Disconnected - if(bad_kb->bt) { - ble_profile_hid_kb_release_all(bad_kb->app->ble_hid); - } else { - furi_hal_hid_kb_release_all(); - } - } else if(flags & WorkerEvtPauseResume) { - pause_state = BadKbStateRunning; - worker_state = BadKbStatePaused; // Pause - } - bad_kb->st.state = worker_state; - bad_kb->st.elapsed += (furi_get_tick() - start); - continue; - } else if( - (flags == (unsigned)FuriFlagErrorTimeout) || - (flags == (unsigned)FuriFlagErrorResource)) { - if(delay_val > 0) { - bad_kb->st.delay_remain--; - bad_kb->st.elapsed += (furi_get_tick() - start); - continue; - } - bad_kb->st.state = BadKbStateRunning; - delay_val = ducky_script_execute_next(bad_kb, script_file); - if(delay_val == SCRIPT_STATE_ERROR) { // Script error - delay_val = 0; - worker_state = BadKbStateScriptError; - bad_kb->st.state = worker_state; - if(bad_kb->bt) { - ble_profile_hid_kb_release_all(bad_kb->app->ble_hid); - } else { - furi_hal_hid_kb_release_all(); - } - } else if(delay_val == SCRIPT_STATE_END) { // End of script - delay_val = 0; - worker_state = BadKbStateIdle; - bad_kb->st.state = BadKbStateDone; - if(bad_kb->bt) { - ble_profile_hid_kb_release_all(bad_kb->app->ble_hid); - } else { - furi_hal_hid_kb_release_all(); - } - bad_kb->st.elapsed += (furi_get_tick() - start); - continue; - } else if(delay_val == SCRIPT_STATE_STRING_START) { // Start printing string with delays - delay_val = bad_kb->defdelay; - bad_kb->string_print_pos = 0; - worker_state = BadKbStateStringDelay; - } else if(delay_val == SCRIPT_STATE_WAIT_FOR_BTN) { // set state to wait for user input - worker_state = BadKbStateWaitForBtn; - bad_kb->st.state = BadKbStateWaitForBtn; // Show long delays - } else if(delay_val > 100) { - bad_kb->st.state = BadKbStateDelay; // Show long delays - bad_kb->st.delay_remain = delay_val / 100; - } - } else { - furi_check((flags & FuriFlagError) == 0); - } - } else if(worker_state == BadKbStateWaitForBtn) { // State: Wait for button Press - start = 0; - FURI_LOG_D(WORKER_TAG, "button wait"); - uint32_t flags = bad_kb_flags_get( - WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtConnect | - WorkerEvtDisconnect, - FuriWaitForever); - FURI_LOG_D(WORKER_TAG, "button flags: %lu", flags); - if(!(flags & FuriFlagError)) { - if(flags & WorkerEvtEnd) { - break; - } else if(flags & WorkerEvtStartStop) { - delay_val = 0; - worker_state = BadKbStateRunning; - } else if(flags & WorkerEvtDisconnect) { - worker_state = BadKbStateNotConnected; // Disconnected - if(bad_kb->bt) { - ble_profile_hid_kb_release_all(bad_kb->app->ble_hid); - } else { - furi_hal_hid_kb_release_all(); - } - } - bad_kb->st.state = worker_state; - continue; - } - } else if(worker_state == BadKbStatePaused) { // State: Paused - start = 0; - FURI_LOG_D(WORKER_TAG, "paused wait"); - uint32_t flags = bad_kb_flags_get( - WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtConnect | - WorkerEvtDisconnect, - FuriWaitForever); - FURI_LOG_D(WORKER_TAG, "paused flags: %lu", flags); - if(!(flags & FuriFlagError)) { - if(flags & WorkerEvtEnd) { - break; - } else if(flags & WorkerEvtStartStop) { - worker_state = BadKbStateIdle; // Stop executing script - bad_kb->st.state = worker_state; - if(bad_kb->bt) { - ble_profile_hid_kb_release_all(bad_kb->app->ble_hid); - } else { - furi_hal_hid_kb_release_all(); - } - } else if(flags & WorkerEvtDisconnect) { - worker_state = BadKbStateNotConnected; // Disconnected - bad_kb->st.state = worker_state; - if(bad_kb->bt) { - ble_profile_hid_kb_release_all(bad_kb->app->ble_hid); - } else { - furi_hal_hid_kb_release_all(); - } - } else if(flags & WorkerEvtPauseResume) { - if(pause_state == BadKbStateRunning) { - if(delay_val > 0) { - bad_kb->st.state = BadKbStateDelay; - bad_kb->st.delay_remain = delay_val / 100; - } else { - bad_kb->st.state = BadKbStateRunning; - delay_val = 0; - } - worker_state = BadKbStateRunning; // Resume - } else if(pause_state == BadKbStateStringDelay) { - bad_kb->st.state = BadKbStateRunning; - worker_state = BadKbStateStringDelay; // Resume - } - } - continue; - } - } else if(worker_state == BadKbStateStringDelay) { // State: print string with delays - FURI_LOG_D(WORKER_TAG, "delay wait"); - uint32_t delay = (bad_kb->stringdelay == 0) ? bad_kb->defstringdelay : - bad_kb->stringdelay; - uint32_t flags = bad_kb_flags_get( - WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtConnect | - WorkerEvtDisconnect, - delay); - FURI_LOG_D(WORKER_TAG, "delay flags: %lu", flags); - - if(!(flags & FuriFlagError)) { - if(flags & WorkerEvtEnd) { - break; - } else if(flags & WorkerEvtStartStop) { - worker_state = BadKbStateIdle; // Stop executing script - if(bad_kb->bt) { - ble_profile_hid_kb_release_all(bad_kb->app->ble_hid); - } else { - furi_hal_hid_kb_release_all(); - } - } else if(flags & WorkerEvtDisconnect) { - worker_state = BadKbStateNotConnected; // Disconnected - if(bad_kb->bt) { - ble_profile_hid_kb_release_all(bad_kb->app->ble_hid); - } else { - furi_hal_hid_kb_release_all(); - } - } else if(flags & WorkerEvtPauseResume) { - pause_state = BadKbStateStringDelay; - worker_state = BadKbStatePaused; // Pause - } - bad_kb->st.state = worker_state; - bad_kb->st.elapsed += (furi_get_tick() - start); - continue; - } else if( - (flags == (unsigned)FuriFlagErrorTimeout) || - (flags == (unsigned)FuriFlagErrorResource)) { - bool string_end = ducky_string_next(bad_kb); - if(string_end) { - bad_kb->stringdelay = 0; - worker_state = BadKbStateRunning; - } - } else { - furi_check((flags & FuriFlagError) == 0); - } - } else if( - (worker_state == BadKbStateFileError) || - (worker_state == BadKbStateScriptError)) { // State: error - start = 0; - FURI_LOG_D(WORKER_TAG, "error wait"); - uint32_t flags = - bad_kb_flags_get(WorkerEvtEnd, FuriWaitForever); // Waiting for exit command - FURI_LOG_D(WORKER_TAG, "error flags: %lu", flags); - - if(flags & WorkerEvtEnd) { - break; - } - } - if(bad_kb->bt) { - update_bt_timeout(bad_kb->bt); - } - if(start) { - bad_kb->st.elapsed += (furi_get_tick() - start); - } - } - - bt_set_status_changed_callback(bad_kb->app->bt, NULL, NULL); - furi_hal_hid_set_state_callback(NULL, NULL); - - storage_file_close(script_file); - storage_file_free(script_file); - furi_string_free(bad_kb->line); - furi_string_free(bad_kb->line_prev); - furi_string_free(bad_kb->string_print); - - FURI_LOG_I(WORKER_TAG, "End"); - - return 0; -} - -static void bad_kb_script_set_default_keyboard_layout(BadKbScript* bad_kb) { - furi_assert(bad_kb); - furi_string_set_str(bad_kb->keyboard_layout, ""); - memset(bad_kb->layout, HID_KEYBOARD_NONE, sizeof(bad_kb->layout)); - memcpy(bad_kb->layout, hid_asciimap, MIN(sizeof(hid_asciimap), sizeof(bad_kb->layout))); -} - -BadKbScript* bad_kb_script_open(FuriString* file_path, Bt* bt, BadKbApp* app) { - furi_assert(file_path); - - BadKbScript* bad_kb = malloc(sizeof(BadKbScript)); - bad_kb->app = app; - bad_kb->file_path = furi_string_alloc(); - furi_string_set(bad_kb->file_path, file_path); - bad_kb->keyboard_layout = furi_string_alloc(); - bad_kb_script_set_default_keyboard_layout(bad_kb); - - bad_kb->st.state = BadKbStateInit; - bad_kb->st.error[0] = '\0'; - bad_kb->st.is_bt = !!bt; - - bad_kb->bt = bt; - - bad_kb->thread = furi_thread_alloc_ex("BadKbWorker", 2048, bad_kb_worker, bad_kb); - furi_thread_start(bad_kb->thread); - return bad_kb; -} //-V773 - -void bad_kb_script_close(BadKbScript* bad_kb) { - furi_assert(bad_kb); - furi_record_close(RECORD_STORAGE); - furi_thread_flags_set(furi_thread_get_id(bad_kb->thread), WorkerEvtEnd); - furi_thread_join(bad_kb->thread); - furi_thread_free(bad_kb->thread); - furi_string_free(bad_kb->file_path); - furi_string_free(bad_kb->keyboard_layout); - free(bad_kb); -} - -void bad_kb_script_set_keyboard_layout(BadKbScript* bad_kb, FuriString* layout_path) { - furi_assert(bad_kb); - - if((bad_kb->st.state == BadKbStateRunning) || (bad_kb->st.state == BadKbStateDelay)) { - // do not update keyboard layout while a script is running - return; - } - - File* layout_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); - if(!furi_string_empty(layout_path)) { //-V1051 - furi_string_set(bad_kb->keyboard_layout, layout_path); - if(storage_file_open( - layout_file, furi_string_get_cstr(layout_path), FSAM_READ, FSOM_OPEN_EXISTING)) { - uint16_t layout[128]; - if(storage_file_read(layout_file, layout, sizeof(layout)) == sizeof(layout)) { - memcpy(bad_kb->layout, layout, sizeof(layout)); - } - } - storage_file_close(layout_file); - } else { - bad_kb_script_set_default_keyboard_layout(bad_kb); - } - storage_file_free(layout_file); -} - -void bad_kb_script_start_stop(BadKbScript* bad_kb) { - furi_assert(bad_kb); - furi_thread_flags_set(furi_thread_get_id(bad_kb->thread), WorkerEvtStartStop); -} - -void bad_kb_script_pause_resume(BadKbScript* bad_kb) { - furi_assert(bad_kb); - furi_thread_flags_set(furi_thread_get_id(bad_kb->thread), WorkerEvtPauseResume); -} - -BadKbState* bad_kb_script_get_state(BadKbScript* bad_kb) { - furi_assert(bad_kb); - return &(bad_kb->st); -} diff --git a/applications/main/bad_kb/helpers/ducky_script.h b/applications/main/bad_kb/helpers/ducky_script.h deleted file mode 100644 index 28c931bc6..000000000 --- a/applications/main/bad_kb/helpers/ducky_script.h +++ /dev/null @@ -1,86 +0,0 @@ -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include - -#include "../bad_kb_app.h" - -typedef enum { - LevelRssi122_100, - LevelRssi99_80, - LevelRssi79_60, - LevelRssi59_40, - LevelRssi39_0, - LevelRssiNum, - LevelRssiError = 0xFF, -} LevelRssiRange; - -extern const uint8_t bt_hid_delays[LevelRssiNum]; - -extern uint8_t bt_timeout; - -typedef enum { - WorkerEvtStartStop = (1 << 0), - WorkerEvtPauseResume = (1 << 1), - WorkerEvtEnd = (1 << 2), - WorkerEvtConnect = (1 << 3), - WorkerEvtDisconnect = (1 << 4), -} WorkerEvtFlags; - -typedef enum { - BadKbStateInit, - BadKbStateNotConnected, - BadKbStateIdle, - BadKbStateWillRun, - BadKbStateRunning, - BadKbStateDelay, - BadKbStateStringDelay, - BadKbStateWaitForBtn, - BadKbStatePaused, - BadKbStateDone, - BadKbStateScriptError, - BadKbStateFileError, -} BadKbWorkerState; - -typedef struct { - BadKbWorkerState state; - bool is_bt; - uint32_t pin; - size_t line_cur; - size_t line_nb; - uint32_t delay_remain; - size_t error_line; - char error[64]; - uint32_t elapsed; -} BadKbState; - -typedef struct BadKbScript BadKbScript; - -BadKbScript* bad_kb_script_open(FuriString* file_path, Bt* bt, BadKbApp* app); - -void bad_kb_script_close(BadKbScript* bad_kb); - -void bad_kb_script_set_keyboard_layout(BadKbScript* bad_kb, FuriString* layout_path); - -void bad_kb_script_start(BadKbScript* bad_kb); - -void bad_kb_script_stop(BadKbScript* bad_kb); - -void bad_kb_script_start_stop(BadKbScript* bad_kb); - -void bad_kb_script_pause_resume(BadKbScript* bad_kb); - -BadKbState* bad_kb_script_get_state(BadKbScript* bad_kb); - -void bad_kb_bt_hid_state_callback(BtStatus status, void* context); - -void bad_kb_usb_hid_state_callback(bool state, void* context); - -#ifdef __cplusplus -} -#endif diff --git a/applications/main/bad_kb/helpers/ducky_script_commands.c b/applications/main/bad_kb/helpers/ducky_script_commands.c deleted file mode 100644 index 6dfb4b683..000000000 --- a/applications/main/bad_kb/helpers/ducky_script_commands.c +++ /dev/null @@ -1,275 +0,0 @@ -#include "../bad_kb_app_i.h" -#include -#include -#include "ble_hid.h" -#include "ducky_script.h" -#include "ducky_script_i.h" - -typedef int32_t (*DuckyCmdCallback)(BadKbScript* bad_kb, const char* line, int32_t param); - -typedef struct { - char* name; - DuckyCmdCallback callback; - int32_t param; -} DuckyCmd; - -static int32_t ducky_fnc_delay(BadKbScript* bad_kb, const char* line, int32_t param) { - UNUSED(param); - - line = &line[ducky_get_command_len(line) + 1]; - uint32_t delay_val = 0; - bool state = ducky_get_number(line, &delay_val); - if((state) && (delay_val > 0)) { - return (int32_t)delay_val; - } - - return ducky_error(bad_kb, "Invalid number %s", line); -} - -static int32_t ducky_fnc_defdelay(BadKbScript* bad_kb, const char* line, int32_t param) { - UNUSED(param); - - line = &line[ducky_get_command_len(line) + 1]; - bool state = ducky_get_number(line, &bad_kb->defdelay); - if(!state) { - return ducky_error(bad_kb, "Invalid number %s", line); - } - return 0; -} - -static int32_t ducky_fnc_strdelay(BadKbScript* bad_kb, const char* line, int32_t param) { - UNUSED(param); - - line = &line[ducky_get_command_len(line) + 1]; - bool state = ducky_get_number(line, &bad_kb->stringdelay); - if(!state) { - return ducky_error(bad_kb, "Invalid number %s", line); - } - return 0; -} - -static int32_t ducky_fnc_defstrdelay(BadKbScript* bad_kb, const char* line, int32_t param) { - UNUSED(param); - - line = &line[ducky_get_command_len(line) + 1]; - bool state = ducky_get_number(line, &bad_kb->defstringdelay); - if(!state) { - return ducky_error(bad_kb, "Invalid number %s", line); - } - return 0; -} - -static int32_t ducky_fnc_string(BadKbScript* bad_kb, const char* line, int32_t param) { - line = &line[ducky_get_command_len(line) + 1]; - furi_string_set_str(bad_kb->string_print, line); - if(param == 1) { - furi_string_cat(bad_kb->string_print, "\n"); - } - - if(bad_kb->stringdelay == 0 && - bad_kb->defstringdelay == 0) { // stringdelay not set - run command immediately - bool state = ducky_string(bad_kb, furi_string_get_cstr(bad_kb->string_print)); - if(!state) { - return ducky_error(bad_kb, "Invalid string %s", line); - } - } else { // stringdelay is set - run command in thread to keep handling external events - return SCRIPT_STATE_STRING_START; - } - - return 0; -} - -static int32_t ducky_fnc_repeat(BadKbScript* bad_kb, const char* line, int32_t param) { - UNUSED(param); - - line = &line[ducky_get_command_len(line) + 1]; - bool state = ducky_get_number(line, &bad_kb->repeat_cnt); - if((!state) || (bad_kb->repeat_cnt == 0)) { - return ducky_error(bad_kb, "Invalid number %s", line); - } - return 0; -} - -static int32_t ducky_fnc_sysrq(BadKbScript* bad_kb, const char* line, int32_t param) { - UNUSED(param); - - line = &line[ducky_get_command_len(line) + 1]; - uint16_t key = ducky_get_keycode(bad_kb, line, true); - if(bad_kb->bt) { - ble_profile_hid_kb_press( - bad_kb->app->ble_hid, KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN); - ble_profile_hid_kb_press(bad_kb->app->ble_hid, key); - furi_delay_ms(bt_timeout); - ble_profile_hid_kb_release_all(bad_kb->app->ble_hid); - } else { - furi_hal_hid_kb_press(KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN); - furi_hal_hid_kb_press(key); - furi_hal_hid_kb_release_all(); - } - return 0; -} - -static int32_t ducky_fnc_altchar(BadKbScript* bad_kb, const char* line, int32_t param) { - UNUSED(param); - - line = &line[ducky_get_command_len(line) + 1]; - ducky_numlock_on(bad_kb); - bool state = ducky_altchar(bad_kb, line); - if(!state) { - return ducky_error(bad_kb, "Invalid altchar %s", line); - } - return 0; -} - -static int32_t ducky_fnc_altstring(BadKbScript* bad_kb, const char* line, int32_t param) { - UNUSED(param); - - line = &line[ducky_get_command_len(line) + 1]; - ducky_numlock_on(bad_kb); - bool state = ducky_altstring(bad_kb, line); - if(!state) { - return ducky_error(bad_kb, "Invalid altstring %s", line); - } - return 0; -} - -static int32_t ducky_fnc_hold(BadKbScript* bad_kb, const char* line, int32_t param) { - UNUSED(param); - - line = &line[ducky_get_command_len(line) + 1]; - uint16_t key = ducky_get_keycode(bad_kb, line, true); - if(key == HID_KEYBOARD_NONE) { - return ducky_error(bad_kb, "No keycode defined for %s", line); - } - bad_kb->key_hold_nb++; - if(bad_kb->key_hold_nb > (HID_KB_MAX_KEYS - 1)) { - return ducky_error(bad_kb, "Too many keys are hold"); - } - if(bad_kb->bt) { - ble_profile_hid_kb_press(bad_kb->app->ble_hid, key); - } else { - furi_hal_hid_kb_press(key); - } - return 0; -} - -static int32_t ducky_fnc_release(BadKbScript* bad_kb, const char* line, int32_t param) { - UNUSED(param); - - line = &line[ducky_get_command_len(line) + 1]; - uint16_t key = ducky_get_keycode(bad_kb, line, true); - if(key == HID_KEYBOARD_NONE) { - return ducky_error(bad_kb, "No keycode defined for %s", line); - } - if(bad_kb->key_hold_nb == 0) { - return ducky_error(bad_kb, "No keys are hold"); - } - bad_kb->key_hold_nb--; - if(bad_kb->bt) { - ble_profile_hid_kb_release(bad_kb->app->ble_hid, key); - } else { - furi_hal_hid_kb_release(key); - } - return 0; -} - -static int32_t ducky_fnc_media(BadKbScript* bad_kb, const char* line, int32_t param) { - UNUSED(param); - - line = &line[ducky_get_command_len(line) + 1]; - uint16_t key = ducky_get_media_keycode_by_name(line); - if(key == HID_CONSUMER_UNASSIGNED) { - return ducky_error(bad_kb, "No keycode defined for %s", line); - } - if(bad_kb->bt) { - ble_profile_hid_kb_press(bad_kb->app->ble_hid, key); - furi_delay_ms(bt_timeout); - ble_profile_hid_kb_release(bad_kb->app->ble_hid, key); - } else { - furi_hal_hid_kb_press(key); - furi_hal_hid_kb_release(key); - } - return 0; -} - -static int32_t ducky_fnc_globe(BadKbScript* bad_kb, const char* line, int32_t param) { - UNUSED(param); - - line = &line[ducky_get_command_len(line) + 1]; - uint16_t key = ducky_get_keycode(bad_kb, line, true); - if(key == HID_KEYBOARD_NONE) { - return ducky_error(bad_kb, "No keycode defined for %s", line); - } - - if(bad_kb->bt) { - ble_profile_hid_consumer_key_press(bad_kb->app->ble_hid, HID_CONSUMER_FN_GLOBE); - ble_profile_hid_kb_press(bad_kb->app->ble_hid, key); - furi_delay_ms(bt_timeout); - ble_profile_hid_kb_release(bad_kb->app->ble_hid, key); - ble_profile_hid_consumer_key_release(bad_kb->app->ble_hid, HID_CONSUMER_FN_GLOBE); - } else { - furi_hal_hid_consumer_key_press(HID_CONSUMER_FN_GLOBE); - furi_hal_hid_kb_press(key); - furi_hal_hid_kb_release(key); - furi_hal_hid_consumer_key_release(HID_CONSUMER_FN_GLOBE); - } - return 0; -} - -static int32_t ducky_fnc_waitforbutton(BadKbScript* bad_kb, const char* line, int32_t param) { - UNUSED(param); - UNUSED(bad_kb); - UNUSED(line); - - return SCRIPT_STATE_WAIT_FOR_BTN; -} - -static const DuckyCmd ducky_commands[] = { - {"REM", NULL, -1}, - {"ID", NULL, -1}, - {"BT_ID", NULL, -1}, - {"DELAY", ducky_fnc_delay, -1}, - {"STRING", ducky_fnc_string, 0}, - {"STRINGLN", ducky_fnc_string, 1}, - {"DEFAULT_DELAY", ducky_fnc_defdelay, -1}, - {"DEFAULTDELAY", ducky_fnc_defdelay, -1}, - {"STRINGDELAY", ducky_fnc_strdelay, -1}, - {"STRING_DELAY", ducky_fnc_strdelay, -1}, - {"DEFAULT_STRING_DELAY", ducky_fnc_defstrdelay, -1}, - {"DEFAULTSTRINGDELAY", ducky_fnc_defstrdelay, -1}, - {"REPEAT", ducky_fnc_repeat, -1}, - {"SYSRQ", ducky_fnc_sysrq, -1}, - {"ALTCHAR", ducky_fnc_altchar, -1}, - {"ALTSTRING", ducky_fnc_altstring, -1}, - {"ALTCODE", ducky_fnc_altstring, -1}, - {"HOLD", ducky_fnc_hold, -1}, - {"RELEASE", ducky_fnc_release, -1}, - {"WAIT_FOR_BUTTON_PRESS", ducky_fnc_waitforbutton, -1}, - {"MEDIA", ducky_fnc_media, -1}, - {"GLOBE", ducky_fnc_globe, -1}, -}; - -#define TAG "BadKb" - -#define WORKER_TAG TAG "Worker" - -int32_t ducky_execute_cmd(BadKbScript* bad_kb, const char* line) { - size_t cmd_word_len = strcspn(line, " "); - for(size_t i = 0; i < COUNT_OF(ducky_commands); i++) { - size_t cmd_compare_len = strlen(ducky_commands[i].name); - - if(cmd_compare_len != cmd_word_len) { - continue; - } - - if(strncmp(line, ducky_commands[i].name, cmd_compare_len) == 0) { - if(ducky_commands[i].callback == NULL) { - return 0; - } else { - return ((ducky_commands[i].callback)(bad_kb, line, ducky_commands[i].param)); - } - } - } - - return SCRIPT_STATE_CMD_UNKNOWN; -} diff --git a/applications/main/bad_kb/scenes/bad_kb_scene.c b/applications/main/bad_kb/scenes/bad_kb_scene.c deleted file mode 100644 index f90d23a77..000000000 --- a/applications/main/bad_kb/scenes/bad_kb_scene.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "bad_kb_scene.h" - -// Generate scene on_enter handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, -void (*const bad_kb_scene_on_enter_handlers[])(void*) = { -#include "bad_kb_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 bad_kb_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = { -#include "bad_kb_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 bad_kb_scene_on_exit_handlers[])(void* context) = { -#include "bad_kb_scene_config.h" -}; -#undef ADD_SCENE - -// Initialize scene handlers configuration structure -const SceneManagerHandlers bad_kb_scene_handlers = { - .on_enter_handlers = bad_kb_scene_on_enter_handlers, - .on_event_handlers = bad_kb_scene_on_event_handlers, - .on_exit_handlers = bad_kb_scene_on_exit_handlers, - .scene_num = BadKbSceneNum, -}; diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config.c b/applications/main/bad_kb/scenes/bad_kb_scene_config.c deleted file mode 100644 index b92f361a7..000000000 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config.c +++ /dev/null @@ -1,213 +0,0 @@ -#include "../bad_kb_app_i.h" - -enum VarItemListIndex { - VarItemListIndexKeyboardLayout, - VarItemListIndexConnection, -}; - -enum VarItemListIndexBt { - VarItemListIndexBtRemember = VarItemListIndexConnection + 1, - VarItemListIndexBtPairing, - VarItemListIndexBtDeviceName, - VarItemListIndexBtMacAddress, - VarItemListIndexBtRandomizeMac, -}; - -enum VarItemListIndexUsb { - VarItemListIndexUsbManufacturer = VarItemListIndexConnection + 1, - VarItemListIndexUsbProductName, - VarItemListIndexUsbVidPid, - VarItemListIndexUsbRandomizeVidPid, -}; - -void bad_kb_scene_config_connection_callback(VariableItem* item) { - BadKbApp* bad_kb = variable_item_get_context(item); - bad_kb->is_bt = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, bad_kb->is_bt ? "BT" : "USB"); - view_dispatcher_send_custom_event(bad_kb->view_dispatcher, VarItemListIndexConnection); -} - -void bad_kb_scene_config_bt_remember_callback(VariableItem* item) { - BadKbApp* bad_kb = variable_item_get_context(item); - bool value = variable_item_get_current_value_index(item); - // Set user config and remember - bad_kb->config.ble.bonding = value; - // Apply to ID config so its temporarily overridden (currently can't set bonding with BT_ID anyway) - if(bad_kb->set_bt_id) { - bad_kb->id_config.ble.bonding = value; - } - variable_item_set_current_value_text(item, value ? "ON" : "OFF"); - view_dispatcher_send_custom_event(bad_kb->view_dispatcher, VarItemListIndexBtRemember); -} - -const char* const bt_pairing_names[GapPairingCount] = { - "YesNo", - "PIN Type", - "PIN Y/N", -}; -void bad_kb_scene_config_bt_pairing_callback(VariableItem* item) { - BadKbApp* bad_kb = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - // Set user config and remember - bad_kb->config.ble.pairing = index; - // Apply to ID config so its temporarily overridden (currently can't set pairing with BT_ID anyway) - if(bad_kb->set_bt_id) { - bad_kb->id_config.ble.pairing = index; - } - variable_item_set_current_value_text(item, bt_pairing_names[index]); - view_dispatcher_send_custom_event(bad_kb->view_dispatcher, VarItemListIndexBtPairing); -} - -void bad_kb_scene_config_var_item_list_callback(void* context, uint32_t index) { - BadKbApp* bad_kb = context; - view_dispatcher_send_custom_event(bad_kb->view_dispatcher, index); -} - -void bad_kb_scene_config_on_enter(void* context) { - BadKbApp* bad_kb = context; - VariableItemList* var_item_list = bad_kb->var_item_list; - VariableItem* item; - - item = variable_item_list_add(var_item_list, "Keyboard layout", 0, NULL, bad_kb); - - item = variable_item_list_add( - var_item_list, "Connection", 2, bad_kb_scene_config_connection_callback, bad_kb); - variable_item_set_current_value_index(item, bad_kb->is_bt); - variable_item_set_current_value_text(item, bad_kb->is_bt ? "BT" : "USB"); - - if(bad_kb->is_bt) { - BadKbConfig* cfg = bad_kb->set_bt_id ? &bad_kb->id_config : &bad_kb->config; - - item = variable_item_list_add( - var_item_list, "BT Remember", 2, bad_kb_scene_config_bt_remember_callback, bad_kb); - variable_item_set_current_value_index(item, cfg->ble.bonding); - variable_item_set_current_value_text(item, cfg->ble.bonding ? "ON" : "OFF"); - - item = variable_item_list_add( - var_item_list, - "BT Pairing", - GapPairingCount, - bad_kb_scene_config_bt_pairing_callback, - bad_kb); - variable_item_set_current_value_index(item, cfg->ble.pairing); - variable_item_set_current_value_text(item, bt_pairing_names[cfg->ble.pairing]); - - item = variable_item_list_add(var_item_list, "BT Device Name", 0, NULL, bad_kb); - - item = variable_item_list_add(var_item_list, "BT MAC Address", 0, NULL, bad_kb); - if(cfg->ble.bonding) { - variable_item_set_locked(item, true, "Remember\nmust be Off!"); - } - - item = variable_item_list_add(var_item_list, "Randomize BT MAC", 0, NULL, bad_kb); - if(cfg->ble.bonding) { - variable_item_set_locked(item, true, "Remember\nmust be Off!"); - } - } else { - item = variable_item_list_add(var_item_list, "USB Manufacturer", 0, NULL, bad_kb); - - item = variable_item_list_add(var_item_list, "USB Product Name", 0, NULL, bad_kb); - - item = variable_item_list_add(var_item_list, "USB VID and PID", 0, NULL, bad_kb); - - item = variable_item_list_add(var_item_list, "Randomize USB VID:PID", 0, NULL, bad_kb); - } - - variable_item_list_set_enter_callback( - var_item_list, bad_kb_scene_config_var_item_list_callback, bad_kb); - - variable_item_list_set_selected_item( - var_item_list, scene_manager_get_scene_state(bad_kb->scene_manager, BadKbSceneConfig)); - - view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewVarItemList); -} - -bool bad_kb_scene_config_on_event(void* context, SceneManagerEvent event) { - BadKbApp* bad_kb = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - scene_manager_set_scene_state(bad_kb->scene_manager, BadKbSceneConfig, event.event); - consumed = true; - switch(event.event) { - case VarItemListIndexKeyboardLayout: - scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigLayout); - break; - case VarItemListIndexConnection: - bad_kb_config_refresh(bad_kb); - break; - default: - break; - } - if(bad_kb->is_bt) { - switch(event.event) { - case VarItemListIndexBtRemember: - bad_kb_config_refresh(bad_kb); - break; - case VarItemListIndexBtPairing: - bad_kb_config_refresh(bad_kb); - break; - case VarItemListIndexBtDeviceName: - scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigBtName); - break; - case VarItemListIndexBtMacAddress: - scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigBtMac); - break; - case VarItemListIndexBtRandomizeMac: - // Set user config and remember - furi_hal_random_fill_buf(bad_kb->config.ble.mac, sizeof(bad_kb->config.ble.mac)); - // Apply to ID config so its temporarily overridden - if(bad_kb->set_bt_id) { - memcpy( - bad_kb->id_config.ble.mac, - bad_kb->config.ble.mac, - sizeof(bad_kb->id_config.ble.mac)); - } - bad_kb_config_refresh(bad_kb); - break; - default: - break; - } - } else { - switch(event.event) { - case VarItemListIndexUsbManufacturer: - scene_manager_set_scene_state( - bad_kb->scene_manager, BadKbSceneConfigUsbName, true); - scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigUsbName); - break; - case VarItemListIndexUsbProductName: - scene_manager_set_scene_state( - bad_kb->scene_manager, BadKbSceneConfigUsbName, false); - scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigUsbName); - break; - case VarItemListIndexUsbVidPid: - scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigUsbVidPid); - break; - case VarItemListIndexUsbRandomizeVidPid: - furi_hal_random_fill_buf( - (void*)bad_kb->usb_vidpid_buf, sizeof(bad_kb->usb_vidpid_buf)); - // Set user config and remember - bad_kb->config.usb.vid = bad_kb->usb_vidpid_buf[0]; - bad_kb->config.usb.pid = bad_kb->usb_vidpid_buf[1]; - // Apply to ID config so its temporarily overridden - if(bad_kb->set_usb_id) { - bad_kb->id_config.usb.vid = bad_kb->config.usb.vid; - bad_kb->id_config.usb.pid = bad_kb->config.usb.pid; - } - bad_kb_config_refresh(bad_kb); - break; - default: - break; - } - } - } - - return consumed; -} - -void bad_kb_scene_config_on_exit(void* context) { - BadKbApp* bad_kb = context; - VariableItemList* var_item_list = bad_kb->var_item_list; - - variable_item_list_reset(var_item_list); -} diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config.h b/applications/main/bad_kb/scenes/bad_kb_scene_config.h deleted file mode 100644 index 034a898a4..000000000 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config.h +++ /dev/null @@ -1,9 +0,0 @@ -ADD_SCENE(bad_kb, file_select, FileSelect) -ADD_SCENE(bad_kb, work, Work) -ADD_SCENE(bad_kb, error, Error) -ADD_SCENE(bad_kb, config, Config) -ADD_SCENE(bad_kb, config_layout, ConfigLayout) -ADD_SCENE(bad_kb, config_bt_name, ConfigBtName) -ADD_SCENE(bad_kb, config_bt_mac, ConfigBtMac) -ADD_SCENE(bad_kb, config_usb_name, ConfigUsbName) -ADD_SCENE(bad_kb, config_usb_vidpid, ConfigUsbVidPid) diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_bt_mac.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_bt_mac.c deleted file mode 100644 index d80a874c7..000000000 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config_bt_mac.c +++ /dev/null @@ -1,61 +0,0 @@ -#include "../bad_kb_app_i.h" - -void bad_kb_scene_config_bt_mac_byte_input_callback(void* context) { - BadKbApp* bad_kb = context; - - view_dispatcher_send_custom_event(bad_kb->view_dispatcher, BadKbAppCustomEventByteInputDone); -} - -void bad_kb_scene_config_bt_mac_on_enter(void* context) { - BadKbApp* bad_kb = context; - ByteInput* byte_input = bad_kb->byte_input; - - memcpy( - bad_kb->bt_mac_buf, - bad_kb->set_bt_id ? bad_kb->id_config.ble.mac : bad_kb->config.ble.mac, - sizeof(bad_kb->bt_mac_buf)); - reverse_mac_addr(bad_kb->bt_mac_buf); - byte_input_set_header_text(byte_input, "Set BT MAC address"); - - byte_input_set_result_callback( - byte_input, - bad_kb_scene_config_bt_mac_byte_input_callback, - NULL, - bad_kb, - bad_kb->bt_mac_buf, - sizeof(bad_kb->bt_mac_buf)); - - view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewByteInput); -} - -bool bad_kb_scene_config_bt_mac_on_event(void* context, SceneManagerEvent event) { - BadKbApp* bad_kb = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - consumed = true; - if(event.event == BadKbAppCustomEventByteInputDone) { - reverse_mac_addr(bad_kb->bt_mac_buf); - // Set user config and remember - memcpy(bad_kb->config.ble.mac, bad_kb->bt_mac_buf, sizeof(bad_kb->config.ble.mac)); - // Apply to ID config so its temporarily overridden - if(bad_kb->set_bt_id) { - memcpy( - bad_kb->id_config.ble.mac, - bad_kb->bt_mac_buf, - sizeof(bad_kb->id_config.ble.mac)); - } - bad_kb_config_refresh(bad_kb); - } - scene_manager_previous_scene(bad_kb->scene_manager); - } - return consumed; -} - -void bad_kb_scene_config_bt_mac_on_exit(void* context) { - BadKbApp* bad_kb = context; - ByteInput* byte_input = bad_kb->byte_input; - - byte_input_set_result_callback(byte_input, NULL, NULL, NULL, NULL, 0); - byte_input_set_header_text(byte_input, ""); -} diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_bt_name.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_bt_name.c deleted file mode 100644 index 67a92c6bf..000000000 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config_bt_name.c +++ /dev/null @@ -1,58 +0,0 @@ -#include "../bad_kb_app_i.h" - -static void bad_kb_scene_config_bt_name_text_input_callback(void* context) { - BadKbApp* bad_kb = context; - - view_dispatcher_send_custom_event(bad_kb->view_dispatcher, BadKbAppCustomEventTextInputDone); -} - -void bad_kb_scene_config_bt_name_on_enter(void* context) { - BadKbApp* bad_kb = context; - TextInput* text_input = bad_kb->text_input; - - strlcpy( - bad_kb->bt_name_buf, - bad_kb->set_bt_id ? bad_kb->id_config.ble.name : bad_kb->config.ble.name, - sizeof(bad_kb->bt_name_buf)); - text_input_set_header_text(text_input, "Set BT device name"); - - text_input_set_result_callback( - text_input, - bad_kb_scene_config_bt_name_text_input_callback, - bad_kb, - bad_kb->bt_name_buf, - sizeof(bad_kb->bt_name_buf), - true); - - view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewTextInput); -} - -bool bad_kb_scene_config_bt_name_on_event(void* context, SceneManagerEvent event) { - BadKbApp* bad_kb = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - consumed = true; - if(event.event == BadKbAppCustomEventTextInputDone) { - // Set user config and remember - strlcpy(bad_kb->config.ble.name, bad_kb->bt_name_buf, sizeof(bad_kb->config.ble.name)); - // Apply to ID config so its temporarily overridden - if(bad_kb->set_bt_id) { - strlcpy( - bad_kb->id_config.ble.name, - bad_kb->bt_name_buf, - sizeof(bad_kb->id_config.ble.name)); - } - bad_kb_config_refresh(bad_kb); - } - scene_manager_previous_scene(bad_kb->scene_manager); - } - return consumed; -} - -void bad_kb_scene_config_bt_name_on_exit(void* context) { - BadKbApp* bad_kb = context; - TextInput* text_input = bad_kb->text_input; - - text_input_reset(text_input); -} diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_layout.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_layout.c deleted file mode 100644 index 5e655c54b..000000000 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config_layout.c +++ /dev/null @@ -1,46 +0,0 @@ -#include "../bad_kb_app_i.h" -#include - -static bool bad_kb_layout_select(BadKbApp* bad_kb) { - furi_assert(bad_kb); - - FuriString* predefined_path; - predefined_path = furi_string_alloc(); - if(!furi_string_empty(bad_kb->keyboard_layout)) { - furi_string_set(predefined_path, bad_kb->keyboard_layout); - } else { - furi_string_set(predefined_path, BAD_KB_APP_PATH_LAYOUT_FOLDER); - } - - DialogsFileBrowserOptions browser_options; - dialog_file_browser_set_basic_options( - &browser_options, BAD_KB_APP_LAYOUT_EXTENSION, &I_keyboard_10px); - browser_options.base_path = BAD_KB_APP_PATH_LAYOUT_FOLDER; - browser_options.skip_assets = false; - - // Input events and views are managed by file_browser - bool res = dialog_file_browser_show( - bad_kb->dialogs, bad_kb->keyboard_layout, predefined_path, &browser_options); - - furi_string_free(predefined_path); - return res; -} - -void bad_kb_scene_config_layout_on_enter(void* context) { - BadKbApp* bad_kb = context; - - if(bad_kb_layout_select(bad_kb)) { - bad_kb_script_set_keyboard_layout(bad_kb->bad_kb_script, bad_kb->keyboard_layout); - } - scene_manager_previous_scene(bad_kb->scene_manager); -} - -bool bad_kb_scene_config_layout_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); - return false; -} - -void bad_kb_scene_config_layout_on_exit(void* context) { - UNUSED(context); -} diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_usb_name.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_usb_name.c deleted file mode 100644 index 0cd9da1c8..000000000 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config_usb_name.c +++ /dev/null @@ -1,84 +0,0 @@ -#include "../bad_kb_app_i.h" - -static void bad_kb_scene_config_usb_name_text_input_callback(void* context) { - BadKbApp* bad_kb = context; - - view_dispatcher_send_custom_event(bad_kb->view_dispatcher, BadKbAppCustomEventTextInputDone); -} - -void bad_kb_scene_config_usb_name_on_enter(void* context) { - BadKbApp* bad_kb = context; - TextInput* text_input = bad_kb->text_input; - - if(scene_manager_get_scene_state(bad_kb->scene_manager, BadKbSceneConfigUsbName)) { - strlcpy( - bad_kb->usb_name_buf, - bad_kb->set_usb_id ? bad_kb->id_config.usb.manuf : bad_kb->config.usb.manuf, - sizeof(bad_kb->usb_name_buf)); - text_input_set_header_text(text_input, "Set USB manufacturer name"); - } else { - strlcpy( - bad_kb->usb_name_buf, - bad_kb->set_usb_id ? bad_kb->id_config.usb.product : bad_kb->config.usb.product, - sizeof(bad_kb->usb_name_buf)); - text_input_set_header_text(text_input, "Set USB product name"); - } - - text_input_set_result_callback( - text_input, - bad_kb_scene_config_usb_name_text_input_callback, - bad_kb, - bad_kb->usb_name_buf, - sizeof(bad_kb->usb_name_buf), - true); - - view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewTextInput); -} - -bool bad_kb_scene_config_usb_name_on_event(void* context, SceneManagerEvent event) { - BadKbApp* bad_kb = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - consumed = true; - if(event.event == BadKbAppCustomEventTextInputDone) { - if(scene_manager_get_scene_state(bad_kb->scene_manager, BadKbSceneConfigUsbName)) { - // Set user config and remember - strlcpy( - bad_kb->config.usb.manuf, - bad_kb->usb_name_buf, - sizeof(bad_kb->config.usb.manuf)); - // Apply to ID config so its temporarily overridden - if(bad_kb->set_usb_id) { - strlcpy( - bad_kb->id_config.usb.manuf, - bad_kb->usb_name_buf, - sizeof(bad_kb->id_config.usb.manuf)); - } - } else { - // Set user config and remember - strlcpy( - bad_kb->config.usb.product, - bad_kb->usb_name_buf, - sizeof(bad_kb->config.usb.product)); - // Apply to ID config so its temporarily overridden - if(bad_kb->set_usb_id) { - strlcpy( - bad_kb->id_config.usb.product, - bad_kb->usb_name_buf, - sizeof(bad_kb->id_config.usb.product)); - } - } - bad_kb_config_refresh(bad_kb); - } - scene_manager_previous_scene(bad_kb->scene_manager); - } - return consumed; -} - -void bad_kb_scene_config_usb_name_on_exit(void* context) { - BadKbApp* bad_kb = context; - TextInput* text_input = bad_kb->text_input; - - text_input_reset(text_input); -} diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_usb_vidpid.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_usb_vidpid.c deleted file mode 100644 index 43b131465..000000000 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config_usb_vidpid.c +++ /dev/null @@ -1,61 +0,0 @@ -#include "../bad_kb_app_i.h" - -void bad_kb_scene_config_usb_vidpid_byte_input_callback(void* context) { - BadKbApp* bad_kb = context; - - view_dispatcher_send_custom_event(bad_kb->view_dispatcher, BadKbAppCustomEventByteInputDone); -} - -void bad_kb_scene_config_usb_vidpid_on_enter(void* context) { - BadKbApp* bad_kb = context; - ByteInput* byte_input = bad_kb->byte_input; - - if(bad_kb->set_usb_id) { - bad_kb->usb_vidpid_buf[0] = __REVSH(bad_kb->id_config.usb.vid); - bad_kb->usb_vidpid_buf[1] = __REVSH(bad_kb->id_config.usb.pid); - } else { - bad_kb->usb_vidpid_buf[0] = __REVSH(bad_kb->config.usb.vid); - bad_kb->usb_vidpid_buf[1] = __REVSH(bad_kb->config.usb.pid); - } - byte_input_set_header_text(byte_input, "Set USB VID:PID"); - - byte_input_set_result_callback( - byte_input, - bad_kb_scene_config_usb_vidpid_byte_input_callback, - NULL, - bad_kb, - (void*)bad_kb->usb_vidpid_buf, - sizeof(bad_kb->usb_vidpid_buf)); - - view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewByteInput); -} - -bool bad_kb_scene_config_usb_vidpid_on_event(void* context, SceneManagerEvent event) { - BadKbApp* bad_kb = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - consumed = true; - if(event.event == BadKbAppCustomEventByteInputDone) { - // Set user config and remember - bad_kb->config.usb.vid = __REVSH(bad_kb->usb_vidpid_buf[0]); - bad_kb->config.usb.pid = __REVSH(bad_kb->usb_vidpid_buf[1]); - // Apply to ID config so its temporarily overridden - if(bad_kb->set_usb_id) { - bad_kb->id_config.usb.vid = bad_kb->config.usb.vid; - bad_kb->id_config.usb.pid = bad_kb->config.usb.pid; - } - bad_kb_config_refresh(bad_kb); - } - scene_manager_previous_scene(bad_kb->scene_manager); - } - return consumed; -} - -void bad_kb_scene_config_usb_vidpid_on_exit(void* context) { - BadKbApp* bad_kb = context; - ByteInput* byte_input = bad_kb->byte_input; - - byte_input_set_result_callback(byte_input, NULL, NULL, NULL, NULL, 0); - byte_input_set_header_text(byte_input, ""); -} diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_file_select.c b/applications/main/bad_kb/scenes/bad_kb_scene_file_select.c deleted file mode 100644 index cefb6f1de..000000000 --- a/applications/main/bad_kb/scenes/bad_kb_scene_file_select.c +++ /dev/null @@ -1,79 +0,0 @@ -#include "../bad_kb_app_i.h" -#include - -static bool bad_kb_file_select(BadKbApp* bad_kb) { - furi_assert(bad_kb); - - bad_kb_app_show_loading_popup(bad_kb, true); - Storage* storage = furi_record_open(RECORD_STORAGE); - if(storage_dir_exists(storage, EXT_PATH("badkb"))) { - DialogMessage* message = dialog_message_alloc(); - dialog_message_set_header(message, "Migrate Scripts?", 64, 0, AlignCenter, AlignTop); - dialog_message_set_buttons(message, "No", NULL, "Yes"); - dialog_message_set_text( - message, - "Momentum uses the 'badusb'\n" - "folder for compatibility.\n" - "Want to migrate from\n" - "'badkb' folder?", - 64, - 32, - AlignCenter, - AlignCenter); - DialogMessageButton res = dialog_message_show(furi_record_open(RECORD_DIALOGS), message); - dialog_message_free(message); - furi_record_close(RECORD_DIALOGS); - if(res == DialogMessageButtonRight) { - storage_common_migrate(storage, EXT_PATH("badkb"), BAD_KB_APP_BASE_FOLDER); - if(bad_kb->conn_init_thread) { - furi_thread_join(bad_kb->conn_init_thread); - } - bad_kb_load_settings(bad_kb); - bad_kb_config_adjust(&bad_kb->config); - } - } - storage_simply_mkdir(storage, BAD_KB_APP_BASE_FOLDER); - furi_record_close(RECORD_STORAGE); - bad_kb_app_show_loading_popup(bad_kb, false); - - DialogsFileBrowserOptions browser_options; - dialog_file_browser_set_basic_options( - &browser_options, BAD_KB_APP_SCRIPT_EXTENSION, &I_badkb_10px); - browser_options.base_path = BAD_KB_APP_BASE_FOLDER; - browser_options.skip_assets = true; - - // Input events and views are managed by file_browser - bool res = dialog_file_browser_show( - bad_kb->dialogs, bad_kb->file_path, bad_kb->file_path, &browser_options); - - return res; -} - -void bad_kb_scene_file_select_on_enter(void* context) { - BadKbApp* bad_kb = context; - - if(bad_kb->bad_kb_script) { - bad_kb_script_close(bad_kb->bad_kb_script); - bad_kb->bad_kb_script = NULL; - } - - if(bad_kb_file_select(bad_kb)) { - bad_kb->bad_kb_script = - bad_kb_script_open(bad_kb->file_path, bad_kb->is_bt ? bad_kb->bt : NULL, bad_kb); - bad_kb_script_set_keyboard_layout(bad_kb->bad_kb_script, bad_kb->keyboard_layout); - - scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneWork); - } else { - view_dispatcher_stop(bad_kb->view_dispatcher); - } -} - -bool bad_kb_scene_file_select_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); - return false; -} - -void bad_kb_scene_file_select_on_exit(void* context) { - UNUSED(context); -} diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_work.c b/applications/main/bad_kb/scenes/bad_kb_scene_work.c deleted file mode 100644 index f2e1fd2da..000000000 --- a/applications/main/bad_kb/scenes/bad_kb_scene_work.c +++ /dev/null @@ -1,59 +0,0 @@ -#include "../helpers/ducky_script.h" -#include "../bad_kb_app_i.h" -#include "../views/bad_kb_view.h" -#include -#include "toolbox/path.h" - -void bad_kb_scene_work_button_callback(InputKey key, void* context) { - furi_assert(context); - BadKbApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, key); -} - -bool bad_kb_scene_work_on_event(void* context, SceneManagerEvent event) { - BadKbApp* app = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == InputKeyLeft) { - if(bad_kb_view_is_idle_state(app->bad_kb_view)) { - scene_manager_next_scene(app->scene_manager, BadKbSceneConfig); - } - consumed = true; - } else if(event.event == InputKeyOk) { - bad_kb_script_start_stop(app->bad_kb_script); - consumed = true; - } else if(event.event == InputKeyRight) { - bad_kb_script_pause_resume(app->bad_kb_script); - consumed = true; - } - } else if(event.type == SceneManagerEventTypeTick) { - bad_kb_view_set_state(app->bad_kb_view, bad_kb_script_get_state(app->bad_kb_script)); - } - return consumed; -} - -void bad_kb_scene_work_on_enter(void* context) { - BadKbApp* app = context; - - FuriString* file_name; - file_name = furi_string_alloc(); - path_extract_filename(app->file_path, file_name, true); - bad_kb_view_set_file_name(app->bad_kb_view, furi_string_get_cstr(file_name)); - furi_string_free(file_name); - - FuriString* layout; - layout = furi_string_alloc(); - path_extract_filename(app->keyboard_layout, layout, true); - bad_kb_view_set_layout(app->bad_kb_view, furi_string_get_cstr(layout)); - furi_string_free(layout); - - bad_kb_view_set_state(app->bad_kb_view, bad_kb_script_get_state(app->bad_kb_script)); - - bad_kb_view_set_button_callback(app->bad_kb_view, bad_kb_scene_work_button_callback, app); - view_dispatcher_switch_to_view(app->view_dispatcher, BadKbAppViewWork); -} - -void bad_kb_scene_work_on_exit(void* context) { - UNUSED(context); -} diff --git a/applications/main/bad_kb/views/bad_kb_view.h b/applications/main/bad_kb/views/bad_kb_view.h deleted file mode 100644 index 427862eb3..000000000 --- a/applications/main/bad_kb/views/bad_kb_view.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include -#include "../helpers/ducky_script.h" - -typedef struct BadKb BadKb; -typedef void (*BadKbButtonCallback)(InputKey key, void* context); - -BadKb* bad_kb_view_alloc(void); - -void bad_kb_view_free(BadKb* bad_kb); - -View* bad_kb_view_get_view(BadKb* bad_kb); - -void bad_kb_view_set_button_callback(BadKb* bad_kb, BadKbButtonCallback callback, void* context); - -void bad_kb_view_set_file_name(BadKb* bad_kb, const char* name); - -void bad_kb_view_set_layout(BadKb* bad_kb, const char* layout); - -void bad_kb_view_set_state(BadKb* bad_kb, BadKbState* st); - -bool bad_kb_view_is_idle_state(BadKb* bad_kb); diff --git a/applications/main/bad_kb/application.fam b/applications/main/bad_usb/application.fam similarity index 54% rename from applications/main/bad_kb/application.fam rename to applications/main/bad_usb/application.fam index 24c90ce28..5847f1100 100644 --- a/applications/main/bad_kb/application.fam +++ b/applications/main/bad_usb/application.fam @@ -1,10 +1,12 @@ App( appid="bad_kb", + # Still called Bad KB for historic reasons + # Code was renamed back to Bad USB for easier update merging name="Bad KB", apptype=FlipperAppType.MENUEXTERNAL, - entry_point="bad_kb_app", + entry_point="bad_usb_app", stack_size=2 * 1024, - icon="A_BadKb_14", + icon="A_BadUsb_14", order=70, resources="resources", fap_icon="icon.png", diff --git a/applications/main/bad_usb/bad_usb_app.c b/applications/main/bad_usb/bad_usb_app.c new file mode 100644 index 000000000..897413071 --- /dev/null +++ b/applications/main/bad_usb/bad_usb_app.c @@ -0,0 +1,326 @@ +#include "bad_usb_app_i.h" +#include +#include +#include +#include +#include + +#define BAD_USB_SETTINGS_PATH BAD_USB_APP_BASE_FOLDER "/.badkb.settings" +#define BAD_USB_SETTINGS_FILE_TYPE "Flipper BadUSB Settings File" +#define BAD_USB_SETTINGS_VERSION 1 +#define BAD_USB_SETTINGS_DEFAULT_LAYOUT BAD_USB_APP_PATH_LAYOUT_FOLDER "/en-US.kl" + +static bool bad_usb_app_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + BadUsbApp* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool bad_usb_app_back_event_callback(void* context) { + furi_assert(context); + BadUsbApp* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +static void bad_usb_app_tick_event_callback(void* context) { + furi_assert(context); + BadUsbApp* app = context; + scene_manager_handle_tick_event(app->scene_manager); +} + +static void bad_usb_load_settings(BadUsbApp* app) { + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* fff = flipper_format_file_alloc(storage); + bool loaded = false; + + BadUsbHidConfig* hid_cfg = &app->user_hid_cfg; + FuriString* temp_str = furi_string_alloc(); + uint32_t temp_uint = 0; + + if(flipper_format_file_open_existing(fff, BAD_USB_SETTINGS_PATH)) { + do { + if(!flipper_format_read_header(fff, temp_str, &temp_uint)) break; + if((strcmp(furi_string_get_cstr(temp_str), BAD_USB_SETTINGS_FILE_TYPE) != 0) || + (temp_uint != BAD_USB_SETTINGS_VERSION)) + break; + + if(flipper_format_read_string(fff, "layout", temp_str)) { + furi_string_set(app->keyboard_layout, temp_str); + FileInfo layout_file_info; + FS_Error file_check_err = storage_common_stat( + storage, furi_string_get_cstr(app->keyboard_layout), &layout_file_info); + if((file_check_err != FSE_OK) || (layout_file_info.size != 256)) { + furi_string_set(app->keyboard_layout, BAD_USB_SETTINGS_DEFAULT_LAYOUT); + } + } else { + furi_string_set(app->keyboard_layout, BAD_USB_SETTINGS_DEFAULT_LAYOUT); + flipper_format_rewind(fff); + } + + if(!flipper_format_read_uint32(fff, "interface", &temp_uint, 1) || + temp_uint >= BadUsbHidInterfaceMAX) { + temp_uint = BadUsbHidInterfaceUsb; + flipper_format_rewind(fff); + } + app->interface = temp_uint; + + if(!flipper_format_read_bool(fff, "ble_bonding", &hid_cfg->ble.bonding, 1)) { + hid_cfg->ble.bonding = true; + flipper_format_rewind(fff); + } + + if(!flipper_format_read_uint32(fff, "ble_pairing", &temp_uint, 1) || + temp_uint >= GapPairingCount) { + temp_uint = GapPairingPinCodeVerifyYesNo; + flipper_format_rewind(fff); + } + hid_cfg->ble.pairing = temp_uint; + + if(flipper_format_read_string(fff, "ble_name", temp_str)) { + strlcpy( + hid_cfg->ble.name, furi_string_get_cstr(temp_str), sizeof(hid_cfg->ble.name)); + } else { + hid_cfg->ble.name[0] = '\0'; + flipper_format_rewind(fff); + } + + if(!flipper_format_read_hex( + fff, "ble_mac", hid_cfg->ble.mac, sizeof(hid_cfg->ble.mac))) { + memset(hid_cfg->ble.mac, 0, sizeof(hid_cfg->ble.mac)); + flipper_format_rewind(fff); + } + + if(flipper_format_read_string(fff, "usb_manuf", temp_str)) { + strlcpy( + hid_cfg->usb.manuf, + furi_string_get_cstr(temp_str), + sizeof(hid_cfg->usb.manuf)); + } else { + hid_cfg->usb.manuf[0] = '\0'; + flipper_format_rewind(fff); + } + + if(flipper_format_read_string(fff, "usb_product", temp_str)) { + strlcpy( + hid_cfg->usb.product, + furi_string_get_cstr(temp_str), + sizeof(hid_cfg->usb.product)); + } else { + hid_cfg->usb.product[0] = '\0'; + flipper_format_rewind(fff); + } + + if(!flipper_format_read_uint32(fff, "usb_vid", &hid_cfg->usb.vid, 1)) { + hid_cfg->usb.vid = 0; + flipper_format_rewind(fff); + } + + if(!flipper_format_read_uint32(fff, "usb_pid", &hid_cfg->usb.pid, 1)) { + hid_cfg->usb.pid = 0; + flipper_format_rewind(fff); + } + + loaded = true; + } while(0); + } + + furi_string_free(temp_str); + + flipper_format_free(fff); + furi_record_close(RECORD_STORAGE); + + if(!loaded) { + furi_string_set(app->keyboard_layout, BAD_USB_SETTINGS_DEFAULT_LAYOUT); + app->interface = BadUsbHidInterfaceUsb; + hid_cfg->ble.bonding = true; + hid_cfg->ble.pairing = GapPairingPinCodeVerifyYesNo; + hid_cfg->ble.name[0] = '\0'; + memset(hid_cfg->ble.mac, 0, sizeof(hid_cfg->ble.mac)); + hid_cfg->usb.manuf[0] = '\0'; + hid_cfg->usb.product[0] = '\0'; + hid_cfg->usb.vid = 0; + hid_cfg->usb.pid = 0; + } +} + +static void bad_usb_save_settings(BadUsbApp* app) { + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* fff = flipper_format_file_alloc(storage); + BadUsbHidConfig* hid_cfg = &app->user_hid_cfg; + uint32_t temp_uint = 0; + + if(flipper_format_file_open_always(fff, BAD_USB_SETTINGS_PATH)) { + do { + if(!flipper_format_write_header_cstr( + fff, BAD_USB_SETTINGS_FILE_TYPE, BAD_USB_SETTINGS_VERSION)) + break; + if(!flipper_format_write_string(fff, "layout", app->keyboard_layout)) break; + temp_uint = app->interface; + if(!flipper_format_write_uint32(fff, "interface", &temp_uint, 1)) break; + if(!flipper_format_write_bool(fff, "ble_bonding", &hid_cfg->ble.bonding, 1)) break; + temp_uint = hid_cfg->ble.pairing; + if(!flipper_format_write_uint32(fff, "ble_pairing", &temp_uint, 1)) break; + if(!flipper_format_write_string_cstr(fff, "ble_name", hid_cfg->ble.name)) break; + if(!flipper_format_write_hex( + fff, "ble_mac", (uint8_t*)&hid_cfg->ble.mac, sizeof(hid_cfg->ble.mac))) + break; + if(!flipper_format_write_string_cstr(fff, "usb_manuf", hid_cfg->usb.manuf)) break; + if(!flipper_format_write_string_cstr(fff, "usb_product", hid_cfg->usb.product)) break; + if(!flipper_format_write_uint32(fff, "usb_vid", &hid_cfg->usb.vid, 1)) break; + if(!flipper_format_write_uint32(fff, "usb_pid", &hid_cfg->usb.pid, 1)) break; + } while(0); + } + + flipper_format_free(fff); + furi_record_close(RECORD_STORAGE); +} + +void bad_usb_set_interface(BadUsbApp* app, BadUsbHidInterface interface) { + app->interface = interface; + bad_usb_view_set_interface(app->bad_usb_view, interface); +} + +void bad_usb_app_show_loading_popup(BadUsbApp* app, bool show) { + if(show) { + // Raise timer priority so that animations can play + furi_timer_set_thread_priority(FuriTimerThreadPriorityElevated); + view_dispatcher_switch_to_view(app->view_dispatcher, BadUsbAppViewLoading); + } else { + // Restore default timer priority + furi_timer_set_thread_priority(FuriTimerThreadPriorityNormal); + } +} + +BadUsbApp* bad_usb_app_alloc(char* arg) { + BadUsbApp* app = malloc(sizeof(BadUsbApp)); + + app->bad_usb_script = NULL; + + app->file_path = furi_string_alloc(); + app->keyboard_layout = furi_string_alloc(); + if(arg && strlen(arg)) { + furi_string_set(app->file_path, arg); + } + + bad_usb_load_settings(app); + + app->gui = furi_record_open(RECORD_GUI); + app->notifications = furi_record_open(RECORD_NOTIFICATION); + app->dialogs = furi_record_open(RECORD_DIALOGS); + + app->view_dispatcher = view_dispatcher_alloc(); + app->scene_manager = scene_manager_alloc(&bad_usb_scene_handlers, app); + + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + view_dispatcher_set_tick_event_callback( + app->view_dispatcher, bad_usb_app_tick_event_callback, 250); + view_dispatcher_set_custom_event_callback( + app->view_dispatcher, bad_usb_app_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, bad_usb_app_back_event_callback); + + // Custom Widget + app->widget = widget_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, BadUsbAppViewWidget, widget_get_view(app->widget)); + + // Popup + app->popup = popup_alloc(); + view_dispatcher_add_view(app->view_dispatcher, BadUsbAppViewPopup, popup_get_view(app->popup)); + + app->var_item_list = variable_item_list_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + BadUsbAppViewConfig, + variable_item_list_get_view(app->var_item_list)); + + app->bad_usb_view = bad_usb_view_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, BadUsbAppViewWork, bad_usb_view_get_view(app->bad_usb_view)); + + app->text_input = text_input_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, BadUsbAppViewTextInput, text_input_get_view(app->text_input)); + + app->byte_input = byte_input_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, BadUsbAppViewByteInput, byte_input_get_view(app->byte_input)); + + app->loading = loading_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, BadUsbAppViewLoading, loading_get_view(app->loading)); + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + if(!furi_string_empty(app->file_path)) { + scene_manager_set_scene_state(app->scene_manager, BadUsbSceneWork, true); + scene_manager_next_scene(app->scene_manager, BadUsbSceneWork); + } else { + furi_string_set(app->file_path, BAD_USB_APP_BASE_FOLDER); + scene_manager_next_scene(app->scene_manager, BadUsbSceneFileSelect); + } + + return app; +} + +void bad_usb_app_free(BadUsbApp* app) { + furi_assert(app); + + if(app->bad_usb_script) { + bad_usb_script_close(app->bad_usb_script); + app->bad_usb_script = NULL; + } + + // Views + view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewWork); + bad_usb_view_free(app->bad_usb_view); + + // Custom Widget + view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewWidget); + widget_free(app->widget); + + // Popup + view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewPopup); + popup_free(app->popup); + + // Config menu + view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewConfig); + variable_item_list_free(app->var_item_list); + + // Text Input + view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewTextInput); + text_input_free(app->text_input); + + // Byte Input + view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewByteInput); + byte_input_free(app->byte_input); + + // Loading + view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewLoading); + loading_free(app->loading); + + // View dispatcher + view_dispatcher_free(app->view_dispatcher); + scene_manager_free(app->scene_manager); + + // Close records + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_NOTIFICATION); + furi_record_close(RECORD_DIALOGS); + + bad_usb_save_settings(app); + + furi_string_free(app->file_path); + furi_string_free(app->keyboard_layout); + + free(app); +} + +int32_t bad_usb_app(void* p) { + BadUsbApp* bad_usb_app = bad_usb_app_alloc((char*)p); + + view_dispatcher_run(bad_usb_app->view_dispatcher); + + bad_usb_app_free(bad_usb_app); + return 0; +} diff --git a/applications/main/bad_kb/bad_kb_app.h b/applications/main/bad_usb/bad_usb_app.h similarity index 69% rename from applications/main/bad_kb/bad_kb_app.h rename to applications/main/bad_usb/bad_usb_app.h index af64be253..afadd87e9 100644 --- a/applications/main/bad_kb/bad_kb_app.h +++ b/applications/main/bad_usb/bad_usb_app.h @@ -4,7 +4,7 @@ extern "C" { #endif -typedef struct BadKbApp BadKbApp; +typedef struct BadUsbApp BadUsbApp; #ifdef __cplusplus } diff --git a/applications/main/bad_usb/bad_usb_app_i.h b/applications/main/bad_usb/bad_usb_app_i.h new file mode 100644 index 000000000..f645fb777 --- /dev/null +++ b/applications/main/bad_usb/bad_usb_app_i.h @@ -0,0 +1,73 @@ +#pragma once + +#include "bad_usb_app.h" +#include "scenes/bad_usb_scene.h" +#include "helpers/ducky_script.h" +#include "helpers/bad_usb_hid.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "views/bad_usb_view.h" +#include + +#define BAD_USB_APP_BASE_FOLDER EXT_PATH("badusb") +#define BAD_USB_APP_PATH_LAYOUT_FOLDER BAD_USB_APP_BASE_FOLDER "/assets/layouts" +#define BAD_USB_APP_SCRIPT_EXTENSION ".txt" +#define BAD_USB_APP_LAYOUT_EXTENSION ".kl" + +typedef enum { + BadUsbAppErrorNoFiles, +} BadUsbAppError; + +struct BadUsbApp { + Gui* gui; + ViewDispatcher* view_dispatcher; + SceneManager* scene_manager; + NotificationApp* notifications; + DialogsApp* dialogs; + Widget* widget; + Popup* popup; + VariableItemList* var_item_list; + TextInput* text_input; + ByteInput* byte_input; + Loading* loading; + + char ble_name_buf[FURI_HAL_BT_ADV_NAME_LENGTH]; + uint8_t ble_mac_buf[GAP_MAC_ADDR_SIZE]; + char usb_name_buf[HID_MANUF_PRODUCT_NAME_LEN]; + uint16_t usb_vidpid_buf[2]; + + BadUsbAppError error; + FuriString* file_path; + FuriString* keyboard_layout; + BadUsb* bad_usb_view; + BadUsbScript* bad_usb_script; + + BadUsbHidInterface interface; + BadUsbHidConfig user_hid_cfg; + BadUsbHidConfig script_hid_cfg; +}; + +typedef enum { + BadUsbAppViewWidget, + BadUsbAppViewPopup, + BadUsbAppViewWork, + BadUsbAppViewConfig, + BadUsbAppViewByteInput, + BadUsbAppViewTextInput, + BadUsbAppViewLoading, +} BadUsbAppView; + +void bad_usb_set_interface(BadUsbApp* app, BadUsbHidInterface interface); + +void bad_usb_app_show_loading_popup(BadUsbApp* app, bool show); diff --git a/applications/main/bad_usb/helpers/bad_usb_hid.c b/applications/main/bad_usb/helpers/bad_usb_hid.c new file mode 100644 index 000000000..3243c9944 --- /dev/null +++ b/applications/main/bad_usb/helpers/bad_usb_hid.c @@ -0,0 +1,321 @@ +#include "bad_usb_hid.h" +#include "ble_hid_profile.h" +#include +#include +#include + +#define TAG "BadUSB HID" + +#define HID_BT_KEYS_STORAGE_NAME ".bt_hid.keys" + +void hid_usb_adjust_config(BadUsbHidConfig* hid_cfg) { + if(hid_cfg->usb.vid == 0) hid_cfg->usb.vid = HID_VID_DEFAULT; + if(hid_cfg->usb.pid == 0) hid_cfg->usb.pid = HID_PID_DEFAULT; +} + +void* hid_usb_init(BadUsbHidConfig* hid_cfg) { + FuriHalUsbInterface* usb_if_prev = furi_hal_usb_get_config(); + furi_hal_usb_unlock(); + hid_usb_adjust_config(hid_cfg); + furi_check(furi_hal_usb_set_config(&usb_hid, &hid_cfg->usb)); + return usb_if_prev; +} + +void hid_usb_deinit(void* inst) { + FuriHalUsbInterface* usb_if_prev = inst; + furi_check(furi_hal_usb_set_config(usb_if_prev, NULL)); +} + +void hid_usb_set_state_callback(void* inst, HidStateCallback cb, void* context) { + UNUSED(inst); + furi_hal_hid_set_state_callback(cb, context); +} + +bool hid_usb_is_connected(void* inst) { + UNUSED(inst); + return furi_hal_hid_is_connected(); +} + +bool hid_usb_kb_press(void* inst, uint16_t button) { + UNUSED(inst); + return furi_hal_hid_kb_press(button); +} + +bool hid_usb_kb_release(void* inst, uint16_t button) { + UNUSED(inst); + return furi_hal_hid_kb_release(button); +} + +bool hid_usb_mouse_press(void* inst, uint8_t button) { + UNUSED(inst); + return furi_hal_hid_mouse_press(button); +} + +bool hid_usb_mouse_release(void* inst, uint8_t button) { + UNUSED(inst); + return furi_hal_hid_mouse_release(button); +} + +bool hid_usb_mouse_scroll(void* inst, int8_t delta) { + UNUSED(inst); + return furi_hal_hid_mouse_scroll(delta); +} + +bool hid_usb_mouse_move(void* inst, int8_t dx, int8_t dy) { + UNUSED(inst); + return furi_hal_hid_mouse_move(dx, dy); +} + +bool hid_usb_mouse_release_all(void* inst) { + UNUSED(inst); + return furi_hal_hid_mouse_release(0); +} + +bool hid_usb_consumer_press(void* inst, uint16_t button) { + UNUSED(inst); + return furi_hal_hid_consumer_key_press(button); +} + +bool hid_usb_consumer_release(void* inst, uint16_t button) { + UNUSED(inst); + return furi_hal_hid_consumer_key_release(button); +} + +bool hid_usb_release_all(void* inst) { + UNUSED(inst); + bool state = furi_hal_hid_kb_release_all(); + state &= furi_hal_hid_consumer_key_release_all(); + state &= hid_usb_mouse_release_all(inst); + return state; +} + +uint8_t hid_usb_get_led_state(void* inst) { + UNUSED(inst); + return furi_hal_hid_get_led_state(); +} + +static const BadUsbHidApi hid_api_usb = { + .adjust_config = hid_usb_adjust_config, + .init = hid_usb_init, + .deinit = hid_usb_deinit, + .set_state_callback = hid_usb_set_state_callback, + .is_connected = hid_usb_is_connected, + + .kb_press = hid_usb_kb_press, + .kb_release = hid_usb_kb_release, + .mouse_press = hid_usb_mouse_press, + .mouse_release = hid_usb_mouse_release, + .mouse_scroll = hid_usb_mouse_scroll, + .mouse_move = hid_usb_mouse_move, + .consumer_press = hid_usb_consumer_press, + .consumer_release = hid_usb_consumer_release, + .release_all = hid_usb_release_all, + .get_led_state = hid_usb_get_led_state, +}; + +typedef struct { + Bt* bt; + FuriHalBleProfileBase* profile; + HidStateCallback state_callback; + void* callback_context; + bool is_connected; +} BleHidInstance; + +static void hid_ble_connection_status_callback(BtStatus status, void* context) { + furi_assert(context); + BleHidInstance* ble_hid = context; + ble_hid->is_connected = (status == BtStatusConnected); + if(ble_hid->state_callback) { + ble_hid->state_callback(ble_hid->is_connected, ble_hid->callback_context); + } +} + +void hid_ble_adjust_config(BadUsbHidConfig* hid_cfg) { + const uint8_t* normal_mac = furi_hal_version_get_ble_mac(); + uint8_t empty_mac[GAP_MAC_ADDR_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + uint8_t default_mac[GAP_MAC_ADDR_SIZE] = {0x6c, 0x7a, 0xd8, 0xac, 0x57, 0x72}; // furi_hal_bt + if(memcmp(hid_cfg->ble.mac, empty_mac, sizeof(hid_cfg->ble.mac)) == 0 || + memcmp(hid_cfg->ble.mac, normal_mac, sizeof(hid_cfg->ble.mac)) == 0 || + memcmp(hid_cfg->ble.mac, default_mac, sizeof(hid_cfg->ble.mac)) == 0) { + // Derive badusb MAC from Flipper MAC + memcpy(hid_cfg->ble.mac, normal_mac, sizeof(hid_cfg->ble.mac)); + hid_cfg->ble.mac[2]++; + uint16_t badusb_mac_xor = 0x0002; + hid_cfg->ble.mac[0] ^= badusb_mac_xor; + hid_cfg->ble.mac[1] ^= badusb_mac_xor >> 8; + } + + if(hid_cfg->ble.name[0] == '\0') { + // Derive badusb name from Flipper name + const char* badusb_device_name_prefix = "BadKB"; + snprintf( + hid_cfg->ble.name, + sizeof(hid_cfg->ble.name), + "%s %s", + badusb_device_name_prefix, + furi_hal_version_get_name_ptr()); + } + + if(hid_cfg->ble.pairing >= GapPairingCount) { + hid_cfg->ble.pairing = GapPairingPinCodeVerifyYesNo; + } +} + +void* hid_ble_init(BadUsbHidConfig* hid_cfg) { + BleHidInstance* ble_hid = malloc(sizeof(BleHidInstance)); + ble_hid->bt = furi_record_open(RECORD_BT); + ble_hid->bt->suppress_pin_screen = true; + bt_disconnect(ble_hid->bt); + + // Wait 2nd core to update nvm storage + furi_delay_ms(200); + + bt_keys_storage_set_storage_path(ble_hid->bt, APP_DATA_PATH(HID_BT_KEYS_STORAGE_NAME)); + + hid_ble_adjust_config(hid_cfg); + ble_hid->profile = bt_profile_start(ble_hid->bt, ble_profile_hid, &hid_cfg->ble); + furi_check(ble_hid->profile); + + furi_hal_bt_start_advertising(); + + bt_set_status_changed_callback(ble_hid->bt, hid_ble_connection_status_callback, ble_hid); + + return ble_hid; +} + +void hid_ble_deinit(void* inst) { + BleHidInstance* ble_hid = inst; + furi_assert(ble_hid); + + bt_set_status_changed_callback(ble_hid->bt, NULL, NULL); + bt_disconnect(ble_hid->bt); + + // Wait 2nd core to update nvm storage + furi_delay_ms(200); + bt_keys_storage_set_default_path(ble_hid->bt); + + furi_check(bt_profile_restore_default(ble_hid->bt)); + ble_hid->bt->suppress_pin_screen = false; + furi_record_close(RECORD_BT); + free(ble_hid); +} + +void hid_ble_set_state_callback(void* inst, HidStateCallback cb, void* context) { + BleHidInstance* ble_hid = inst; + furi_assert(ble_hid); + ble_hid->state_callback = cb; + ble_hid->callback_context = context; +} + +bool hid_ble_is_connected(void* inst) { + BleHidInstance* ble_hid = inst; + furi_assert(ble_hid); + return ble_hid->is_connected; +} + +bool hid_ble_kb_press(void* inst, uint16_t button) { + BleHidInstance* ble_hid = inst; + furi_assert(ble_hid); + return ble_profile_hid_kb_press(ble_hid->profile, button); +} + +bool hid_ble_kb_release(void* inst, uint16_t button) { + BleHidInstance* ble_hid = inst; + furi_assert(ble_hid); + return ble_profile_hid_kb_release(ble_hid->profile, button); +} + +bool hid_ble_mouse_press(void* inst, uint8_t button) { + BleHidInstance* ble_hid = inst; + furi_assert(ble_hid); + return ble_profile_hid_mouse_press(ble_hid->profile, button); +} +bool hid_ble_mouse_release(void* inst, uint8_t button) { + BleHidInstance* ble_hid = inst; + furi_assert(ble_hid); + return ble_profile_hid_mouse_release(ble_hid->profile, button); +} +bool hid_ble_mouse_scroll(void* inst, int8_t delta) { + BleHidInstance* ble_hid = inst; + furi_assert(ble_hid); + return ble_profile_hid_mouse_scroll(ble_hid->profile, delta); +} +bool hid_ble_mouse_move(void* inst, int8_t dx, int8_t dy) { + BleHidInstance* ble_hid = inst; + furi_assert(ble_hid); + return ble_profile_hid_mouse_move(ble_hid->profile, dx, dy); +} + +bool hid_ble_consumer_press(void* inst, uint16_t button) { + BleHidInstance* ble_hid = inst; + furi_assert(ble_hid); + return ble_profile_hid_consumer_key_press(ble_hid->profile, button); +} + +bool hid_ble_consumer_release(void* inst, uint16_t button) { + BleHidInstance* ble_hid = inst; + furi_assert(ble_hid); + return ble_profile_hid_consumer_key_release(ble_hid->profile, button); +} + +bool hid_ble_release_all(void* inst) { + BleHidInstance* ble_hid = inst; + furi_assert(ble_hid); + bool state = ble_profile_hid_kb_release_all(ble_hid->profile); + state &= ble_profile_hid_consumer_key_release_all(ble_hid->profile); + state &= ble_profile_hid_mouse_release_all(ble_hid->profile); + return state; +} + +uint8_t hid_ble_get_led_state(void* inst) { + UNUSED(inst); + FURI_LOG_W(TAG, "hid_ble_get_led_state not implemented"); + return 0; +} + +static const BadUsbHidApi hid_api_ble = { + .adjust_config = hid_ble_adjust_config, + .init = hid_ble_init, + .deinit = hid_ble_deinit, + .set_state_callback = hid_ble_set_state_callback, + .is_connected = hid_ble_is_connected, + + .kb_press = hid_ble_kb_press, + .kb_release = hid_ble_kb_release, + .mouse_press = hid_ble_mouse_press, + .mouse_release = hid_ble_mouse_release, + .mouse_scroll = hid_ble_mouse_scroll, + .mouse_move = hid_ble_mouse_move, + .consumer_press = hid_ble_consumer_press, + .consumer_release = hid_ble_consumer_release, + .release_all = hid_ble_release_all, + .get_led_state = hid_ble_get_led_state, +}; + +const BadUsbHidApi* bad_usb_hid_get_interface(BadUsbHidInterface interface) { + if(interface == BadUsbHidInterfaceUsb) { + return &hid_api_usb; + } else { + return &hid_api_ble; + } +} + +void bad_usb_hid_ble_remove_pairing(void) { + Bt* bt = furi_record_open(RECORD_BT); + bt_disconnect(bt); + + // Wait 2nd core to update nvm storage + furi_delay_ms(200); + + furi_hal_bt_stop_advertising(); + + bt_keys_storage_set_storage_path(bt, APP_DATA_PATH(HID_BT_KEYS_STORAGE_NAME)); + bt_forget_bonded_devices(bt); + + // Wait 2nd core to update nvm storage + furi_delay_ms(200); + bt_keys_storage_set_default_path(bt); + + furi_check(bt_profile_restore_default(bt)); + furi_record_close(RECORD_BT); +} diff --git a/applications/main/bad_usb/helpers/bad_usb_hid.h b/applications/main/bad_usb/helpers/bad_usb_hid.h new file mode 100644 index 000000000..8749bdc3b --- /dev/null +++ b/applications/main/bad_usb/helpers/bad_usb_hid.h @@ -0,0 +1,48 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include "ble_hid_profile.h" + +typedef enum { + BadUsbHidInterfaceUsb, + BadUsbHidInterfaceBle, + BadUsbHidInterfaceMAX, +} BadUsbHidInterface; + +typedef struct { + BleProfileHidParams ble; + FuriHalUsbHidConfig usb; +} BadUsbHidConfig; + +typedef struct { + void (*adjust_config)(BadUsbHidConfig* hid_cfg); + void* (*init)(BadUsbHidConfig* hid_cfg); + void (*deinit)(void* inst); + void (*set_state_callback)(void* inst, HidStateCallback cb, void* context); + bool (*is_connected)(void* inst); + + bool (*kb_press)(void* inst, uint16_t button); + bool (*kb_release)(void* inst, uint16_t button); + bool (*mouse_press)(void* inst, uint8_t button); + bool (*mouse_release)(void* inst, uint8_t button); + bool (*mouse_scroll)(void* inst, int8_t delta); + bool (*mouse_move)(void* inst, int8_t dx, int8_t dy); + bool (*consumer_press)(void* inst, uint16_t button); + bool (*consumer_release)(void* inst, uint16_t button); + bool (*release_all)(void* inst); + uint8_t (*get_led_state)(void* inst); +} BadUsbHidApi; + +const BadUsbHidApi* bad_usb_hid_get_interface(BadUsbHidInterface interface); + +void bad_usb_hid_ble_remove_pairing(void); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/bad_kb/helpers/ble_hid.c b/applications/main/bad_usb/helpers/ble_hid_profile.c similarity index 98% rename from applications/main/bad_kb/helpers/ble_hid.c rename to applications/main/bad_usb/helpers/ble_hid_profile.c index 2f6dd8c2a..7b9b2e06f 100644 --- a/applications/main/bad_kb/helpers/ble_hid.c +++ b/applications/main/bad_usb/helpers/ble_hid_profile.c @@ -1,9 +1,11 @@ -#include "ble_hid.h" +#include "ble_hid_profile.h" + +// Based on #include #include #include -#include "ble_hid_svc.h" +#include "ble_hid_service.h" #include #include @@ -394,12 +396,13 @@ static GapConfig template_config = { }; static void ble_profile_hid_get_config(GapConfig* config, FuriHalBleProfileParams profile_params) { + furi_check(profile_params); BleProfileHidParams* hid_profile_params = profile_params; furi_check(config); memcpy(config, &template_config, sizeof(GapConfig)); - // Set mac address + // Set MAC address memcpy(config->mac_address, hid_profile_params->mac, sizeof(config->mac_address)); // Set advertise name diff --git a/applications/main/bad_kb/helpers/ble_hid.h b/applications/main/bad_usb/helpers/ble_hid_profile.h similarity index 98% rename from applications/main/bad_kb/helpers/ble_hid.h rename to applications/main/bad_usb/helpers/ble_hid_profile.h index 717625354..2302aa581 100644 --- a/applications/main/bad_kb/helpers/ble_hid.h +++ b/applications/main/bad_usb/helpers/ble_hid_profile.h @@ -1,5 +1,7 @@ #pragma once +// Based on + #include #ifdef __cplusplus diff --git a/applications/main/bad_kb/helpers/ble_hid_svc.c b/applications/main/bad_usb/helpers/ble_hid_service.c similarity index 98% rename from applications/main/bad_kb/helpers/ble_hid_svc.c rename to applications/main/bad_usb/helpers/ble_hid_service.c index 024e25a78..b546368dd 100644 --- a/applications/main/bad_kb/helpers/ble_hid_svc.c +++ b/applications/main/bad_usb/helpers/ble_hid_service.c @@ -1,5 +1,8 @@ -#include "ble_hid_svc.h" -#include "app_common.h" +#include "ble_hid_service.h" + +// Based on + +#include "app_common.h" // IWYU pragma: keep #include #include #include @@ -170,7 +173,7 @@ static BleEventAckStatus ble_svc_hid_event_handler(void* event, void* context) { return ret; } -BleServiceHid* ble_svc_hid_start() { +BleServiceHid* ble_svc_hid_start(void) { BleServiceHid* hid_svc = malloc(sizeof(BleServiceHid)); // Register event handler diff --git a/applications/main/bad_kb/helpers/ble_hid_svc.h b/applications/main/bad_usb/helpers/ble_hid_service.h similarity index 85% rename from applications/main/bad_kb/helpers/ble_hid_svc.h rename to applications/main/bad_usb/helpers/ble_hid_service.h index c6e3059cd..e1ac3b0be 100644 --- a/applications/main/bad_kb/helpers/ble_hid_svc.h +++ b/applications/main/bad_usb/helpers/ble_hid_service.h @@ -1,5 +1,7 @@ #pragma once +// Based on + #include #include @@ -9,7 +11,7 @@ extern "C" { typedef struct BleServiceHid BleServiceHid; -BleServiceHid* ble_svc_hid_start(); +BleServiceHid* ble_svc_hid_start(void); void ble_svc_hid_stop(BleServiceHid* service); diff --git a/applications/main/bad_usb/helpers/ducky_script.c b/applications/main/bad_usb/helpers/ducky_script.c new file mode 100644 index 000000000..1b07f2ac6 --- /dev/null +++ b/applications/main/bad_usb/helpers/ducky_script.c @@ -0,0 +1,811 @@ +#include +#include +#include +#include +#include +#include +#include +#include "ducky_script.h" +#include "ducky_script_i.h" +#include + +#define TAG "BadUsb" + +#define WORKER_TAG TAG "Worker" + +#define BADUSB_ASCII_TO_KEY(script, x) \ + (((uint8_t)x < 128) ? (script->layout[(uint8_t)x]) : HID_KEYBOARD_NONE) + +typedef enum { + WorkerEvtStartStop = (1 << 0), + WorkerEvtPauseResume = (1 << 1), + WorkerEvtEnd = (1 << 2), + WorkerEvtConnect = (1 << 3), + WorkerEvtDisconnect = (1 << 4), +} WorkerEvtFlags; + +static const char ducky_cmd_id[] = {"ID"}; +static const char ducky_cmd_bt_id[] = {"BT_ID"}; +static const char ducky_cmd_ble_id[] = {"BLE_ID"}; + +static const uint8_t numpad_keys[10] = { + HID_KEYPAD_0, + HID_KEYPAD_1, + HID_KEYPAD_2, + HID_KEYPAD_3, + HID_KEYPAD_4, + HID_KEYPAD_5, + HID_KEYPAD_6, + HID_KEYPAD_7, + HID_KEYPAD_8, + HID_KEYPAD_9, +}; + +uint32_t ducky_get_command_len(const char* line) { + uint32_t len = strlen(line); + for(uint32_t i = 0; i < len; i++) { + if(line[i] == ' ') return i; + } + return 0; +} + +bool ducky_is_line_end(const char chr) { + return (chr == ' ') || (chr == '\0') || (chr == '\r') || (chr == '\n'); +} + +uint16_t ducky_get_keycode(BadUsbScript* bad_usb, const char* param, bool accept_chars) { + uint16_t keycode = ducky_get_keycode_by_name(param); + if(keycode != HID_KEYBOARD_NONE) { + return keycode; + } + + if((accept_chars) && (strlen(param) > 0)) { + return BADUSB_ASCII_TO_KEY(bad_usb, param[0]) & 0xFF; + } + return 0; +} + +bool ducky_get_number(const char* param, uint32_t* val) { + uint32_t value = 0; + if(strint_to_uint32(param, NULL, &value, 10) == StrintParseNoError) { + *val = value; + return true; + } + return false; +} + +void ducky_numlock_on(BadUsbScript* bad_usb) { + if((bad_usb->hid->get_led_state(bad_usb->hid_inst) & HID_KB_LED_NUM) == 0) { + bad_usb->hid->kb_press(bad_usb->hid_inst, HID_KEYBOARD_LOCK_NUM_LOCK); + bad_usb->hid->kb_release(bad_usb->hid_inst, HID_KEYBOARD_LOCK_NUM_LOCK); + } +} + +bool ducky_numpad_press(BadUsbScript* bad_usb, const char num) { + if((num < '0') || (num > '9')) return false; + + uint16_t key = numpad_keys[num - '0']; + bad_usb->hid->kb_press(bad_usb->hid_inst, key); + bad_usb->hid->kb_release(bad_usb->hid_inst, key); + + return true; +} + +bool ducky_altchar(BadUsbScript* bad_usb, const char* charcode) { + uint8_t i = 0; + bool state = false; + + bad_usb->hid->kb_press(bad_usb->hid_inst, KEY_MOD_LEFT_ALT); + + while(!ducky_is_line_end(charcode[i])) { + state = ducky_numpad_press(bad_usb, charcode[i]); + if(state == false) break; + i++; + } + + bad_usb->hid->kb_release(bad_usb->hid_inst, KEY_MOD_LEFT_ALT); + return state; +} + +bool ducky_altstring(BadUsbScript* bad_usb, const char* param) { + uint32_t i = 0; + bool state = false; + + while(param[i] != '\0') { + if((param[i] < ' ') || (param[i] > '~')) { + i++; + continue; // Skip non-printable chars + } + + char temp_str[4]; + snprintf(temp_str, 4, "%u", param[i]); + + state = ducky_altchar(bad_usb, temp_str); + if(state == false) break; + i++; + } + return state; +} + +int32_t ducky_error(BadUsbScript* bad_usb, const char* text, ...) { + va_list args; + va_start(args, text); + + vsnprintf(bad_usb->st.error, sizeof(bad_usb->st.error), text, args); + + va_end(args); + return SCRIPT_STATE_ERROR; +} + +bool ducky_string(BadUsbScript* bad_usb, const char* param) { + uint32_t i = 0; + + while(param[i] != '\0') { + if(param[i] != '\n') { + uint16_t keycode = BADUSB_ASCII_TO_KEY(bad_usb, param[i]); + if(keycode != HID_KEYBOARD_NONE) { + bad_usb->hid->kb_press(bad_usb->hid_inst, keycode); + bad_usb->hid->kb_release(bad_usb->hid_inst, keycode); + } + } else { + bad_usb->hid->kb_press(bad_usb->hid_inst, HID_KEYBOARD_RETURN); + bad_usb->hid->kb_release(bad_usb->hid_inst, HID_KEYBOARD_RETURN); + } + i++; + } + bad_usb->stringdelay = 0; + return true; +} + +static bool ducky_string_next(BadUsbScript* bad_usb) { + if(bad_usb->string_print_pos >= furi_string_size(bad_usb->string_print)) { + return true; + } + + char print_char = furi_string_get_char(bad_usb->string_print, bad_usb->string_print_pos); + + if(print_char != '\n') { + uint16_t keycode = BADUSB_ASCII_TO_KEY(bad_usb, print_char); + if(keycode != HID_KEYBOARD_NONE) { + bad_usb->hid->kb_press(bad_usb->hid_inst, keycode); + bad_usb->hid->kb_release(bad_usb->hid_inst, keycode); + } + } else { + bad_usb->hid->kb_press(bad_usb->hid_inst, HID_KEYBOARD_RETURN); + bad_usb->hid->kb_release(bad_usb->hid_inst, HID_KEYBOARD_RETURN); + } + + bad_usb->string_print_pos++; + + return false; +} + +static int32_t ducky_parse_line(BadUsbScript* bad_usb, FuriString* line) { + uint32_t line_len = furi_string_size(line); + const char* line_tmp = furi_string_get_cstr(line); + + if(line_len == 0) { + return SCRIPT_STATE_NEXT_LINE; // Skip empty lines + } + FURI_LOG_D(WORKER_TAG, "line:%s", line_tmp); + + // Ducky Lang Functions + int32_t cmd_result = ducky_execute_cmd(bad_usb, line_tmp); + if(cmd_result != SCRIPT_STATE_CMD_UNKNOWN) { + return cmd_result; + } + + // Mouse Keys + uint16_t key = ducky_get_mouse_keycode_by_name(line_tmp); + if(key != HID_MOUSE_INVALID) { + bad_usb->hid->mouse_press(bad_usb->hid_inst, key); + bad_usb->hid->mouse_release(bad_usb->hid_inst, key); + return 0; + } + + // Special keys + modifiers + key = ducky_get_keycode(bad_usb, line_tmp, false); + if(key == HID_KEYBOARD_NONE) { + return ducky_error(bad_usb, "No keycode defined for %s", line_tmp); + } + if((key & 0xFF00) != 0) { + // It's a modifier key + uint32_t offset = ducky_get_command_len(line_tmp) + 1; + // ducky_get_command_len() returns 0 without space, so check for != 1 + if(offset != 1 && line_len > offset) { + // It's also a key combination + line_tmp = &line_tmp[offset]; + key |= ducky_get_keycode(bad_usb, line_tmp, true); + } + } + bad_usb->hid->kb_press(bad_usb->hid_inst, key); + bad_usb->hid->kb_release(bad_usb->hid_inst, key); + return 0; +} + +static bool ducky_set_usb_id(BadUsbScript* bad_usb, const char* line) { + FuriHalUsbHidConfig* usb_hid_cfg = &bad_usb->hid_cfg->usb; + + if(sscanf(line, "%lX:%lX", &usb_hid_cfg->vid, &usb_hid_cfg->pid) == 2) { + usb_hid_cfg->manuf[0] = '\0'; + usb_hid_cfg->product[0] = '\0'; + + uint8_t id_len = ducky_get_command_len(line); + if(!ducky_is_line_end(line[id_len + 1])) { + sscanf( + &line[id_len + 1], + "%31[^\r\n:]:%31[^\r\n]", + usb_hid_cfg->manuf, + usb_hid_cfg->product); + } + FURI_LOG_D( + WORKER_TAG, + "set id: %04lX:%04lX mfr:%s product:%s", + usb_hid_cfg->vid, + usb_hid_cfg->pid, + usb_hid_cfg->manuf, + usb_hid_cfg->product); + return true; + } + return false; +} + +static bool ducky_set_ble_id(BadUsbScript* bad_usb, const char* line) { + BleProfileHidParams* ble_hid_cfg = &bad_usb->hid_cfg->ble; + + size_t line_len = strlen(line); + size_t mac_len = sizeof(ble_hid_cfg->mac) * 3; // 2 hex chars + separator per byte + if(line_len < mac_len + 1) return false; // MAC + at least 1 char for name + + for(size_t i = 0; i < sizeof(ble_hid_cfg->mac); i++) { + const char* hex_byte = &line[i * 3]; + // This sscanf() doesn't work well with %02hhX, need to use a u32 + uint32_t temp_uint; + if(sscanf(hex_byte, "%02lX", &temp_uint) != 1) { + return false; + } + ble_hid_cfg->mac[sizeof(ble_hid_cfg->mac) - 1 - i] = temp_uint; + } + + strlcpy(ble_hid_cfg->name, line + mac_len, sizeof(ble_hid_cfg->name)); + FURI_LOG_D(WORKER_TAG, "set ble id: %s", line); + return true; +} + +static void bad_usb_hid_state_callback(bool state, void* context) { + furi_assert(context); + BadUsbScript* bad_usb = context; + + if(state == true) { + furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtConnect); + } else { + furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtDisconnect); + } +} + +static bool ducky_script_preload(BadUsbScript* bad_usb, File* script_file) { + uint8_t ret = 0; + uint32_t line_len = 0; + + furi_string_reset(bad_usb->line); + + do { + ret = storage_file_read(script_file, bad_usb->file_buf, FILE_BUFFER_LEN); + for(uint16_t i = 0; i < ret; i++) { + if(bad_usb->file_buf[i] == '\n' && line_len > 0) { + bad_usb->st.line_nb++; + line_len = 0; + } else { + if(bad_usb->st.line_nb == 0) { // Save first line + furi_string_push_back(bad_usb->line, bad_usb->file_buf[i]); + } + line_len++; + } + } + if(storage_file_eof(script_file)) { + if(line_len > 0) { + bad_usb->st.line_nb++; + break; + } + } + } while(ret > 0); + + if(bad_usb->load_id_cfg) { + const char* line_tmp = furi_string_get_cstr(bad_usb->line); + BadUsbHidInterface interface = *bad_usb->interface; + // Look for ID/BLE_ID/BT_ID command on first line + if(strncmp(line_tmp, ducky_cmd_id, strlen(ducky_cmd_id)) == 0) { + if(ducky_set_usb_id(bad_usb, &line_tmp[strlen(ducky_cmd_id) + 1])) { + interface = BadUsbHidInterfaceUsb; + } + } else if( + strncmp(line_tmp, ducky_cmd_ble_id, strlen(ducky_cmd_ble_id)) == 0 || + strncmp(line_tmp, ducky_cmd_bt_id, strlen(ducky_cmd_bt_id)) == 0) { + if(ducky_set_ble_id(bad_usb, &line_tmp[ducky_get_command_len(line_tmp) + 1])) { + interface = BadUsbHidInterfaceBle; + } + } + + // Auto-switch based on ID/BLE_ID/BT_ID command, user can override manually after + if(interface != *bad_usb->interface) { + *bad_usb->interface = interface; + bad_usb->hid = bad_usb_hid_get_interface(*bad_usb->interface); + } + } + + bad_usb->hid_inst = bad_usb->hid->init(bad_usb->hid_cfg); + bad_usb->hid->set_state_callback(bad_usb->hid_inst, bad_usb_hid_state_callback, bad_usb); + + storage_file_seek(script_file, 0, true); + furi_string_reset(bad_usb->line); + + return true; +} + +static int32_t ducky_script_execute_next(BadUsbScript* bad_usb, File* script_file) { + int32_t delay_val = 0; + + if(bad_usb->repeat_cnt > 0) { + bad_usb->repeat_cnt--; + delay_val = ducky_parse_line(bad_usb, bad_usb->line_prev); + if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line + return 0; + } else if(delay_val == SCRIPT_STATE_STRING_START) { // Print string with delays + return delay_val; + } else if(delay_val == SCRIPT_STATE_WAIT_FOR_BTN) { // wait for button + return delay_val; + } else if(delay_val < 0) { // Script error + bad_usb->st.error_line = bad_usb->st.line_cur - 1; + FURI_LOG_E(WORKER_TAG, "Unknown command at line %zu", bad_usb->st.line_cur - 1U); + return SCRIPT_STATE_ERROR; + } else { + return delay_val + bad_usb->defdelay; + } + } + + furi_string_set(bad_usb->line_prev, bad_usb->line); + furi_string_reset(bad_usb->line); + + while(1) { + if(bad_usb->buf_len == 0) { + bad_usb->buf_len = storage_file_read(script_file, bad_usb->file_buf, FILE_BUFFER_LEN); + if(storage_file_eof(script_file)) { + if((bad_usb->buf_len < FILE_BUFFER_LEN) && (bad_usb->file_end == false)) { + bad_usb->file_buf[bad_usb->buf_len] = '\n'; + bad_usb->buf_len++; + bad_usb->file_end = true; + } + } + + bad_usb->buf_start = 0; + if(bad_usb->buf_len == 0) return SCRIPT_STATE_END; + } + for(uint8_t i = bad_usb->buf_start; i < (bad_usb->buf_start + bad_usb->buf_len); i++) { + if(bad_usb->file_buf[i] == '\n' && furi_string_size(bad_usb->line) > 0) { + bad_usb->st.line_cur++; + bad_usb->buf_len = bad_usb->buf_len + bad_usb->buf_start - (i + 1); + bad_usb->buf_start = i + 1; + furi_string_trim(bad_usb->line); + delay_val = ducky_parse_line(bad_usb, bad_usb->line); + if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line + return 0; + } else if(delay_val == SCRIPT_STATE_STRING_START) { // Print string with delays + return delay_val; + } else if(delay_val == SCRIPT_STATE_WAIT_FOR_BTN) { // wait for button + return delay_val; + } else if(delay_val < 0) { + bad_usb->st.error_line = bad_usb->st.line_cur; + FURI_LOG_E(WORKER_TAG, "Unknown command at line %zu", bad_usb->st.line_cur); + return SCRIPT_STATE_ERROR; + } else { + return delay_val + bad_usb->defdelay; + } + } else { + furi_string_push_back(bad_usb->line, bad_usb->file_buf[i]); + } + } + bad_usb->buf_len = 0; + if(bad_usb->file_end) return SCRIPT_STATE_END; + } + + return 0; +} + +static uint32_t bad_usb_flags_get(uint32_t flags_mask, uint32_t timeout) { + uint32_t flags = furi_thread_flags_get(); + furi_check((flags & FuriFlagError) == 0); + if(flags == 0) { + flags = furi_thread_flags_wait(flags_mask, FuriFlagWaitAny, timeout); + furi_check(((flags & FuriFlagError) == 0) || (flags == (unsigned)FuriFlagErrorTimeout)); + } else { + uint32_t state = furi_thread_flags_clear(flags); + furi_check((state & FuriFlagError) == 0); + } + return flags; +} + +static int32_t bad_usb_worker(void* context) { + BadUsbScript* bad_usb = context; + + BadUsbWorkerState worker_state = BadUsbStateInit; + BadUsbWorkerState pause_state = BadUsbStateRunning; + int32_t delay_val = 0; + + FURI_LOG_I(WORKER_TAG, "Init"); + File* script_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); + bad_usb->line = furi_string_alloc(); + bad_usb->line_prev = furi_string_alloc(); + bad_usb->string_print = furi_string_alloc(); + bad_usb->st.elapsed = 0; + + while(1) { + uint32_t start = furi_get_tick(); + if(worker_state == BadUsbStateInit) { // State: initialization + start = 0; + FURI_LOG_D(WORKER_TAG, "init start"); + if(storage_file_open( + script_file, + furi_string_get_cstr(bad_usb->file_path), + FSAM_READ, + FSOM_OPEN_EXISTING)) { + if((ducky_script_preload(bad_usb, script_file)) && (bad_usb->st.line_nb > 0)) { + if(bad_usb->hid->is_connected(bad_usb->hid_inst)) { + worker_state = BadUsbStateIdle; // Ready to run + } else { + worker_state = BadUsbStateNotConnected; // USB not connected + } + } else { + worker_state = BadUsbStateScriptError; // Script preload error + } + } else { + FURI_LOG_E(WORKER_TAG, "File open error"); + worker_state = BadUsbStateFileError; // File open error + } + bad_usb->st.state = worker_state; + FURI_LOG_D(WORKER_TAG, "init done"); + + } else if(worker_state == BadUsbStateNotConnected) { // State: Not connected + start = 0; + FURI_LOG_D(WORKER_TAG, "not connected wait"); + uint32_t flags = bad_usb_flags_get( + WorkerEvtEnd | WorkerEvtConnect | WorkerEvtDisconnect | WorkerEvtStartStop, + FuriWaitForever); + FURI_LOG_D(WORKER_TAG, "not connected flags: %lu", flags); + + if(flags & WorkerEvtEnd) { + break; + } else if(flags & WorkerEvtConnect) { + worker_state = BadUsbStateIdle; // Ready to run + } else if(flags & WorkerEvtStartStop) { + worker_state = BadUsbStateWillRun; // Will run when connected + } + bad_usb->st.state = worker_state; + + } else if(worker_state == BadUsbStateIdle) { // State: ready to start + start = 0; + FURI_LOG_D(WORKER_TAG, "idle wait"); + uint32_t flags = bad_usb_flags_get( + WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtDisconnect, FuriWaitForever); + FURI_LOG_D(WORKER_TAG, "idle flags: %lu", flags); + + if(flags & WorkerEvtEnd) { + break; + } else if(flags & WorkerEvtStartStop) { // Start executing script + dolphin_deed(DolphinDeedBadUsbPlayScript); + delay_val = 0; + bad_usb->buf_len = 0; + bad_usb->st.line_cur = 0; + bad_usb->defdelay = 0; + bad_usb->stringdelay = 0; + bad_usb->defstringdelay = 0; + bad_usb->repeat_cnt = 0; + bad_usb->key_hold_nb = 0; + bad_usb->file_end = false; + storage_file_seek(script_file, 0, true); + worker_state = BadUsbStateRunning; + bad_usb->st.elapsed = 0; + } else if(flags & WorkerEvtDisconnect) { + worker_state = BadUsbStateNotConnected; // Disconnected + } + bad_usb->st.state = worker_state; + + } else if(worker_state == BadUsbStateWillRun) { // State: start on connection + start = 0; + FURI_LOG_D(WORKER_TAG, "will run wait"); + uint32_t flags = bad_usb_flags_get( + WorkerEvtEnd | WorkerEvtConnect | WorkerEvtStartStop, FuriWaitForever); + FURI_LOG_D(WORKER_TAG, "will run flags: %lu", flags); + + if(flags & WorkerEvtEnd) { + break; + } else if(flags & WorkerEvtConnect) { // Start executing script + dolphin_deed(DolphinDeedBadUsbPlayScript); + delay_val = 0; + bad_usb->buf_len = 0; + bad_usb->st.line_cur = 0; + bad_usb->defdelay = 0; + bad_usb->stringdelay = 0; + bad_usb->defstringdelay = 0; + bad_usb->repeat_cnt = 0; + bad_usb->file_end = false; + storage_file_seek(script_file, 0, true); + // extra time for PC to recognize Flipper as keyboard + flags = furi_thread_flags_wait( + WorkerEvtEnd | WorkerEvtDisconnect | WorkerEvtStartStop, + FuriFlagWaitAny | FuriFlagNoClear, + 1500); + if(flags == (unsigned)FuriFlagErrorTimeout) { + // If nothing happened - start script execution + worker_state = BadUsbStateRunning; + bad_usb->st.elapsed = 0; + } else if(flags & WorkerEvtStartStop) { + worker_state = BadUsbStateIdle; + furi_thread_flags_clear(WorkerEvtStartStop); + } + } else if(flags & WorkerEvtStartStop) { // Cancel scheduled execution + worker_state = BadUsbStateNotConnected; + } + bad_usb->st.state = worker_state; + + } else if(worker_state == BadUsbStateRunning) { // State: running + FURI_LOG_D(WORKER_TAG, "running"); + uint16_t delay_cur = (delay_val > 100) ? (100) : (delay_val); + uint32_t flags = furi_thread_flags_wait( + WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect, + FuriFlagWaitAny, + delay_cur); + FURI_LOG_D(WORKER_TAG, "running flags: %lu", flags); + + delay_val -= delay_cur; + if(!(flags & FuriFlagError)) { + if(flags & WorkerEvtEnd) { + break; + } else if(flags & WorkerEvtStartStop) { + worker_state = BadUsbStateIdle; // Stop executing script + bad_usb->hid->release_all(bad_usb->hid_inst); + } else if(flags & WorkerEvtDisconnect) { + worker_state = BadUsbStateNotConnected; // Disconnected + bad_usb->hid->release_all(bad_usb->hid_inst); + } else if(flags & WorkerEvtPauseResume) { + pause_state = BadUsbStateRunning; + worker_state = BadUsbStatePaused; // Pause + } + bad_usb->st.state = worker_state; + bad_usb->st.elapsed += (furi_get_tick() - start); + continue; + } else if( + (flags == (unsigned)FuriFlagErrorTimeout) || + (flags == (unsigned)FuriFlagErrorResource)) { + if(delay_val > 0) { + bad_usb->st.delay_remain--; + bad_usb->st.elapsed += (furi_get_tick() - start); + continue; + } + bad_usb->st.state = BadUsbStateRunning; + delay_val = ducky_script_execute_next(bad_usb, script_file); + if(delay_val == SCRIPT_STATE_ERROR) { // Script error + delay_val = 0; + worker_state = BadUsbStateScriptError; + bad_usb->st.state = worker_state; + bad_usb->hid->release_all(bad_usb->hid_inst); + } else if(delay_val == SCRIPT_STATE_END) { // End of script + delay_val = 0; + worker_state = BadUsbStateIdle; + bad_usb->st.state = BadUsbStateDone; + bad_usb->hid->release_all(bad_usb->hid_inst); + bad_usb->st.elapsed += (furi_get_tick() - start); + continue; + } else if(delay_val == SCRIPT_STATE_STRING_START) { // Start printing string with delays + delay_val = bad_usb->defdelay; + bad_usb->string_print_pos = 0; + worker_state = BadUsbStateStringDelay; + } else if(delay_val == SCRIPT_STATE_WAIT_FOR_BTN) { // set state to wait for user input + worker_state = BadUsbStateWaitForBtn; + bad_usb->st.state = BadUsbStateWaitForBtn; // Show long delays + } else if(delay_val > 100) { + bad_usb->st.state = BadUsbStateDelay; // Show long delays + bad_usb->st.delay_remain = delay_val / 100; + } + } else { + furi_check((flags & FuriFlagError) == 0); + } + } else if(worker_state == BadUsbStateWaitForBtn) { // State: Wait for button Press + start = 0; + FURI_LOG_D(WORKER_TAG, "button wait"); + uint32_t flags = bad_usb_flags_get( + WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect, + FuriWaitForever); + FURI_LOG_D(WORKER_TAG, "button flags: %lu", flags); + if(!(flags & FuriFlagError)) { + if(flags & WorkerEvtEnd) { + break; + } else if(flags & WorkerEvtStartStop) { + delay_val = 0; + worker_state = BadUsbStateRunning; + } else if(flags & WorkerEvtDisconnect) { + worker_state = BadUsbStateNotConnected; // Disconnected + bad_usb->hid->release_all(bad_usb->hid_inst); + } + bad_usb->st.state = worker_state; + continue; + } + } else if(worker_state == BadUsbStatePaused) { // State: Paused + start = 0; + FURI_LOG_D(WORKER_TAG, "paused wait"); + uint32_t flags = bad_usb_flags_get( + WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect, + FuriWaitForever); + FURI_LOG_D(WORKER_TAG, "paused flags: %lu", flags); + if(!(flags & FuriFlagError)) { + if(flags & WorkerEvtEnd) { + break; + } else if(flags & WorkerEvtStartStop) { + worker_state = BadUsbStateIdle; // Stop executing script + bad_usb->st.state = worker_state; + bad_usb->hid->release_all(bad_usb->hid_inst); + } else if(flags & WorkerEvtDisconnect) { + worker_state = BadUsbStateNotConnected; // Disconnected + bad_usb->st.state = worker_state; + bad_usb->hid->release_all(bad_usb->hid_inst); + } else if(flags & WorkerEvtPauseResume) { + if(pause_state == BadUsbStateRunning) { + if(delay_val > 0) { + bad_usb->st.state = BadUsbStateDelay; + bad_usb->st.delay_remain = delay_val / 100; + } else { + bad_usb->st.state = BadUsbStateRunning; + delay_val = 0; + } + worker_state = BadUsbStateRunning; // Resume + } else if(pause_state == BadUsbStateStringDelay) { + bad_usb->st.state = BadUsbStateRunning; + worker_state = BadUsbStateStringDelay; // Resume + } + } + continue; + } + } else if(worker_state == BadUsbStateStringDelay) { // State: print string with delays + FURI_LOG_D(WORKER_TAG, "delay wait"); + uint32_t delay = (bad_usb->stringdelay == 0) ? bad_usb->defstringdelay : + bad_usb->stringdelay; + uint32_t flags = bad_usb_flags_get( + WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect, + delay); + FURI_LOG_D(WORKER_TAG, "delay flags: %lu", flags); + + if(!(flags & FuriFlagError)) { + if(flags & WorkerEvtEnd) { + break; + } else if(flags & WorkerEvtStartStop) { + worker_state = BadUsbStateIdle; // Stop executing script + bad_usb->hid->release_all(bad_usb->hid_inst); + } else if(flags & WorkerEvtDisconnect) { + worker_state = BadUsbStateNotConnected; // Disconnected + bad_usb->hid->release_all(bad_usb->hid_inst); + } else if(flags & WorkerEvtPauseResume) { + pause_state = BadUsbStateStringDelay; + worker_state = BadUsbStatePaused; // Pause + } + bad_usb->st.state = worker_state; + bad_usb->st.elapsed += (furi_get_tick() - start); + continue; + } else if( + (flags == (unsigned)FuriFlagErrorTimeout) || + (flags == (unsigned)FuriFlagErrorResource)) { + bool string_end = ducky_string_next(bad_usb); + if(string_end) { + bad_usb->stringdelay = 0; + worker_state = BadUsbStateRunning; + } + } else { + furi_check((flags & FuriFlagError) == 0); + } + } else if( + (worker_state == BadUsbStateFileError) || + (worker_state == BadUsbStateScriptError)) { // State: error + start = 0; + FURI_LOG_D(WORKER_TAG, "error wait"); + uint32_t flags = + bad_usb_flags_get(WorkerEvtEnd, FuriWaitForever); // Waiting for exit command + FURI_LOG_D(WORKER_TAG, "error flags: %lu", flags); + + if(flags & WorkerEvtEnd) { + break; + } + } + if(start) { + bad_usb->st.elapsed += (furi_get_tick() - start); + } + } + + bad_usb->hid->set_state_callback(bad_usb->hid_inst, NULL, NULL); + bad_usb->hid->deinit(bad_usb->hid_inst); + + storage_file_close(script_file); + storage_file_free(script_file); + furi_string_free(bad_usb->line); + furi_string_free(bad_usb->line_prev); + furi_string_free(bad_usb->string_print); + + FURI_LOG_I(WORKER_TAG, "End"); + + return 0; +} + +static void bad_usb_script_set_default_keyboard_layout(BadUsbScript* bad_usb) { + furi_assert(bad_usb); + memset(bad_usb->layout, HID_KEYBOARD_NONE, sizeof(bad_usb->layout)); + memcpy(bad_usb->layout, hid_asciimap, MIN(sizeof(hid_asciimap), sizeof(bad_usb->layout))); +} + +BadUsbScript* bad_usb_script_open( + FuriString* file_path, + BadUsbHidInterface* interface, + BadUsbHidConfig* hid_cfg, + bool load_id_cfg) { + furi_assert(file_path); + + BadUsbScript* bad_usb = malloc(sizeof(BadUsbScript)); + bad_usb->file_path = furi_string_alloc(); + furi_string_set(bad_usb->file_path, file_path); + bad_usb_script_set_default_keyboard_layout(bad_usb); + + bad_usb->st.state = BadUsbStateInit; + bad_usb->st.error[0] = '\0'; + bad_usb->interface = interface; + bad_usb->hid_cfg = hid_cfg; + bad_usb->load_id_cfg = load_id_cfg; + bad_usb->hid = bad_usb_hid_get_interface(*bad_usb->interface); + + bad_usb->thread = furi_thread_alloc_ex("BadUsbWorker", 2048, bad_usb_worker, bad_usb); + furi_thread_start(bad_usb->thread); + return bad_usb; +} //-V773 + +void bad_usb_script_close(BadUsbScript* bad_usb) { + furi_assert(bad_usb); + furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtEnd); + furi_thread_join(bad_usb->thread); + furi_thread_free(bad_usb->thread); + furi_string_free(bad_usb->file_path); + free(bad_usb); +} + +void bad_usb_script_set_keyboard_layout(BadUsbScript* bad_usb, FuriString* layout_path) { + furi_assert(bad_usb); + + if((bad_usb->st.state == BadUsbStateRunning) || (bad_usb->st.state == BadUsbStateDelay)) { + // do not update keyboard layout while a script is running + return; + } + + File* layout_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); + if(!furi_string_empty(layout_path)) { //-V1051 + if(storage_file_open( + layout_file, furi_string_get_cstr(layout_path), FSAM_READ, FSOM_OPEN_EXISTING)) { + uint16_t layout[128]; + if(storage_file_read(layout_file, layout, sizeof(layout)) == sizeof(layout)) { + memcpy(bad_usb->layout, layout, sizeof(layout)); + } + } + storage_file_close(layout_file); + } else { + bad_usb_script_set_default_keyboard_layout(bad_usb); + } + storage_file_free(layout_file); +} + +void bad_usb_script_start_stop(BadUsbScript* bad_usb) { + furi_assert(bad_usb); + furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtStartStop); +} + +void bad_usb_script_pause_resume(BadUsbScript* bad_usb) { + furi_assert(bad_usb); + furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtPauseResume); +} + +BadUsbState* bad_usb_script_get_state(BadUsbScript* bad_usb) { + furi_assert(bad_usb); + return &(bad_usb->st); +} diff --git a/applications/main/bad_usb/helpers/ducky_script.h b/applications/main/bad_usb/helpers/ducky_script.h new file mode 100644 index 000000000..9131ef43e --- /dev/null +++ b/applications/main/bad_usb/helpers/ducky_script.h @@ -0,0 +1,60 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include "bad_usb_hid.h" + +typedef enum { + BadUsbStateInit, + BadUsbStateNotConnected, + BadUsbStateIdle, + BadUsbStateWillRun, + BadUsbStateRunning, + BadUsbStateDelay, + BadUsbStateStringDelay, + BadUsbStateWaitForBtn, + BadUsbStatePaused, + BadUsbStateDone, + BadUsbStateScriptError, + BadUsbStateFileError, +} BadUsbWorkerState; + +typedef struct { + BadUsbWorkerState state; + size_t line_cur; + size_t line_nb; + uint32_t delay_remain; + size_t error_line; + char error[64]; + uint32_t elapsed; +} BadUsbState; + +typedef struct BadUsbScript BadUsbScript; + +BadUsbScript* bad_usb_script_open( + FuriString* file_path, + BadUsbHidInterface* interface, + BadUsbHidConfig* hid_cfg, + bool load_id_cfg); + +void bad_usb_script_close(BadUsbScript* bad_usb); + +void bad_usb_script_set_keyboard_layout(BadUsbScript* bad_usb, FuriString* layout_path); + +void bad_usb_script_start(BadUsbScript* bad_usb); + +void bad_usb_script_stop(BadUsbScript* bad_usb); + +void bad_usb_script_start_stop(BadUsbScript* bad_usb); + +void bad_usb_script_pause_resume(BadUsbScript* bad_usb); + +BadUsbState* bad_usb_script_get_state(BadUsbScript* bad_usb); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/bad_usb/helpers/ducky_script_commands.c b/applications/main/bad_usb/helpers/ducky_script_commands.c new file mode 100644 index 000000000..6c6fe36c7 --- /dev/null +++ b/applications/main/bad_usb/helpers/ducky_script_commands.c @@ -0,0 +1,309 @@ +#include +#include +#include "ducky_script.h" +#include "ducky_script_i.h" + +typedef int32_t (*DuckyCmdCallback)(BadUsbScript* bad_usb, const char* line, int32_t param); + +typedef struct { + char* name; + DuckyCmdCallback callback; + int32_t param; +} DuckyCmd; + +static int32_t ducky_fnc_delay(BadUsbScript* bad_usb, const char* line, int32_t param) { + UNUSED(param); + + line = &line[ducky_get_command_len(line) + 1]; + uint32_t delay_val = 0; + bool state = ducky_get_number(line, &delay_val); + if((state) && (delay_val > 0)) { + return (int32_t)delay_val; + } + + return ducky_error(bad_usb, "Invalid number %s", line); +} + +static int32_t ducky_fnc_defdelay(BadUsbScript* bad_usb, const char* line, int32_t param) { + UNUSED(param); + + line = &line[ducky_get_command_len(line) + 1]; + bool state = ducky_get_number(line, &bad_usb->defdelay); + if(!state) { + return ducky_error(bad_usb, "Invalid number %s", line); + } + return 0; +} + +static int32_t ducky_fnc_strdelay(BadUsbScript* bad_usb, const char* line, int32_t param) { + UNUSED(param); + + line = &line[ducky_get_command_len(line) + 1]; + bool state = ducky_get_number(line, &bad_usb->stringdelay); + if(!state) { + return ducky_error(bad_usb, "Invalid number %s", line); + } + return 0; +} + +static int32_t ducky_fnc_defstrdelay(BadUsbScript* bad_usb, const char* line, int32_t param) { + UNUSED(param); + + line = &line[ducky_get_command_len(line) + 1]; + bool state = ducky_get_number(line, &bad_usb->defstringdelay); + if(!state) { + return ducky_error(bad_usb, "Invalid number %s", line); + } + return 0; +} + +static int32_t ducky_fnc_string(BadUsbScript* bad_usb, const char* line, int32_t param) { + line = &line[ducky_get_command_len(line) + 1]; + furi_string_set_str(bad_usb->string_print, line); + if(param == 1) { + furi_string_cat(bad_usb->string_print, "\n"); + } + + if(bad_usb->stringdelay == 0 && + bad_usb->defstringdelay == 0) { // stringdelay not set - run command immediately + bool state = ducky_string(bad_usb, furi_string_get_cstr(bad_usb->string_print)); + if(!state) { + return ducky_error(bad_usb, "Invalid string %s", line); + } + } else { // stringdelay is set - run command in thread to keep handling external events + return SCRIPT_STATE_STRING_START; + } + + return 0; +} + +static int32_t ducky_fnc_repeat(BadUsbScript* bad_usb, const char* line, int32_t param) { + UNUSED(param); + + line = &line[ducky_get_command_len(line) + 1]; + bool state = ducky_get_number(line, &bad_usb->repeat_cnt); + if((!state) || (bad_usb->repeat_cnt == 0)) { + return ducky_error(bad_usb, "Invalid number %s", line); + } + return 0; +} + +static int32_t ducky_fnc_sysrq(BadUsbScript* bad_usb, const char* line, int32_t param) { + UNUSED(param); + + line = &line[ducky_get_command_len(line) + 1]; + uint16_t key = ducky_get_keycode(bad_usb, line, true); + bad_usb->hid->kb_press(bad_usb->hid_inst, KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN); + bad_usb->hid->kb_press(bad_usb->hid_inst, key); + bad_usb->hid->release_all(bad_usb->hid_inst); + return 0; +} + +static int32_t ducky_fnc_altchar(BadUsbScript* bad_usb, const char* line, int32_t param) { + UNUSED(param); + + line = &line[ducky_get_command_len(line) + 1]; + ducky_numlock_on(bad_usb); + bool state = ducky_altchar(bad_usb, line); + if(!state) { + return ducky_error(bad_usb, "Invalid altchar %s", line); + } + return 0; +} + +static int32_t ducky_fnc_altstring(BadUsbScript* bad_usb, const char* line, int32_t param) { + UNUSED(param); + + line = &line[ducky_get_command_len(line) + 1]; + ducky_numlock_on(bad_usb); + bool state = ducky_altstring(bad_usb, line); + if(!state) { + return ducky_error(bad_usb, "Invalid altstring %s", line); + } + return 0; +} + +static int32_t ducky_fnc_hold(BadUsbScript* bad_usb, const char* line, int32_t param) { + UNUSED(param); + line = &line[ducky_get_command_len(line) + 1]; + + if(bad_usb->key_hold_nb > (HID_KB_MAX_KEYS - 1)) { + return ducky_error(bad_usb, "Too many keys are held"); + } + + // Handle Mouse Keys here + uint16_t key = ducky_get_mouse_keycode_by_name(line); + if(key != HID_MOUSE_NONE) { + bad_usb->key_hold_nb++; + bad_usb->hid->mouse_press(bad_usb->hid_inst, key); + return 0; + } + + // Handle Keyboard keys here + key = ducky_get_keycode(bad_usb, line, true); + if(key != HID_KEYBOARD_NONE) { + bad_usb->key_hold_nb++; + bad_usb->hid->kb_press(bad_usb->hid_inst, key); + return 0; + } + + // keyboard and mouse were none + return ducky_error(bad_usb, "Unknown keycode for %s", line); +} + +static int32_t ducky_fnc_release(BadUsbScript* bad_usb, const char* line, int32_t param) { + UNUSED(param); + line = &line[ducky_get_command_len(line) + 1]; + + if(bad_usb->key_hold_nb == 0) { + return ducky_error(bad_usb, "No keys are held"); + } + + // Handle Mouse Keys here + uint16_t key = ducky_get_mouse_keycode_by_name(line); + if(key != HID_MOUSE_NONE) { + bad_usb->key_hold_nb--; + bad_usb->hid->mouse_release(bad_usb->hid_inst, key); + return 0; + } + + //Handle Keyboard Keys here + key = ducky_get_keycode(bad_usb, line, true); + if(key != HID_KEYBOARD_NONE) { + bad_usb->key_hold_nb--; + bad_usb->hid->kb_release(bad_usb->hid_inst, key); + return 0; + } + + // keyboard and mouse were none + return ducky_error(bad_usb, "No keycode defined for %s", line); +} + +static int32_t ducky_fnc_media(BadUsbScript* bad_usb, const char* line, int32_t param) { + UNUSED(param); + + line = &line[ducky_get_command_len(line) + 1]; + uint16_t key = ducky_get_media_keycode_by_name(line); + if(key == HID_CONSUMER_UNASSIGNED) { + return ducky_error(bad_usb, "No keycode defined for %s", line); + } + bad_usb->hid->consumer_press(bad_usb->hid_inst, key); + bad_usb->hid->consumer_release(bad_usb->hid_inst, key); + return 0; +} + +static int32_t ducky_fnc_globe(BadUsbScript* bad_usb, const char* line, int32_t param) { + UNUSED(param); + + line = &line[ducky_get_command_len(line) + 1]; + uint16_t key = ducky_get_keycode(bad_usb, line, true); + if(key == HID_KEYBOARD_NONE) { + return ducky_error(bad_usb, "No keycode defined for %s", line); + } + + bad_usb->hid->consumer_press(bad_usb->hid_inst, HID_CONSUMER_FN_GLOBE); + bad_usb->hid->kb_press(bad_usb->hid_inst, key); + bad_usb->hid->kb_release(bad_usb->hid_inst, key); + bad_usb->hid->consumer_release(bad_usb->hid_inst, HID_CONSUMER_FN_GLOBE); + return 0; +} + +static int32_t ducky_fnc_waitforbutton(BadUsbScript* bad_usb, const char* line, int32_t param) { + UNUSED(param); + UNUSED(bad_usb); + UNUSED(line); + + return SCRIPT_STATE_WAIT_FOR_BTN; +} + +static int32_t ducky_fnc_mouse_scroll(BadUsbScript* bad_usb, const char* line, int32_t param) { + UNUSED(param); + + line = &line[strcspn(line, " ") + 1]; + int32_t mouse_scroll_dist = 0; + + if(strint_to_int32(line, NULL, &mouse_scroll_dist, 10) != StrintParseNoError) { + return ducky_error(bad_usb, "Invalid Number %s", line); + } + + bad_usb->hid->mouse_scroll(bad_usb->hid_inst, mouse_scroll_dist); + + return 0; +} + +static int32_t ducky_fnc_mouse_move(BadUsbScript* bad_usb, const char* line, int32_t param) { + UNUSED(param); + + line = &line[strcspn(line, " ") + 1]; + int32_t mouse_move_x = 0; + int32_t mouse_move_y = 0; + + if(strint_to_int32(line, NULL, &mouse_move_x, 10) != StrintParseNoError) { + return ducky_error(bad_usb, "Invalid Number %s", line); + } + + line = &line[strcspn(line, " ") + 1]; + + if(strint_to_int32(line, NULL, &mouse_move_y, 10) != StrintParseNoError) { + return ducky_error(bad_usb, "Invalid Number %s", line); + } + + bad_usb->hid->mouse_move(bad_usb->hid_inst, mouse_move_x, mouse_move_y); + + return 0; +} + +static const DuckyCmd ducky_commands[] = { + {"REM", NULL, -1}, + {"ID", NULL, -1}, + {"BT_ID", NULL, -1}, + {"BLE_ID", NULL, -1}, + {"DELAY", ducky_fnc_delay, -1}, + {"STRING", ducky_fnc_string, 0}, + {"STRINGLN", ducky_fnc_string, 1}, + {"DEFAULT_DELAY", ducky_fnc_defdelay, -1}, + {"DEFAULTDELAY", ducky_fnc_defdelay, -1}, + {"STRINGDELAY", ducky_fnc_strdelay, -1}, + {"STRING_DELAY", ducky_fnc_strdelay, -1}, + {"DEFAULT_STRING_DELAY", ducky_fnc_defstrdelay, -1}, + {"DEFAULTSTRINGDELAY", ducky_fnc_defstrdelay, -1}, + {"REPEAT", ducky_fnc_repeat, -1}, + {"SYSRQ", ducky_fnc_sysrq, -1}, + {"ALTCHAR", ducky_fnc_altchar, -1}, + {"ALTSTRING", ducky_fnc_altstring, -1}, + {"ALTCODE", ducky_fnc_altstring, -1}, + {"HOLD", ducky_fnc_hold, -1}, + {"RELEASE", ducky_fnc_release, -1}, + {"WAIT_FOR_BUTTON_PRESS", ducky_fnc_waitforbutton, -1}, + {"MEDIA", ducky_fnc_media, -1}, + {"GLOBE", ducky_fnc_globe, -1}, + {"MOUSEMOVE", ducky_fnc_mouse_move, -1}, + {"MOUSE_MOVE", ducky_fnc_mouse_move, -1}, + {"MOUSESCROLL", ducky_fnc_mouse_scroll, -1}, + {"MOUSE_SCROLL", ducky_fnc_mouse_scroll, -1}, +}; + +#define TAG "BadUsb" + +#define WORKER_TAG TAG "Worker" + +int32_t ducky_execute_cmd(BadUsbScript* bad_usb, const char* line) { + size_t cmd_word_len = strcspn(line, " "); + for(size_t i = 0; i < COUNT_OF(ducky_commands); i++) { + size_t cmd_compare_len = strlen(ducky_commands[i].name); + + if(cmd_compare_len != cmd_word_len) { + continue; + } + + if(strncmp(line, ducky_commands[i].name, cmd_compare_len) == 0) { + if(ducky_commands[i].callback == NULL) { + return 0; + } else { + return (ducky_commands[i].callback)(bad_usb, line, ducky_commands[i].param); + } + } + } + + return SCRIPT_STATE_CMD_UNKNOWN; +} diff --git a/applications/main/bad_kb/helpers/ducky_script_i.h b/applications/main/bad_usb/helpers/ducky_script_i.h similarity index 56% rename from applications/main/bad_kb/helpers/ducky_script_i.h rename to applications/main/bad_usb/helpers/ducky_script_i.h index d4d5fab39..673c1c248 100644 --- a/applications/main/bad_kb/helpers/ducky_script_i.h +++ b/applications/main/bad_usb/helpers/ducky_script_i.h @@ -7,6 +7,7 @@ extern "C" { #include #include #include "ducky_script.h" +#include "bad_usb_hid.h" #define SCRIPT_STATE_ERROR (-1) #define SCRIPT_STATE_END (-2) @@ -17,12 +18,19 @@ extern "C" { #define FILE_BUFFER_LEN 16 -struct BadKbScript { +#define HID_MOUSE_INVALID 0 +#define HID_MOUSE_NONE 0 + +struct BadUsbScript { + BadUsbHidInterface* interface; + BadUsbHidConfig* hid_cfg; + bool load_id_cfg; + const BadUsbHidApi* hid; + void* hid_inst; FuriThread* thread; - BadKbState st; + BadUsbState st; FuriString* file_path; - FuriString* keyboard_layout; uint8_t file_buf[FILE_BUFFER_LEN + 1]; uint8_t buf_start; uint8_t buf_len; @@ -40,12 +48,9 @@ struct BadKbScript { FuriString* string_print; size_t string_print_pos; - - Bt* bt; - BadKbApp* app; }; -uint16_t ducky_get_keycode(BadKbScript* bad_kb, const char* param, bool accept_chars); +uint16_t ducky_get_keycode(BadUsbScript* bad_usb, const char* param, bool accept_chars); uint32_t ducky_get_command_len(const char* line); @@ -55,21 +60,23 @@ uint16_t ducky_get_keycode_by_name(const char* param); uint16_t ducky_get_media_keycode_by_name(const char* param); +uint8_t ducky_get_mouse_keycode_by_name(const char* param); + bool ducky_get_number(const char* param, uint32_t* val); -void ducky_numlock_on(BadKbScript* bad_kb); +void ducky_numlock_on(BadUsbScript* bad_usb); -bool ducky_numpad_press(BadKbScript* bad_kb, const char num); +bool ducky_numpad_press(BadUsbScript* bad_usb, const char num); -bool ducky_altchar(BadKbScript* bad_kb, const char* charcode); +bool ducky_altchar(BadUsbScript* bad_usb, const char* charcode); -bool ducky_altstring(BadKbScript* bad_kb, const char* param); +bool ducky_altstring(BadUsbScript* bad_usb, const char* param); -bool ducky_string(BadKbScript* bad_kb, const char* param); +bool ducky_string(BadUsbScript* bad_usb, const char* param); -int32_t ducky_execute_cmd(BadKbScript* bad_kb, const char* line); +int32_t ducky_execute_cmd(BadUsbScript* bad_usb, const char* line); -int32_t ducky_error(BadKbScript* bad_kb, const char* text, ...); +int32_t ducky_error(BadUsbScript* bad_usb, const char* text, ...); #ifdef __cplusplus } diff --git a/applications/main/bad_kb/helpers/ducky_script_keycodes.c b/applications/main/bad_usb/helpers/ducky_script_keycodes.c similarity index 84% rename from applications/main/bad_kb/helpers/ducky_script_keycodes.c rename to applications/main/bad_usb/helpers/ducky_script_keycodes.c index 290618c13..7dd2e4d16 100644 --- a/applications/main/bad_kb/helpers/ducky_script_keycodes.c +++ b/applications/main/bad_usb/helpers/ducky_script_keycodes.c @@ -108,6 +108,17 @@ static const DuckyKey ducky_media_keys[] = { {"BRIGHT_DOWN", HID_CONSUMER_BRIGHTNESS_DECREMENT}, }; +static const DuckyKey ducky_mouse_keys[] = { + {"LEFTCLICK", HID_MOUSE_BTN_LEFT}, + {"LEFT_CLICK", HID_MOUSE_BTN_LEFT}, + {"RIGHTCLICK", HID_MOUSE_BTN_RIGHT}, + {"RIGHT_CLICK", HID_MOUSE_BTN_RIGHT}, + {"MIDDLECLICK", HID_MOUSE_BTN_WHEEL}, + {"MIDDLE_CLICK", HID_MOUSE_BTN_WHEEL}, + {"WHEELCLICK", HID_MOUSE_BTN_WHEEL}, + {"WHEEL_CLICK", HID_MOUSE_BTN_WHEEL}, +}; + uint16_t ducky_get_keycode_by_name(const char* param) { for(size_t i = 0; i < COUNT_OF(ducky_keys); i++) { size_t key_cmd_len = strlen(ducky_keys[i].name); @@ -131,3 +142,15 @@ uint16_t ducky_get_media_keycode_by_name(const char* param) { return HID_CONSUMER_UNASSIGNED; } + +uint8_t ducky_get_mouse_keycode_by_name(const char* param) { + for(size_t i = 0; i < COUNT_OF(ducky_mouse_keys); i++) { + size_t key_cmd_len = strlen(ducky_mouse_keys[i].name); + if((strncmp(param, ducky_mouse_keys[i].name, key_cmd_len) == 0) && + (ducky_is_line_end(param[key_cmd_len]))) { + return ducky_mouse_keys[i].keycode; + } + } + + return HID_MOUSE_INVALID; +} diff --git a/applications/main/bad_kb/icon.png b/applications/main/bad_usb/icon.png similarity index 100% rename from applications/main/bad_kb/icon.png rename to applications/main/bad_usb/icon.png diff --git a/applications/main/bad_kb/resources/badusb/Demos/Install_qFlipper_gnome.txt b/applications/main/bad_usb/resources/badusb/Demos/Install_qFlipper_gnome.txt similarity index 100% rename from applications/main/bad_kb/resources/badusb/Demos/Install_qFlipper_gnome.txt rename to applications/main/bad_usb/resources/badusb/Demos/Install_qFlipper_gnome.txt diff --git a/applications/main/bad_kb/resources/badusb/Demos/Install_qFlipper_macOS.txt b/applications/main/bad_usb/resources/badusb/Demos/Install_qFlipper_macOS.txt similarity index 100% rename from applications/main/bad_kb/resources/badusb/Demos/Install_qFlipper_macOS.txt rename to applications/main/bad_usb/resources/badusb/Demos/Install_qFlipper_macOS.txt diff --git a/applications/main/bad_kb/resources/badusb/Demos/Install_qFlipper_windows.txt b/applications/main/bad_usb/resources/badusb/Demos/Install_qFlipper_windows.txt similarity index 100% rename from applications/main/bad_kb/resources/badusb/Demos/Install_qFlipper_windows.txt rename to applications/main/bad_usb/resources/badusb/Demos/Install_qFlipper_windows.txt diff --git a/applications/main/bad_kb/resources/badusb/Demos/demo_android.txt b/applications/main/bad_usb/resources/badusb/Demos/demo_android.txt similarity index 100% rename from applications/main/bad_kb/resources/badusb/Demos/demo_android.txt rename to applications/main/bad_usb/resources/badusb/Demos/demo_android.txt diff --git a/applications/main/bad_kb/resources/badusb/Demos/demo_chromeos.txt b/applications/main/bad_usb/resources/badusb/Demos/demo_chromeos.txt similarity index 100% rename from applications/main/bad_kb/resources/badusb/Demos/demo_chromeos.txt rename to applications/main/bad_usb/resources/badusb/Demos/demo_chromeos.txt diff --git a/applications/main/bad_kb/resources/badusb/Demos/demo_gnome.txt b/applications/main/bad_usb/resources/badusb/Demos/demo_gnome.txt similarity index 100% rename from applications/main/bad_kb/resources/badusb/Demos/demo_gnome.txt rename to applications/main/bad_usb/resources/badusb/Demos/demo_gnome.txt diff --git a/applications/main/bad_kb/resources/badusb/Demos/demo_ios.txt b/applications/main/bad_usb/resources/badusb/Demos/demo_ios.txt similarity index 100% rename from applications/main/bad_kb/resources/badusb/Demos/demo_ios.txt rename to applications/main/bad_usb/resources/badusb/Demos/demo_ios.txt diff --git a/applications/main/bad_kb/resources/badusb/Demos/demo_macos.txt b/applications/main/bad_usb/resources/badusb/Demos/demo_macos.txt similarity index 100% rename from applications/main/bad_kb/resources/badusb/Demos/demo_macos.txt rename to applications/main/bad_usb/resources/badusb/Demos/demo_macos.txt diff --git a/applications/main/bad_kb/resources/badusb/Demos/demo_windows.txt b/applications/main/bad_usb/resources/badusb/Demos/demo_windows.txt similarity index 100% rename from applications/main/bad_kb/resources/badusb/Demos/demo_windows.txt rename to applications/main/bad_usb/resources/badusb/Demos/demo_windows.txt diff --git a/applications/main/bad_usb/resources/badusb/Demos/test_mouse.txt b/applications/main/bad_usb/resources/badusb/Demos/test_mouse.txt new file mode 100644 index 000000000..97391cf17 --- /dev/null +++ b/applications/main/bad_usb/resources/badusb/Demos/test_mouse.txt @@ -0,0 +1,46 @@ +ID 1234:abcd Generic:USB Keyboard +REM Declare ourselves as a generic usb keyboard +REM You can override this to use something else +REM Check the `lsusb` command to know your own devices IDs + +DEFAULT_DELAY 200 +DEFAULT_STRING_DELAY 100 + +DELAY 1000 + +REM Test all mouse functions +LEFTCLICK +RIGHTCLICK +MIDDLECLICK + +DELAY 1000 + +MOUSEMOVE -10 0 +REPEAT 20 +MOUSEMOVE 0 10 +REPEAT 20 +MOUSEMOVE 10 0 +REPEAT 20 +MOUSEMOVE 0 -10 +REPEAT 20 + +DELAY 1000 + +MOUSESCROLL -50 +MOUSESCROLL 50 + +DELAY 1000 + +REM Verify Mouse hold working +HOLD LEFTCLICK +DELAY 2000 +RELEASE LEFTCLICK + +DELAY 1000 + +REM Verify KB hold working +HOLD M +DELAY 2000 +RELEASE M + +ENTER \ No newline at end of file diff --git a/applications/main/bad_kb/resources/badusb/assets/layouts/ba-BA.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/ba-BA.kl similarity index 100% rename from applications/main/bad_kb/resources/badusb/assets/layouts/ba-BA.kl rename to applications/main/bad_usb/resources/badusb/assets/layouts/ba-BA.kl diff --git a/applications/main/bad_kb/resources/badusb/assets/layouts/cz_CS.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/cz_CS.kl similarity index 100% rename from applications/main/bad_kb/resources/badusb/assets/layouts/cz_CS.kl rename to applications/main/bad_usb/resources/badusb/assets/layouts/cz_CS.kl diff --git a/applications/main/bad_kb/resources/badusb/assets/layouts/da-DA.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/da-DA.kl similarity index 100% rename from applications/main/bad_kb/resources/badusb/assets/layouts/da-DA.kl rename to applications/main/bad_usb/resources/badusb/assets/layouts/da-DA.kl diff --git a/applications/main/bad_kb/resources/badusb/assets/layouts/de-CH.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/de-CH.kl similarity index 100% rename from applications/main/bad_kb/resources/badusb/assets/layouts/de-CH.kl rename to applications/main/bad_usb/resources/badusb/assets/layouts/de-CH.kl diff --git a/applications/main/bad_kb/resources/badusb/assets/layouts/de-DE-mac.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/de-DE-mac.kl similarity index 100% rename from applications/main/bad_kb/resources/badusb/assets/layouts/de-DE-mac.kl rename to applications/main/bad_usb/resources/badusb/assets/layouts/de-DE-mac.kl diff --git a/applications/main/bad_kb/resources/badusb/assets/layouts/de-DE.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/de-DE.kl similarity index 100% rename from applications/main/bad_kb/resources/badusb/assets/layouts/de-DE.kl rename to applications/main/bad_usb/resources/badusb/assets/layouts/de-DE.kl diff --git a/applications/main/bad_kb/resources/badusb/assets/layouts/dvorak.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/dvorak.kl similarity index 100% rename from applications/main/bad_kb/resources/badusb/assets/layouts/dvorak.kl rename to applications/main/bad_usb/resources/badusb/assets/layouts/dvorak.kl diff --git a/applications/main/bad_kb/resources/badusb/assets/layouts/en-UK.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/en-UK.kl similarity index 100% rename from applications/main/bad_kb/resources/badusb/assets/layouts/en-UK.kl rename to applications/main/bad_usb/resources/badusb/assets/layouts/en-UK.kl diff --git a/applications/main/bad_kb/resources/badusb/assets/layouts/en-US.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/en-US.kl similarity index 100% rename from applications/main/bad_kb/resources/badusb/assets/layouts/en-US.kl rename to applications/main/bad_usb/resources/badusb/assets/layouts/en-US.kl diff --git a/applications/main/bad_kb/resources/badusb/assets/layouts/es-ES.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/es-ES.kl similarity index 100% rename from applications/main/bad_kb/resources/badusb/assets/layouts/es-ES.kl rename to applications/main/bad_usb/resources/badusb/assets/layouts/es-ES.kl diff --git a/applications/main/bad_kb/resources/badusb/assets/layouts/es-LA.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/es-LA.kl similarity index 100% rename from applications/main/bad_kb/resources/badusb/assets/layouts/es-LA.kl rename to applications/main/bad_usb/resources/badusb/assets/layouts/es-LA.kl diff --git a/applications/main/bad_kb/resources/badusb/assets/layouts/fi-FI.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/fi-FI.kl similarity index 100% rename from applications/main/bad_kb/resources/badusb/assets/layouts/fi-FI.kl rename to applications/main/bad_usb/resources/badusb/assets/layouts/fi-FI.kl diff --git a/applications/main/bad_kb/resources/badusb/assets/layouts/fr-BE.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/fr-BE.kl similarity index 100% rename from applications/main/bad_kb/resources/badusb/assets/layouts/fr-BE.kl rename to applications/main/bad_usb/resources/badusb/assets/layouts/fr-BE.kl diff --git a/applications/main/bad_kb/resources/badusb/assets/layouts/fr-CA.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/fr-CA.kl similarity index 100% rename from applications/main/bad_kb/resources/badusb/assets/layouts/fr-CA.kl rename to applications/main/bad_usb/resources/badusb/assets/layouts/fr-CA.kl diff --git a/applications/main/bad_kb/resources/badusb/assets/layouts/fr-CH.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/fr-CH.kl similarity index 100% rename from applications/main/bad_kb/resources/badusb/assets/layouts/fr-CH.kl rename to applications/main/bad_usb/resources/badusb/assets/layouts/fr-CH.kl diff --git a/applications/main/bad_kb/resources/badusb/assets/layouts/fr-FR-mac.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/fr-FR-mac.kl similarity index 100% rename from applications/main/bad_kb/resources/badusb/assets/layouts/fr-FR-mac.kl rename to applications/main/bad_usb/resources/badusb/assets/layouts/fr-FR-mac.kl diff --git a/applications/main/bad_kb/resources/badusb/assets/layouts/fr-FR.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/fr-FR.kl similarity index 100% rename from applications/main/bad_kb/resources/badusb/assets/layouts/fr-FR.kl rename to applications/main/bad_usb/resources/badusb/assets/layouts/fr-FR.kl diff --git a/applications/main/bad_kb/resources/badusb/assets/layouts/hr-HR.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/hr-HR.kl similarity index 100% rename from applications/main/bad_kb/resources/badusb/assets/layouts/hr-HR.kl rename to applications/main/bad_usb/resources/badusb/assets/layouts/hr-HR.kl diff --git a/applications/main/bad_kb/resources/badusb/assets/layouts/hu-HU.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/hu-HU.kl similarity index 100% rename from applications/main/bad_kb/resources/badusb/assets/layouts/hu-HU.kl rename to applications/main/bad_usb/resources/badusb/assets/layouts/hu-HU.kl diff --git a/applications/main/bad_kb/resources/badusb/assets/layouts/it-IT-mac.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/it-IT-mac.kl similarity index 100% rename from applications/main/bad_kb/resources/badusb/assets/layouts/it-IT-mac.kl rename to applications/main/bad_usb/resources/badusb/assets/layouts/it-IT-mac.kl diff --git a/applications/main/bad_kb/resources/badusb/assets/layouts/it-IT.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/it-IT.kl similarity index 100% rename from applications/main/bad_kb/resources/badusb/assets/layouts/it-IT.kl rename to applications/main/bad_usb/resources/badusb/assets/layouts/it-IT.kl diff --git a/applications/main/bad_kb/resources/badusb/assets/layouts/nb-NO.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/nb-NO.kl similarity index 100% rename from applications/main/bad_kb/resources/badusb/assets/layouts/nb-NO.kl rename to applications/main/bad_usb/resources/badusb/assets/layouts/nb-NO.kl diff --git a/applications/main/bad_kb/resources/badusb/assets/layouts/nl-NL.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/nl-NL.kl similarity index 100% rename from applications/main/bad_kb/resources/badusb/assets/layouts/nl-NL.kl rename to applications/main/bad_usb/resources/badusb/assets/layouts/nl-NL.kl diff --git a/applications/main/bad_kb/resources/badusb/assets/layouts/pt-BR.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/pt-BR.kl similarity index 100% rename from applications/main/bad_kb/resources/badusb/assets/layouts/pt-BR.kl rename to applications/main/bad_usb/resources/badusb/assets/layouts/pt-BR.kl diff --git a/applications/main/bad_kb/resources/badusb/assets/layouts/pt-PT.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/pt-PT.kl similarity index 100% rename from applications/main/bad_kb/resources/badusb/assets/layouts/pt-PT.kl rename to applications/main/bad_usb/resources/badusb/assets/layouts/pt-PT.kl diff --git a/applications/main/bad_kb/resources/badusb/assets/layouts/si-SI.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/si-SI.kl similarity index 100% rename from applications/main/bad_kb/resources/badusb/assets/layouts/si-SI.kl rename to applications/main/bad_usb/resources/badusb/assets/layouts/si-SI.kl diff --git a/applications/main/bad_kb/resources/badusb/assets/layouts/sk-SK.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/sk-SK.kl similarity index 100% rename from applications/main/bad_kb/resources/badusb/assets/layouts/sk-SK.kl rename to applications/main/bad_usb/resources/badusb/assets/layouts/sk-SK.kl diff --git a/applications/main/bad_kb/resources/badusb/assets/layouts/sv-SE.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/sv-SE.kl similarity index 100% rename from applications/main/bad_kb/resources/badusb/assets/layouts/sv-SE.kl rename to applications/main/bad_usb/resources/badusb/assets/layouts/sv-SE.kl diff --git a/applications/main/bad_kb/resources/badusb/assets/layouts/tr-TR.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/tr-TR.kl similarity index 100% rename from applications/main/bad_kb/resources/badusb/assets/layouts/tr-TR.kl rename to applications/main/bad_usb/resources/badusb/assets/layouts/tr-TR.kl diff --git a/applications/main/bad_usb/scenes/bad_usb_scene.c b/applications/main/bad_usb/scenes/bad_usb_scene.c new file mode 100644 index 000000000..03c7c4471 --- /dev/null +++ b/applications/main/bad_usb/scenes/bad_usb_scene.c @@ -0,0 +1,30 @@ +#include "bad_usb_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const bad_usb_scene_on_enter_handlers[])(void*) = { +#include "bad_usb_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 bad_usb_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "bad_usb_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 bad_usb_scene_on_exit_handlers[])(void* context) = { +#include "bad_usb_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers bad_usb_scene_handlers = { + .on_enter_handlers = bad_usb_scene_on_enter_handlers, + .on_event_handlers = bad_usb_scene_on_event_handlers, + .on_exit_handlers = bad_usb_scene_on_exit_handlers, + .scene_num = BadUsbSceneNum, +}; diff --git a/applications/main/bad_kb/scenes/bad_kb_scene.h b/applications/main/bad_usb/scenes/bad_usb_scene.h similarity index 68% rename from applications/main/bad_kb/scenes/bad_kb_scene.h rename to applications/main/bad_usb/scenes/bad_usb_scene.h index 82db02873..68a753210 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene.h +++ b/applications/main/bad_usb/scenes/bad_usb_scene.h @@ -3,27 +3,27 @@ #include // Generate scene id and total number -#define ADD_SCENE(prefix, name, id) BadKbScene##id, +#define ADD_SCENE(prefix, name, id) BadUsbScene##id, typedef enum { -#include "bad_kb_scene_config.h" - BadKbSceneNum, -} BadKbScene; +#include "bad_usb_scene_config.h" + BadUsbSceneNum, +} BadUsbScene; #undef ADD_SCENE -extern const SceneManagerHandlers bad_kb_scene_handlers; +extern const SceneManagerHandlers bad_usb_scene_handlers; // Generate scene on_enter handlers declaration #define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); -#include "bad_kb_scene_config.h" +#include "bad_usb_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 "bad_kb_scene_config.h" +#include "bad_usb_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 "bad_kb_scene_config.h" +#include "bad_usb_scene_config.h" #undef ADD_SCENE diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config.c b/applications/main/bad_usb/scenes/bad_usb_scene_config.c new file mode 100644 index 000000000..cacc5b5c2 --- /dev/null +++ b/applications/main/bad_usb/scenes/bad_usb_scene_config.c @@ -0,0 +1,212 @@ +#include "../bad_usb_app_i.h" + +enum ConfigIndex { + ConfigIndexKeyboardLayout, + ConfigIndexConnection, +}; + +enum ConfigIndexBle { + ConfigIndexBleRemember = ConfigIndexConnection + 1, + ConfigIndexBlePairing, + ConfigIndexBleDeviceName, + ConfigIndexBleMacAddress, + ConfigIndexBleRandomizeMac, + ConfigIndexBleUnpair, +}; + +enum ConfigIndexUsb { + ConfigIndexUsbManufacturer = ConfigIndexConnection + 1, + ConfigIndexUsbProductName, + ConfigIndexUsbVidPid, + ConfigIndexUsbRandomizeVidPid, +}; + +void bad_usb_scene_config_connection_callback(VariableItem* item) { + BadUsbApp* bad_usb = variable_item_get_context(item); + bad_usb_set_interface( + bad_usb, + bad_usb->interface == BadUsbHidInterfaceBle ? BadUsbHidInterfaceUsb : + BadUsbHidInterfaceBle); + variable_item_set_current_value_text( + item, bad_usb->interface == BadUsbHidInterfaceBle ? "BLE" : "USB"); + view_dispatcher_send_custom_event(bad_usb->view_dispatcher, ConfigIndexConnection); +} + +void bad_usb_scene_config_ble_remember_callback(VariableItem* item) { + BadUsbApp* bad_usb = variable_item_get_context(item); + bool value = variable_item_get_current_value_index(item); + // Apply to current script config + bad_usb->script_hid_cfg.ble.bonding = value; + // Set in user config to save in settings file + bad_usb->user_hid_cfg.ble.bonding = value; + variable_item_set_current_value_text(item, value ? "ON" : "OFF"); +} + +const char* const ble_pairing_names[GapPairingCount] = { + "YesNo", + "PIN Type", + "PIN Y/N", +}; +void bad_usb_scene_config_ble_pairing_callback(VariableItem* item) { + BadUsbApp* bad_usb = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + // Apply to current script config + bad_usb->script_hid_cfg.ble.pairing = index; + // Set in user config to save in settings file + bad_usb->user_hid_cfg.ble.pairing = index; + variable_item_set_current_value_text(item, ble_pairing_names[index]); +} + +void bad_usb_scene_config_select_callback(void* context, uint32_t index) { + BadUsbApp* bad_usb = context; + + view_dispatcher_send_custom_event(bad_usb->view_dispatcher, index); +} + +static void draw_menu(BadUsbApp* bad_usb) { + VariableItemList* var_item_list = bad_usb->var_item_list; + VariableItem* item; + + variable_item_list_reset(var_item_list); + + variable_item_list_add(var_item_list, "Keyboard Layout (global)", 0, NULL, NULL); + + item = variable_item_list_add( + var_item_list, "Connection", 2, bad_usb_scene_config_connection_callback, bad_usb); + variable_item_set_current_value_index(item, bad_usb->interface == BadUsbHidInterfaceBle); + variable_item_set_current_value_text( + item, bad_usb->interface == BadUsbHidInterfaceBle ? "BLE" : "USB"); + + if(bad_usb->interface == BadUsbHidInterfaceBle) { + BleProfileHidParams* ble_hid_cfg = &bad_usb->script_hid_cfg.ble; + + item = variable_item_list_add( + var_item_list, "BLE Remember", 2, bad_usb_scene_config_ble_remember_callback, bad_usb); + variable_item_set_current_value_index(item, ble_hid_cfg->bonding); + variable_item_set_current_value_text(item, ble_hid_cfg->bonding ? "ON" : "OFF"); + + item = variable_item_list_add( + var_item_list, + "BLE Pairing", + GapPairingCount, + bad_usb_scene_config_ble_pairing_callback, + bad_usb); + variable_item_set_current_value_index(item, ble_hid_cfg->pairing); + variable_item_set_current_value_text(item, ble_pairing_names[ble_hid_cfg->pairing]); + + variable_item_list_add(var_item_list, "BLE Device Name", 0, NULL, NULL); + + variable_item_list_add(var_item_list, "BLE MAC Address", 0, NULL, NULL); + + variable_item_list_add(var_item_list, "Randomize BLE MAC", 0, NULL, NULL); + + variable_item_list_add(var_item_list, "Remove BLE Pairing", 0, NULL, NULL); + } else { + variable_item_list_add(var_item_list, "USB Manufacturer", 0, NULL, NULL); + + variable_item_list_add(var_item_list, "USB Product Name", 0, NULL, NULL); + + variable_item_list_add(var_item_list, "USB VID and PID", 0, NULL, NULL); + + variable_item_list_add(var_item_list, "Randomize USB VID:PID", 0, NULL, NULL); + } +} + +void bad_usb_scene_config_on_enter(void* context) { + BadUsbApp* bad_usb = context; + VariableItemList* var_item_list = bad_usb->var_item_list; + + variable_item_list_set_enter_callback( + var_item_list, bad_usb_scene_config_select_callback, bad_usb); + draw_menu(bad_usb); + variable_item_list_set_selected_item( + var_item_list, scene_manager_get_scene_state(bad_usb->scene_manager, BadUsbSceneConfig)); + + view_dispatcher_switch_to_view(bad_usb->view_dispatcher, BadUsbAppViewConfig); +} + +bool bad_usb_scene_config_on_event(void* context, SceneManagerEvent event) { + BadUsbApp* bad_usb = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(bad_usb->scene_manager, BadUsbSceneConfig, event.event); + consumed = true; + switch(event.event) { + case ConfigIndexKeyboardLayout: + scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigLayout); + break; + case ConfigIndexConnection: + // Refresh default values for new interface + const BadUsbHidApi* hid = bad_usb_hid_get_interface(bad_usb->interface); + hid->adjust_config(&bad_usb->script_hid_cfg); + // Redraw menu with new interface options + draw_menu(bad_usb); + break; + default: + break; + } + if(bad_usb->interface == BadUsbHidInterfaceBle) { + switch(event.event) { + case ConfigIndexBleDeviceName: + scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigBleName); + break; + case ConfigIndexBleMacAddress: + scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigBleMac); + break; + case ConfigIndexBleRandomizeMac: + // Apply to current script config + furi_hal_random_fill_buf( + bad_usb->script_hid_cfg.ble.mac, sizeof(bad_usb->script_hid_cfg.ble.mac)); + // Set in user config to save in settings file + memcpy( + bad_usb->user_hid_cfg.ble.mac, + bad_usb->script_hid_cfg.ble.mac, + sizeof(bad_usb->user_hid_cfg.ble.mac)); + break; + case ConfigIndexBleUnpair: + scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfirmUnpair); + break; + default: + break; + } + } else { + switch(event.event) { + case ConfigIndexUsbManufacturer: + scene_manager_set_scene_state( + bad_usb->scene_manager, BadUsbSceneConfigUsbName, true); + scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigUsbName); + break; + case ConfigIndexUsbProductName: + scene_manager_set_scene_state( + bad_usb->scene_manager, BadUsbSceneConfigUsbName, false); + scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigUsbName); + break; + case ConfigIndexUsbVidPid: + scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigUsbVidPid); + break; + case ConfigIndexUsbRandomizeVidPid: + furi_hal_random_fill_buf( + (void*)bad_usb->usb_vidpid_buf, sizeof(bad_usb->usb_vidpid_buf)); + // Apply to current script config + bad_usb->script_hid_cfg.usb.vid = bad_usb->usb_vidpid_buf[0]; + bad_usb->script_hid_cfg.usb.pid = bad_usb->usb_vidpid_buf[1]; + // Set in user config to save in settings file + bad_usb->user_hid_cfg.usb.vid = bad_usb->script_hid_cfg.usb.vid; + bad_usb->user_hid_cfg.usb.pid = bad_usb->script_hid_cfg.usb.pid; + break; + default: + break; + } + } + } + + return consumed; +} + +void bad_usb_scene_config_on_exit(void* context) { + BadUsbApp* bad_usb = context; + VariableItemList* var_item_list = bad_usb->var_item_list; + + variable_item_list_reset(var_item_list); +} diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config.h b/applications/main/bad_usb/scenes/bad_usb_scene_config.h new file mode 100644 index 000000000..4c989d681 --- /dev/null +++ b/applications/main/bad_usb/scenes/bad_usb_scene_config.h @@ -0,0 +1,11 @@ +ADD_SCENE(bad_usb, file_select, FileSelect) +ADD_SCENE(bad_usb, work, Work) +ADD_SCENE(bad_usb, error, Error) +ADD_SCENE(bad_usb, config, Config) +ADD_SCENE(bad_usb, config_layout, ConfigLayout) +ADD_SCENE(bad_usb, config_ble_name, ConfigBleName) +ADD_SCENE(bad_usb, config_ble_mac, ConfigBleMac) +ADD_SCENE(bad_usb, config_usb_name, ConfigUsbName) +ADD_SCENE(bad_usb, config_usb_vidpid, ConfigUsbVidPid) +ADD_SCENE(bad_usb, confirm_unpair, ConfirmUnpair) +ADD_SCENE(bad_usb, unpair_done, UnpairDone) diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config_ble_mac.c b/applications/main/bad_usb/scenes/bad_usb_scene_config_ble_mac.c new file mode 100644 index 000000000..42f742106 --- /dev/null +++ b/applications/main/bad_usb/scenes/bad_usb_scene_config_ble_mac.c @@ -0,0 +1,71 @@ +#include "../bad_usb_app_i.h" + +enum ByteInputResult { + ByteInputResultOk, +}; + +static void reverse_mac_addr(uint8_t mac_addr[GAP_MAC_ADDR_SIZE]) { + uint8_t tmp; + for(size_t i = 0; i < GAP_MAC_ADDR_SIZE / 2; i++) { + tmp = mac_addr[i]; + mac_addr[i] = mac_addr[GAP_MAC_ADDR_SIZE - 1 - i]; + mac_addr[GAP_MAC_ADDR_SIZE - 1 - i] = tmp; + } +} + +void bad_usb_scene_config_ble_mac_byte_input_callback(void* context) { + BadUsbApp* bad_usb = context; + + view_dispatcher_send_custom_event(bad_usb->view_dispatcher, ByteInputResultOk); +} + +void bad_usb_scene_config_ble_mac_on_enter(void* context) { + BadUsbApp* bad_usb = context; + ByteInput* byte_input = bad_usb->byte_input; + + memcpy(bad_usb->ble_mac_buf, bad_usb->script_hid_cfg.ble.mac, sizeof(bad_usb->ble_mac_buf)); + reverse_mac_addr(bad_usb->ble_mac_buf); + byte_input_set_header_text(byte_input, "Set BLE MAC address"); + + byte_input_set_result_callback( + byte_input, + bad_usb_scene_config_ble_mac_byte_input_callback, + NULL, + bad_usb, + bad_usb->ble_mac_buf, + sizeof(bad_usb->ble_mac_buf)); + + view_dispatcher_switch_to_view(bad_usb->view_dispatcher, BadUsbAppViewByteInput); +} + +bool bad_usb_scene_config_ble_mac_on_event(void* context, SceneManagerEvent event) { + BadUsbApp* bad_usb = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == ByteInputResultOk) { + reverse_mac_addr(bad_usb->ble_mac_buf); + // Apply to current script config + memcpy( + bad_usb->script_hid_cfg.ble.mac, + bad_usb->ble_mac_buf, + sizeof(bad_usb->script_hid_cfg.ble.mac)); + // Set in user config to save in settings file + memcpy( + bad_usb->user_hid_cfg.ble.mac, + bad_usb->ble_mac_buf, + sizeof(bad_usb->user_hid_cfg.ble.mac)); + } + scene_manager_previous_scene(bad_usb->scene_manager); + } + return consumed; +} + +void bad_usb_scene_config_ble_mac_on_exit(void* context) { + BadUsbApp* bad_usb = context; + ByteInput* byte_input = bad_usb->byte_input; + + byte_input_set_result_callback(byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(byte_input, ""); +} diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config_ble_name.c b/applications/main/bad_usb/scenes/bad_usb_scene_config_ble_name.c new file mode 100644 index 000000000..0f984f054 --- /dev/null +++ b/applications/main/bad_usb/scenes/bad_usb_scene_config_ble_name.c @@ -0,0 +1,60 @@ +#include "../bad_usb_app_i.h" + +enum TextInputResult { + TextInputResultOk, +}; + +static void bad_usb_scene_config_ble_name_text_input_callback(void* context) { + BadUsbApp* bad_usb = context; + + view_dispatcher_send_custom_event(bad_usb->view_dispatcher, TextInputResultOk); +} + +void bad_usb_scene_config_ble_name_on_enter(void* context) { + BadUsbApp* bad_usb = context; + TextInput* text_input = bad_usb->text_input; + + strlcpy( + bad_usb->ble_name_buf, bad_usb->script_hid_cfg.ble.name, sizeof(bad_usb->ble_name_buf)); + text_input_set_header_text(text_input, "Set BLE device name"); + + text_input_set_result_callback( + text_input, + bad_usb_scene_config_ble_name_text_input_callback, + bad_usb, + bad_usb->ble_name_buf, + sizeof(bad_usb->ble_name_buf), + true); + + view_dispatcher_switch_to_view(bad_usb->view_dispatcher, BadUsbAppViewTextInput); +} + +bool bad_usb_scene_config_ble_name_on_event(void* context, SceneManagerEvent event) { + BadUsbApp* bad_usb = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == TextInputResultOk) { + // Apply to current script config + strlcpy( + bad_usb->script_hid_cfg.ble.name, + bad_usb->ble_name_buf, + sizeof(bad_usb->script_hid_cfg.ble.name)); + // Set in user config to save in settings file + strlcpy( + bad_usb->user_hid_cfg.ble.name, + bad_usb->ble_name_buf, + sizeof(bad_usb->user_hid_cfg.ble.name)); + } + scene_manager_previous_scene(bad_usb->scene_manager); + } + return consumed; +} + +void bad_usb_scene_config_ble_name_on_exit(void* context) { + BadUsbApp* bad_usb = context; + TextInput* text_input = bad_usb->text_input; + + text_input_reset(text_input); +} diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config_layout.c b/applications/main/bad_usb/scenes/bad_usb_scene_config_layout.c new file mode 100644 index 000000000..80ab44ab3 --- /dev/null +++ b/applications/main/bad_usb/scenes/bad_usb_scene_config_layout.c @@ -0,0 +1,45 @@ +#include "../bad_usb_app_i.h" +#include + +static bool bad_usb_layout_select(BadUsbApp* bad_usb) { + furi_assert(bad_usb); + + FuriString* predefined_path; + predefined_path = furi_string_alloc(); + if(!furi_string_empty(bad_usb->keyboard_layout)) { + furi_string_set(predefined_path, bad_usb->keyboard_layout); + } else { + furi_string_set(predefined_path, BAD_USB_APP_PATH_LAYOUT_FOLDER); + } + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options( + &browser_options, BAD_USB_APP_LAYOUT_EXTENSION, &I_keyboard_10px); + browser_options.base_path = BAD_USB_APP_PATH_LAYOUT_FOLDER; + browser_options.skip_assets = false; + + // Input events and views are managed by file_browser + bool res = dialog_file_browser_show( + bad_usb->dialogs, bad_usb->keyboard_layout, predefined_path, &browser_options); + + furi_string_free(predefined_path); + return res; +} + +void bad_usb_scene_config_layout_on_enter(void* context) { + BadUsbApp* bad_usb = context; + + bad_usb_layout_select(bad_usb); + + scene_manager_previous_scene(bad_usb->scene_manager); +} + +bool bad_usb_scene_config_layout_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void bad_usb_scene_config_layout_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config_usb_name.c b/applications/main/bad_usb/scenes/bad_usb_scene_config_usb_name.c new file mode 100644 index 000000000..5eb8fe917 --- /dev/null +++ b/applications/main/bad_usb/scenes/bad_usb_scene_config_usb_name.c @@ -0,0 +1,83 @@ +#include "../bad_usb_app_i.h" + +enum TextInputResult { + TextInputResultOk, +}; + +static void bad_usb_scene_config_usb_name_text_input_callback(void* context) { + BadUsbApp* bad_usb = context; + + view_dispatcher_send_custom_event(bad_usb->view_dispatcher, TextInputResultOk); +} + +void bad_usb_scene_config_usb_name_on_enter(void* context) { + BadUsbApp* bad_usb = context; + TextInput* text_input = bad_usb->text_input; + + if(scene_manager_get_scene_state(bad_usb->scene_manager, BadUsbSceneConfigUsbName)) { + strlcpy( + bad_usb->usb_name_buf, + bad_usb->script_hid_cfg.usb.manuf, + sizeof(bad_usb->usb_name_buf)); + text_input_set_header_text(text_input, "Set USB manufacturer name"); + } else { + strlcpy( + bad_usb->usb_name_buf, + bad_usb->script_hid_cfg.usb.product, + sizeof(bad_usb->usb_name_buf)); + text_input_set_header_text(text_input, "Set USB product name"); + } + + text_input_set_result_callback( + text_input, + bad_usb_scene_config_usb_name_text_input_callback, + bad_usb, + bad_usb->usb_name_buf, + sizeof(bad_usb->usb_name_buf), + true); + + view_dispatcher_switch_to_view(bad_usb->view_dispatcher, BadUsbAppViewTextInput); +} + +bool bad_usb_scene_config_usb_name_on_event(void* context, SceneManagerEvent event) { + BadUsbApp* bad_usb = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == TextInputResultOk) { + if(scene_manager_get_scene_state(bad_usb->scene_manager, BadUsbSceneConfigUsbName)) { + // Apply to current script config + strlcpy( + bad_usb->script_hid_cfg.usb.manuf, + bad_usb->usb_name_buf, + sizeof(bad_usb->script_hid_cfg.usb.manuf)); + // Set in user config to save in settings file + strlcpy( + bad_usb->user_hid_cfg.usb.manuf, + bad_usb->usb_name_buf, + sizeof(bad_usb->user_hid_cfg.usb.manuf)); + } else { + // Apply to current script config + strlcpy( + bad_usb->script_hid_cfg.usb.product, + bad_usb->usb_name_buf, + sizeof(bad_usb->script_hid_cfg.usb.product)); + // Set in user config to save in settings file + strlcpy( + bad_usb->user_hid_cfg.usb.product, + bad_usb->usb_name_buf, + sizeof(bad_usb->user_hid_cfg.usb.product)); + } + } + scene_manager_previous_scene(bad_usb->scene_manager); + } + return consumed; +} + +void bad_usb_scene_config_usb_name_on_exit(void* context) { + BadUsbApp* bad_usb = context; + TextInput* text_input = bad_usb->text_input; + + text_input_reset(text_input); +} diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config_usb_vidpid.c b/applications/main/bad_usb/scenes/bad_usb_scene_config_usb_vidpid.c new file mode 100644 index 000000000..ada66bb5c --- /dev/null +++ b/applications/main/bad_usb/scenes/bad_usb_scene_config_usb_vidpid.c @@ -0,0 +1,57 @@ +#include "../bad_usb_app_i.h" + +enum ByteInputResult { + ByteInputResultOk, +}; + +void bad_usb_scene_config_usb_vidpid_byte_input_callback(void* context) { + BadUsbApp* bad_usb = context; + + view_dispatcher_send_custom_event(bad_usb->view_dispatcher, ByteInputResultOk); +} + +void bad_usb_scene_config_usb_vidpid_on_enter(void* context) { + BadUsbApp* bad_usb = context; + ByteInput* byte_input = bad_usb->byte_input; + + bad_usb->usb_vidpid_buf[0] = __REVSH(bad_usb->script_hid_cfg.usb.vid); + bad_usb->usb_vidpid_buf[1] = __REVSH(bad_usb->script_hid_cfg.usb.pid); + byte_input_set_header_text(byte_input, "Set USB VID:PID"); + + byte_input_set_result_callback( + byte_input, + bad_usb_scene_config_usb_vidpid_byte_input_callback, + NULL, + bad_usb, + (void*)bad_usb->usb_vidpid_buf, + sizeof(bad_usb->usb_vidpid_buf)); + + view_dispatcher_switch_to_view(bad_usb->view_dispatcher, BadUsbAppViewByteInput); +} + +bool bad_usb_scene_config_usb_vidpid_on_event(void* context, SceneManagerEvent event) { + BadUsbApp* bad_usb = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == ByteInputResultOk) { + // Apply to current script config + bad_usb->script_hid_cfg.usb.vid = __REVSH(bad_usb->usb_vidpid_buf[0]); + bad_usb->script_hid_cfg.usb.pid = __REVSH(bad_usb->usb_vidpid_buf[1]); + // Set in user config to save in settings file + bad_usb->user_hid_cfg.usb.vid = bad_usb->script_hid_cfg.usb.vid; + bad_usb->user_hid_cfg.usb.pid = bad_usb->script_hid_cfg.usb.pid; + } + scene_manager_previous_scene(bad_usb->scene_manager); + } + return consumed; +} + +void bad_usb_scene_config_usb_vidpid_on_exit(void* context) { + BadUsbApp* bad_usb = context; + ByteInput* byte_input = bad_usb->byte_input; + + byte_input_set_result_callback(byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(byte_input, ""); +} diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_confirm_unpair.c b/applications/main/bad_usb/scenes/bad_usb_scene_confirm_unpair.c new file mode 100644 index 000000000..b8fd993e2 --- /dev/null +++ b/applications/main/bad_usb/scenes/bad_usb_scene_confirm_unpair.c @@ -0,0 +1,53 @@ +#include "../bad_usb_app_i.h" + +void bad_usb_scene_confirm_unpair_widget_callback( + GuiButtonType type, + InputType input_type, + void* context) { + UNUSED(input_type); + SceneManagerEvent event = {.type = SceneManagerEventTypeCustom, .event = type}; + bad_usb_scene_confirm_unpair_on_event(context, event); +} + +void bad_usb_scene_confirm_unpair_on_enter(void* context) { + BadUsbApp* bad_usb = context; + Widget* widget = bad_usb->widget; + + widget_add_button_element( + widget, GuiButtonTypeLeft, "Cancel", bad_usb_scene_confirm_unpair_widget_callback, context); + widget_add_button_element( + widget, + GuiButtonTypeRight, + "Unpair", + bad_usb_scene_confirm_unpair_widget_callback, + context); + + widget_add_text_box_element( + widget, 0, 0, 128, 64, AlignCenter, AlignTop, "\e#Unpair the Device?\e#\n", false); + + view_dispatcher_switch_to_view(bad_usb->view_dispatcher, BadUsbAppViewWidget); +} + +bool bad_usb_scene_confirm_unpair_on_event(void* context, SceneManagerEvent event) { + BadUsbApp* bad_usb = context; + SceneManager* scene_manager = bad_usb->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == GuiButtonTypeRight) { + scene_manager_next_scene(scene_manager, BadUsbSceneUnpairDone); + } else if(event.event == GuiButtonTypeLeft) { + scene_manager_previous_scene(scene_manager); + } + } + + return consumed; +} + +void bad_usb_scene_confirm_unpair_on_exit(void* context) { + BadUsbApp* bad_usb = context; + Widget* widget = bad_usb->widget; + + widget_reset(widget); +} diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_error.c b/applications/main/bad_usb/scenes/bad_usb_scene_error.c similarity index 50% rename from applications/main/bad_kb/scenes/bad_kb_scene_error.c rename to applications/main/bad_usb/scenes/bad_usb_scene_error.c index b0edac36c..14ecc4b80 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_error.c +++ b/applications/main/bad_usb/scenes/bad_usb_scene_error.c @@ -1,19 +1,23 @@ -#include "../bad_kb_app_i.h" +#include "../bad_usb_app_i.h" + +typedef enum { + BadUsbCustomEventErrorBack, +} BadUsbCustomEvent; static void - bad_kb_scene_error_event_callback(GuiButtonType result, InputType type, void* context) { + bad_usb_scene_error_event_callback(GuiButtonType result, InputType type, void* context) { furi_assert(context); - BadKbApp* app = context; + BadUsbApp* app = context; if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) { - view_dispatcher_send_custom_event(app->view_dispatcher, BadKbCustomEventErrorBack); + view_dispatcher_send_custom_event(app->view_dispatcher, BadUsbCustomEventErrorBack); } } -void bad_kb_scene_error_on_enter(void* context) { - BadKbApp* app = context; +void bad_usb_scene_error_on_enter(void* context) { + BadUsbApp* app = context; - if(app->error == BadKbAppErrorNoFiles) { + if(app->error == BadUsbAppErrorNoFiles) { widget_add_icon_element(app->widget, 0, 0, &I_SDQuestion_35x43); widget_add_string_multiline_element( app->widget, @@ -24,18 +28,18 @@ void bad_kb_scene_error_on_enter(void* context) { FontSecondary, "No SD card or\napp data found.\nThis app will not\nwork without\nrequired files."); widget_add_button_element( - app->widget, GuiButtonTypeLeft, "Back", bad_kb_scene_error_event_callback, app); + app->widget, GuiButtonTypeLeft, "Back", bad_usb_scene_error_event_callback, app); } - view_dispatcher_switch_to_view(app->view_dispatcher, BadKbAppViewWidget); + view_dispatcher_switch_to_view(app->view_dispatcher, BadUsbAppViewWidget); } -bool bad_kb_scene_error_on_event(void* context, SceneManagerEvent event) { - BadKbApp* app = context; +bool bad_usb_scene_error_on_event(void* context, SceneManagerEvent event) { + BadUsbApp* app = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - if(event.event == BadKbCustomEventErrorBack) { + if(event.event == BadUsbCustomEventErrorBack) { view_dispatcher_stop(app->view_dispatcher); consumed = true; } @@ -43,7 +47,7 @@ bool bad_kb_scene_error_on_event(void* context, SceneManagerEvent event) { return consumed; } -void bad_kb_scene_error_on_exit(void* context) { - BadKbApp* app = context; +void bad_usb_scene_error_on_exit(void* context) { + BadUsbApp* app = context; widget_reset(app->widget); } diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c b/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c new file mode 100644 index 000000000..7e8df4358 --- /dev/null +++ b/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c @@ -0,0 +1,71 @@ +#include "../bad_usb_app_i.h" +#include + +static bool bad_usb_file_select(BadUsbApp* bad_usb) { + furi_assert(bad_usb); + + bad_usb_app_show_loading_popup(bad_usb, true); + Storage* storage = furi_record_open(RECORD_STORAGE); + if(storage_dir_exists(storage, EXT_PATH("badkb"))) { + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_header(message, "Migrate Scripts?", 64, 0, AlignCenter, AlignTop); + dialog_message_set_buttons(message, "No", NULL, "Yes"); + dialog_message_set_text( + message, + "Momentum uses the 'badusb'\n" + "folder for compatibility.\n" + "Want to migrate from\n" + "'badkb' folder?", + 64, + 32, + AlignCenter, + AlignCenter); + DialogMessageButton res = dialog_message_show(furi_record_open(RECORD_DIALOGS), message); + dialog_message_free(message); + furi_record_close(RECORD_DIALOGS); + if(res == DialogMessageButtonRight) { + storage_common_migrate(storage, EXT_PATH("badkb"), BAD_USB_APP_BASE_FOLDER); + } + } + storage_simply_mkdir(storage, BAD_USB_APP_BASE_FOLDER); + furi_record_close(RECORD_STORAGE); + bad_usb_app_show_loading_popup(bad_usb, false); + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options( + &browser_options, BAD_USB_APP_SCRIPT_EXTENSION, &I_badusb_10px); + browser_options.base_path = BAD_USB_APP_BASE_FOLDER; + browser_options.skip_assets = true; + + // Input events and views are managed by file_browser + bool res = dialog_file_browser_show( + bad_usb->dialogs, bad_usb->file_path, bad_usb->file_path, &browser_options); + + return res; +} + +void bad_usb_scene_file_select_on_enter(void* context) { + BadUsbApp* bad_usb = context; + + if(bad_usb->bad_usb_script) { + bad_usb_script_close(bad_usb->bad_usb_script); + bad_usb->bad_usb_script = NULL; + } + + if(bad_usb_file_select(bad_usb)) { + scene_manager_set_scene_state(bad_usb->scene_manager, BadUsbSceneWork, true); + scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneWork); + } else { + view_dispatcher_stop(bad_usb->view_dispatcher); + } +} + +bool bad_usb_scene_file_select_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void bad_usb_scene_file_select_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_unpair_done.c b/applications/main/bad_usb/scenes/bad_usb_scene_unpair_done.c new file mode 100644 index 000000000..9583f9bfb --- /dev/null +++ b/applications/main/bad_usb/scenes/bad_usb_scene_unpair_done.c @@ -0,0 +1,39 @@ +#include "../bad_usb_app_i.h" + +static void bad_usb_scene_unpair_done_popup_callback(void* context) { + BadUsbApp* bad_usb = context; + scene_manager_search_and_switch_to_previous_scene(bad_usb->scene_manager, BadUsbSceneConfig); +} + +void bad_usb_scene_unpair_done_on_enter(void* context) { + BadUsbApp* bad_usb = context; + Popup* popup = bad_usb->popup; + + bad_usb_hid_ble_remove_pairing(); + + popup_set_icon(popup, 48, 4, &I_DolphinDone_80x58); + popup_set_header(popup, "Done", 20, 19, AlignLeft, AlignBottom); + popup_set_callback(popup, bad_usb_scene_unpair_done_popup_callback); + popup_set_context(popup, bad_usb); + popup_set_timeout(popup, 1500); + popup_enable_timeout(popup); + + view_dispatcher_switch_to_view(bad_usb->view_dispatcher, BadUsbAppViewPopup); +} + +bool bad_usb_scene_unpair_done_on_event(void* context, SceneManagerEvent event) { + BadUsbApp* bad_usb = context; + UNUSED(bad_usb); + UNUSED(event); + bool consumed = false; + + return consumed; +} + +void bad_usb_scene_unpair_done_on_exit(void* context) { + BadUsbApp* bad_usb = context; + Popup* popup = bad_usb->popup; + UNUSED(popup); + + popup_reset(popup); +} diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_work.c b/applications/main/bad_usb/scenes/bad_usb_scene_work.c new file mode 100644 index 000000000..cf5e94d9e --- /dev/null +++ b/applications/main/bad_usb/scenes/bad_usb_scene_work.c @@ -0,0 +1,90 @@ +#include "../helpers/ducky_script.h" +#include "../bad_usb_app_i.h" +#include "../views/bad_usb_view.h" +#include +#include "toolbox/path.h" + +void bad_usb_scene_work_button_callback(InputKey key, void* context) { + furi_assert(context); + BadUsbApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, key); +} + +bool bad_usb_scene_work_on_event(void* context, SceneManagerEvent event) { + BadUsbApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == InputKeyLeft) { + if(bad_usb_view_is_idle_state(app->bad_usb_view)) { + bad_usb_script_close(app->bad_usb_script); + app->bad_usb_script = NULL; + + scene_manager_next_scene(app->scene_manager, BadUsbSceneConfig); + } + consumed = true; + } else if(event.event == InputKeyOk) { + bad_usb_script_start_stop(app->bad_usb_script); + consumed = true; + } else if(event.event == InputKeyRight) { + if(bad_usb_view_is_idle_state(app->bad_usb_view)) { + bad_usb_set_interface( + app, + app->interface == BadUsbHidInterfaceBle ? BadUsbHidInterfaceUsb : + BadUsbHidInterfaceBle); + bad_usb_script_close(app->bad_usb_script); + app->bad_usb_script = bad_usb_script_open( + app->file_path, &app->interface, &app->script_hid_cfg, false); + bad_usb_script_set_keyboard_layout(app->bad_usb_script, app->keyboard_layout); + } else { + bad_usb_script_pause_resume(app->bad_usb_script); + } + consumed = true; + } + } else if(event.type == SceneManagerEventTypeTick) { + bad_usb_view_set_state(app->bad_usb_view, bad_usb_script_get_state(app->bad_usb_script)); + bad_usb_view_set_interface(app->bad_usb_view, app->interface); + } + return consumed; +} + +void bad_usb_scene_work_on_enter(void* context) { + BadUsbApp* app = context; + + bad_usb_view_set_interface(app->bad_usb_view, app->interface); + + // Opening script the first time: + // - copy user settings as base config + // - load ID/BLE_ID/BT_ID config if present + // Then disable this until next script selected so user can customize options + bool first_script_load = scene_manager_get_scene_state(app->scene_manager, BadUsbSceneWork); + if(first_script_load) { + memcpy(&app->script_hid_cfg, &app->user_hid_cfg, sizeof(app->script_hid_cfg)); + scene_manager_set_scene_state(app->scene_manager, BadUsbSceneWork, false); + } + // Interface and config are passed as pointers as ID/BLE_ID/BT_ID config can modify them + app->bad_usb_script = bad_usb_script_open( + app->file_path, &app->interface, &app->script_hid_cfg, first_script_load); + bad_usb_script_set_keyboard_layout(app->bad_usb_script, app->keyboard_layout); + + FuriString* file_name; + file_name = furi_string_alloc(); + path_extract_filename(app->file_path, file_name, true); + bad_usb_view_set_file_name(app->bad_usb_view, furi_string_get_cstr(file_name)); + furi_string_free(file_name); + + FuriString* layout; + layout = furi_string_alloc(); + path_extract_filename(app->keyboard_layout, layout, true); + bad_usb_view_set_layout(app->bad_usb_view, furi_string_get_cstr(layout)); + furi_string_free(layout); + + bad_usb_view_set_state(app->bad_usb_view, bad_usb_script_get_state(app->bad_usb_script)); + + bad_usb_view_set_button_callback(app->bad_usb_view, bad_usb_scene_work_button_callback, app); + view_dispatcher_switch_to_view(app->view_dispatcher, BadUsbAppViewWork); +} + +void bad_usb_scene_work_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/main/bad_kb/views/bad_kb_view.c b/applications/main/bad_usb/views/bad_usb_view.c similarity index 63% rename from applications/main/bad_kb/views/bad_kb_view.c rename to applications/main/bad_usb/views/bad_usb_view.c index 72064190a..d6f6683ec 100644 --- a/applications/main/bad_kb/views/bad_kb_view.c +++ b/applications/main/bad_usb/views/bad_usb_view.c @@ -1,5 +1,4 @@ -#include "../bad_kb_app_i.h" -#include "bad_kb_view.h" +#include "bad_usb_view.h" #include "../helpers/ducky_script.h" #include #include @@ -8,29 +7,26 @@ #define MAX_NAME_LEN 64 -struct BadKb { +struct BadUsb { View* view; - BadKbButtonCallback callback; + BadUsbButtonCallback callback; void* context; }; typedef struct { char file_name[MAX_NAME_LEN]; char layout[MAX_NAME_LEN]; - BadKbState state; + BadUsbState state; bool pause_wait; uint8_t anim_frame; -} BadKbModel; + BadUsbHidInterface interface; + Bt* bt; +} BadUsbModel; -static void bad_kb_draw_callback(Canvas* canvas, void* _model) { - BadKbModel* model = _model; - BadKbWorkerState state = model->state.state; +static void bad_usb_draw_callback(Canvas* canvas, void* _model) { + BadUsbModel* model = _model; - FuriString* disp_str = furi_string_alloc_set( - state == BadKbStateInit ? "( . . . )" : - model->state.is_bt ? "(BT) " : - "(USB) "); - furi_string_cat_str(disp_str, model->file_name); + FuriString* disp_str = furi_string_alloc_set(model->file_name); elements_string_fit_width(canvas, disp_str, 128 - 2); canvas_set_font(canvas, FontSecondary); canvas_draw_str(canvas, 2, 8, furi_string_get_cstr(disp_str)); @@ -40,8 +36,8 @@ static void bad_kb_draw_callback(Canvas* canvas, void* _model) { } else { furi_string_printf(disp_str, "(%s)", model->layout); } - if(model->state.pin) { - furi_string_cat_printf(disp_str, " PIN: %ld", model->state.pin); + if(model->interface == BadUsbHidInterfaceBle && model->bt->pin_code) { + furi_string_cat_printf(disp_str, " PIN: %ld", model->bt->pin_code); } else { uint32_t e = model->state.elapsed; furi_string_cat_printf(disp_str, " %02lu:%02lu.%ld", e / 60 / 1000, e / 1000, e % 1000); @@ -52,42 +48,49 @@ static void bad_kb_draw_callback(Canvas* canvas, void* _model) { furi_string_reset(disp_str); - canvas_draw_icon(canvas, 22, 24, &I_UsbTree_48x22); + if(model->interface == BadUsbHidInterfaceBle) { + canvas_draw_icon(canvas, 22, 24, &I_Bad_BLE_48x22); + } else { + canvas_draw_icon(canvas, 22, 24, &I_UsbTree_48x22); + } - if((state == BadKbStateIdle) || (state == BadKbStateDone) || - (state == BadKbStateNotConnected)) { + BadUsbWorkerState state = model->state.state; + + if((state == BadUsbStateIdle) || (state == BadUsbStateDone) || + (state == BadUsbStateNotConnected)) { elements_button_center(canvas, "Run"); elements_button_left(canvas, "Config"); - } else if((state == BadKbStateRunning) || (state == BadKbStateDelay)) { + elements_button_right(canvas, model->interface == BadUsbHidInterfaceBle ? "USB" : "BLE"); + } else if((state == BadUsbStateRunning) || (state == BadUsbStateDelay)) { elements_button_center(canvas, "Stop"); if(!model->pause_wait) { elements_button_right(canvas, "Pause"); } - } else if(state == BadKbStatePaused) { + } else if(state == BadUsbStatePaused) { elements_button_center(canvas, "End"); elements_button_right(canvas, "Resume"); - } else if(state == BadKbStateWaitForBtn) { + } else if(state == BadUsbStateWaitForBtn) { elements_button_center(canvas, "Press to continue"); - } else if(state == BadKbStateWillRun) { + } else if(state == BadUsbStateWillRun) { elements_button_center(canvas, "Cancel"); } - if(state == BadKbStateNotConnected) { + if(state == BadUsbStateNotConnected) { canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Connect"); canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "to device"); - } else if(state == BadKbStateWillRun) { + } else if(state == BadUsbStateWillRun) { canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will run"); canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "on connect"); - } else if(state == BadKbStateFileError) { + } else if(state == BadUsbStateFileError) { canvas_draw_icon(canvas, 4, 26, &I_Error_18x18); canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "File"); canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "ERROR"); - } else if(state == BadKbStateScriptError) { + } else if(state == BadUsbStateScriptError) { canvas_draw_icon(canvas, 4, 26, &I_Error_18x18); furi_string_printf(disp_str, "line %zu", model->state.error_line); canvas_draw_str_aligned( @@ -98,7 +101,7 @@ static void bad_kb_draw_callback(Canvas* canvas, void* _model) { canvas, 127, 56, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, 127, 33, AlignRight, AlignBottom, "ERROR:"); - } else if(state == BadKbStateIdle) { + } else if(state == BadUsbStateIdle) { canvas_draw_icon(canvas, 4, 26, &I_Smile_18x18); furi_string_printf(disp_str, "0/%zu", model->state.line_nb); canvas_draw_str_aligned( @@ -106,7 +109,7 @@ static void bad_kb_draw_callback(Canvas* canvas, void* _model) { canvas_set_font(canvas, FontBigNumbers); canvas_draw_str_aligned(canvas, 112, 37, AlignRight, AlignBottom, "0"); canvas_draw_icon(canvas, 115, 23, &I_Percent_10x14); - } else if(state == BadKbStateRunning) { + } else if(state == BadUsbStateRunning) { if(model->anim_frame == 0) { canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21); } else { @@ -121,7 +124,7 @@ static void bad_kb_draw_callback(Canvas* canvas, void* _model) { canvas_draw_str_aligned( canvas, 112, 37, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); canvas_draw_icon(canvas, 115, 23, &I_Percent_10x14); - } else if(state == BadKbStateDone) { + } else if(state == BadUsbStateDone) { canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21); furi_string_printf(disp_str, "%zu/%zu", model->state.line_nb, model->state.line_nb); canvas_draw_str_aligned( @@ -129,7 +132,7 @@ static void bad_kb_draw_callback(Canvas* canvas, void* _model) { canvas_set_font(canvas, FontBigNumbers); canvas_draw_str_aligned(canvas, 112, 37, AlignRight, AlignBottom, "100"); canvas_draw_icon(canvas, 115, 23, &I_Percent_10x14); - } else if(state == BadKbStateDelay) { + } else if(state == BadUsbStateDelay) { if(model->anim_frame == 0) { canvas_draw_icon(canvas, 4, 23, &I_EviWaiting1_18x21); } else { @@ -150,13 +153,13 @@ static void bad_kb_draw_callback(Canvas* canvas, void* _model) { canvas_draw_str_aligned( canvas, 112, 37, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); canvas_draw_icon(canvas, 115, 23, &I_Percent_10x14); - } else if((state == BadKbStatePaused) || (state == BadKbStateWaitForBtn)) { + } else if((state == BadUsbStatePaused) || (state == BadUsbStateWaitForBtn)) { if(model->anim_frame == 0) { canvas_draw_icon(canvas, 4, 23, &I_EviWaiting1_18x21); } else { canvas_draw_icon(canvas, 4, 23, &I_EviWaiting2_18x21); } - if(state != BadKbStateWaitForBtn) { + if(state != BadUsbStateWaitForBtn) { canvas_draw_str_aligned(canvas, 4, 61, AlignLeft, AlignBottom, "Paused"); } furi_string_printf(disp_str, "%zu/%zu", model->state.line_cur, model->state.line_nb); @@ -175,122 +178,132 @@ static void bad_kb_draw_callback(Canvas* canvas, void* _model) { furi_string_free(disp_str); } -static bool bad_kb_input_callback(InputEvent* event, void* context) { +static bool bad_usb_input_callback(InputEvent* event, void* context) { furi_assert(context); - BadKb* bad_kb = context; + BadUsb* bad_usb = context; bool consumed = false; if(event->type == InputTypeShort) { if(event->key == InputKeyLeft) { consumed = true; - furi_assert(bad_kb->callback); - bad_kb->callback(event->key, bad_kb->context); + furi_assert(bad_usb->callback); + bad_usb->callback(event->key, bad_usb->context); } else if(event->key == InputKeyOk) { with_view_model( - bad_kb->view, BadKbModel * model, { model->pause_wait = false; }, true); + bad_usb->view, BadUsbModel * model, { model->pause_wait = false; }, true); consumed = true; - furi_assert(bad_kb->callback); - bad_kb->callback(event->key, bad_kb->context); + furi_assert(bad_usb->callback); + bad_usb->callback(event->key, bad_usb->context); } else if(event->key == InputKeyRight) { with_view_model( - bad_kb->view, - BadKbModel * model, + bad_usb->view, + BadUsbModel * model, { - if((model->state.state == BadKbStateRunning) || - (model->state.state == BadKbStateDelay)) { + if((model->state.state == BadUsbStateRunning) || + (model->state.state == BadUsbStateDelay)) { model->pause_wait = true; } }, true); consumed = true; - furi_assert(bad_kb->callback); - bad_kb->callback(event->key, bad_kb->context); + furi_assert(bad_usb->callback); + bad_usb->callback(event->key, bad_usb->context); } } return consumed; } -BadKb* bad_kb_view_alloc(void) { - BadKb* bad_kb = malloc(sizeof(BadKb)); +BadUsb* bad_usb_view_alloc(void) { + BadUsb* bad_usb = malloc(sizeof(BadUsb)); - bad_kb->view = view_alloc(); - view_allocate_model(bad_kb->view, ViewModelTypeLocking, sizeof(BadKbModel)); - view_set_context(bad_kb->view, bad_kb); - view_set_draw_callback(bad_kb->view, bad_kb_draw_callback); - view_set_input_callback(bad_kb->view, bad_kb_input_callback); + bad_usb->view = view_alloc(); + view_allocate_model(bad_usb->view, ViewModelTypeLocking, sizeof(BadUsbModel)); + view_set_context(bad_usb->view, bad_usb); + view_set_draw_callback(bad_usb->view, bad_usb_draw_callback); + view_set_input_callback(bad_usb->view, bad_usb_input_callback); - return bad_kb; + with_view_model( + bad_usb->view, BadUsbModel * model, { model->bt = furi_record_open(RECORD_BT); }, true); + + return bad_usb; } -void bad_kb_view_free(BadKb* bad_kb) { - furi_assert(bad_kb); - view_free(bad_kb->view); - free(bad_kb); +void bad_usb_view_free(BadUsb* bad_usb) { + furi_assert(bad_usb); + furi_record_close(RECORD_BT); + view_free(bad_usb->view); + free(bad_usb); } -View* bad_kb_view_get_view(BadKb* bad_kb) { - furi_assert(bad_kb); - return bad_kb->view; +View* bad_usb_view_get_view(BadUsb* bad_usb) { + furi_assert(bad_usb); + return bad_usb->view; } -void bad_kb_view_set_button_callback(BadKb* bad_kb, BadKbButtonCallback callback, void* context) { - furi_assert(bad_kb); +void bad_usb_view_set_button_callback( + BadUsb* bad_usb, + BadUsbButtonCallback callback, + void* context) { + furi_assert(bad_usb); furi_assert(callback); with_view_model( - bad_kb->view, - BadKbModel * model, + bad_usb->view, + BadUsbModel * model, { UNUSED(model); - bad_kb->callback = callback; - bad_kb->context = context; + bad_usb->callback = callback; + bad_usb->context = context; }, true); } -void bad_kb_view_set_file_name(BadKb* bad_kb, const char* name) { +void bad_usb_view_set_file_name(BadUsb* bad_usb, const char* name) { furi_assert(name); with_view_model( - bad_kb->view, BadKbModel * model, { strlcpy(model->file_name, name, MAX_NAME_LEN); }, true); + bad_usb->view, + BadUsbModel * model, + { strlcpy(model->file_name, name, MAX_NAME_LEN); }, + true); } -void bad_kb_view_set_layout(BadKb* bad_kb, const char* layout) { +void bad_usb_view_set_layout(BadUsb* bad_usb, const char* layout) { furi_assert(layout); with_view_model( - bad_kb->view, BadKbModel * model, { strlcpy(model->layout, layout, MAX_NAME_LEN); }, true); + bad_usb->view, + BadUsbModel * model, + { strlcpy(model->layout, layout, MAX_NAME_LEN); }, + true); } -void bad_kb_view_set_state(BadKb* bad_kb, BadKbState* st) { +void bad_usb_view_set_state(BadUsb* bad_usb, BadUsbState* st) { furi_assert(st); - uint32_t pin = 0; - if(bad_kb->context != NULL) { - BadKbApp* app = bad_kb->context; - if(app->bt != NULL) { - pin = app->bt->pin; - } - } - st->pin = pin; with_view_model( - bad_kb->view, - BadKbModel * model, + bad_usb->view, + BadUsbModel * model, { - memcpy(&(model->state), st, sizeof(BadKbState)); + memcpy(&(model->state), st, sizeof(BadUsbState)); model->anim_frame ^= 1; - if(model->state.state == BadKbStatePaused) { + if(model->state.state == BadUsbStatePaused) { model->pause_wait = false; } }, true); } -bool bad_kb_view_is_idle_state(BadKb* bad_kb) { +void bad_usb_view_set_interface(BadUsb* bad_usb, BadUsbHidInterface interface) { + with_view_model(bad_usb->view, BadUsbModel * model, { model->interface = interface; }, true); +} + +bool bad_usb_view_is_idle_state(BadUsb* bad_usb) { bool is_idle = false; with_view_model( - bad_kb->view, - BadKbModel * model, + bad_usb->view, + BadUsbModel * model, { - if((model->state.state == BadKbStateIdle) || (model->state.state == BadKbStateDone) || - (model->state.state == BadKbStateNotConnected)) { + if((model->state.state == BadUsbStateIdle) || + (model->state.state == BadUsbStateDone) || + (model->state.state == BadUsbStateNotConnected)) { is_idle = true; } }, diff --git a/applications/main/bad_usb/views/bad_usb_view.h b/applications/main/bad_usb/views/bad_usb_view.h new file mode 100644 index 000000000..bca2f4bc0 --- /dev/null +++ b/applications/main/bad_usb/views/bad_usb_view.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include "../helpers/ducky_script.h" + +typedef struct BadUsb BadUsb; +typedef void (*BadUsbButtonCallback)(InputKey key, void* context); + +BadUsb* bad_usb_view_alloc(void); + +void bad_usb_view_free(BadUsb* bad_usb); + +View* bad_usb_view_get_view(BadUsb* bad_usb); + +void bad_usb_view_set_button_callback( + BadUsb* bad_usb, + BadUsbButtonCallback callback, + void* context); + +void bad_usb_view_set_file_name(BadUsb* bad_usb, const char* name); + +void bad_usb_view_set_layout(BadUsb* bad_usb, const char* layout); + +void bad_usb_view_set_state(BadUsb* bad_usb, BadUsbState* st); + +void bad_usb_view_set_interface(BadUsb* bad_usb, BadUsbHidInterface interface); + +bool bad_usb_view_is_idle_state(BadUsb* bad_usb); diff --git a/applications/main/infrared/infrared_brute_force.c b/applications/main/infrared/infrared_brute_force.c index e4525f868..e68ea12fb 100644 --- a/applications/main/infrared/infrared_brute_force.c +++ b/applications/main/infrared/infrared_brute_force.c @@ -2,6 +2,7 @@ #include #include +#include #include #include "infrared_signal.h" @@ -12,22 +13,56 @@ #define INFRARED_LIBRARY_HEADER "IR library file" #define INFRARED_LIBRARY_VERSION (1) +ARRAY_DEF(SignalPositionArray, size_t, M_DEFAULT_OPLIST); + typedef struct { - uint32_t index; - uint32_t count; + size_t index; + SignalPositionArray_t signals; } InfraredBruteForceRecord; +static inline void ir_bf_record_init(InfraredBruteForceRecord* record) { + record->index = 0; + SignalPositionArray_init(record->signals); +} +#define IR_BF_RECORD_INIT(r) (ir_bf_record_init(&(r))) + +static inline void + ir_bf_record_init_set(InfraredBruteForceRecord* dest, const InfraredBruteForceRecord* src) { + dest->index = src->index; + SignalPositionArray_init_set(dest->signals, src->signals); +} +#define IR_BF_RECORD_INIT_SET(d, s) (ir_bf_record_init_set(&(d), &(s))) + +static inline void + ir_bf_record_set(InfraredBruteForceRecord* dest, const InfraredBruteForceRecord* src) { + dest->index = src->index; + SignalPositionArray_set(dest->signals, src->signals); +} +#define IR_BF_RECORD_SET(d, s) (ir_bf_record_set(&(d), &(s))) + +static inline void ir_bf_record_clear(InfraredBruteForceRecord* record) { + SignalPositionArray_clear(record->signals); +} +#define IR_BF_RECORD_CLEAR(r) (ir_bf_record_clear(&(r))) + +#define IR_BF_RECORD_OPLIST \ + (INIT(IR_BF_RECORD_INIT), \ + INIT_SET(IR_BF_RECORD_INIT_SET), \ + SET(IR_BF_RECORD_SET), \ + CLEAR(IR_BF_RECORD_CLEAR)) + DICT_DEF2( InfraredBruteForceRecordDict, FuriString*, FURI_STRING_OPLIST, InfraredBruteForceRecord, - M_POD_OPLIST); + IR_BF_RECORD_OPLIST); struct InfraredBruteForce { FlipperFormat* ff; const char* db_filename; FuriString* current_record_name; + InfraredBruteForceRecord current_record; InfraredSignal* current_signal; InfraredBruteForceRecordDict_t records; bool is_started; @@ -45,6 +80,7 @@ InfraredBruteForce* infrared_brute_force_alloc(void) { } void infrared_brute_force_free(InfraredBruteForce* brute_force) { + furi_check(brute_force); furi_assert(!brute_force->is_started); InfraredBruteForceRecordDict_clear(brute_force->records); furi_string_free(brute_force->current_record_name); @@ -52,6 +88,7 @@ void infrared_brute_force_free(InfraredBruteForce* brute_force) { } void infrared_brute_force_set_db_filename(InfraredBruteForce* brute_force, const char* db_filename) { + furi_check(brute_force); furi_assert(!brute_force->is_started); brute_force->db_filename = db_filename; } @@ -59,6 +96,7 @@ void infrared_brute_force_set_db_filename(InfraredBruteForce* brute_force, const InfraredErrorCode infrared_brute_force_calculate_messages( InfraredBruteForce* brute_force, bool auto_detect_buttons) { + furi_check(brute_force); furi_assert(!brute_force->is_started); furi_assert(brute_force->db_filename); InfraredErrorCode error = InfraredErrorCodeNone; @@ -99,12 +137,13 @@ InfraredErrorCode infrared_brute_force_calculate_messages( break; } - bool signals_valid = false; + size_t signal_start = flipper_format_tell(ff); + bool signal_valid = false; uint32_t auto_detect_button_index = 0; while(infrared_signal_read_name(ff, signal_name) == InfraredErrorCodeNone) { error = infrared_signal_read_body(signal, ff); - signals_valid = (!INFRARED_ERROR_PRESENT(error)) && infrared_signal_is_valid(signal); - if(!signals_valid) break; + signal_valid = (!INFRARED_ERROR_PRESENT(error)) && infrared_signal_is_valid(signal); + if(!signal_valid) break; InfraredBruteForceRecord* record = InfraredBruteForceRecordDict_get(brute_force->records, signal_name); @@ -113,11 +152,10 @@ InfraredErrorCode infrared_brute_force_calculate_messages( brute_force, auto_detect_button_index++, furi_string_get_cstr(signal_name)); record = InfraredBruteForceRecordDict_get(brute_force->records, signal_name); } - if(record) { //-V547 - ++(record->count); - } + furi_assert(record); + SignalPositionArray_push_back(record->signals, signal_start); } - if(!signals_valid) break; + if(!signal_valid) break; } while(false); infrared_signal_free(signal); @@ -132,6 +170,7 @@ bool infrared_brute_force_start( InfraredBruteForce* brute_force, uint32_t index, uint32_t* record_count) { + furi_check(brute_force); furi_assert(!brute_force->is_started); bool success = false; *record_count = 0; @@ -142,9 +181,10 @@ bool infrared_brute_force_start( InfraredBruteForceRecordDict_next(it)) { const InfraredBruteForceRecordDict_itref_t* record = InfraredBruteForceRecordDict_cref(it); if(record->value.index == index) { - *record_count = record->value.count; + *record_count = SignalPositionArray_size(record->value.signals); if(*record_count) { furi_string_set(brute_force->current_record_name, record->key); + brute_force->current_record = record->value; } break; } @@ -163,10 +203,12 @@ bool infrared_brute_force_start( } bool infrared_brute_force_is_started(const InfraredBruteForce* brute_force) { + furi_check(brute_force); return brute_force->is_started; } void infrared_brute_force_stop(InfraredBruteForce* brute_force) { + furi_check(brute_force); furi_assert(brute_force->is_started); furi_string_reset(brute_force->current_record_name); infrared_signal_free(brute_force->current_signal); @@ -177,25 +219,32 @@ void infrared_brute_force_stop(InfraredBruteForce* brute_force) { furi_record_close(RECORD_STORAGE); } -bool infrared_brute_force_send_next(InfraredBruteForce* brute_force) { +bool infrared_brute_force_send(InfraredBruteForce* brute_force, uint32_t signal_index) { + furi_check(brute_force); furi_assert(brute_force->is_started); - const bool success = infrared_signal_search_by_name_and_read( - brute_force->current_signal, - brute_force->ff, - furi_string_get_cstr(brute_force->current_record_name)) == - InfraredErrorCodeNone; - if(success) { - infrared_signal_transmit(brute_force->current_signal); - } - return success; + if(signal_index >= SignalPositionArray_size(brute_force->current_record.signals)) return false; + + size_t signal_start = + *SignalPositionArray_cget(brute_force->current_record.signals, signal_index); + if(!flipper_format_seek(brute_force->ff, signal_start, FlipperFormatOffsetFromStart)) + return false; + + if(INFRARED_ERROR_PRESENT( + infrared_signal_read_body(brute_force->current_signal, brute_force->ff))) + return false; + + infrared_signal_transmit(brute_force->current_signal); + return true; } void infrared_brute_force_add_record( InfraredBruteForce* brute_force, uint32_t index, const char* name) { - InfraredBruteForceRecord value = {.index = index, .count = 0}; + InfraredBruteForceRecord value; + ir_bf_record_init(&value); + value.index = index; FuriString* key; key = furi_string_alloc_set(name); InfraredBruteForceRecordDict_set_at(brute_force->records, key, value); diff --git a/applications/main/infrared/infrared_brute_force.h b/applications/main/infrared/infrared_brute_force.h index c1985ee3f..2434649d1 100644 --- a/applications/main/infrared/infrared_brute_force.h +++ b/applications/main/infrared/infrared_brute_force.h @@ -82,18 +82,16 @@ bool infrared_brute_force_is_started(const InfraredBruteForce* brute_force); void infrared_brute_force_stop(InfraredBruteForce* brute_force); /** - * @brief Send the next signal from the chosen category. - * - * This function is called repeatedly until no more signals are left - * in the chosen signal category. - * - * @warning Transmission must be started first by calling infrared_brute_force_start() - * before calling this function. - * - * @param[in,out] brute_force pointer to the instance to be used. - * @returns true if the next signal existed and could be transmitted, false otherwise. + * @brief Send an arbitrary signal from the chosen category. + * + * @param[in] brute_force pointer to the instance + * @param signal_index the index of the signal within the category, must be + * between 0 and `record_count` as told by + * `infrared_brute_force_start` + * + * @returns true on success, false otherwise */ -bool infrared_brute_force_send_next(InfraredBruteForce* brute_force); +bool infrared_brute_force_send(InfraredBruteForce* brute_force, uint32_t signal_index); /** * @brief Add a signal category to an InfraredBruteForce instance's dictionary. diff --git a/applications/main/infrared/infrared_cli.c b/applications/main/infrared/infrared_cli.c index 3579c683a..e1ad3a99d 100644 --- a/applications/main/infrared/infrared_cli.c +++ b/applications/main/infrared/infrared_cli.c @@ -475,25 +475,24 @@ static void break; } - uint32_t record_count; + uint32_t signal_count, current_signal = 0; bool running = infrared_brute_force_start( - brute_force, INFRARED_BRUTE_FORCE_DUMMY_INDEX, &record_count); + brute_force, INFRARED_BRUTE_FORCE_DUMMY_INDEX, &signal_count); - if(record_count <= 0) { + if(signal_count <= 0) { printf("Invalid signal name.\r\n"); break; } - printf("Sending %lu signal(s)...\r\n", record_count); + printf("Sending %lu signal(s)...\r\n", signal_count); printf("Press Ctrl-C to stop.\r\n"); - int records_sent = 0; while(running) { - running = infrared_brute_force_send_next(brute_force); + running = infrared_brute_force_send(brute_force, current_signal); if(cli_cmd_interrupt_received(cli)) break; - printf("\r%d%% complete.", (int)((float)records_sent++ / (float)record_count * 100)); + printf("\r%d%% complete.", (int)((float)current_signal++ / (float)signal_count * 100)); fflush(stdout); } diff --git a/applications/main/infrared/infrared_custom_event.h b/applications/main/infrared/infrared_custom_event.h index 2efc99f4b..7109a48b7 100644 --- a/applications/main/infrared/infrared_custom_event.h +++ b/applications/main/infrared/infrared_custom_event.h @@ -3,7 +3,7 @@ #include #include -enum InfraredCustomEventType { +typedef enum { // Reserve first 100 events for button types and indexes, starting from 0 InfraredCustomEventTypeReserved = 100, InfraredCustomEventTypeMenuSelected, @@ -13,7 +13,7 @@ enum InfraredCustomEventType { InfraredCustomEventTypeTextEditDone, InfraredCustomEventTypePopupClosed, InfraredCustomEventTypeButtonSelected, - InfraredCustomEventTypeBackPressed, + InfraredCustomEventTypePopupInput, InfraredCustomEventTypeTaskFinished, InfraredCustomEventTypeRpcLoadFile, @@ -27,7 +27,7 @@ enum InfraredCustomEventType { InfraredCustomEventTypeGpioTxPinChanged, InfraredCustomEventTypeGpioOtgChanged, -}; +} InfraredCustomEventType; #pragma pack(push, 1) typedef union { diff --git a/applications/main/infrared/scenes/common/infrared_scene_universal_common.c b/applications/main/infrared/scenes/common/infrared_scene_universal_common.c index dbccabb9b..17037cc6b 100644 --- a/applications/main/infrared/scenes/common/infrared_scene_universal_common.c +++ b/applications/main/infrared/scenes/common/infrared_scene_universal_common.c @@ -2,15 +2,28 @@ #include +#pragma pack(push, 1) +typedef union { + uint32_t packed_value; + struct { + bool is_paused; + uint8_t padding; + uint16_t signal_index; + }; +} InfraredSceneState; +#pragma pack(pop) + void infrared_scene_universal_common_item_callback(void* context, uint32_t index) { InfraredApp* infrared = context; uint32_t event = infrared_custom_event_pack(InfraredCustomEventTypeButtonSelected, index); view_dispatcher_send_custom_event(infrared->view_dispatcher, event); } -static void infrared_scene_universal_common_progress_back_callback(void* context) { +static void infrared_scene_universal_common_progress_input_callback( + void* context, + InfraredProgressViewInput input) { InfraredApp* infrared = context; - uint32_t event = infrared_custom_event_pack(InfraredCustomEventTypeBackPressed, -1); + uint32_t event = infrared_custom_event_pack(InfraredCustomEventTypePopupInput, input); view_dispatcher_send_custom_event(infrared->view_dispatcher, event); } @@ -19,8 +32,8 @@ static void ViewStack* view_stack = infrared->view_stack; InfraredProgressView* progress = infrared->progress; infrared_progress_view_set_progress_total(progress, record_count); - infrared_progress_view_set_back_callback( - progress, infrared_scene_universal_common_progress_back_callback, infrared); + infrared_progress_view_set_input_callback( + progress, infrared_scene_universal_common_progress_input_callback, infrared); view_stack_add_view(view_stack, infrared_progress_view_get_view(progress)); infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartSend); } @@ -52,29 +65,111 @@ void infrared_scene_universal_common_on_enter(void* context) { infrared_blocking_task_start(infrared, infrared_scene_universal_common_task_callback); } +static void infrared_scene_universal_common_handle_popup_input( + InfraredApp* infrared, + InfraredProgressViewInput input) { + InfraredBruteForce* brute_force = infrared->brute_force; + SceneManager* scene_manager = infrared->scene_manager; + uint32_t scene_id = scene_manager_get_current_scene(infrared->scene_manager); + switch(input) { + case InfraredProgressViewInputStop: { + infrared_brute_force_stop(brute_force); + infrared_scene_universal_common_hide_popup(infrared); + break; + } + + case InfraredProgressViewInputPause: { + infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStop); + infrared_progress_view_set_paused(infrared->progress, true); + InfraredSceneState scene_state = { + .packed_value = scene_manager_get_scene_state(scene_manager, scene_id)}; + scene_state.is_paused = true; + if(scene_state.signal_index) + scene_state.signal_index--; // when running, the state stores the next index + scene_manager_set_scene_state(scene_manager, scene_id, scene_state.packed_value); + break; + } + + case InfraredProgressViewInputResume: { + infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartSend); + infrared_progress_view_set_paused(infrared->progress, false); + InfraredSceneState scene_state = { + .packed_value = scene_manager_get_scene_state(scene_manager, scene_id)}; + scene_state.is_paused = false; + scene_manager_set_scene_state(scene_manager, scene_id, scene_state.packed_value); + break; + } + + case InfraredProgressViewInputNextSignal: { + InfraredSceneState scene_state = { + .packed_value = scene_manager_get_scene_state(scene_manager, scene_id)}; + scene_state.signal_index++; + if(infrared_progress_view_set_progress(infrared->progress, scene_state.signal_index + 1)) + scene_manager_set_scene_state(scene_manager, scene_id, scene_state.packed_value); + break; + } + + case InfraredProgressViewInputPreviousSignal: { + InfraredSceneState scene_state = { + .packed_value = scene_manager_get_scene_state(scene_manager, scene_id)}; + if(scene_state.signal_index) { + scene_state.signal_index--; + if(infrared_progress_view_set_progress( + infrared->progress, scene_state.signal_index + 1)) + scene_manager_set_scene_state(scene_manager, scene_id, scene_state.packed_value); + } + break; + } + + case InfraredProgressViewInputSendSingle: { + InfraredSceneState scene_state = { + .packed_value = scene_manager_get_scene_state(scene_manager, scene_id)}; + infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartSend); + infrared_brute_force_send(infrared->brute_force, scene_state.signal_index); + infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStop); + break; + } + + default: + furi_crash(); + } +} + bool infrared_scene_universal_common_on_event(void* context, SceneManagerEvent event) { InfraredApp* infrared = context; SceneManager* scene_manager = infrared->scene_manager; InfraredBruteForce* brute_force = infrared->brute_force; + uint32_t scene_id = scene_manager_get_current_scene(infrared->scene_manager); bool consumed = false; if(infrared_brute_force_is_started(brute_force)) { if(event.type == SceneManagerEventTypeTick) { - bool success = infrared_brute_force_send_next(brute_force); - if(success) { - success = infrared_progress_view_increase_progress(infrared->progress); + InfraredSceneState scene_state = { + .packed_value = scene_manager_get_scene_state(scene_manager, scene_id)}; + + if(!scene_state.is_paused) { + bool success = infrared_brute_force_send(brute_force, scene_state.signal_index); + if(success) { + success = infrared_progress_view_set_progress( + infrared->progress, scene_state.signal_index + 1); + scene_state.signal_index++; + scene_manager_set_scene_state( + scene_manager, scene_id, scene_state.packed_value); + } + if(!success) { + infrared_brute_force_stop(brute_force); + infrared_scene_universal_common_hide_popup(infrared); + } + consumed = true; } - if(!success) { - infrared_brute_force_stop(brute_force); - infrared_scene_universal_common_hide_popup(infrared); - } - consumed = true; } else if(event.type == SceneManagerEventTypeCustom) { - if(infrared_custom_event_get_type(event.event) == InfraredCustomEventTypeBackPressed) { - infrared_brute_force_stop(brute_force); - infrared_scene_universal_common_hide_popup(infrared); + uint16_t event_type; + int16_t event_value; + infrared_custom_event_unpack(event.event, &event_type, &event_value); + if(event_type == InfraredCustomEventTypePopupInput) { + infrared_scene_universal_common_handle_popup_input(infrared, event_value); + consumed = true; } - consumed = true; } } else { if(event.type == SceneManagerEventTypeBack) { @@ -88,6 +183,7 @@ bool infrared_scene_universal_common_on_event(void* context, SceneManagerEvent e if(event_type == InfraredCustomEventTypeButtonSelected) { uint32_t record_count; if(infrared_brute_force_start(brute_force, event_value, &record_count)) { + scene_manager_set_scene_state(infrared->scene_manager, scene_id, 0); dolphin_deed(DolphinDeedIrSend); infrared_scene_universal_common_show_popup(infrared, record_count); } else { diff --git a/applications/main/infrared/views/infrared_progress_view.c b/applications/main/infrared/views/infrared_progress_view.c index 1f491e4ab..716027297 100644 --- a/applications/main/infrared/views/infrared_progress_view.c +++ b/applications/main/infrared/views/infrared_progress_view.c @@ -14,54 +14,80 @@ struct InfraredProgressView { View* view; - InfraredProgressViewBackCallback back_callback; + InfraredProgressViewInputCallback input_callback; void* context; }; typedef struct { size_t progress; size_t progress_total; + bool is_paused; } InfraredProgressViewModel; -bool infrared_progress_view_increase_progress(InfraredProgressView* progress) { - furi_assert(progress); - bool result = false; - - InfraredProgressViewModel* model = view_get_model(progress->view); - if(model->progress < model->progress_total) { - ++model->progress; - result = model->progress < model->progress_total; - } - view_commit_model(progress->view, true); - - return result; -} - static void infrared_progress_view_draw_callback(Canvas* canvas, void* _model) { InfraredProgressViewModel* model = (InfraredProgressViewModel*)_model; uint8_t x = 0; - uint8_t y = 36; + uint8_t y = 25; uint8_t width = 63; - uint8_t height = 59; + uint8_t height = 81; elements_bold_rounded_frame(canvas, x, y, width, height); canvas_set_font(canvas, FontSecondary); elements_multiline_text_aligned( - canvas, x + 34, y + 9, AlignCenter, AlignCenter, "Sending ..."); + canvas, + x + 32, + y + 9, + AlignCenter, + AlignCenter, + model->is_paused ? "Paused" : "Sending..."); float progress_value = (float)model->progress / model->progress_total; elements_progress_bar(canvas, x + 4, y + 19, width - 7, progress_value); - char number_string[10] = {0}; - snprintf( - number_string, sizeof(number_string), "%d/%d", model->progress, model->progress_total); + char progress_string[16] = {0}; + if(model->is_paused) { + snprintf( + progress_string, + sizeof(progress_string), + "%zu/%zu", + model->progress, + model->progress_total); + } else { + uint8_t percent_value = 100 * model->progress / model->progress_total; + snprintf(progress_string, sizeof(progress_string), "%d%%", percent_value); + } elements_multiline_text_aligned( - canvas, x + 33, y + 37, AlignCenter, AlignCenter, number_string); + canvas, x + 33, y + 37, AlignCenter, AlignCenter, progress_string); - canvas_draw_icon(canvas, x + 14, y + height - 14, &I_Pin_back_arrow_10x8); - canvas_draw_str(canvas, x + 30, y + height - 6, "= stop"); + uint8_t buttons_x = x + (model->is_paused ? 10 : 14); + uint8_t buttons_y = y + (model->is_paused ? 46 : 50); + + canvas_draw_icon(canvas, buttons_x + 0, buttons_y + 0, &I_Pin_back_arrow_10x8); + canvas_draw_str(canvas, buttons_x + 14, buttons_y + 8, model->is_paused ? "resume" : "stop"); + + canvas_draw_icon(canvas, buttons_x + 1, buttons_y + 10, &I_Ok_btn_9x9); + canvas_draw_str(canvas, buttons_x + 14, buttons_y + 17, model->is_paused ? "send 1" : "pause"); + + if(model->is_paused) { + canvas_draw_icon(canvas, buttons_x + 2, buttons_y + 21, &I_ButtonLeftSmall_3x5); + canvas_draw_icon(canvas, buttons_x + 7, buttons_y + 21, &I_ButtonRightSmall_3x5); + canvas_draw_str(canvas, buttons_x + 14, buttons_y + 26, "select"); + } +} + +bool infrared_progress_view_set_progress(InfraredProgressView* instance, uint16_t progress) { + bool result; + with_view_model( + instance->view, + InfraredProgressViewModel * model, + { + result = progress <= model->progress_total; + if(result) model->progress = progress; + }, + true); + return result; } void infrared_progress_view_set_progress_total( @@ -74,14 +100,40 @@ void infrared_progress_view_set_progress_total( view_commit_model(progress->view, false); } +void infrared_progress_view_set_paused(InfraredProgressView* instance, bool is_paused) { + with_view_model( + instance->view, InfraredProgressViewModel * model, { model->is_paused = is_paused; }, true); +} + bool infrared_progress_view_input_callback(InputEvent* event, void* context) { InfraredProgressView* instance = context; + if(event->type != InputTypeShort && event->type != InputTypeRepeat) return false; + if(!instance->input_callback) return false; - if((event->type == InputTypeShort) && (event->key == InputKeyBack)) { - if(instance->back_callback) { - instance->back_callback(instance->context); - } - } + with_view_model( + instance->view, + InfraredProgressViewModel * model, + { + if(model->is_paused) { + if(event->key == InputKeyLeft) + instance->input_callback( + instance->context, InfraredProgressViewInputPreviousSignal); + else if(event->key == InputKeyRight) + instance->input_callback( + instance->context, InfraredProgressViewInputNextSignal); + else if(event->key == InputKeyOk) + instance->input_callback( + instance->context, InfraredProgressViewInputSendSingle); + else if(event->key == InputKeyBack) + instance->input_callback(instance->context, InfraredProgressViewInputResume); + } else { + if(event->key == InputKeyOk) + instance->input_callback(instance->context, InfraredProgressViewInputPause); + else if(event->key == InputKeyBack) + instance->input_callback(instance->context, InfraredProgressViewInputStop); + } + }, + false); return true; } @@ -106,12 +158,12 @@ void infrared_progress_view_free(InfraredProgressView* progress) { free(progress); } -void infrared_progress_view_set_back_callback( +void infrared_progress_view_set_input_callback( InfraredProgressView* instance, - InfraredProgressViewBackCallback callback, + InfraredProgressViewInputCallback callback, void* context) { furi_assert(instance); - instance->back_callback = callback; + instance->input_callback = callback; instance->context = context; } diff --git a/applications/main/infrared/views/infrared_progress_view.h b/applications/main/infrared/views/infrared_progress_view.h index 9d1f70e91..c33f1e553 100644 --- a/applications/main/infrared/views/infrared_progress_view.h +++ b/applications/main/infrared/views/infrared_progress_view.h @@ -13,8 +13,17 @@ extern "C" { /** Anonymous instance */ typedef struct InfraredProgressView InfraredProgressView; -/** Callback for back button handling */ -typedef void (*InfraredProgressViewBackCallback)(void*); +typedef enum { + InfraredProgressViewInputStop, + InfraredProgressViewInputPause, + InfraredProgressViewInputResume, + InfraredProgressViewInputPreviousSignal, + InfraredProgressViewInputNextSignal, + InfraredProgressViewInputSendSingle, +} InfraredProgressViewInput; + +/** Callback for input handling */ +typedef void (*InfraredProgressViewInputCallback)(void* context, InfraredProgressViewInput event); /** Allocate and initialize Infrared view * @@ -35,13 +44,12 @@ void infrared_progress_view_free(InfraredProgressView* instance); */ View* infrared_progress_view_get_view(InfraredProgressView* instance); -/** Increase progress on progress view module +/** Set progress of progress view module * * @param instance view module - * @retval true - value is incremented and maximum is reached, - * false - value is incremented and maximum is not reached + * @param progress progress value */ -bool infrared_progress_view_increase_progress(InfraredProgressView* instance); +bool infrared_progress_view_set_progress(InfraredProgressView* instance, uint16_t progress); /** Set maximum progress value * @@ -52,15 +60,22 @@ void infrared_progress_view_set_progress_total( InfraredProgressView* instance, uint16_t progress_max); -/** Set back button callback +/** Selects the variant of the View + * + * @param instance view instance + * @param is_paused the "paused" variant is displayed if true; the "sending" one if false + */ +void infrared_progress_view_set_paused(InfraredProgressView* instance, bool is_paused); + +/** Set input callback * * @param instance - view module - * @param callback - callback to call for back button + * @param callback - callback to call for input * @param context - context to pass to callback */ -void infrared_progress_view_set_back_callback( +void infrared_progress_view_set_input_callback( InfraredProgressView* instance, - InfraredProgressViewBackCallback callback, + InfraredProgressViewInputCallback callback, void* context); #ifdef __cplusplus diff --git a/applications/main/nfc/application.fam b/applications/main/nfc/application.fam index 8e07c9ac8..a07e0946f 100644 --- a/applications/main/nfc/application.fam +++ b/applications/main/nfc/application.fam @@ -321,6 +321,16 @@ App( sources=["plugins/supported_cards/trt.c"], ) +App( + appid="disney_infinity_parser", + apptype=FlipperAppType.PLUGIN, + entry_point="disney_infinity_plugin_ep", + targets=["f7"], + requires=["nfc"], + fap_libs=["mbedtls"], + sources=["plugins/supported_cards/disney_infinity.c"], +) + App( appid="sonicare_parser", apptype=FlipperAppType.PLUGIN, diff --git a/applications/main/nfc/plugins/supported_cards/disney_infinity.c b/applications/main/nfc/plugins/supported_cards/disney_infinity.c new file mode 100644 index 000000000..a98d39ec2 --- /dev/null +++ b/applications/main/nfc/plugins/supported_cards/disney_infinity.c @@ -0,0 +1,121 @@ +#include +#include "nfc_supported_card_plugin.h" + +#include + +#include +#include +#include +#include + +#define TAG "DisneyInfinity" +#define UID_LEN 7 + +// Derived from https://nfc.toys/#new-interoperability-for-infinity +static uint8_t seed[38] = {0x0A, 0x14, 0xFD, 0x05, 0x07, 0xFF, 0x4B, 0xCD, 0x02, 0x6B, + 0xA8, 0x3F, 0x0A, 0x3B, 0x89, 0xA9, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x28, 0x63, 0x29, 0x20, 0x44, 0x69, 0x73, + 0x6E, 0x65, 0x79, 0x20, 0x32, 0x30, 0x31, 0x33}; + +void di_key(const uint8_t* uid, MfClassicKey* key) { + uint8_t hash[20]; + memcpy(seed + 16, uid, UID_LEN); + mbedtls_sha1(seed, sizeof(seed), hash); + key->data[0] = hash[3]; + key->data[1] = hash[2]; + key->data[2] = hash[1]; + key->data[3] = hash[0]; + key->data[4] = hash[7]; + key->data[5] = hash[6]; +} + +static bool disney_infinity_read(Nfc* nfc, NfcDevice* device) { + furi_assert(nfc); + furi_assert(device); + size_t* uid_len = 0; + bool is_read = false; + MfClassicData* data = mf_classic_alloc(); + + nfc_device_copy_data(device, NfcProtocolMfClassic, data); + const uint8_t* uid_bytes = mf_classic_get_uid(data, uid_len); + MfClassicDeviceKeys keys = {}; + + do { + MfClassicType type = MfClassicTypeMini; + MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type); + if(error != MfClassicErrorNone) break; + + data->type = type; + for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + di_key(uid_bytes, &keys.key_a[i]); + di_key(uid_bytes, &keys.key_b[i]); + FURI_BIT_SET(keys.key_a_mask, i); + FURI_BIT_SET(keys.key_b_mask, i); + } + + error = mf_classic_poller_sync_read(nfc, &keys, data); + if(error != MfClassicErrorNone) { + FURI_LOG_W(TAG, "Failed to read data: %d", error); + break; + } + + nfc_device_set_data(device, NfcProtocolMfClassic, data); + + is_read = mf_classic_is_card_read(data); + } while(false); + + mf_classic_free(data); + + return is_read; +} + +static bool disney_infinity_parse(const NfcDevice* device, FuriString* parsed_data) { + furi_assert(device); + size_t* uid_len = 0; + bool parsed = false; + FuriString* name = furi_string_alloc(); + const uint8_t verify_sector = 0; + MfClassicKey key = {}; + + const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic); + const uint8_t* uid_bytes = mf_classic_get_uid(data, uid_len); + + do { + // verify key + MfClassicSectorTrailer* sec_tr = + mf_classic_get_sector_trailer_by_sector(data, verify_sector); + + di_key(uid_bytes, &key); + if(memcmp(key.data, sec_tr->key_a.data, 6) != 0) break; + + // At some point I'd like to add name lookup like Skylanders + furi_string_printf(parsed_data, "\e#Disney Infinity\n"); + + parsed = true; + + } while(false); + + furi_string_free(name); + + return parsed; +} + +/* Actual implementation of app<>plugin interface */ +static const NfcSupportedCardsPlugin disney_infinity_plugin = { + .protocol = NfcProtocolMfClassic, + .verify = NULL, // Need UID to verify key(s) + .read = disney_infinity_read, + .parse = disney_infinity_parse, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor disney_infinity_plugin_descriptor = { + .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, + .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, + .entry_point = &disney_infinity_plugin, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* disney_infinity_plugin_ep(void) { + return &disney_infinity_plugin_descriptor; +} diff --git a/applications/main/subghz/subghz_cli.c b/applications/main/subghz/subghz_cli.c index 70ce5b138..278a63a58 100644 --- a/applications/main/subghz/subghz_cli.c +++ b/applications/main/subghz/subghz_cli.c @@ -617,7 +617,7 @@ void subghz_cli_command_tx_from_file(Cli* cli, FuriString* args, void* context) if(furi_string_size(args)) { char* args_cstr = (char*)furi_string_get_cstr(args); StrintParseError parse_err = StrintParseNoError; - parse_err |= strint_to_uint32(args_cstr, &args_cstr, &frequency, 10); + parse_err |= strint_to_uint32(args_cstr, &args_cstr, &repeat, 10); parse_err |= strint_to_uint32(args_cstr, &args_cstr, &device_ind, 10); if(parse_err) { cli_print_usage( diff --git a/applications/services/bt/bt_service/bt.c b/applications/services/bt/bt_service/bt.c index a2bb29e11..a99568da5 100644 --- a/applications/services/bt/bt_service/bt.c +++ b/applications/services/bt/bt_service/bt.c @@ -186,8 +186,6 @@ Bt* bt_alloc(void) { // API evnent bt->api_event = furi_event_flag_alloc(); - bt->pin = 0; - return bt; } @@ -264,7 +262,6 @@ static bool bt_on_gap_event_callback(GapEvent event, void* context) { furi_assert(context); Bt* bt = context; bool ret = false; - bt->pin = 0; bool do_update_status = false; bool current_profile_is_serial = furi_hal_bt_check_profile_type(bt->current_profile, ble_profile_serial); @@ -303,14 +300,12 @@ static bool bt_on_gap_event_callback(GapEvent event, void* context) { do_update_status = true; ret = true; } else if(event.type == GapEventTypePinCodeShow) { - bt->pin = event.data.pin_code; BtMessage message = { .type = BtMessageTypePinCodeShow, .data.pin_code = event.data.pin_code}; furi_check( furi_message_queue_put(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk); ret = true; } else if(event.type == GapEventTypePinCodeVerify) { - bt->pin = event.data.pin_code; ret = bt_pin_code_verify_event_handler(bt, event.data.pin_code); } else if(event.type == GapEventTypeUpdateMTU) { bt->max_packet_size = event.data.max_packet_size; diff --git a/applications/services/bt/bt_service/bt_i.h b/applications/services/bt/bt_service/bt_i.h index e5578ff22..2ee5e971a 100644 --- a/applications/services/bt/bt_service/bt_i.h +++ b/applications/services/bt/bt_service/bt_i.h @@ -87,6 +87,5 @@ struct Bt { BtStatusChangedCallback status_changed_cb; void* status_changed_ctx; - uint32_t pin; bool suppress_pin_screen; }; diff --git a/applications/services/dolphin/helpers/dolphin_deed.c b/applications/services/dolphin/helpers/dolphin_deed.c index 8d33d9527..059d2f42d 100644 --- a/applications/services/dolphin/helpers/dolphin_deed.c +++ b/applications/services/dolphin/helpers/dolphin_deed.c @@ -34,7 +34,7 @@ static const DolphinDeedWeight dolphin_deed_weights[] = { {2, DolphinAppIbutton}, // DolphinDeedIbuttonEmulate {2, DolphinAppIbutton}, // DolphinDeedIbuttonAdd - {3, DolphinAppBadKb}, // DolphinDeedBadKbPlayScript + {3, DolphinAppBadUsb}, // DolphinDeedBadUsbPlayScript {3, DolphinAppPlugin}, // DolphinDeedU2fAuthorized @@ -54,7 +54,7 @@ static uint8_t dolphin_deed_limits[] = { 69, // DolphinAppNfc 69, // DolphinAppIr 69, // DolphinAppIbutton - 69, // DolphinAppBadKb + 69, // DolphinAppBadUsb 69, // DolphinAppPlugin }; diff --git a/applications/services/dolphin/helpers/dolphin_deed.h b/applications/services/dolphin/helpers/dolphin_deed.h index 9cf30a3e6..fcd0e05b4 100644 --- a/applications/services/dolphin/helpers/dolphin_deed.h +++ b/applications/services/dolphin/helpers/dolphin_deed.h @@ -12,7 +12,7 @@ typedef enum { DolphinAppNfc, DolphinAppIr, DolphinAppIbutton, - DolphinAppBadKb, + DolphinAppBadUsb, DolphinAppPlugin, DolphinAppMAX, } DolphinApp; @@ -50,7 +50,7 @@ typedef enum { DolphinDeedIbuttonEmulate, DolphinDeedIbuttonAdd, - DolphinDeedBadKbPlayScript, + DolphinDeedBadUsbPlayScript, DolphinDeedU2fAuthorized, diff --git a/applications/services/gui/scene_manager.c b/applications/services/gui/scene_manager.c index 35556e322..47d168007 100644 --- a/applications/services/gui/scene_manager.c +++ b/applications/services/gui/scene_manager.c @@ -230,6 +230,11 @@ bool scene_manager_search_and_switch_to_another_scene( } } +uint32_t scene_manager_get_current_scene(SceneManager* scene_manager) { + furi_check(scene_manager); + return *SceneManagerIdStack_back(scene_manager->scene_id_stack); +} + void scene_manager_stop(SceneManager* scene_manager) { furi_check(scene_manager); diff --git a/applications/services/gui/scene_manager.h b/applications/services/gui/scene_manager.h index 54dfa9cd4..8dad92aac 100644 --- a/applications/services/gui/scene_manager.h +++ b/applications/services/gui/scene_manager.h @@ -170,6 +170,14 @@ bool scene_manager_search_and_switch_to_another_scene( SceneManager* scene_manager, uint32_t scene_id); +/** Get id of current scene + * + * @param scene_manager SceneManager instance + * + * @return Scene ID + */ +uint32_t scene_manager_get_current_scene(SceneManager* scene_manager); + /** Exit from current scene * * @param scene_manager SceneManager instance diff --git a/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c b/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c index cfc945751..86088e28c 100644 --- a/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c +++ b/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c @@ -1,6 +1,6 @@ #include "../bt_settings_app.h" #include -#include +#include void bt_settings_scene_forget_dev_confirm_dialog_callback(DialogExResult result, void* context) { furi_assert(context); @@ -35,7 +35,7 @@ bool bt_settings_scene_forget_dev_confirm_on_event(void* context, SceneManagerEv // also remove keys for apps const char* keys_paths[] = { - BAD_KB_KEYS_PATH, + EXT_PATH("apps_data/bad_kb/.bt_hid.keys"), EXT_PATH("apps_data/hid_ble/.bt_hid.keys"), EXT_PATH("apps_data/air_mouse/.bt_hid.keys"), EXT_PATH("apps_data/vgm_air_mouse/.bt_hid.keys"), diff --git a/assets/icons/Archive/badkb_10px.png b/assets/icons/Archive/badusb_10px.png similarity index 100% rename from assets/icons/Archive/badkb_10px.png rename to assets/icons/Archive/badusb_10px.png diff --git a/assets/icons/BadUsb/Bad_BLE_48x22.png b/assets/icons/BadUsb/Bad_BLE_48x22.png new file mode 100644 index 000000000..5f6fa6f46 Binary files /dev/null and b/assets/icons/BadUsb/Bad_BLE_48x22.png differ diff --git a/assets/icons/BadKb/Clock_18x18.png b/assets/icons/BadUsb/Clock_18x18.png similarity index 100% rename from assets/icons/BadKb/Clock_18x18.png rename to assets/icons/BadUsb/Clock_18x18.png diff --git a/assets/icons/BadKb/Error_18x18.png b/assets/icons/BadUsb/Error_18x18.png similarity index 100% rename from assets/icons/BadKb/Error_18x18.png rename to assets/icons/BadUsb/Error_18x18.png diff --git a/assets/icons/BadKb/EviSmile1_18x21.png b/assets/icons/BadUsb/EviSmile1_18x21.png similarity index 100% rename from assets/icons/BadKb/EviSmile1_18x21.png rename to assets/icons/BadUsb/EviSmile1_18x21.png diff --git a/assets/icons/BadKb/EviSmile2_18x21.png b/assets/icons/BadUsb/EviSmile2_18x21.png similarity index 100% rename from assets/icons/BadKb/EviSmile2_18x21.png rename to assets/icons/BadUsb/EviSmile2_18x21.png diff --git a/assets/icons/BadKb/EviWaiting1_18x21.png b/assets/icons/BadUsb/EviWaiting1_18x21.png similarity index 100% rename from assets/icons/BadKb/EviWaiting1_18x21.png rename to assets/icons/BadUsb/EviWaiting1_18x21.png diff --git a/assets/icons/BadKb/EviWaiting2_18x21.png b/assets/icons/BadUsb/EviWaiting2_18x21.png similarity index 100% rename from assets/icons/BadKb/EviWaiting2_18x21.png rename to assets/icons/BadUsb/EviWaiting2_18x21.png diff --git a/assets/icons/BadKb/Percent_10x14.png b/assets/icons/BadUsb/Percent_10x14.png similarity index 100% rename from assets/icons/BadKb/Percent_10x14.png rename to assets/icons/BadUsb/Percent_10x14.png diff --git a/assets/icons/BadKb/Smile_18x18.png b/assets/icons/BadUsb/Smile_18x18.png similarity index 100% rename from assets/icons/BadKb/Smile_18x18.png rename to assets/icons/BadUsb/Smile_18x18.png diff --git a/assets/icons/BadKb/UsbTree_48x22.png b/assets/icons/BadUsb/UsbTree_48x22.png similarity index 100% rename from assets/icons/BadKb/UsbTree_48x22.png rename to assets/icons/BadUsb/UsbTree_48x22.png diff --git a/assets/icons/MainMenu/BadKb_14/frame_01.png b/assets/icons/MainMenu/BadUsb_14/frame_01.png similarity index 100% rename from assets/icons/MainMenu/BadKb_14/frame_01.png rename to assets/icons/MainMenu/BadUsb_14/frame_01.png diff --git a/assets/icons/MainMenu/BadKb_14/frame_02.png b/assets/icons/MainMenu/BadUsb_14/frame_02.png similarity index 100% rename from assets/icons/MainMenu/BadKb_14/frame_02.png rename to assets/icons/MainMenu/BadUsb_14/frame_02.png diff --git a/assets/icons/MainMenu/BadKb_14/frame_03.png b/assets/icons/MainMenu/BadUsb_14/frame_03.png similarity index 100% rename from assets/icons/MainMenu/BadKb_14/frame_03.png rename to assets/icons/MainMenu/BadUsb_14/frame_03.png diff --git a/assets/icons/MainMenu/BadKb_14/frame_04.png b/assets/icons/MainMenu/BadUsb_14/frame_04.png similarity index 100% rename from assets/icons/MainMenu/BadKb_14/frame_04.png rename to assets/icons/MainMenu/BadUsb_14/frame_04.png diff --git a/assets/icons/MainMenu/BadKb_14/frame_05.png b/assets/icons/MainMenu/BadUsb_14/frame_05.png similarity index 100% rename from assets/icons/MainMenu/BadKb_14/frame_05.png rename to assets/icons/MainMenu/BadUsb_14/frame_05.png diff --git a/assets/icons/MainMenu/BadKb_14/frame_06.png b/assets/icons/MainMenu/BadUsb_14/frame_06.png similarity index 100% rename from assets/icons/MainMenu/BadKb_14/frame_06.png rename to assets/icons/MainMenu/BadUsb_14/frame_06.png diff --git a/assets/icons/MainMenu/BadKb_14/frame_07.png b/assets/icons/MainMenu/BadUsb_14/frame_07.png similarity index 100% rename from assets/icons/MainMenu/BadKb_14/frame_07.png rename to assets/icons/MainMenu/BadUsb_14/frame_07.png diff --git a/assets/icons/MainMenu/BadKb_14/frame_08.png b/assets/icons/MainMenu/BadUsb_14/frame_08.png similarity index 100% rename from assets/icons/MainMenu/BadKb_14/frame_08.png rename to assets/icons/MainMenu/BadUsb_14/frame_08.png diff --git a/assets/icons/MainMenu/BadKb_14/frame_09.png b/assets/icons/MainMenu/BadUsb_14/frame_09.png similarity index 100% rename from assets/icons/MainMenu/BadKb_14/frame_09.png rename to assets/icons/MainMenu/BadUsb_14/frame_09.png diff --git a/assets/icons/MainMenu/BadKb_14/frame_10.png b/assets/icons/MainMenu/BadUsb_14/frame_10.png similarity index 100% rename from assets/icons/MainMenu/BadKb_14/frame_10.png rename to assets/icons/MainMenu/BadUsb_14/frame_10.png diff --git a/assets/icons/MainMenu/BadKb_14/frame_11.png b/assets/icons/MainMenu/BadUsb_14/frame_11.png similarity index 100% rename from assets/icons/MainMenu/BadKb_14/frame_11.png rename to assets/icons/MainMenu/BadUsb_14/frame_11.png diff --git a/assets/icons/MainMenu/BadKb_14/frame_rate b/assets/icons/MainMenu/BadUsb_14/frame_rate similarity index 100% rename from assets/icons/MainMenu/BadKb_14/frame_rate rename to assets/icons/MainMenu/BadUsb_14/frame_rate diff --git a/documentation/file_formats/BadUsbScriptFormat.md b/documentation/file_formats/BadUsbScriptFormat.md index 0b8381c1e..df5e535f7 100644 --- a/documentation/file_formats/BadUsbScriptFormat.md +++ b/documentation/file_formats/BadUsbScriptFormat.md @@ -189,7 +189,19 @@ Example: ID 1234:abcd Flipper Devices:Flipper Zero ``` -> [!IMPORTANT] -> -> VID and PID are hex codes and are mandatory. -> Manufacturer and Product are text strings and are optional. +VID and PID are hex codes and are mandatory. Manufacturer and Product are text strings and are optional. + +## Mouse Commands + +Mouse movement and click commands. Mouse click commands support HOLD functionality. + +| Command | Parameters | Notes | +| ------------- | -------------------------------| -------------------------------- | +| LEFTCLICK | None | | +| LEFT_CLICK | None | functionally same as LEFTCLICK | +| RIGHTCLICK | None | | +| RIGHT_CLICK | None | functionally same as RIGHTCLICK | +| MOUSEMOVE | x y: int move mount/direction | | +| MOUSE_MOVE | x y: int move mount/direction | functionally same as MOUSEMOVE | +| MOUSESCROLL | delta: int scroll distance | | +| MOUSE_SCROLL | delta: int scroll distance | functionally same as MOUSESCROLL | diff --git a/lib/flipper_format/flipper_format.c b/lib/flipper_format/flipper_format.c index d07022e12..8aebf853f 100644 --- a/lib/flipper_format/flipper_format.c +++ b/lib/flipper_format/flipper_format.c @@ -8,6 +8,11 @@ #include "flipper_format_stream.h" #include "flipper_format_stream_i.h" +// permits direct casting between `FlipperFormatOffset` and `StreamOffset` +static_assert((size_t)FlipperFormatOffsetFromCurrent == (size_t)StreamOffsetFromCurrent); +static_assert((size_t)FlipperFormatOffsetFromStart == (size_t)StreamOffsetFromStart); +static_assert((size_t)FlipperFormatOffsetFromEnd == (size_t)StreamOffsetFromEnd); + /********************************** Private **********************************/ struct FlipperFormat { Stream* stream; @@ -127,6 +132,17 @@ bool flipper_format_rewind(FlipperFormat* flipper_format) { return stream_rewind(flipper_format->stream); } +size_t flipper_format_tell(FlipperFormat* flipper_format) { + furi_check(flipper_format); + return stream_tell(flipper_format->stream); +} + +bool flipper_format_seek(FlipperFormat* flipper_format, int32_t offset, FlipperFormatOffset anchor) { + furi_check(flipper_format); + // direct usage of `anchor` made valid by `static_assert`s at the top of this file + return stream_seek(flipper_format->stream, offset, (StreamOffset)anchor); +} + bool flipper_format_seek_to_end(FlipperFormat* flipper_format) { furi_check(flipper_format); return stream_seek(flipper_format->stream, 0, StreamOffsetFromEnd); diff --git a/lib/flipper_format/flipper_format.h b/lib/flipper_format/flipper_format.h index 4a1bb767b..5b13496e1 100644 --- a/lib/flipper_format/flipper_format.h +++ b/lib/flipper_format/flipper_format.h @@ -94,6 +94,12 @@ extern "C" { typedef struct FlipperFormat FlipperFormat; +typedef enum { + FlipperFormatOffsetFromCurrent, + FlipperFormatOffsetFromStart, + FlipperFormatOffsetFromEnd, +} FlipperFormatOffset; + /** Allocate FlipperFormat as string. * * @return FlipperFormat* pointer to a FlipperFormat instance @@ -216,6 +222,24 @@ void flipper_format_set_strict_mode(FlipperFormat* flipper_format, bool strict_m */ bool flipper_format_rewind(FlipperFormat* flipper_format); +/** Get the RW pointer position + * + * @param flipper_format Pointer to a FlipperFormat instance + * + * @return RW pointer position + */ +size_t flipper_format_tell(FlipperFormat* flipper_format); + +/** Set the RW pointer position to an arbitrary value + * + * @param flipper_format Pointer to a FlipperFormat instance + * @param offset Offset relative to the anchor point + * @param anchor Anchor point (e.g. start of file) + * + * @return True on success + */ +bool flipper_format_seek(FlipperFormat* flipper_format, int32_t offset, FlipperFormatOffset anchor); + /** Move the RW pointer at the end. Can be useful if you want to add some data * after reading. * diff --git a/lib/lfrfid/protocols/lfrfid_protocols.c b/lib/lfrfid/protocols/lfrfid_protocols.c index 32cd5e9a8..48d405b6b 100644 --- a/lib/lfrfid/protocols/lfrfid_protocols.c +++ b/lib/lfrfid/protocols/lfrfid_protocols.c @@ -20,6 +20,7 @@ #include "protocol_nexwatch.h" #include "protocol_securakey.h" #include "protocol_gproxii.h" +#include "protocol_noralsy.h" #include "protocol_insta_fob.h" const ProtocolBase* lfrfid_protocols[] = { @@ -46,5 +47,6 @@ const ProtocolBase* lfrfid_protocols[] = { [LFRFIDProtocolNexwatch] = &protocol_nexwatch, [LFRFIDProtocolSecurakey] = &protocol_securakey, [LFRFIDProtocolGProxII] = &protocol_gproxii, + [LFRFIDProtocolNoralsy] = &protocol_noralsy, [LFRFIDProtocolInstaFob] = &protocol_insta_fob, }; diff --git a/lib/lfrfid/protocols/lfrfid_protocols.h b/lib/lfrfid/protocols/lfrfid_protocols.h index 8f52347d0..5b5c95b3c 100644 --- a/lib/lfrfid/protocols/lfrfid_protocols.h +++ b/lib/lfrfid/protocols/lfrfid_protocols.h @@ -32,6 +32,7 @@ typedef enum { LFRFIDProtocolNexwatch, LFRFIDProtocolSecurakey, LFRFIDProtocolGProxII, + LFRFIDProtocolNoralsy, LFRFIDProtocolInstaFob, LFRFIDProtocolMax, diff --git a/lib/lfrfid/protocols/protocol_noralsy.c b/lib/lfrfid/protocols/protocol_noralsy.c new file mode 100644 index 000000000..27cf8cb6b --- /dev/null +++ b/lib/lfrfid/protocols/protocol_noralsy.c @@ -0,0 +1,220 @@ +#include +#include +#include +#include +#include "lfrfid_protocols.h" + +#define NORALSY_CLOCK_PER_BIT (32) + +#define NORALSY_ENCODED_BIT_SIZE (96) +#define NORALSY_ENCODED_BYTE_SIZE ((NORALSY_ENCODED_BIT_SIZE) / 8) +#define NORALSY_PREAMBLE_BIT_SIZE (32) +#define NORALSY_PREAMBLE_BYTE_SIZE ((NORALSY_PREAMBLE_BIT_SIZE) / 8) +#define NORALSY_ENCODED_BYTE_FULL_SIZE ((NORALSY_ENCODED_BIT_SIZE) / 8) +#define NORALSY_DECODED_DATA_SIZE ((NORALSY_ENCODED_BIT_SIZE) / 8) + +#define NORALSY_READ_SHORT_TIME (128) +#define NORALSY_READ_LONG_TIME (256) +#define NORALSY_READ_JITTER_TIME (60) + +#define NORALSY_READ_SHORT_TIME_LOW (NORALSY_READ_SHORT_TIME - NORALSY_READ_JITTER_TIME) +#define NORALSY_READ_SHORT_TIME_HIGH (NORALSY_READ_SHORT_TIME + NORALSY_READ_JITTER_TIME) +#define NORALSY_READ_LONG_TIME_LOW (NORALSY_READ_LONG_TIME - NORALSY_READ_JITTER_TIME) +#define NORALSY_READ_LONG_TIME_HIGH (NORALSY_READ_LONG_TIME + NORALSY_READ_JITTER_TIME) + +#define TAG "NORALSY" + +typedef struct { + uint8_t data[NORALSY_ENCODED_BYTE_SIZE]; + uint8_t encoded_data[NORALSY_ENCODED_BYTE_SIZE]; + + uint8_t encoded_data_index; + bool encoded_polarity; + + ManchesterState decoder_manchester_state; +} ProtocolNoralsy; + +ProtocolNoralsy* protocol_noralsy_alloc(void) { + ProtocolNoralsy* protocol = malloc(sizeof(ProtocolNoralsy)); + return (void*)protocol; +} + +void protocol_noralsy_free(ProtocolNoralsy* protocol) { + free(protocol); +} + +static uint8_t noralsy_chksum(uint8_t* bits, uint8_t len) { + uint8_t sum = 0; + for(uint8_t i = 0; i < len; i += 4) + sum ^= bit_lib_get_bits(bits, i, 4); + return sum & 0x0F; +} + +uint8_t* protocol_noralsy_get_data(ProtocolNoralsy* protocol) { + return protocol->data; +} + +static void protocol_noralsy_decode(ProtocolNoralsy* protocol) { + bit_lib_copy_bits(protocol->data, 0, NORALSY_ENCODED_BIT_SIZE, protocol->encoded_data, 0); +} + +static bool protocol_noralsy_can_be_decoded(ProtocolNoralsy* protocol) { + // check 12 bits preamble + // If necessary, use 0xBB0214FF for 32 bit preamble check + // However, it is not confirmed the 13-16 bit are static. + if(bit_lib_get_bits_16(protocol->encoded_data, 0, 12) != 0b101110110000) return false; + uint8_t calc1 = noralsy_chksum(&protocol->encoded_data[4], 40); + uint8_t calc2 = noralsy_chksum(&protocol->encoded_data[0], 76); + uint8_t chk1 = bit_lib_get_bits(protocol->encoded_data, 72, 4); + uint8_t chk2 = bit_lib_get_bits(protocol->encoded_data, 76, 4); + if(calc1 != chk1 || calc2 != chk2) return false; + + return true; +} + +void protocol_noralsy_decoder_start(ProtocolNoralsy* protocol) { + memset(protocol->encoded_data, 0, NORALSY_ENCODED_BYTE_FULL_SIZE); + manchester_advance( + protocol->decoder_manchester_state, + ManchesterEventReset, + &protocol->decoder_manchester_state, + NULL); +} + +bool protocol_noralsy_decoder_feed(ProtocolNoralsy* protocol, bool level, uint32_t duration) { + bool result = false; + + ManchesterEvent event = ManchesterEventReset; + + if(duration > NORALSY_READ_SHORT_TIME_LOW && duration < NORALSY_READ_SHORT_TIME_HIGH) { + if(!level) { + event = ManchesterEventShortHigh; + } else { + event = ManchesterEventShortLow; + } + } else if(duration > NORALSY_READ_LONG_TIME_LOW && duration < NORALSY_READ_LONG_TIME_HIGH) { + if(!level) { + event = ManchesterEventLongHigh; + } else { + event = ManchesterEventLongLow; + } + } + + if(event != ManchesterEventReset) { + bool data; + bool data_ok = manchester_advance( + protocol->decoder_manchester_state, event, &protocol->decoder_manchester_state, &data); + + if(data_ok) { + bit_lib_push_bit(protocol->encoded_data, NORALSY_ENCODED_BYTE_FULL_SIZE, data); + + if(protocol_noralsy_can_be_decoded(protocol)) { + protocol_noralsy_decode(protocol); + result = true; + } + } + } + + return result; +} + +bool protocol_noralsy_encoder_start(ProtocolNoralsy* protocol) { + bit_lib_copy_bits(protocol->encoded_data, 0, NORALSY_ENCODED_BIT_SIZE, protocol->data, 0); + + return true; +} + +LevelDuration protocol_noralsy_encoder_yield(ProtocolNoralsy* protocol) { + bool level = bit_lib_get_bit(protocol->encoded_data, protocol->encoded_data_index); + uint32_t duration = NORALSY_CLOCK_PER_BIT / 2; + + if(protocol->encoded_polarity) { + protocol->encoded_polarity = false; + } else { + level = !level; + + protocol->encoded_polarity = true; + bit_lib_increment_index(protocol->encoded_data_index, NORALSY_ENCODED_BIT_SIZE); + } + + return level_duration_make(level, duration); +} + +bool protocol_noralsy_write_data(ProtocolNoralsy* protocol, void* data) { + LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data; + bool result = false; + + // Correct protocol data by redecoding + protocol_noralsy_encoder_start(protocol); + protocol_noralsy_decode(protocol); + + protocol_noralsy_encoder_start(protocol); + + if(request->write_type == LFRFIDWriteTypeT5577) { + request->t5577.block[0] = + (LFRFID_T5577_MODULATION_MANCHESTER | LFRFID_T5577_BITRATE_RF_32 | + (3 << LFRFID_T5577_MAXBLOCK_SHIFT) | LFRFID_T5577_ST_TERMINATOR); + // In fact, base on the current two dump samples from Iceman server, + // Noralsy are usually T5577s with config = 0x00088C6A + // But the `C` and `A` are not explainable by the ATA5577C datasheet + // and they don't affect reading whatsoever. + // So we are mimicing Proxmark's solution here. Leave those nibbles as zero. + request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32); + request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32); + request->t5577.block[3] = bit_lib_get_bits_32(protocol->encoded_data, 64, 32); + request->t5577.blocks_to_write = 4; + result = true; + } + return result; +} + +static void protocol_noralsy_render_data_internal(ProtocolNoralsy* protocol, FuriString* result) { + UNUSED(protocol); + uint32_t raw2 = bit_lib_get_bits_32(protocol->data, 32, 32); + uint32_t raw3 = bit_lib_get_bits_32(protocol->data, 64, 32); + uint32_t cardid = ((raw2 & 0xFFF00000) >> 20) << 16; + cardid |= (raw2 & 0xFF) << 8; + cardid |= ((raw3 & 0xFF000000) >> 24); + + uint8_t year = (raw2 & 0x000ff000) >> 12; + bool tag_is_gen_z = (year > 0x60); + furi_string_printf( + result, + "Card ID: %07lx\n" + "Year: %s%02x", + cardid, + tag_is_gen_z ? "19" : "20", + year); +} + +void protocol_noralsy_render_data(ProtocolNoralsy* protocol, FuriString* result) { + protocol_noralsy_render_data_internal(protocol, result); +} + +void protocol_noralsy_render_brief_data(ProtocolNoralsy* protocol, FuriString* result) { + protocol_noralsy_render_data_internal(protocol, result); +} + +const ProtocolBase protocol_noralsy = { + .name = "Noralsy", + .manufacturer = "Noralsy", + .data_size = NORALSY_DECODED_DATA_SIZE, + .features = LFRFIDFeatureASK, + .validate_count = 3, + .alloc = (ProtocolAlloc)protocol_noralsy_alloc, + .free = (ProtocolFree)protocol_noralsy_free, + .get_data = (ProtocolGetData)protocol_noralsy_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_noralsy_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_noralsy_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_noralsy_encoder_start, + .yield = (ProtocolEncoderYield)protocol_noralsy_encoder_yield, + }, + .render_data = (ProtocolRenderData)protocol_noralsy_render_data, + .render_brief_data = (ProtocolRenderData)protocol_noralsy_render_brief_data, + .write_data = (ProtocolWriteData)protocol_noralsy_write_data, +}; diff --git a/lib/lfrfid/protocols/protocol_noralsy.h b/lib/lfrfid/protocols/protocol_noralsy.h new file mode 100644 index 000000000..b1ee51a2e --- /dev/null +++ b/lib/lfrfid/protocols/protocol_noralsy.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_noralsy; diff --git a/lib/subghz/protocols/bin_raw.c b/lib/subghz/protocols/bin_raw.c index 2b2a57b16..bbff97428 100644 --- a/lib/subghz/protocols/bin_raw.c +++ b/lib/subghz/protocols/bin_raw.c @@ -319,6 +319,7 @@ SubGhzProtocolStatus res = SubGhzProtocolStatusErrorEncoderGetUpload; break; } + instance->encoder.is_running = true; res = SubGhzProtocolStatusOk; diff --git a/scripts/ufbt/project_template/.vscode/settings.json b/scripts/ufbt/project_template/.vscode/settings.json index d304752a9..b2ee0ca3d 100644 --- a/scripts/ufbt/project_template/.vscode/settings.json +++ b/scripts/ufbt/project_template/.vscode/settings.json @@ -19,6 +19,7 @@ "[python]": { "editor.defaultFormatter": "ms-python.black-formatter" }, + "clangd.checkUpdates": false, "clangd.path": "@UFBT_TOOLCHAIN_CLANGD@", "clangd.arguments": [ "--query-driver=**/arm-none-eabi-*", diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index 928826d0d..a5acd29d4 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1038,6 +1038,7 @@ Function,+,flipper_format_read_int32,_Bool,"FlipperFormat*, const char*, int32_t Function,+,flipper_format_read_string,_Bool,"FlipperFormat*, const char*, FuriString*" Function,+,flipper_format_read_uint32,_Bool,"FlipperFormat*, const char*, uint32_t*, const uint16_t" Function,+,flipper_format_rewind,_Bool,FlipperFormat* +Function,+,flipper_format_seek,_Bool,"FlipperFormat*, int32_t, FlipperFormatOffset" Function,+,flipper_format_seek_to_end,_Bool,FlipperFormat* Function,+,flipper_format_set_strict_mode,void,"FlipperFormat*, _Bool" Function,+,flipper_format_stream_delete_key_and_write,_Bool,"Stream*, FlipperStreamWriteData*, _Bool" @@ -1046,6 +1047,7 @@ Function,+,flipper_format_stream_read_value_line,_Bool,"Stream*, const char*, Fl Function,+,flipper_format_stream_write_comment_cstr,_Bool,"Stream*, const char*" Function,+,flipper_format_stream_write_value_line,_Bool,"Stream*, FlipperStreamWriteData*" Function,+,flipper_format_string_alloc,FlipperFormat*, +Function,+,flipper_format_tell,size_t,FlipperFormat* Function,+,flipper_format_update_bool,_Bool,"FlipperFormat*, const char*, const _Bool*, const uint16_t" Function,+,flipper_format_update_float,_Bool,"FlipperFormat*, const char*, const float*, const uint16_t" Function,+,flipper_format_update_hex,_Bool,"FlipperFormat*, const char*, const uint8_t*, const uint16_t" @@ -2469,6 +2471,7 @@ Function,-,scalbnl,long double,"long double, int" Function,-,scanf,int,"const char*, ..." Function,+,scene_manager_alloc,SceneManager*,"const SceneManagerHandlers*, void*" Function,+,scene_manager_free,void,SceneManager* +Function,+,scene_manager_get_current_scene,uint32_t,SceneManager* Function,+,scene_manager_get_scene_state,uint32_t,"const SceneManager*, uint32_t" Function,+,scene_manager_handle_back_event,_Bool,SceneManager* Function,+,scene_manager_handle_custom_event,_Bool,"SceneManager*, uint32_t" diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index a500bc152..359a39309 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1200,6 +1200,7 @@ Function,+,flipper_format_read_int32,_Bool,"FlipperFormat*, const char*, int32_t Function,+,flipper_format_read_string,_Bool,"FlipperFormat*, const char*, FuriString*" Function,+,flipper_format_read_uint32,_Bool,"FlipperFormat*, const char*, uint32_t*, const uint16_t" Function,+,flipper_format_rewind,_Bool,FlipperFormat* +Function,+,flipper_format_seek,_Bool,"FlipperFormat*, int32_t, FlipperFormatOffset" Function,+,flipper_format_seek_to_end,_Bool,FlipperFormat* Function,+,flipper_format_set_strict_mode,void,"FlipperFormat*, _Bool" Function,+,flipper_format_stream_delete_key_and_write,_Bool,"Stream*, FlipperStreamWriteData*, _Bool" @@ -1208,6 +1209,7 @@ Function,+,flipper_format_stream_read_value_line,_Bool,"Stream*, const char*, Fl Function,+,flipper_format_stream_write_comment_cstr,_Bool,"Stream*, const char*" Function,+,flipper_format_stream_write_value_line,_Bool,"Stream*, FlipperStreamWriteData*" Function,+,flipper_format_string_alloc,FlipperFormat*, +Function,+,flipper_format_tell,size_t,FlipperFormat* Function,+,flipper_format_update_bool,_Bool,"FlipperFormat*, const char*, const _Bool*, const uint16_t" Function,+,flipper_format_update_float,_Bool,"FlipperFormat*, const char*, const float*, const uint16_t" Function,+,flipper_format_update_hex,_Bool,"FlipperFormat*, const char*, const uint8_t*, const uint16_t" @@ -3203,6 +3205,7 @@ Function,-,scalbnl,long double,"long double, int" Function,-,scanf,int,"const char*, ..." Function,+,scene_manager_alloc,SceneManager*,"const SceneManagerHandlers*, void*" Function,+,scene_manager_free,void,SceneManager* +Function,+,scene_manager_get_current_scene,uint32_t,SceneManager* Function,+,scene_manager_get_scene_state,uint32_t,"const SceneManager*, uint32_t" Function,+,scene_manager_handle_back_event,_Bool,SceneManager* Function,+,scene_manager_handle_custom_event,_Bool,"SceneManager*, uint32_t" @@ -3904,7 +3907,7 @@ Variable,-,AHBPrescTable,const uint32_t[16], Variable,-,APBPrescTable,const uint32_t[8], Variable,+,A_125khz_14,const Icon, Variable,+,A_Alarm_47x39,const Icon, -Variable,+,A_BadKb_14,const Icon, +Variable,+,A_BadUsb_14,const Icon, Variable,+,A_GPIO_14,const Icon, Variable,+,A_Infrared_14,const Icon, Variable,+,A_Levelup_128x64,const Icon, @@ -3946,6 +3949,7 @@ Variable,+,I_Auth_62x31,const Icon, Variable,+,I_BLE_Pairing_128x64,const Icon, Variable,+,I_BLE_beacon_7x8,const Icon, Variable,+,I_Background_128x11,const Icon, +Variable,+,I_Bad_BLE_48x22,const Icon, Variable,+,I_BatteryBody_52x28,const Icon, Variable,+,I_Battery_16x16,const Icon, Variable,+,I_Battery_25x8,const Icon, @@ -4076,7 +4080,7 @@ Variable,+,I_Weather_7x8,const Icon, Variable,+,I_arrow_nano_down,const Icon, Variable,+,I_arrow_nano_up,const Icon, Variable,+,I_back_10px,const Icon, -Variable,+,I_badkb_10px,const Icon, +Variable,+,I_badusb_10px,const Icon, Variable,+,I_blue_19x20,const Icon, Variable,+,I_blue_hover_19x20,const Icon, Variable,+,I_brightness_text_40x5,const Icon,