Skip to content

Commit 6d85b34

Browse files
author
Erica Garcia
committed
Initial commit
0 parents  commit 6d85b34

25 files changed

+4756
-0
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.vscode/
2+
test.txt
3+
inject.bin

LICENSE.txt

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Copyright 2019 Erica Garcia (athenaorerica) <me@athenas.space>
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4+
5+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6+
7+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

README.md

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# USB Rubber Ducky Encoder (in Python!)
2+
This is a straight rewrite of [Hak5's Rubber Ducky Encoder](https://github.com/hak5darren/USB-Rubber-Ducky/tree/master/Encoder) in Python 3. I made this because it's easier to run on Android, and because [ducktoolkit](https://github.com/kevthehermit/DuckToolkit) does not use the same keymap .resource files as Hak5's encoder.
3+
4+
Licensed under the [MIT License](https://mit.athenas.space).

encoder.py

+342
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,342 @@
1+
# encoder.py
2+
# this file is part of usbrubberduckyencoder-python
3+
#
4+
# converts DuckyScript into usable hexfiles
5+
#
6+
# written by and copyright (C) Erica Garcia [athenaorerica] <me@athenas.space> 2019
7+
# licensed under the MIT license <http://mit.athenas.space>
8+
9+
import sys, os, argparse
10+
layoutProps = {}
11+
keyboardProps = {}
12+
13+
version = "0.2.1"
14+
debug = False
15+
16+
def main(args):
17+
global debug
18+
debug = args.debug
19+
if debug: print(args)
20+
inputfile = args.inputfile
21+
outputfile = args.outputfile
22+
layoutfile = args.layoutfile
23+
scriptStr = ""
24+
if ".rtf" in inputfile.name:
25+
print("rtfs aren't supported yet. why are you using RTF anyway?")
26+
raise NotImplementedError
27+
else:
28+
try:
29+
print("Loading DuckyScript...",end="",flush=True)
30+
scriptStr = inputfile.read()
31+
print("\t[ OK ]")
32+
inputfile.close()
33+
if debug: print(scriptStr)
34+
except:
35+
print("\t[ FAIL ]")
36+
raise
37+
loadProperties(layoutfile)
38+
print("Encoding...\t", end="",flush=True)
39+
encodeToFile(scriptStr, (outputfile))
40+
41+
def loadProperties(lang):
42+
try:
43+
print("Loading keyboard...",end="",flush=True)
44+
with open(os.path.dirname(os.path.abspath(__file__)) + "/resources/keyboard.properties", 'r') as file:
45+
for line in file:
46+
strStrp = line.strip()
47+
if strStrp.startswith("//"): continue
48+
if strStrp == "": continue
49+
lsplTmp = strStrp.split("=")
50+
lspl = []
51+
for ls in lsplTmp:
52+
lspl.append(ls.strip())
53+
keyboardProps.update({lspl[0]: lspl[1]})
54+
print("\t[ OK ]")
55+
if len(keyboardProps) == 0: raise Exception("keyprops empty")
56+
except:
57+
print("\t[ FAIL ]")
58+
raise
59+
try:
60+
print("Loading language...", end="",flush=True)
61+
with open(os.path.dirname(os.path.abspath(__file__)) + "/resources/{0}.properties".format(lang), 'r') as file:
62+
for line in file:
63+
if line.startswith("//"): continue
64+
strStrp = line.strip()
65+
if strStrp == "": continue
66+
lsplTmp = strStrp.split("=")
67+
lspl = []
68+
for ls in lsplTmp:
69+
lspl.append(ls.strip())
70+
layoutProps.update({lspl[0]: lspl[1]})
71+
print("\t[ OK ]")
72+
if len(layoutProps) == 0: raise Exception("langfile empty")
73+
except:
74+
print("\t[ FAIL ]")
75+
raise
76+
77+
def strInstrToByte(instruction): #must return byte
78+
instruction = instruction.strip()
79+
if "KEY_{0}".format(instruction) in keyboardProps:
80+
newB = strToByte(keyboardProps["KEY_{0}".format(instruction)])
81+
elif instruction == "ESCAPE":
82+
newB = strInstrToByte("ESC")
83+
elif instruction == "DEL":
84+
newB = strInstrToByte("DELETE")
85+
elif instruction == "BREAK":
86+
newB = strInstrToByte("PAUSE")
87+
elif instruction == "CONTROL":
88+
newB = strInstrToByte("CTRL")
89+
elif instruction == "DOWNARROW":
90+
newB = strInstrToByte("DOWN")
91+
elif instruction == "UPARROW":
92+
newB = strInstrToByte("UP")
93+
elif instruction == "LEFTARROW":
94+
newB = strInstrToByte("LEFT")
95+
elif instruction == "RIGHTARROW":
96+
newB = strInstrToByte("RIGHT")
97+
elif instruction == "MENU":
98+
newB = strInstrToByte("APP")
99+
elif instruction == "WINDOWS":
100+
newB = strInstrToByte("GUI")
101+
elif instruction == "PLAY" or instruction == "PAUSE":
102+
newB = strInstrToByte("MEDIA_PLAY_PAUSE")
103+
elif instruction == "STOP":
104+
newB = strInstrToByte("MEDIA_STOP")
105+
elif instruction == "MUTE":
106+
newB = strInstrToByte("MEDIA_MUTE")
107+
elif instruction == "VOLUMEUP":
108+
newB = strInstrToByte("MEDIA_VOLUME_INC")
109+
elif instruction == "VOLUMEDOWN":
110+
newB = strInstrToByte("MEDIA_VOLUME_DEC")
111+
elif instruction == "SCROLLLOCK":
112+
newB = strInstrToByte("SCROLL_LOCK")
113+
elif instruction == "NUMLOCK":
114+
newB = strInstrToByte("NUM_LOCK")
115+
elif instruction == "CAPSLOCK":
116+
newB = strInstrToByte("CAPS_LOCK")
117+
else:
118+
newB = int_to_bytes(charToBytes(instruction[0])[0])
119+
return newB
120+
121+
def addBytes(file, byteTab):
122+
for i in range(0, len(byteTab)):
123+
file += byteTab[i].to_bytes(1,'big')
124+
if (len(byteTab) % 2) != 0:
125+
file += b'\x00'
126+
return file
127+
128+
def int_to_bytes(x): #must return byte
129+
return x.to_bytes((x.bit_length() + 7) // 8, 'big')
130+
131+
def int_from_bytes(xbytes): #must return int
132+
return int.from_bytes(xbytes, 'big')
133+
134+
def strToByte(st): #must return byte
135+
return int_to_bytes(int(st,0))
136+
137+
def codeToBytes(st): #must return byte or bytearray
138+
if st in layoutProps:
139+
keys = layoutProps[st].split(",")
140+
byteTab = b''
141+
j = 0
142+
while j < len(keys):
143+
key = keys[j].strip()
144+
if key in keyboardProps:
145+
b = strToByte(keyboardProps[key].strip())
146+
byteTab += b
147+
elif key in layoutProps:
148+
b = strToByte(layoutProps[key].strip())
149+
byteTab += b
150+
else:
151+
print("Key not found: {0}".format(key))
152+
byteTab += b'\x00'
153+
j += 1
154+
return byteTab
155+
else:
156+
print("Char not found: {0}".format(st))
157+
byteTab = b'\x00'
158+
return byteTab
159+
160+
def charToCode(c): #must return str
161+
code = ""
162+
c = ord(c)
163+
ch = "{:02x}".format(c).upper()
164+
if c < 128:
165+
code = "ASCII_{0}".format(ch)
166+
elif c < 256:
167+
code = "ISO_8859_1_{0}".format(ch)
168+
else:
169+
code = "UNICODE_{0}".format(ch)
170+
return code
171+
172+
def charToBytes(c): #must return byte
173+
return codeToBytes(charToCode(c))
174+
175+
def encodeToFile(inStr, fileDest):
176+
inStr = inStr.replace('\r','')
177+
instructions = inStr.split('\n')
178+
last_instruction = instructions
179+
file = b''
180+
defaultDelay = 0
181+
loop = 0
182+
for i in range(0, len(instructions)):
183+
try:
184+
delayOverride = False
185+
if instructions[i].strip().startswith("//") or instructions[i].strip().startswith("#") or instructions[i] == "\n" or instructions[i] == "": continue # the line was a comment
186+
instruction = instructions[i].strip().split(" ", 1)
187+
if i > 0:
188+
last_instruction = instructions[i-1].split(" ", 1)
189+
last_instruction[0] = last_instruction[0].strip()
190+
if len(last_instruction) == 2:
191+
last_instruction[1] = last_instruction[1].strip()
192+
193+
else:
194+
last_instruction = instructions[i].split(" ", 1)
195+
last_instruction[0] = last_instruction[0].strip()
196+
if len(last_instruction) == 2:
197+
last_instruction[1] = last_instruction[1].strip()
198+
instruction[0] = instruction[0].strip().upper()
199+
200+
if len(instruction) == 2:
201+
instruction[1] = instruction[1].strip()
202+
203+
if instruction[0] == "REM": continue # line is a comment
204+
if instruction[0] == "REPEAT":
205+
loop = int(instruction[1].strip())
206+
else:
207+
loop = 1
208+
while loop > 0:
209+
if debug: print(str(instruction))
210+
if instruction[0] == "DEFAULT_DELAY" or instruction[0] == "DEFAULTDELAY":
211+
defaultDelay = int(instruction[1].strip())
212+
delayOverride = True
213+
elif instruction[0] == "DELAY":
214+
delay = int(instruction[1].strip())
215+
while delay > 0:
216+
file += b'\x00'
217+
if delay > 255:
218+
file += b'\xff'
219+
delay -= 255
220+
else:
221+
file += delay.to_bytes(1,'big')
222+
delay = 0
223+
delayOverride = True
224+
elif instruction[0] == "STRING":
225+
for i in range(0, len(instruction[1])): # can probably be simplified to "for i in instruction[1]"
226+
file = addBytes(file, charToBytes(instruction[1][i]))
227+
elif instruction[0] == "STRING_DELAY":
228+
twoOptions = instruction[1].split(" ", 1)
229+
delayMillis = int(twoOptions[0].strip())
230+
userText = twoOptions[1].strip()
231+
if debug: print(delayMillis)
232+
if debug: print(userText)
233+
for i in range(0, len(userText)):
234+
c = userText[i]
235+
file = addBytes(file, charToBytes(c))
236+
for i in range(0, (delayMillis // 255)):
237+
file += b'\xff'
238+
file += (delayMillis % 255).to_bytes(1,'big')
239+
elif instruction[0] == "CONTROL" or instruction[0] == "CTRL":
240+
if len(instruction) != 1:
241+
file += strInstrToByte(instruction[1])
242+
file += strToByte(keyboardProps["MODIFIERKEY_CTRL"])
243+
else:
244+
file += strToByte(keyboardProps["KEY_LEFT_CTRL"])
245+
file += b'\x00'
246+
elif instruction[0] == "ALT":
247+
if len(instruction) != 1:
248+
file += strInstrToByte(instruction[1])
249+
file += strToByte(keyboardProps["MODIFIERKEY_ALT"])
250+
else:
251+
file += strToByte(keyboardProps["KEY_LEFT_ALT"])
252+
file += b'\x00'
253+
elif instruction[0] == "SHIFT":
254+
if len(instruction) != 1:
255+
file += strInstrToByte(instruction[1])
256+
file += strToByte(keyboardProps["MODIFIERKEY_SHIFT"])
257+
else:
258+
file += strToByte(keyboardProps["KEY_LEFT_SHIFT"])
259+
file += b'\x00'
260+
elif instruction[0] == "CTRL-ALT":
261+
if len(instruction) != 1:
262+
file += strInstrToByte(instruction[1])
263+
file += (strToByte(keyboardProps["MODIFIERKEY_CTRL"]) | strToByte(keyboardProps["MODIFIERKEY_ALT"]))
264+
else:
265+
continue
266+
elif instruction[0] == "CTRL-SHIFT":
267+
if len(instruction) != 1:
268+
file += strInstrToByte(instruction[1])
269+
file += (strToByte(keyboardProps["MODIFIERKEY_CTRL"]) | strToByte(keyboardProps["MODIFIERKEY_SHIFT"]))
270+
else:
271+
continue
272+
elif instruction[0] == "COMMAND-OPTION":
273+
if len(instruction) != 1:
274+
file += strInstrToByte(instruction[1])
275+
file += (strToByte(keyboardProps["MODIFIERKEY_KEY_LEFT_GUI"]) | strToByte(keyboardProps["MODIFIERKEY_ALT"]))
276+
else:
277+
continue
278+
elif instruction[0] == "ALT-SHIFT":
279+
if len(instruction) != 1:
280+
file += strInstrToByte(instruction[1])
281+
file += (strToByte(keyboardProps["MODIFIERKEY_LEFT_ALT"]) | strToByte(keyboardProps["MODIFIERKEY_SHIFT"]))
282+
else:
283+
file += strToByte(keyboardProps["KEY_LEFT_ALT"])
284+
file += (strToByte(keyboardProps["MODIFIERKEY_LEFT_ALT"]) | strToByte(keyboardProps["MODIFIERKEY_SHIFT"]))
285+
elif instruction[0] == "ALT-TAB":
286+
if len(instruction) == 1:
287+
file += strToByte(keyboardProps["KEY_TAB"])
288+
file += strToByte(keyboardProps["MODIFIERKEY_LEFT_ALT"])
289+
else:
290+
pass #this was marked as do something in the original Ducky encoder
291+
elif instruction[0] == "REM":
292+
delayOverride = True #no default delay for comments
293+
continue
294+
elif instruction[0] == "WINDOWS" or instruction[0] == "GUI":
295+
if len(instruction) == 1:
296+
file += strToByte(keyboardProps["MODIFIERKEY_LEFT_GUI"])
297+
file += b'\x00'
298+
else:
299+
file += strInstrToByte(instruction[1])
300+
file += strToByte(keyboardProps["MODIFIERKEY_LEFT_GUI"])
301+
elif instruction[0] == "COMMAND":
302+
if len(instruction) == 1:
303+
file += strToByte(keyboardProps["KEY_COMMAND"])
304+
file += b'\x00'
305+
else:
306+
file += strInstrToByte(instruction[1])
307+
file += strToByte(keyboardProps["MODIFIERKEY_LEFT_GUI"])
308+
else:
309+
tmpB = strInstrToByte(instruction[0])
310+
file += tmpB
311+
file += b'\x00'
312+
loop -= 1
313+
if (not delayOverride) and (defaultDelay > 0):
314+
delayCounter = defaultDelay
315+
while delayCounter > 0:
316+
file += b'\x00'
317+
if delayCounter > 255:
318+
file += b'\xff'
319+
delayCounter -= 255
320+
else:
321+
file += delayCounter.to_bytes(1, 'big')
322+
delayCounter = 0
323+
except IndexError:
324+
pass
325+
except:
326+
print("Error on line {0}".format(int(i+1)))
327+
raise
328+
try:
329+
fileDest.write(file)
330+
fileDest.flush()
331+
fileDest.close()
332+
print("\t[ OK ]")
333+
except:
334+
print("Failed to write hex file!")
335+
336+
if __name__ == "__main__":
337+
parser = argparse.ArgumentParser(description="Rubber Ducky Encoder (Python rewrite) v{0}\n".format(version))
338+
parser.add_argument('inputfile', nargs='?', default=sys.stdin, metavar="INPUTFILE", type=argparse.FileType('r',encoding='utf-8'))
339+
parser.add_argument('-o', metavar="OUTFILE", type=argparse.FileType('wb'), default='inject.bin',dest='outputfile')
340+
parser.add_argument('-l', metavar="LAYOUTFILE", type=str, default='us', dest='layoutfile')
341+
parser.add_argument('-d', action='store_true', dest='debug', help="enables debug mode")
342+
main(parser.parse_args())

0 commit comments

Comments
 (0)