Skip to content

Commit

Permalink
BadUSB: Mouse control (#4004)
Browse files Browse the repository at this point in the history
* add usb hid mouse functions, add mouse functions to BadUsbHidApi
* add ble mouse functionality
* add hid_usb_mouse_release_all
* ducky mouse command skeleton
* implement mouse click functions
* corrected missing semicolon
* added mouse functionality
* corrected mouse scroll functionality
* mouse key functionality, removed mouse commands, supporting get_mouse_keycode function, added mouse buttons as Keys for HOLD function
* add mouse commands
* removed mouse middle click
* Format sources and fix bunch of mistakes in nfc and subghz
* added HID_MOUSE_NONE: added to help with better readability
* added script for mouse movement test
* Fix: hold and release, imrpove readability
* simplified the mouse demo/test
* Format sources

Co-authored-by: あく <alleteam@gmail.com>
  • Loading branch information
jetrp1 and skotopes authored Feb 19, 2025
1 parent 3a42bf8 commit 1541c36
Show file tree
Hide file tree
Showing 9 changed files with 243 additions and 19 deletions.
56 changes: 56 additions & 0 deletions applications/main/bad_usb/helpers/bad_usb_hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,31 @@ bool hid_usb_kb_release(void* inst, uint16_t button) {
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);
Expand All @@ -51,6 +76,7 @@ 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;
}

Expand All @@ -67,6 +93,10 @@ static const BadUsbHidApi hid_api_usb = {

.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,
Expand Down Expand Up @@ -157,6 +187,27 @@ bool hid_ble_kb_release(void* inst, uint16_t button) {
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);
Expand All @@ -174,6 +225,7 @@ bool hid_ble_release_all(void* 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;
}

Expand All @@ -191,6 +243,10 @@ static const BadUsbHidApi hid_api_ble = {

.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,
Expand Down
4 changes: 4 additions & 0 deletions applications/main/bad_usb/helpers/bad_usb_hid.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ typedef struct {

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);
Expand Down
10 changes: 9 additions & 1 deletion applications/main/bad_usb/helpers/ducky_script.c
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,16 @@ static int32_t ducky_parse_line(BadUsbScript* bad_usb, FuriString* line) {
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
uint16_t key = ducky_get_keycode(bad_usb, line_tmp, false);
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);
}
Expand Down
102 changes: 84 additions & 18 deletions applications/main/bad_usb/helpers/ducky_script_commands.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include <furi_hal.h>
#include <lib/toolbox/strint.h>
#include "ducky_script.h"
#include "ducky_script_i.h"

Expand Down Expand Up @@ -124,34 +125,58 @@ static int32_t ducky_fnc_altstring(BadUsbScript* bad_usb, const char* line, int3

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];
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->key_hold_nb++;

if(bad_usb->key_hold_nb > (HID_KB_MAX_KEYS - 1)) {
return ducky_error(bad_usb, "Too many keys are hold");
return ducky_error(bad_usb, "Too many keys are held");
}
bad_usb->hid->kb_press(bad_usb->hid_inst, key);
return 0;

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

if(bad_usb->key_hold_nb == 0) {
return ducky_error(bad_usb, "No keys are hold");
return ducky_error(bad_usb, "No keys are held");
}
bad_usb->key_hold_nb--;
bad_usb->hid->kb_release(bad_usb->hid_inst, key);
return 0;

// 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) {
Expand Down Expand Up @@ -191,6 +216,43 @@ static int32_t ducky_fnc_waitforbutton(BadUsbScript* bad_usb, const char* 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},
Expand All @@ -213,6 +275,10 @@ static const DuckyCmd ducky_commands[] = {
{"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"
Expand Down
5 changes: 5 additions & 0 deletions applications/main/bad_usb/helpers/ducky_script_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ extern "C" {

#define FILE_BUFFER_LEN 16

#define HID_MOUSE_INVALID 0
#define HID_MOUSE_NONE 0

struct BadUsbScript {
FuriHalUsbHidConfig hid_cfg;
const BadUsbHidApi* hid;
Expand Down Expand Up @@ -55,6 +58,8 @@ 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(BadUsbScript* bad_usb);
Expand Down
23 changes: 23 additions & 0 deletions applications/main/bad_usb/helpers/ducky_script_keycodes.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;
}
46 changes: 46 additions & 0 deletions applications/main/bad_usb/resources/badusb/test_mouse.txt
Original file line number Diff line number Diff line change
@@ -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
15 changes: 15 additions & 0 deletions documentation/file_formats/BadUsbScriptFormat.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,3 +177,18 @@ Example:
`ID 1234:abcd Flipper Devices:Flipper Zero`

VID and PID are hex codes and are mandatory. Manufacturer and Product are text strings and are optional.

## Mouse Commands

Mouse movement and click commands. Mouse click commands support HOLD functionality.

| Command | Parameters | Notes |
| ------------- | -------------------------------| -------------------------------- |
| LEFTCLICK | None | |
| LEFT_CLICK | None | functionally same as LEFTCLICK |
| RIGHTCLICK | None | |
| RIGHT_CLICK | None | functionally same as RIGHTCLICK |
| MOUSEMOVE | x y: int move mount/direction | |
| MOUSE_MOVE | x y: int move mount/direction | functionally same as MOUSEMOVE |
| MOUSESCROLL | delta: int scroll distance | |
| MOUSE_SCROLL | delta: int scroll distance | functionally same as MOUSESCROLL |
1 change: 1 addition & 0 deletions lib/subghz/protocols/bin_raw.c
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@ SubGhzProtocolStatus
res = SubGhzProtocolStatusErrorEncoderGetUpload;
break;
}

instance->encoder.is_running = true;

res = SubGhzProtocolStatusOk;
Expand Down

0 comments on commit 1541c36

Please sign in to comment.