Merge pull request #376 from Next-Flip/fix/badkb-rewrite

BadKB: Rewrite BadKB extras on "new" OFW BadUSB structure
This commit is contained in:
WillyJL
2025-02-28 00:23:19 +00:00
committed by GitHub
152 changed files with 3822 additions and 2890 deletions

View File

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

View File

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

View File

@@ -71,7 +71,7 @@ After installing the packs to Flipper, hit the <code>Arrow Up</code> button on t
<h2 align="center">Bad Keyboard:</h2>
<img src=".github/assets/badkb.png" align="left" width="250px"/>
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.

View File

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

View File

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

View File

Before

Width:  |  Height:  |  Size: 96 B

After

Width:  |  Height:  |  Size: 96 B

View File

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

View File

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

View File

@@ -18,7 +18,7 @@ typedef enum {
ArchiveFileTypeSubghzPlaylist,
ArchiveFileTypeSubghzRemote,
ArchiveFileTypeInfraredRemote,
ArchiveFileTypeBadKb,
ArchiveFileTypeBadUsb,
ArchiveFileTypeWAV,
ArchiveFileTypeMag,
ArchiveFileTypeU2f,

View File

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

View File

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

View File

@@ -28,7 +28,7 @@ typedef enum {
ArchiveTabNFC,
ArchiveTabInfrared,
ArchiveTabIButton,
ArchiveTabBadKb,
ArchiveTabBadUsb,
ArchiveTabU2f,
ArchiveTabApplications,
ArchiveTabSearch,

View File

@@ -1,467 +0,0 @@
#include "bad_kb_app_i.h"
#include <furi.h>
#include <furi_hal.h>
#include <storage/storage.h>
#include <lib/toolbox/path.h>
#include <lib/flipper_format/flipper_format.h>
#include <bt/bt_service/bt_i.h>
#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;
}

View File

@@ -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 <gui/gui.h>
#include <assets_icons.h>
#include <gui/view_dispatcher.h>
#include <gui/scene_manager.h>
#include <gui/modules/submenu.h>
#include <dialogs/dialogs.h>
#include <notification/notification_messages.h>
#include <gui/modules/variable_item_list.h>
#include <gui/modules/text_input.h>
#include <gui/modules/byte_input.h>
#include <gui/modules/loading.h>
#include <gui/modules/widget.h>
#include "views/bad_kb_view.h"
#include <furi_hal_usb.h>
#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]);

View File

@@ -1,8 +0,0 @@
#pragma once
#include <storage/storage.h>
#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"

View File

@@ -1,946 +0,0 @@
#include "../bad_kb_app_i.h"
#include <furi.h>
#include <furi_hal.h>
#include <gui/gui.h>
#include <input/input.h>
#include <lib/toolbox/args.h>
#include <lib/toolbox/strint.h>
#include <furi_hal_usb_hid.h>
#include "ble_hid.h"
#include <storage/storage.h>
#include "ducky_script.h"
#include "ducky_script_i.h"
#include <dolphin/dolphin.h>
#include <toolbox/hex.h>
#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);
}

View File

@@ -1,86 +0,0 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <furi.h>
#include <furi_hal.h>
#include <bt/bt_service/bt.h>
#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

View File

@@ -1,275 +0,0 @@
#include "../bad_kb_app_i.h"
#include <furi_hal.h>
#include <furi_hal_usb_hid.h>
#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;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,46 +0,0 @@
#include "../bad_kb_app_i.h"
#include <storage/storage.h>
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);
}

View File

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

View File

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

View File

@@ -1,79 +0,0 @@
#include "../bad_kb_app_i.h"
#include <storage/storage.h>
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);
}

View File

@@ -1,59 +0,0 @@
#include "../helpers/ducky_script.h"
#include "../bad_kb_app_i.h"
#include "../views/bad_kb_view.h"
#include <furi_hal.h>
#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);
}

View File

@@ -1,23 +0,0 @@
#pragma once
#include <gui/view.h>
#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);

View File

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

View File

