From 66961dab0657b8d1a7d1196f8b89b1021969ca59 Mon Sep 17 00:00:00 2001 From: Nikolay Minaylov Date: Mon, 29 May 2023 12:21:18 +0300 Subject: [PATCH] BadUSB: script execution pause (#2700) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- .../main/bad_usb/helpers/ducky_script.c | 96 ++++++++++++++----- .../main/bad_usb/helpers/ducky_script.h | 5 +- .../main/bad_usb/scenes/bad_usb_scene_work.c | 5 +- .../main/bad_usb/views/bad_usb_view.c | 82 ++++++++++++---- 4 files changed, 142 insertions(+), 46 deletions(-) diff --git a/applications/main/bad_usb/helpers/ducky_script.c b/applications/main/bad_usb/helpers/ducky_script.c index 47d8a7e0517..5a834ad0afe 100644 --- a/applications/main/bad_usb/helpers/ducky_script.c +++ b/applications/main/bad_usb/helpers/ducky_script.c @@ -16,10 +16,11 @@ (((uint8_t)x < 128) ? (script->layout[(uint8_t)x]) : HID_KEYBOARD_NONE) typedef enum { - WorkerEvtToggle = (1 << 0), - WorkerEvtEnd = (1 << 1), - WorkerEvtConnect = (1 << 2), - WorkerEvtDisconnect = (1 << 3), + WorkerEvtStartStop = (1 << 0), + WorkerEvtPauseResume = (1 << 1), + WorkerEvtEnd = (1 << 2), + WorkerEvtConnect = (1 << 3), + WorkerEvtDisconnect = (1 << 4), } WorkerEvtFlags; static const char ducky_cmd_id[] = {"ID"}; @@ -372,6 +373,7 @@ 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"); @@ -406,24 +408,24 @@ static int32_t bad_usb_worker(void* context) { } else if(worker_state == BadUsbStateNotConnected) { // State: USB not connected uint32_t flags = bad_usb_flags_get( - WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle, FuriWaitForever); + WorkerEvtEnd | WorkerEvtConnect | WorkerEvtStartStop, FuriWaitForever); if(flags & WorkerEvtEnd) { break; } else if(flags & WorkerEvtConnect) { worker_state = BadUsbStateIdle; // Ready to run - } else if(flags & WorkerEvtToggle) { + } else if(flags & WorkerEvtStartStop) { worker_state = BadUsbStateWillRun; // Will run when USB is connected } bad_usb->st.state = worker_state; } else if(worker_state == BadUsbStateIdle) { // State: ready to start uint32_t flags = bad_usb_flags_get( - WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, FuriWaitForever); + WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtDisconnect, FuriWaitForever); if(flags & WorkerEvtEnd) { break; - } else if(flags & WorkerEvtToggle) { // Start executing script + } else if(flags & WorkerEvtStartStop) { // Start executing script DOLPHIN_DEED(DolphinDeedBadUsbPlayScript); delay_val = 0; bad_usb->buf_len = 0; @@ -442,7 +444,7 @@ static int32_t bad_usb_worker(void* context) { } else if(worker_state == BadUsbStateWillRun) { // State: start on connection uint32_t flags = bad_usb_flags_get( - WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle, FuriWaitForever); + WorkerEvtEnd | WorkerEvtConnect | WorkerEvtStartStop, FuriWaitForever); if(flags & WorkerEvtEnd) { break; @@ -458,17 +460,17 @@ static int32_t bad_usb_worker(void* context) { storage_file_seek(script_file, 0, true); // extra time for PC to recognize Flipper as keyboard flags = furi_thread_flags_wait( - WorkerEvtEnd | WorkerEvtDisconnect | WorkerEvtToggle, + WorkerEvtEnd | WorkerEvtDisconnect | WorkerEvtStartStop, FuriFlagWaitAny | FuriFlagNoClear, 1500); if(flags == (unsigned)FuriFlagErrorTimeout) { // If nothing happened - start script execution worker_state = BadUsbStateRunning; - } else if(flags & WorkerEvtToggle) { + } else if(flags & WorkerEvtStartStop) { worker_state = BadUsbStateIdle; - furi_thread_flags_clear(WorkerEvtToggle); + furi_thread_flags_clear(WorkerEvtStartStop); } - } else if(flags & WorkerEvtToggle) { // Cancel scheduled execution + } else if(flags & WorkerEvtStartStop) { // Cancel scheduled execution worker_state = BadUsbStateNotConnected; } bad_usb->st.state = worker_state; @@ -476,18 +478,23 @@ static int32_t bad_usb_worker(void* context) { } else if(worker_state == BadUsbStateRunning) { // State: running uint16_t delay_cur = (delay_val > 1000) ? (1000) : (delay_val); uint32_t flags = furi_thread_flags_wait( - WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, FuriFlagWaitAny, delay_cur); + WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect, + FuriFlagWaitAny, + delay_cur); delay_val -= delay_cur; if(!(flags & FuriFlagError)) { if(flags & WorkerEvtEnd) { break; - } else if(flags & WorkerEvtToggle) { + } else if(flags & WorkerEvtStartStop) { worker_state = BadUsbStateIdle; // Stop executing script furi_hal_hid_kb_release_all(); } else if(flags & WorkerEvtDisconnect) { worker_state = BadUsbStateNotConnected; // USB disconnected furi_hal_hid_kb_release_all(); + } else if(flags & WorkerEvtPauseResume) { + pause_state = BadUsbStateRunning; + worker_state = BadUsbStatePaused; // Pause } bad_usb->st.state = worker_state; continue; @@ -526,13 +533,13 @@ static int32_t bad_usb_worker(void* context) { furi_check((flags & FuriFlagError) == 0); } } else if(worker_state == BadUsbStateWaitForBtn) { // State: Wait for button Press - uint16_t delay_cur = (delay_val > 1000) ? (1000) : (delay_val); - uint32_t flags = furi_thread_flags_wait( - WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, FuriFlagWaitAny, delay_cur); + uint32_t flags = bad_usb_flags_get( + WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect, + FuriWaitForever); if(!(flags & FuriFlagError)) { if(flags & WorkerEvtEnd) { break; - } else if(flags & WorkerEvtToggle) { + } else if(flags & WorkerEvtStartStop) { delay_val = 0; worker_state = BadUsbStateRunning; } else if(flags & WorkerEvtDisconnect) { @@ -542,21 +549,55 @@ static int32_t bad_usb_worker(void* context) { bad_usb->st.state = worker_state; continue; } + } else if(worker_state == BadUsbStatePaused) { // State: Paused + uint32_t flags = bad_usb_flags_get( + WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect, + FuriWaitForever); + if(!(flags & FuriFlagError)) { + if(flags & WorkerEvtEnd) { + break; + } else if(flags & WorkerEvtStartStop) { + worker_state = BadUsbStateIdle; // Stop executing script + bad_usb->st.state = worker_state; + furi_hal_hid_kb_release_all(); + } else if(flags & WorkerEvtDisconnect) { + worker_state = BadUsbStateNotConnected; // USB disconnected + bad_usb->st.state = worker_state; + furi_hal_hid_kb_release_all(); + } else if(flags & WorkerEvtPauseResume) { + if(pause_state == BadUsbStateRunning) { + if(delay_val > 0) { + bad_usb->st.state = BadUsbStateDelay; + bad_usb->st.delay_remain = delay_val / 1000; + } 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 - uint32_t flags = furi_thread_flags_wait( - WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, - FuriFlagWaitAny, + uint32_t flags = bad_usb_flags_get( + WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect, bad_usb->stringdelay); if(!(flags & FuriFlagError)) { if(flags & WorkerEvtEnd) { break; - } else if(flags & WorkerEvtToggle) { + } else if(flags & WorkerEvtStartStop) { worker_state = BadUsbStateIdle; // Stop executing script furi_hal_hid_kb_release_all(); } else if(flags & WorkerEvtDisconnect) { worker_state = BadUsbStateNotConnected; // USB disconnected furi_hal_hid_kb_release_all(); + } else if(flags & WorkerEvtPauseResume) { + pause_state = BadUsbStateStringDelay; + worker_state = BadUsbStatePaused; // Pause } bad_usb->st.state = worker_state; continue; @@ -651,9 +692,14 @@ void bad_usb_script_set_keyboard_layout(BadUsbScript* bad_usb, FuriString* layou storage_file_free(layout_file); } -void bad_usb_script_toggle(BadUsbScript* bad_usb) { +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), WorkerEvtToggle); + furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtPauseResume); } BadUsbState* bad_usb_script_get_state(BadUsbScript* bad_usb) { diff --git a/applications/main/bad_usb/helpers/ducky_script.h b/applications/main/bad_usb/helpers/ducky_script.h index cff7239420e..c8705dbdd10 100644 --- a/applications/main/bad_usb/helpers/ducky_script.h +++ b/applications/main/bad_usb/helpers/ducky_script.h @@ -16,6 +16,7 @@ typedef enum { BadUsbStateDelay, BadUsbStateStringDelay, BadUsbStateWaitForBtn, + BadUsbStatePaused, BadUsbStateDone, BadUsbStateScriptError, BadUsbStateFileError, @@ -42,7 +43,9 @@ void bad_usb_script_start(BadUsbScript* bad_usb); void bad_usb_script_stop(BadUsbScript* bad_usb); -void bad_usb_script_toggle(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); diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_work.c b/applications/main/bad_usb/scenes/bad_usb_scene_work.c index afc2e6f6f13..ad33a124d2a 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_work.c +++ b/applications/main/bad_usb/scenes/bad_usb_scene_work.c @@ -21,7 +21,10 @@ bool bad_usb_scene_work_on_event(void* context, SceneManagerEvent event) { } consumed = true; } else if(event.event == InputKeyOk) { - bad_usb_script_toggle(app->bad_usb_script); + bad_usb_script_start_stop(app->bad_usb_script); + consumed = true; + } else if(event.event == InputKeyRight) { + bad_usb_script_pause_resume(app->bad_usb_script); consumed = true; } } else if(event.type == SceneManagerEventTypeTick) { diff --git a/applications/main/bad_usb/views/bad_usb_view.c b/applications/main/bad_usb/views/bad_usb_view.c index 0ab4365b7b1..fa75b50d038 100644 --- a/applications/main/bad_usb/views/bad_usb_view.c +++ b/applications/main/bad_usb/views/bad_usb_view.c @@ -16,6 +16,7 @@ typedef struct { char file_name[MAX_NAME_LEN]; char layout[MAX_NAME_LEN]; BadUsbState state; + bool pause_wait; uint8_t anim_frame; } BadUsbModel; @@ -31,11 +32,7 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { if(strlen(model->layout) == 0) { furi_string_set(disp_str, "(default)"); } else { - furi_string_reset(disp_str); - furi_string_push_back(disp_str, '('); - for(size_t i = 0; i < strlen(model->layout); i++) - furi_string_push_back(disp_str, model->layout[i]); - furi_string_push_back(disp_str, ')'); + furi_string_printf(disp_str, "(%s)", model->layout); } elements_string_fit_width(canvas, disp_str, 128 - 2); canvas_draw_str( @@ -45,34 +42,42 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { canvas_draw_icon(canvas, 22, 24, &I_UsbTree_48x22); - if((model->state.state == BadUsbStateIdle) || (model->state.state == BadUsbStateDone) || - (model->state.state == BadUsbStateNotConnected)) { + BadUsbWorkerState state = model->state.state; + + if((state == BadUsbStateIdle) || (state == BadUsbStateDone) || + (state == BadUsbStateNotConnected)) { elements_button_center(canvas, "Run"); elements_button_left(canvas, "Config"); - } else if((model->state.state == BadUsbStateRunning) || (model->state.state == BadUsbStateDelay)) { + } else if((state == BadUsbStateRunning) || (state == BadUsbStateDelay)) { elements_button_center(canvas, "Stop"); - } else if(model->state.state == BadUsbStateWaitForBtn) { + if(!model->pause_wait) { + elements_button_right(canvas, "Pause"); + } + } else if(state == BadUsbStatePaused) { + elements_button_center(canvas, "End"); + elements_button_right(canvas, "Resume"); + } else if(state == BadUsbStateWaitForBtn) { elements_button_center(canvas, "Press to continue"); - } else if(model->state.state == BadUsbStateWillRun) { + } else if(state == BadUsbStateWillRun) { elements_button_center(canvas, "Cancel"); } - if(model->state.state == BadUsbStateNotConnected) { + 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 USB"); - } else if(model->state.state == BadUsbStateWillRun) { + } 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(model->state.state == BadUsbStateFileError) { + } 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(model->state.state == BadUsbStateScriptError) { + } else if(state == BadUsbStateScriptError) { canvas_draw_icon(canvas, 4, 26, &I_Error_18x18); canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, 127, 33, AlignRight, AlignBottom, "ERROR:"); @@ -87,12 +92,12 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { canvas_draw_str_aligned( canvas, 127, 56, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); furi_string_reset(disp_str); - } else if(model->state.state == BadUsbStateIdle) { + } else if(state == BadUsbStateIdle) { canvas_draw_icon(canvas, 4, 26, &I_Smile_18x18); canvas_set_font(canvas, FontBigNumbers); canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "0"); canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); - } else if(model->state.state == BadUsbStateRunning) { + } else if(state == BadUsbStateRunning) { if(model->anim_frame == 0) { canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21); } else { @@ -105,13 +110,13 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); furi_string_reset(disp_str); canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); - } else if(model->state.state == BadUsbStateDone) { + } else if(state == BadUsbStateDone) { canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21); canvas_set_font(canvas, FontBigNumbers); canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "100"); furi_string_reset(disp_str); canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); - } else if(model->state.state == BadUsbStateDelay) { + } else if(state == BadUsbStateDelay) { if(model->anim_frame == 0) { canvas_draw_icon(canvas, 4, 23, &I_EviWaiting1_18x21); } else { @@ -129,6 +134,22 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { canvas_draw_str_aligned( canvas, 127, 50, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); furi_string_reset(disp_str); + } 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); + } + canvas_set_font(canvas, FontBigNumbers); + furi_string_printf( + disp_str, "%u", ((model->state.line_cur - 1) * 100) / model->state.line_nb); + canvas_draw_str_aligned( + canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + furi_string_reset(disp_str); + canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 127, 50, AlignRight, AlignBottom, "Paused"); + furi_string_reset(disp_str); } else { canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); } @@ -142,7 +163,27 @@ static bool bad_usb_input_callback(InputEvent* event, void* context) { bool consumed = false; if(event->type == InputTypeShort) { - if((event->key == InputKeyLeft) || (event->key == InputKeyOk)) { + if(event->key == InputKeyLeft) { + consumed = true; + furi_assert(bad_usb->callback); + bad_usb->callback(event->key, bad_usb->context); + } else if(event->key == InputKeyOk) { + with_view_model( + bad_usb->view, BadUsbModel * model, { model->pause_wait = false; }, true); + consumed = true; + furi_assert(bad_usb->callback); + bad_usb->callback(event->key, bad_usb->context); + } else if(event->key == InputKeyRight) { + with_view_model( + bad_usb->view, + BadUsbModel * model, + { + if((model->state.state == BadUsbStateRunning) || + (model->state.state == BadUsbStateDelay)) { + model->pause_wait = true; + } + }, + true); consumed = true; furi_assert(bad_usb->callback); bad_usb->callback(event->key, bad_usb->context); @@ -215,6 +256,9 @@ void bad_usb_set_state(BadUsb* bad_usb, BadUsbState* st) { { memcpy(&(model->state), st, sizeof(BadUsbState)); model->anim_frame ^= 1; + if(model->state.state == BadUsbStatePaused) { + model->pause_wait = false; + } }, true); }