Update BlueDucky.py
This commit is contained in:
parent
43af3d43da
commit
3ce22ff5e4
385
BlueDucky.py
385
BlueDucky.py
|
@ -96,6 +96,27 @@ class PairingAgent:
|
||||||
log.error(f"Error terminating agent process: {e}")
|
log.error(f"Error terminating agent process: {e}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
class L2CAPConnectionManager:
|
||||||
|
def __init__(self, target_address):
|
||||||
|
self.target_address = target_address
|
||||||
|
self.clients = {}
|
||||||
|
|
||||||
|
def create_connection(self, port):
|
||||||
|
client = L2CAPClient(self.target_address, port)
|
||||||
|
self.clients[port] = client
|
||||||
|
return client
|
||||||
|
|
||||||
|
def connect_all(self):
|
||||||
|
try:
|
||||||
|
return sum(client.connect() for client in self.clients.values())
|
||||||
|
except ConnectionFailureException as e:
|
||||||
|
log.error(f"Connection failure: {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
def close_all(self):
|
||||||
|
for client in self.clients.values():
|
||||||
|
client.close()
|
||||||
|
|
||||||
class L2CAPClient:
|
class L2CAPClient:
|
||||||
def __init__(self, addr, port):
|
def __init__(self, addr, port):
|
||||||
self.addr = addr
|
self.addr = addr
|
||||||
|
@ -103,20 +124,6 @@ class L2CAPClient:
|
||||||
self.connected = False
|
self.connected = False
|
||||||
self.sock = None
|
self.sock = None
|
||||||
|
|
||||||
def encode_combo_input(*args):
|
|
||||||
if not args:
|
|
||||||
return bytes([0xA1, 0x01] + [0] * 8) # Empty report for key release
|
|
||||||
|
|
||||||
# Filter out non-Key_Codes arguments and process
|
|
||||||
valid_args = [a for a in args if isinstance(a, Key_Codes)]
|
|
||||||
|
|
||||||
# Properly sum the values of modifiers
|
|
||||||
modifiers = sum(a.value for a in valid_args if a in Key_Codes.MODIFIERS)
|
|
||||||
|
|
||||||
keycodes = [a.value for a in valid_args if a not in Key_Codes.MODIFIERS]
|
|
||||||
keycodes += [0] * (6 - len(keycodes))
|
|
||||||
return bytes([0xA1, 0x01, modifiers, 0x00] + keycodes)
|
|
||||||
|
|
||||||
def encode_keyboard_input(*args):
|
def encode_keyboard_input(*args):
|
||||||
keycodes = []
|
keycodes = []
|
||||||
flags = 0
|
flags = 0
|
||||||
|
@ -142,7 +149,7 @@ class L2CAPClient:
|
||||||
return
|
return
|
||||||
|
|
||||||
log.debug(f"[TX-{self.port}] Attempting to send data: {binascii.hexlify(data).decode()}")
|
log.debug(f"[TX-{self.port}] Attempting to send data: {binascii.hexlify(data).decode()}")
|
||||||
if self.attempt_send(data, 0.1):
|
if self.attempt_send(data, 0.001):
|
||||||
log.debug(f"[TX-{self.port}] Data sent successfully")
|
log.debug(f"[TX-{self.port}] Data sent successfully")
|
||||||
else:
|
else:
|
||||||
log.error(f"[TX-{self.port}] ERROR! Timed out sending data")
|
log.error(f"[TX-{self.port}] ERROR! Timed out sending data")
|
||||||
|
@ -205,7 +212,7 @@ class L2CAPClient:
|
||||||
def send_keyboard_report(self, *args):
|
def send_keyboard_report(self, *args):
|
||||||
self.send(self.encode_keyboard_input(*args))
|
self.send(self.encode_keyboard_input(*args))
|
||||||
|
|
||||||
def send_keypress(self, *args, delay=0.05):
|
def send_keypress(self, *args, delay=0.01):
|
||||||
if args:
|
if args:
|
||||||
log.debug(f"Attempting to send... {args}")
|
log.debug(f"Attempting to send... {args}")
|
||||||
self.send(self.encode_keyboard_input(*args))
|
self.send(self.encode_keyboard_input(*args))
|
||||||
|
@ -214,117 +221,194 @@ class L2CAPClient:
|
||||||
self.send(self.encode_keyboard_input())
|
self.send(self.encode_keyboard_input())
|
||||||
time.sleep(delay)
|
time.sleep(delay)
|
||||||
|
|
||||||
def send_combination(self, *keys, delay=0.05):
|
def send_keyboard_combination(self, modifier, key, delay=0.01):
|
||||||
"""
|
# Press the combination
|
||||||
Send a combination of keys, which can include modifiers and regular keys.
|
press_report = self.encode_keyboard_input(modifier, key)
|
||||||
"""
|
self.send(press_report)
|
||||||
modifiers = 0
|
time.sleep(delay) # Delay to simulate key press
|
||||||
regular_keys = []
|
|
||||||
|
|
||||||
for key in keys:
|
# Release the combination
|
||||||
if key in Key_Codes.MODIFIERS:
|
release_report = self.encode_keyboard_input()
|
||||||
modifiers |= key.value
|
self.send(release_report)
|
||||||
else:
|
|
||||||
regular_keys.append(key.value)
|
|
||||||
|
|
||||||
# Ensure that no more than 6 regular keys are sent
|
|
||||||
regular_keys = regular_keys[:6] + [0] * (6 - len(regular_keys))
|
|
||||||
|
|
||||||
# Create the HID report and send it
|
|
||||||
report = bytes([0xa1, 0x01, modifiers, 0x00] + regular_keys)
|
|
||||||
self.send(report)
|
|
||||||
time.sleep(delay)
|
time.sleep(delay)
|
||||||
|
|
||||||
# Send an empty report to release the keys
|
def process_duckyscript(client, duckyscript):
|
||||||
self.send(self.encode_combo_input())
|
client.send_keypress('') # Send empty report
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
class L2CAPConnectionManager:
|
shift_required_characters = "!@#$%^&*()_+{}|:\"<>?ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
def __init__(self, target_address):
|
|
||||||
self.target_address = target_address
|
|
||||||
self.clients = {}
|
|
||||||
|
|
||||||
def create_connection(self, port):
|
for line in duckyscript:
|
||||||
client = L2CAPClient(self.target_address, port)
|
line = line.strip()
|
||||||
self.clients[port] = client
|
if not line or line.startswith("REM"):
|
||||||
return client
|
continue
|
||||||
|
|
||||||
def connect_all(self):
|
if line.startswith("STRING"):
|
||||||
|
text = line[7:]
|
||||||
|
for char in text:
|
||||||
try:
|
try:
|
||||||
return sum(client.connect() for client in self.clients.values())
|
if char.isdigit():
|
||||||
except ConnectionFailureException as e:
|
key_code = getattr(Key_Codes, f"_{char}")
|
||||||
log.error(f"Connection failure: {e}")
|
client.send_keypress(key_code)
|
||||||
raise
|
elif char == " ":
|
||||||
|
client.send_keypress(Key_Codes.SPACE)
|
||||||
|
elif char == "[":
|
||||||
|
client.send_keypress(Key_Codes.LEFTBRACE)
|
||||||
|
elif char == "]":
|
||||||
|
client.send_keypress(Key_Codes.RIGHTBRACE)
|
||||||
|
elif char == ";":
|
||||||
|
client.send_keypress(Key_Codes.SEMICOLON)
|
||||||
|
elif char == "'":
|
||||||
|
client.send_keypress(Key_Codes.QUOTE)
|
||||||
|
elif char == "/":
|
||||||
|
client.send_keypress(Key_Codes.SLASH)
|
||||||
|
elif char == ".":
|
||||||
|
client.send_keypress(Key_Codes.DOT)
|
||||||
|
elif char == ",":
|
||||||
|
client.send_keypress(Key_Codes.COMMA)
|
||||||
|
elif char == "|":
|
||||||
|
client.send_keypress(Key_Codes.PIPE)
|
||||||
|
elif char == "-":
|
||||||
|
client.send_keypress(Key_Codes.MINUS)
|
||||||
|
elif char == "=":
|
||||||
|
client.send_keypress(Key_Codes.EQUAL)
|
||||||
|
elif char in shift_required_characters:
|
||||||
|
key_code_str = char_to_key_code(char)
|
||||||
|
if key_code_str:
|
||||||
|
key_code = getattr(Key_Codes, key_code_str)
|
||||||
|
client.send_keyboard_combination(Modifier_Codes.SHIFT, key_code)
|
||||||
|
else:
|
||||||
|
log.warning(f"Unsupported character '{char}' in Duckyscript")
|
||||||
|
elif char.isalpha():
|
||||||
|
key_code = getattr(Key_Codes, char.lower())
|
||||||
|
if char.isupper():
|
||||||
|
client.send_keyboard_combination(Modifier_Codes.SHIFT, key_code)
|
||||||
|
else:
|
||||||
|
client.send_keypress(key_code)
|
||||||
|
else:
|
||||||
|
key_code = char_to_key_code(char)
|
||||||
|
if key_code:
|
||||||
|
client.send_keypress(key_code)
|
||||||
|
else:
|
||||||
|
log.warning(f"Unsupported character '{char}' in Duckyscript")
|
||||||
|
|
||||||
def close_all(self):
|
client.send_keypress() # Release after each key press
|
||||||
for client in self.clients.values():
|
except AttributeError as e:
|
||||||
client.close()
|
log.warning(f"Attribute error: {e} - Unsupported character '{char}' in Duckyscript")
|
||||||
|
|
||||||
def terminate_child_processes():
|
elif any(mod in line for mod in ["SHIFT", "ALT", "CTRL", "GUI", "COMMAND", "WINDOWS"]):
|
||||||
for proc in child_processes:
|
# Process modifier key combinations
|
||||||
if proc.is_alive():
|
components = line.split()
|
||||||
proc.terminate()
|
if len(components) == 2:
|
||||||
proc.join()
|
modifier, key = components
|
||||||
|
try:
|
||||||
|
# Convert to appropriate enums
|
||||||
|
modifier_enum = getattr(Modifier_Codes, modifier.upper())
|
||||||
|
key_enum = getattr(Key_Codes, key.lower())
|
||||||
|
client.send_keyboard_combination(modifier_enum, key_enum)
|
||||||
|
log.debug(f"Sent combination: {line}")
|
||||||
|
except AttributeError:
|
||||||
|
log.warning(f"Unsupported combination: {line}")
|
||||||
|
else:
|
||||||
|
log.warning(f"Invalid combination format: {line}")
|
||||||
|
|
||||||
def setup_bluetooth(target_address):
|
def char_to_key_code(char):
|
||||||
restart_bluetooth_daemon()
|
# Mapping for special characters that always require SHIFT
|
||||||
profile_proc = Process(target=register_hid_profile, args=('hci0', target_address))
|
shift_char_map = {
|
||||||
profile_proc.start()
|
'!': 'EXCLAMATION_MARK',
|
||||||
child_processes.append(profile_proc)
|
'@': 'AT_SYMBOL',
|
||||||
adapter = Adapter('hci0')
|
'#': 'HASHTAG',
|
||||||
adapter.set_property("name", "Robot POC")
|
'$': 'DOLLAR',
|
||||||
adapter.set_property("class", 0x002540)
|
'%': 'PERCENT_SYMBOL',
|
||||||
adapter.power(True)
|
'^': 'CARET_SYMBOL',
|
||||||
return adapter
|
'&': 'AMPERSAND_SYMBOL',
|
||||||
|
'*': 'ASTERISK_SYMBOL',
|
||||||
|
'(': 'OPEN_PARENTHESIS',
|
||||||
|
')': 'CLOSE_PARENTHESIS',
|
||||||
|
'_': 'UNDERSCORE_SYMBOL',
|
||||||
|
'+': 'KEYPADPLUS',
|
||||||
|
'{': 'LEFTBRACE',
|
||||||
|
'}': 'RIGHTBRACE',
|
||||||
|
':': 'SEMICOLON',
|
||||||
|
'\\': 'BACKSLASH',
|
||||||
|
'"': 'QUOTE',
|
||||||
|
'<': 'COMMA',
|
||||||
|
'>': 'DOT',
|
||||||
|
'?': 'QUESTIONMARK',
|
||||||
|
'A': 'a',
|
||||||
|
'B': 'b',
|
||||||
|
'C': 'c',
|
||||||
|
'D': 'd',
|
||||||
|
'E': 'e',
|
||||||
|
'F': 'f',
|
||||||
|
'G': 'g',
|
||||||
|
'H': 'h',
|
||||||
|
'I': 'i',
|
||||||
|
'J': 'j',
|
||||||
|
'K': 'k',
|
||||||
|
'L': 'l',
|
||||||
|
'M': 'm',
|
||||||
|
'N': 'n',
|
||||||
|
'O': 'o',
|
||||||
|
'P': 'p',
|
||||||
|
'Q': 'q',
|
||||||
|
'R': 'r',
|
||||||
|
'S': 's',
|
||||||
|
'T': 't',
|
||||||
|
'U': 'u',
|
||||||
|
'V': 'v',
|
||||||
|
'W': 'w',
|
||||||
|
'X': 'x',
|
||||||
|
'Y': 'y',
|
||||||
|
'Z': 'z',
|
||||||
|
|
||||||
|
}
|
||||||
|
return shift_char_map.get(char)
|
||||||
|
|
||||||
# Key codes for modifier keys
|
# Key codes for modifier keys
|
||||||
class Modifier_Codes(Enum):
|
class Modifier_Codes(Enum):
|
||||||
LEFTCONTROL = 0xe0
|
CTRL = 0x01
|
||||||
LEFTSHIFT = 0xe1
|
RIGHTCTRL = 0x10
|
||||||
LEFTALT = 0xe2
|
|
||||||
LEFTGUI = 0xe3
|
|
||||||
RIGHTCONTROL = 0xe4
|
|
||||||
RIGHTSHIFT = 0xe5
|
|
||||||
RIGHTALT = 0xe6
|
|
||||||
RIGHTGUI = 0xe7
|
|
||||||
|
|
||||||
# Convenience mappings for common names
|
SHIFT = 0x02
|
||||||
CTRL = LEFTCONTROL
|
RIGHTSHIFT = 0x20
|
||||||
ALT = LEFTALT
|
|
||||||
SHIFT = LEFTSHIFT
|
|
||||||
GUI = LEFTGUI
|
|
||||||
|
|
||||||
# Modifier Key Set for easy checking
|
ALT = 0x04
|
||||||
MODIFIER_KEYS_SET = {Modifier_Codes.LEFTCONTROL, Modifier_Codes.LEFTSHIFT, Modifier_Codes.LEFTALT, Modifier_Codes.LEFTGUI,
|
RIGHTALT = 0x40
|
||||||
Modifier_Codes.RIGHTCONTROL, Modifier_Codes.RIGHTSHIFT, Modifier_Codes.RIGHTALT, Modifier_Codes.RIGHTGUI}
|
|
||||||
|
GUI = 0x08
|
||||||
|
WINDOWS = 0x08
|
||||||
|
COMMAND = 0x08
|
||||||
|
RIGHTGUI = 0x80
|
||||||
|
|
||||||
class Key_Codes(Enum):
|
class Key_Codes(Enum):
|
||||||
NONE = 0x00
|
NONE = 0x00
|
||||||
A = 0x04
|
a = 0x04
|
||||||
B = 0x05
|
b = 0x05
|
||||||
C = 0x06
|
c = 0x06
|
||||||
D = 0x07
|
d = 0x07
|
||||||
E = 0x08
|
e = 0x08
|
||||||
F = 0x09
|
f = 0x09
|
||||||
G = 0x0a
|
g = 0x0a
|
||||||
H = 0x0b
|
h = 0x0b
|
||||||
I = 0x0c
|
i = 0x0c
|
||||||
J = 0x0d
|
j = 0x0d
|
||||||
K = 0x0e
|
k = 0x0e
|
||||||
L = 0x0f
|
l = 0x0f
|
||||||
M = 0x10
|
m = 0x10
|
||||||
N = 0x11
|
n = 0x11
|
||||||
O = 0x12
|
o = 0x12
|
||||||
P = 0x13
|
p = 0x13
|
||||||
Q = 0x14
|
q = 0x14
|
||||||
R = 0x15
|
r = 0x15
|
||||||
S = 0x16
|
s = 0x16
|
||||||
T = 0x17
|
t = 0x17
|
||||||
U = 0x18
|
u = 0x18
|
||||||
V = 0x19
|
v = 0x19
|
||||||
W = 0x1a
|
w = 0x1a
|
||||||
X = 0x1b
|
x = 0x1b
|
||||||
Y = 0x1c
|
y = 0x1c
|
||||||
Z = 0x1d
|
z = 0x1d
|
||||||
_1 = 0x1e
|
_1 = 0x1e
|
||||||
_2 = 0x1f
|
_2 = 0x1f
|
||||||
_3 = 0x20
|
_3 = 0x20
|
||||||
|
@ -344,50 +428,53 @@ class Key_Codes(Enum):
|
||||||
EQUAL = 0x2e
|
EQUAL = 0x2e
|
||||||
LEFTBRACE = 0x2f
|
LEFTBRACE = 0x2f
|
||||||
RIGHTBRACE = 0x30
|
RIGHTBRACE = 0x30
|
||||||
BACKSLASH = 0x31
|
|
||||||
SEMICOLON = 0x33
|
|
||||||
QUOTE = 0x34
|
|
||||||
BACKTICK = 0x35
|
|
||||||
COMMA = 0x36
|
|
||||||
DOT = 0x37
|
|
||||||
SLASH = 0x38
|
|
||||||
CAPSLOCK = 0x39
|
CAPSLOCK = 0x39
|
||||||
|
VOLUME_UP = 0xed
|
||||||
|
VOLUME_DOWN = 0xee
|
||||||
|
SEMICOLON = 0x33
|
||||||
|
COMMA = 0x36
|
||||||
|
PERIOD = 0x37
|
||||||
|
SLASH = 0x38
|
||||||
|
PIPE = 0x31
|
||||||
|
BACKSLASH = 0x31
|
||||||
|
GRAVE = 0x35
|
||||||
|
APOSTROPHE = 0x34
|
||||||
|
LEFT_BRACKET = 0x2f
|
||||||
|
RIGHT_BRACKET = 0x30
|
||||||
|
DOT = 0x37
|
||||||
|
|
||||||
def process_duckyscript(client, duckyscript):
|
# SHIFT KEY MAPPING
|
||||||
client.send_keypress('') # Send empty report
|
EXCLAMATION_MARK = 0x1e
|
||||||
time.sleep(0.5)
|
AT_SYMBOL = 0x1f
|
||||||
|
HASHTAG = 0x20
|
||||||
|
DOLLAR = 0x21
|
||||||
|
PERCENT_SYMBOL = 0x22
|
||||||
|
CARET_SYMBOL = 0x23
|
||||||
|
AMPERSAND_SYMBOL = 0x24
|
||||||
|
ASTERISK_SYMBOL = 0x25
|
||||||
|
OPEN_PARENTHESIS = 0x26
|
||||||
|
CLOSE_PARENTHESIS = 0x27
|
||||||
|
UNDERSCORE_SYMBOL = 0x2d
|
||||||
|
QUOTE = 0x34
|
||||||
|
QUESTIONMARK = 0x38
|
||||||
|
KEYPADPLUS = 0x57
|
||||||
|
|
||||||
for line in duckyscript:
|
def terminate_child_processes():
|
||||||
line = line.strip()
|
for proc in child_processes:
|
||||||
if not line or line.startswith("REM"):
|
if proc.is_alive():
|
||||||
continue # Skip empty lines and comments
|
proc.terminate()
|
||||||
|
proc.join()
|
||||||
|
|
||||||
if line.startswith("STRING"):
|
def setup_bluetooth(target_address):
|
||||||
text = line[7:]
|
restart_bluetooth_daemon()
|
||||||
for letter in text:
|
profile_proc = Process(target=register_hid_profile, args=('hci0', target_address))
|
||||||
try:
|
profile_proc.start()
|
||||||
# Use upper() to match the uppercase keys defined in Key_Codes
|
child_processes.append(profile_proc)
|
||||||
key_code = getattr(Key_Codes, letter.upper()) if letter != " " else Key_Codes.SPACE
|
adapter = Adapter('hci0')
|
||||||
client.send_keypress(key_code)
|
adapter.set_property("name", "Robot POC")
|
||||||
client.send_keypress()
|
adapter.set_property("class", 0x002540)
|
||||||
time.sleep(0.05) # Add a small delay between keypresses
|
adapter.power(True)
|
||||||
except AttributeError:
|
return adapter
|
||||||
log.warning(f"Unsupported character '{letter}' in Duckyscript")
|
|
||||||
|
|
||||||
elif line.startswith("GUI"):
|
|
||||||
# Handle combination keys
|
|
||||||
components = line.split()
|
|
||||||
try:
|
|
||||||
# Use Modifier_Codes for modifier keys
|
|
||||||
modifier = getattr(Modifier_Codes, components[0].upper())
|
|
||||||
for key in components[1:]:
|
|
||||||
# Use Key_Codes for regular keys
|
|
||||||
key_code = getattr(Key_Codes, key.upper(), None)
|
|
||||||
if key_code:
|
|
||||||
client.send_combination(modifier, key_code)
|
|
||||||
client.send_combination() # Release keys
|
|
||||||
except AttributeError:
|
|
||||||
log.warning(f"Unsupported key or modifier in line: {line}")
|
|
||||||
|
|
||||||
def initialize_pairing(agent_iface, target_address):
|
def initialize_pairing(agent_iface, target_address):
|
||||||
try:
|
try:
|
||||||
|
|
Loading…
Reference in New Issue