@@ -0,0 +1,326 @@
#include "bad_usb_app_i.h"
#include <furi.h>
#include <furi_hal.h>
#include <storage/storage.h>
#include <lib/toolbox/path.h>
#include <flipper_format/flipper_format.h>
#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;
}

View File

@@ -4,7 +4,7 @@
extern "C" {
#endif
typedef struct BadKbApp BadKbApp;
typedef struct BadUsbApp BadUsbApp;
#ifdef __cplusplus
}

View File

@@ -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 <gui/gui.h>
#include <assets_icons.h>
#include <gui/view_dispatcher.h>
#include <gui/scene_manager.h>
#include <dialogs/dialogs.h>
#include <notification/notification_messages.h>
#include <gui/modules/variable_item_list.h>
#include <gui/modules/text_input.h>
#include <gui/modules/byte_input.h>
#include <gui/modules/loading.h>
#include <gui/modules/widget.h>
#include <gui/modules/popup.h>
#include "views/bad_usb_view.h"
#include <furi_hal_usb.h>
#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);

View File

@@ -0,0 +1,321 @@
#include "bad_usb_hid.h"
#include "ble_hid_profile.h"
#include <bt/bt_service/bt.h>
#include <bt/bt_service/bt_i.h>
#include <storage/storage.h>
#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);
}

View File

@@ -0,0 +1,48 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <furi.h>
#include <furi_hal.h>
#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

View File

@@ -1,9 +1,11 @@
#include "ble_hid.h"
#include "ble_hid_profile.h"
// Based on <lib/ble_profile/extra_profiles/hid_profile.c>
#include <furi_hal_usb_hid.h>
#include <services/dev_info_service.h>
#include <services/battery_service.h>
#include "ble_hid_svc.h"
#include "ble_hid_service.h"
#include <furi.h>
#include <usb_hid.h>
@@ -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

View File

@@ -1,5 +1,7 @@
#pragma once
// Based on <lib/ble_profile/extra_profiles/hid_profile.h>
#include <furi_ble/profile_interface.h>
#ifdef __cplusplus

View File

@@ -1,5 +1,8 @@
#include "ble_hid_svc.h"
#include "app_common.h"
#include "ble_hid_service.h"
// Based on <lib/ble_profile/extra_services/hid_service.c>
#include "app_common.h" // IWYU pragma: keep
#include <ble/ble.h>
#include <furi_ble/event_dispatcher.h>
#include <furi_ble/gatt.h>
@@ -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

View File

@@ -1,5 +1,7 @@
#pragma once
// Based on <lib/ble_profile/extra_services/hid_service.h>
#include <stdint.h>
#include <stdbool.h>
@@ -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);

View File

@@ -0,0 +1,811 @@
#include <furi.h>
#include <furi_hal.h>
#include <gui/gui.h>
#include <input/input.h>
#include <lib/toolbox/args.h>
#include <lib/toolbox/strint.h>
#include <storage/storage.h>
#include "ducky_script.h"
#include "ducky_script_i.h"
#include <dolphin/dolphin.h>
#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);
}

View File

@@ -0,0 +1,60 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <furi.h>
#include <furi_hal.h>
#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

View File

@@ -0,0 +1,309 @@
#include <furi_hal.h>
#include <lib/toolbox/strint.h>
#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;
}

View File

@@ -7,6 +7,7 @@ extern "C" {
#include <furi.h>
#include <furi_hal.h>
#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
}

View File

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

View File

Before

Width:  |  Height:  |  Size: 96 B

After

Width:  |  Height:  |  Size: 96 B

View File

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

View File

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

View File

@@ -3,27 +3,27 @@
#include <gui/scene_manager.h>
// 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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,45 @@
#include "../bad_usb_app_i.h"
#include <storage/storage.h>
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);
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,71 @@
#include "../bad_usb_app_i.h"
#include <storage/storage.h>
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);
}

View File

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

View File

@@ -0,0 +1,90 @@
#include "../helpers/ducky_script.h"
#include "../bad_usb_app_i.h"
#include "../views/bad_usb_view.h"
#include <furi_hal.h>
#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);
}

View File

@@ -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 <toolbox/path.h>
#include <gui/elements.h>
@@ -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;
}
},

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