1
mirror of https://github.com/flipperdevices/flipperzero-firmware.git synced 2025-12-12 04:41:26 +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);
}
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);
@@ -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;
}
@@ -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,
@@ -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);
@@ -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;
}
@@ -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,

View File

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

View File

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

View File

@@ -1,4 +1,5 @@
#include <furi_hal.h>
#include <lib/toolbox/strint.h>
#include "ducky_script.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) {
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) {
@@ -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},
@@ -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"

View File

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

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

@@ -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`
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;
break;
}
instance->encoder.is_running = true;
res = SubGhzProtocolStatusOk;