feat: move from ducky script to python

This commit is contained in:
Himadri Bhattacharjee
2025-12-01 15:45:42 +05:30
parent 6e20e7e61e
commit 2563c7fe08
3 changed files with 28 additions and 76 deletions

View File

@@ -23,7 +23,7 @@ async def setup_server():
"""
wifi.radio.start_ap(ssid=os.getenv("SSID"), password=os.getenv("PASSWORD"))
pool = socketpool.SocketPool(wifi.radio)
server = Server(pool)
server = Server(pool, debug=True)
@server.route("/")
def base(request: Request):
@@ -41,7 +41,7 @@ async def setup_server():
def api(request: Request):
return handle(request)
server.serve_forever(str(wifi.radio.ipv4_address_ap))
server.serve_forever("0.0.0.0", 80)
async def main():

View File

@@ -1,6 +1,7 @@
"""
Logic to interpret and execute user defined ducky script payloads.
Execute user defined python scripts in a sandbox
"""
import time
import usb_hid
@@ -23,95 +24,48 @@ kbd = Keyboard(usb_hid.devices)
layout = KeyboardLayout(kbd)
def delay(millis):
def delay(millis=1000.0):
"""
Sleep, do absolutely nothing.
"""
time.sleep(float(millis) / 1000)
def prefix_checker(line: str):
"""
Returns a function that checks if line begins with
any of the prefixes supplied to it.
Syntax sugar so that we can later use it in conditional
statements like if something := checker("foo", "bar")
"""
def checker(*prefixes):
for prefix in prefixes:
if line.startswith(prefix):
return line[len(prefix) + 1:]
return None
return checker
def press_keys(line: str):
def press(*keys):
"""
Press all the keys and then release them.
Really useful for keyboard shortcuts like Meta+R.
"""
# loop on each key filtering empty values
for key in filter(None, line.upper().split(" ")):
# If a single key is invalid, fail fast
for key in keys:
if key not in Keycode.__dict__:
warn(f"unknown key: <{key}>")
return
for key in keys:
if command_keycode := Keycode.__dict__.get(key):
# If this is a valid key, send its keycode
kbd.press(command_keycode)
continue
# If it's not a known key name, log it for diagnosis
warn(f"unknown key: <{key}>")
kbd.release_all()
def repeat(contents: str, times: int):
"""
If the contents supplied is not empty or None,
repeat those ducky script lines `times` times.
"""
if not contents:
return
for _ in range(times):
run_script(contents)
def led(value: bool):
LED.value = value
def run_script(contents):
def run_script(contents: str):
"""
Interpret the ducky script and execute it line by line
"""
default_delay = 0
previous_line = None
for line in filter(None, contents.splitlines()):
line = line.rstrip()
after = prefix_checker(line)
if times := after("REPEAT"):
repeat(previous_line, int(times))
elif after("REM"):
continue
elif (millis := after("DELAY")) is not None:
delay(millis or default_delay)
elif message := after("PRINT"):
info(message)
elif path := after("IMPORT"):
run_script_file(path)
elif millis := after("DEFAULT_DELAY", "DEFAULTDELAY"):
default_delay = int(millis)
elif after("LED") is not None:
LED.value ^= True
elif string := after("STRING"):
layout.write(string)
else:
press_keys(line)
previous_line = line
delay(default_delay)
exec(
contents,
dict(print=info, write=layout.write, led=led, delay=delay, press=press),
)
def run_script_file(path: str):
"""
Try reading and running a ducky script from the supplied path.
Try reading and running a python script from the supplied path.
"""
try:
with open(path, "r", encoding="utf-8") as handle:
@@ -119,14 +73,12 @@ def run_script_file(path: str):
except OSError as error:
warn(f"unable to open file {path}: {error}")
async def run_boot_script():
"""
If a script with the name 'boot.dd' exists,
run it without user interaction on boot.
Try reading and running a python script from the supplied path.
"""
try:
with open("payloads/boot.dd", "r", encoding="utf-8") as handle:
with open('boot_payload.py', "r", encoding="utf-8") as handle:
run_script(handle.read())
except OSError:
info("boot script does not exist, skipping its execution")
info("not boot script set")

View File

@@ -16,15 +16,15 @@ def consume() -> str:
return dump
def info(message: str):
def info(message):
"""
Add a log entry with the message prepended with the info marker
"""
logs.append("info: " + message)
logs.append("info: " + str(message))
def warn(message: str):
"""
Add a log entry with the message prepended with the warning marker
"""
logs.append("warning: " + message)
logs.append("warn: " + str(message))