Skip to content
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
04350c7
Expose some useful BLE/USB enums/macros
WillyJL Mar 3, 2025
e51f92c
Allow max possible length of BLE name
WillyJL Mar 3, 2025
f9179a9
BadUSB: Full customization of USB/BLE parameters
WillyJL Mar 3, 2025
6f72608
BadUSB: Allow pressing modifier with no key combo
WillyJL Mar 3, 2025
fa3f6c1
BadUSB: Show current line numbers, simplify draw code
WillyJL Mar 3, 2025
334cd92
BadUSB: Show script elapsed/run timer
WillyJL Mar 3, 2025
a219a50
Some cleanup
WillyJL Mar 3, 2025
74743ef
BadUSB: Add de-DE-mac and fi-FI keyboard layouts
WillyJL Mar 3, 2025
3c84063
Update docs for BLE_ID and BT_ID
WillyJL Apr 1, 2025
2023740
Merge remote-tracking branch 'ofw/dev' into feat/badusb-things
WillyJL Apr 1, 2025
08ff253
Merge remote-tracking branch 'ofw/dev' into feat/badusb-things
WillyJL Apr 1, 2025
c684068
Merge branch 'dev' into feat/badusb-things
hedger Apr 1, 2025
3b1225f
Use __builtin_bswap16()
WillyJL Apr 2, 2025
c26dd07
Update naming
WillyJL Apr 2, 2025
a21a650
Mark as Random Static Address
WillyJL Apr 2, 2025
fbd0372
Validate before copying to user config
WillyJL Apr 2, 2025
74b04fb
Show Done popup for randomize options
WillyJL Apr 2, 2025
ea56d0a
Options to restore USB and BLE defaults
WillyJL Apr 2, 2025
8358309
Naming
WillyJL Apr 2, 2025
94070d7
Focus top of config menu when entering
WillyJL Apr 2, 2025
fbf1342
Merge remote-tracking branch 'ofw/dev' into feat/badusb-things
WillyJL Apr 2, 2025
a35ba39
Simpler HID profile name generation
WillyJL Apr 3, 2025
5a4c2f0
Merge remote-tracking branch 'ofw/dev' into feat/badusb-things
WillyJL Aug 4, 2025
7702694
Use a BLE profile wrapper for just setting parameters
WillyJL Jul 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion applications/main/bad_usb/application.fam
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ App(
icon="A_BadUsb_14",
order=70,
resources="resources",
fap_libs=["assets", "ble_profile"],
fap_libs=["assets"],
fap_icon="icon.png",
fap_category="USB",
)
154 changes: 126 additions & 28 deletions applications/main/bad_usb/bad_usb_app.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,62 +31,143 @@ static void bad_usb_app_tick_event_callback(void* context) {
static void bad_usb_load_settings(BadUsbApp* app) {
Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* fff = flipper_format_file_alloc(storage);
bool state = false;
bool loaded = false;

BadUsbHidConfig* hid_cfg = &app->user_hid_cfg;
FuriString* temp_str = furi_string_alloc();
uint32_t version = 0;
uint32_t interface = 0;
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, &version)) break;
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) ||
(version != BAD_USB_SETTINGS_VERSION))
(temp_uint != BAD_USB_SETTINGS_VERSION))
break;

if(!flipper_format_read_string(fff, "layout", temp_str)) break;
if(!flipper_format_read_uint32(fff, "interface", &interface, 1)) break;
if(interface > BadUsbHidInterfaceBle) break;

state = true;
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(state) {
furi_string_set(app->keyboard_layout, temp_str);
app->interface = interface;

Storage* fs_api = furi_record_open(RECORD_STORAGE);
FileInfo layout_file_info;
FS_Error file_check_err = storage_common_stat(
fs_api, furi_string_get_cstr(app->keyboard_layout), &layout_file_info);
furi_record_close(RECORD_STORAGE);
if((file_check_err != FSE_OK) || (layout_file_info.size != 256)) {
furi_string_set(app->keyboard_layout, BAD_USB_SETTINGS_DEFAULT_LAYOUT);
}
} else {
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;
}

furi_string_free(temp_str);
}

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;
uint32_t interface_id = app->interface;
if(!flipper_format_write_uint32(fff, "interface", (const uint32_t*)&interface_id, 1))
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);
}

Expand Down Expand Up @@ -121,7 +202,7 @@ BadUsbApp* bad_usb_app_alloc(char* arg) {

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, 500);
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(
Expand All @@ -146,6 +227,14 @@ BadUsbApp* bad_usb_app_alloc(char* arg) {
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));

