├── LICENSE ├── README.md └── code.py /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Shayne Hartford 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CircuitPython-DuckyScript 2 | DuckyScript interpreter written in CircuitPython tested on a RaspberryPI Pico 3 | 4 | This may not be 100% compatible and bug free 5 | Pull requests are welcome 6 | 7 | Known Issues: 8 | - macOS FN modifier key (CircuitPython limitation) 9 | - F20+ (CircuitPython limitation) 10 | - Missing Modes and Mode switching (PR Welcome) 11 | -------------------------------------------------------------------------------- /code.py: -------------------------------------------------------------------------------- 1 | import time 2 | import usb_hid 3 | from adafruit_hid.keyboard import Keyboard 4 | from adafruit_hid.keycode import Keycode 5 | from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS 6 | 7 | kbd = Keyboard(usb_hid.devices) 8 | layout = KeyboardLayoutUS(kbd) 9 | 10 | default_delay = 0 11 | previous_line = '' 12 | 13 | def press(k): 14 | if len(k) == 1: 15 | layout.write(k[0]) 16 | elif k == 'ENTER' or k == 'RETURN': 17 | kbd.press(Keycode.ENTER) 18 | elif k == 'ESC' or k == 'ESCAPE': 19 | kbd.press(Keycode.ESCAPE) 20 | elif k == 'BACKSPACE': 21 | kbd.press(Keycode.BACKSPACE) 22 | elif k == 'TAB': 23 | kbd.press(Keycode.TAB) 24 | elif k == 'SPACE' or k == 'SPACEBAR': 25 | kbd.press(Keycode.SPACEBAR) 26 | elif k == 'MINUS': 27 | kbd.press(Keycode.MINUS) 28 | elif k == 'EQUAL' or k == 'EQUALS': 29 | kbd.press(Keycode.EQUALS) 30 | elif k == 'LEFT_BRACKET' or k == 'LEFTBRACKET': 31 | kbd.press(Keycode.LEFT_BRACKET) 32 | elif k == 'RIGHT_BRACKET' or k == 'RIGHTBRACKET': 33 | kbd.press(Keycode.RIGHT_BRACKET) 34 | elif k == 'BACKSLASH': 35 | kbd.press(Keycode.BACKSLASH) 36 | elif k == 'POUND': 37 | kbd.press(Keycode.POUND) 38 | elif k == 'SEMICOLON': 39 | kbd.press(Keycode.SEMICOLON) 40 | elif k == 'QUOTE': 41 | kbd.press(Keycode.QUOTE) 42 | elif k == 'TILDE': 43 | kbd.press(Keycode.GRAVE_ACCENT) 44 | elif k == 'COMMA': 45 | kbd.press(Keycode.COMMA) 46 | elif k == 'PERIOD': 47 | kbd.press(Keycode.PERIOD) 48 | elif k == 'SLASH' or k == 'FORWARD_SLASH' or k == 'FORWARDSLASH': 49 | kbd.press(Keycode.FORWARD_SLASH) 50 | elif k == 'CAPS' or k == 'CAPS_LOCK' or k == 'CAPSLOCK': 51 | kbd.press(Keycode.CAPS_LOCK) 52 | elif k == 'PRINT' or k == 'PRINT_SCREEN' or k == 'PRINTSCREEN': 53 | kbd.press(Keycode.PRINT_SCREEN) 54 | elif k == 'SCROLL' or k == 'SCROLL_LOCK' or k == 'SCROLLLOCK': 55 | kbd.press(Keycode.SCROLL_LOCK) 56 | elif k == 'PAUSE' or k == 'BREAK': 57 | kbd.press(Keycode.PAUSE) 58 | elif k == 'INSERT': 59 | kbd.press(Keycode.INSERT) 60 | elif k == 'HOME': 61 | kbd.press(Keycode.HOME) 62 | elif k == 'PAGE_UP' or k == 'PAGEUP': 63 | kbd.press(Keycode.PAGE_UP) 64 | elif k == 'DELETE': 65 | kbd.press(Keycode.DELETE) 66 | elif k == 'END': 67 | kbd.press(Keycode.END) 68 | elif k == 'PAGE_DOWN' or k == 'PAGEDOWN': 69 | kbd.press(Keycode.PAGE_DOWN) 70 | elif k == 'RIGHT' or k == 'RIGHT_ARROW' or k == 'RIGHTARROW': 71 | kbd.press(Keycode.RIGHT_ARROW) 72 | elif k == 'LEFT' or k == 'LEFT_ARROW' or k == 'LEFTARROW': 73 | kbd.press(Keycode.LEFT_ARROW) 74 | elif k == 'DOWN' or k == 'DOWN_ARROW' or k == 'DOWNARROW': 75 | kbd.press(Keycode.DOWN_ARROW) 76 | elif k == 'UP' or k == 'UP_ARROW' or k == 'UPARROW': 77 | kbd.press(Keycode.UP_ARROW) 78 | elif k == 'NUM' or k == 'NUM_LOCK' or k == 'NUMLOCK': 79 | kbd.press(Keycode.NUM_LOCK) 80 | elif k == 'APPLICATION' or k == 'MENU': 81 | kbd.press(Keycode.APPLICATION) 82 | elif k == 'LEFT_CONTROL' or k == 'CONTROL' or k == 'CTRL': 83 | kbd.press(Keycode.LEFT_CONTROL) 84 | elif k == 'LEFT_SHIFT' or k == 'SHIFT': 85 | kbd.press(Keycode.LEFT_SHIFT) 86 | elif k == 'LEFT_GUI' or k == 'GUI': 87 | kbd.press(Keycode.LEFT_GUI) 88 | elif k == 'LEFT_WINDOWS' or k == 'WINDOWS': 89 | kbd.press(Keycode.WINDOWS) 90 | elif k == 'RIGHT_CONTROL': 91 | kbd.press(Keycode.RIGHT_CONTROL) 92 | elif k == 'RIGHT_SHIFT': 93 | kbd.press(Keycode.RIGHT_SHIFT) 94 | elif k == 'RIGHT_ALT': 95 | kbd.press(Keycode.RIGHT_ALT) 96 | elif k == 'RIGHT_GUI': 97 | kbd.press(Keycode.RIGHT_GUI) 98 | elif k == 'F1': 99 | kbd.press(Keycode.F1) 100 | elif k == 'F2': 101 | kbd.press(Keycode.F2) 102 | elif k == 'F3': 103 | kbd.press(Keycode.F3) 104 | elif k == 'F4': 105 | kbd.press(Keycode.F4) 106 | elif k == 'F5': 107 | kbd.press(Keycode.F5) 108 | elif k == 'F6': 109 | kbd.press(Keycode.F6) 110 | elif k == 'F7': 111 | kbd.press(Keycode.F7) 112 | elif k == 'F8': 113 | kbd.press(Keycode.F8) 114 | elif k == 'F9': 115 | kbd.press(Keycode.F9) 116 | elif k == 'F10': 117 | kbd.press(Keycode.F10) 118 | elif k == 'F11': 119 | kbd.press(Keycode.F11) 120 | elif k == 'F12': 121 | kbd.press(Keycode.F12) 122 | elif k == 'F13': 123 | kbd.press(Keycode.F13) 124 | elif k == 'F14': 125 | kbd.press(Keycode.F14) 126 | elif k == 'F15': 127 | kbd.press(Keycode.F15) 128 | elif k == 'F16': 129 | kbd.press(Keycode.F16) 130 | elif k == 'F17': 131 | kbd.press(Keycode.F17) 132 | elif k == 'F18': 133 | kbd.press(Keycode.F18) 134 | elif k == 'F19': 135 | kbd.press(Keycode.F19) 136 | 137 | def process(line): 138 | args = line.split(' ', 1) 139 | inst = args[0] 140 | 141 | if inst == 'REM': 142 | return 143 | 144 | global default_delay 145 | global previous_line 146 | 147 | if default_delay > 0: 148 | time.sleep(default_delay / 1000) 149 | 150 | if inst == 'DEFAULT_DELAY' or inst == 'DEFAULTDELAY': 151 | try: 152 | default_delay = int(args[1]) 153 | except ValueError: 154 | return 155 | elif inst == 'DELAY': 156 | try: 157 | time.sleep(int(args[1]) / 1000) 158 | except ValueError: 159 | return 160 | elif inst == 'STRING_DELAY' or inst == 'STRINGDELAY': 161 | try: 162 | args_1 = args[1].split(' ', 1) 163 | for c in args_1[1]: 164 | layout.write(c) 165 | time.sleep(int(args_1[0]) / 1000) 166 | except ValueError: 167 | return 168 | elif inst == 'STRING': 169 | layout.write(args[1]) 170 | elif inst == 'REPEAT': 171 | try: 172 | for _ in range(int(args[1])): 173 | process(previous_line) 174 | except ValueError: 175 | return 176 | else: 177 | press(inst) 178 | if len(args) > 1: 179 | for s in args[1].split(' '): 180 | press(s) 181 | 182 | previous_line = line 183 | 184 | kbd.release_all() 185 | 186 | with open('payload.txt') as file: 187 | lines = file.read().splitlines() 188 | for line in lines: 189 | process(line) 190 | --------------------------------------------------------------------------------