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

BadUSB: Mouse control (#4004)

* 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>
This commit is contained in:
Ryan Peel
2025-02-19 17:24:34 -06:00
committed by GitHub
parent 3a42bf812d
commit 1541c36b14
9 changed files with 243 additions and 19 deletions

View File

@@ -37,6 +37,31 @@ bool hid_usb_kb_release(void* inst, uint16_t button) {
return furi_hal_hid_kb_release(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) { bool hid_usb_consumer_press(void* inst, uint16_t button) {
UNUSED(inst); UNUSED(inst);
return furi_hal_hid_consumer_key_press(button); return furi_hal_hid_consumer_key_press(button);
@@ -51,6 +76,7 @@ bool hid_usb_release_all(void* inst) {
UNUSED(inst); UNUSED(inst);
bool state = furi_hal_hid_kb_release_all(); bool state = furi_hal_hid_kb_release_all();
state &= furi_hal_hid_consumer_key_release_all(); state &= furi_hal_hid_consumer_key_release_all();
state &= hid_usb_mouse_release_all(inst);
return state; return state;
} }
@@ -67,6 +93,10 @@ static const BadUsbHidApi hid_api_usb = {
.kb_press = hid_usb_kb_press, .kb_press = hid_usb_kb_press,
.kb_release = hid_usb_kb_release, .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_press = hid_usb_consumer_press,
.consumer_release = hid_usb_consumer_release, .consumer_release = hid_usb_consumer_release,
.release_all = hid_usb_release_all, .release_all = hid_usb_release_all,
@@ -157,6 +187,27 @@ bool hid_ble_kb_release(void* inst, uint16_t button) {
return ble_profile_hid_kb_release(ble_hid->profile, 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) { bool hid_ble_consumer_press(void* inst, uint16_t button) {
BleHidInstance* ble_hid = inst; BleHidInstance* ble_hid = inst;
furi_assert(ble_hid); furi_assert(ble_hid);
@@ -174,6 +225,7 @@ bool hid_ble_release_all(void* inst) {
furi_assert(ble_hid); furi_assert(ble_hid);
bool state = ble_profile_hid_kb_release_all(ble_hid->profile); 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_consumer_key_release_all(ble_hid->profile);
state &= ble_profile_hid_mouse_release_all(ble_hid->profile);
return state; return state;
} }
@@ -191,6 +243,10 @@ static const BadUsbHidApi hid_api_ble = {
.kb_press = hid_ble_kb_press, .kb_press = hid_ble_kb_press,
.kb_release = hid_ble_kb_release, .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_press = hid_ble_consumer_press,
.consumer_release = hid_ble_consumer_release, .consumer_release = hid_ble_consumer_release,
.release_all = hid_ble_release_all, .release_all = hid_ble_release_all,

View File

@@ -20,6 +20,10 @@ typedef struct {
bool (*kb_press)(void* inst, uint16_t button); bool (*kb_press)(void* inst, uint16_t button);
bool (*kb_release)(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_press)(void* inst, uint16_t button);
bool (*consumer_release)(void* inst, uint16_t button); bool (*consumer_release)(void* inst, uint16_t button);
bool (*release_all)(void* inst); bool (*release_all)(void* inst);

View File

@@ -193,8 +193,16 @@ static int32_t ducky_parse_line(BadUsbScript* bad_usb, FuriString* line) {
return cmd_result; 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 // 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) { if(key == HID_KEYBOARD_NONE) {
return ducky_error(bad_usb, "No keycode defined for %s", line_tmp); return ducky_error(bad_usb, "No keycode defined for %s", line_tmp);
} }

View File

@@ -1,4 +1,5 @@
#include <furi_hal.h> #include <furi_hal.h>
#include <lib/toolbox/strint.h>
#include "ducky_script.h" #include "ducky_script.h"
#include "ducky_script_i.h" #include "ducky_script_i.h"
@@ -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) { static int32_t ducky_fnc_hold(BadUsbScript* bad_usb, const char* line, int32_t param) {
UNUSED(param); UNUSED(param);
line = &line[ducky_get_command_len(line) + 1]; 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)) { 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");
} }
// 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); bad_usb->hid->kb_press(bad_usb->hid_inst, key);
return 0; 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) { static int32_t ducky_fnc_release(BadUsbScript* bad_usb, const char* line, int32_t param) {
UNUSED(param); UNUSED(param);
line = &line[ducky_get_command_len(line) + 1]; 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) { 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");
} }
// 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->key_hold_nb--;
bad_usb->hid->kb_release(bad_usb->hid_inst, key); bad_usb->hid->kb_release(bad_usb->hid_inst, key);
return 0; 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) { static int32_t ducky_fnc_media(BadUsbScript* bad_usb, const char* line, int32_t param) {
@@ -191,6 +216,43 @@ static int32_t ducky_fnc_waitforbutton(BadUsbScript* bad_usb, const char* line,
return SCRIPT_STATE_WAIT_FOR_BTN; 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[] = { static const DuckyCmd ducky_commands[] = {
{"REM", NULL, -1}, {"REM", NULL, -1},
{"ID", NULL, -1}, {"ID", NULL, -1},
@@ -213,6 +275,10 @@ static const DuckyCmd ducky_commands[] = {
{"WAIT_FOR_BUTTON_PRESS", ducky_fnc_waitforbutton, -1}, {"WAIT_FOR_BUTTON_PRESS", ducky_fnc_waitforbutton, -1},
{"MEDIA", ducky_fnc_media, -1}, {"MEDIA", ducky_fnc_media, -1},
{"GLOBE", ducky_fnc_globe, -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 TAG "BadUsb"

View File

@@ -18,6 +18,9 @@ extern "C" {
#define FILE_BUFFER_LEN 16 #define FILE_BUFFER_LEN 16
#define HID_MOUSE_INVALID 0
#define HID_MOUSE_NONE 0
struct BadUsbScript { struct BadUsbScript {
FuriHalUsbHidConfig hid_cfg; FuriHalUsbHidConfig hid_cfg;
const BadUsbHidApi* hid; const BadUsbHidApi* hid;
@@ -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); 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); bool ducky_get_number(const char* param, uint32_t* val);
void ducky_numlock_on(BadUsbScript* bad_usb); void ducky_numlock_on(BadUsbScript* bad_usb);

View File

@@ -108,6 +108,17 @@ static const DuckyKey ducky_media_keys[] = {
{"BRIGHT_DOWN", HID_CONSUMER_BRIGHTNESS_DECREMENT}, {"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) { uint16_t ducky_get_keycode_by_name(const char* param) {
for(size_t i = 0; i < COUNT_OF(ducky_keys); i++) { for(size_t i = 0; i < COUNT_OF(ducky_keys); i++) {
size_t key_cmd_len = strlen(ducky_keys[i].name); 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; 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

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

@@ -177,3 +177,18 @@ Example:
`ID 1234:abcd Flipper Devices:Flipper Zero` `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. 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 |

View File

@@ -317,6 +317,7 @@ SubGhzProtocolStatus
res = SubGhzProtocolStatusErrorEncoderGetUpload; res = SubGhzProtocolStatusErrorEncoderGetUpload;
break; break;
} }
instance->encoder.is_running = true; instance->encoder.is_running = true;
res = SubGhzProtocolStatusOk; res = SubGhzProtocolStatusOk;