view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);

if(furi_hal_usb_is_locked()) {
Expand All @@ -157,6 +246,7 @@ BadUsbApp* bad_usb_app_alloc(char* arg) {
furi_check(furi_hal_usb_set_config(NULL, NULL));

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);
Expand Down Expand Up @@ -191,6 +281,14 @@ void bad_usb_app_free(BadUsbApp* app) {
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);

// View dispatcher
view_dispatcher_free(app->view_dispatcher);
scene_manager_free(app->scene_manager);
Expand Down
13 changes: 13 additions & 0 deletions applications/main/bad_usb/bad_usb_app_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#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/widget.h>
#include <gui/modules/popup.h>
#include "views/bad_usb_view.h"
Expand All @@ -36,6 +38,13 @@ struct BadUsbApp {
Widget* widget;
Popup* popup;
VariableItemList* var_item_list;
TextInput* text_input;
ByteInput* byte_input;

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;
Expand All @@ -44,6 +53,8 @@ struct BadUsbApp {
BadUsbScript* bad_usb_script;

BadUsbHidInterface interface;
BadUsbHidConfig user_hid_cfg;
BadUsbHidConfig script_hid_cfg;
FuriHalUsbInterface* usb_if_prev;
};

Expand All @@ -52,6 +63,8 @@ typedef enum {
BadUsbAppViewPopup,
BadUsbAppViewWork,
BadUsbAppViewConfig,
BadUsbAppViewByteInput,
BadUsbAppViewTextInput,
} BadUsbAppView;

void bad_usb_set_interface(BadUsbApp* app, BadUsbHidInterface interface);
56 changes: 45 additions & 11 deletions applications/main/bad_usb/helpers/bad_usb_hid.c
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
#include "bad_usb_hid.h"
#include <extra_profiles/hid_profile.h>
#include "ble_hid_profile.h"
#include <bt/bt_service/bt.h>
#include <storage/storage.h>

#define TAG "BadUSB HID"

#define HID_BT_KEYS_STORAGE_NAME ".bt_hid.keys"

void* hid_usb_init(FuriHalUsbHidConfig* hid_cfg) {
furi_check(furi_hal_usb_set_config(&usb_hid, hid_cfg));
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) {
hid_usb_adjust_config(hid_cfg);
furi_check(furi_hal_usb_set_config(&usb_hid, &hid_cfg->usb));
return NULL;
}

Expand Down Expand Up @@ -86,6 +92,7 @@ uint8_t hid_usb_get_led_state(void* inst) {
}

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,
Expand All @@ -111,11 +118,6 @@ typedef struct {
bool is_connected;
} BleHidInstance;

static const BleProfileHidParams ble_hid_params = {
.device_name_prefix = "BadUSB",
.mac_xor = 0x0002,
};

static void hid_ble_connection_status_callback(BtStatus status, void* context) {
furi_assert(context);
BleHidInstance* ble_hid = context;
Expand All @@ -125,8 +127,38 @@ static void hid_ble_connection_status_callback(BtStatus status, void* context) {
}
}

void* hid_ble_init(FuriHalUsbHidConfig* hid_cfg) {
UNUSED(hid_cfg);
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 = "BadUSB";
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);
bt_disconnect(ble_hid->bt);
Expand All @@ -136,7 +168,8 @@ void* hid_ble_init(FuriHalUsbHidConfig* hid_cfg) {

bt_keys_storage_set_storage_path(ble_hid->bt, APP_DATA_PATH(HID_BT_KEYS_STORAGE_NAME));

ble_hid->profile = bt_profile_start(ble_hid->bt, ble_profile_hid, (void*)&ble_hid_params);
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();
Expand Down Expand Up @@ -236,6 +269,7 @@ uint8_t hid_ble_get_led_state(void* inst) {
}

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,
Expand Down
11 changes: 10 additions & 1 deletion applications/main/bad_usb/helpers/bad_usb_hid.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,22 @@ extern "C" {
#include <furi.h>
#include <furi_hal.h>

#include "ble_hid_profile.h"

typedef enum {
BadUsbHidInterfaceUsb,
BadUsbHidInterfaceBle,
BadUsbHidInterfaceMAX,
} BadUsbHidInterface;

typedef struct {
void* (*init)(FuriHalUsbHidConfig* hid_cfg);
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);
Expand Down
Loading