esp32_bluetooth_classic_sni.../libs/ESP32BTDriver.py
Matheus Eduardo Garbelini 86890704fd initial commit
todo: add documentation & wireshark dissector
2021-08-31 19:51:03 +08:00

164 lines
4.9 KiB
Python
Executable file

from binascii import hexlify
from colorama import Fore
import binascii
import os
import sys
import pty
import subprocess
import serial
import tty
import time
import ctypes
import serial.tools.list_ports
from threading import Thread
class ConnectionStatus(ctypes.LittleEndianStructure):
_fields_ = [
("clock", ctypes.c_uint32, 8*4),
("channel", ctypes.c_uint8, 8),
("ptt", ctypes.c_uint8, 1),
("role", ctypes.c_uint8, 1),
("custom_lmp", ctypes.c_uint8, 1),
("retry_flag", ctypes.c_uint8, 1),
("intercept_req", ctypes.c_uint8, 1),
("tx_encrypted", ctypes.c_uint8, 1),
("rx_encrypted", ctypes.c_uint8, 1),
("is_eir", ctypes.c_uint8, 1),
]
def getdict(self):
return dict((f, getattr(self, f)) for f, _, _ in self._fields_)
# USB Serial commands
ESP32_CMD_DATA_RX = b'\xA7'
ESP32_CMD_DATA_TX = b'\xBB'
ESP32_CMD_CHECKSUM_ERROR = b'\xA8'
ESP32_CMD_CONFIG_AUTO_EMPTY_PDU = b'\xA9'
ESP32_CMD_CONFIG_ACK = b'\xAA'
ESP32_CMD_CONFIG_LOG_TX = b'\xCC'
ESP32_CMD_LOG = b'\x7F'
ESP32_CMD_RESET = b'\x86'
ESP32_CMD_VERSION = b'\xEE'
ESP32_CMD_ENABLE_LMP_SNIFFING = b'\x81'
ESP32_CMD_DISABLE_POLL_NULL = b'\x89'
# HCI Codes to bridge
H4_NONE = b'\x00'
H4_CMD = b'\x01'
H4_ACL = b'\x02'
H4_SCO = b'\x03'
H4_EVT = b'\x04'
H4_EVT_HW_ERROR = b'\x10'
# Driver class
class ESP32BTDriver:
n_debug = False
n_log = False
logs_pcap = False
event_counter = 0
pcap_filename = None
pcap_tx_handover = False
sent_pkt = None
direction = None
version = None
status = None # type: ConnectionStatus
serial_bridge = None
serial_bridge_name = None
serial_hci_thread = None
# Constructor ------------------------------------
def __init__(self, port_name=None, baudrate=115200, debug=False, logs=True, logs_pcap=False, pcap_filename=None):
self.serial = serial.Serial(
port_name, baudrate, rtscts=0, xonxoff=0, timeout=1)
self.logs_pcap = logs_pcap
self.n_log = logs
self.n_debug = debug
self.get_version()
if self.n_debug:
print('ESP32: Instance started')
master, slave = pty.openpty()
self.serial_bridge_name = os.ttyname(slave)
self.serial_bridge = master
tty.setraw(master)
tty.setraw(slave)
print(Fore.GREEN + 'HCI Bridge started on ' + os.ttyname(slave))
self.serial_hci_thread = Thread(target=self.hci_handler)
self.serial_hci_thread.daemon = True
self.serial_hci_thread.start()
def close(self):
print('NRF52 Dongle closed')
# UART functions ---------------------------
def get_version(self):
self.serial.write(ESP32_CMD_VERSION)
version_string = self.serial.readline()
if version_string and len(version_string):
self.version = version_string.decode('utf-8').split('\n')[0]
print("[ESP32BT] Firmware version: " + self.version)
else:
raise Exception(
"Version not received. Make sure to flash ESP32BT firmware")
def enable_sniffing(self, val):
self.serial.write(ESP32_CMD_ENABLE_LMP_SNIFFING + bytearray([val]))
def reset_firmware(self, val):
self.serial.write(ESP32_CMD_RESET + bytearray([0x86, 0xAA]))
def disable_poll_null(self, val):
self.serial.write(ESP32_CMD_DISABLE_POLL_NULL + bytearray([val]))
self.serial.read(1)
def hci_handler(self):
while True:
c = os.read(self.serial_bridge, 1)
self.serial.write(c)
def raw_receive(self):
cmd = self.serial.read(1)
if cmd == H4_EVT:
opcode = self.serial.read(1)
length = self.serial.read(1)
payload = self.serial.read(ord(length))
os.write(self.serial_bridge, H4_EVT + opcode + length + payload)
elif cmd == H4_ACL:
opcode = self.serial.read(2)
length = self.serial.read(2)
payload = self.serial.read(length[0] | (length[1] << 8))
print(payload)
os.write(self.serial_bridge, H4_ACL + opcode + length + payload)
elif cmd == H4_CMD:
opcode = self.serial.read(2)
length = self.serial.read(1)
payload = self.serial.read(ord(length))
os.write(self.serial_bridge, H4_CMD + opcode + length + payload)
# Receive BT packets
elif cmd == ESP32_CMD_DATA_RX or cmd == ESP32_CMD_DATA_TX:
raw_sz = self.serial.read(2)
sz = raw_sz[0] | (raw_sz[1] << 8)
data = self.serial.read(sz)
checksum = self.serial.read(1)
# Check data checksum
if (checksum and (sum(data) & 0xFF) == ord(checksum)):
self.status = ConnectionStatus(*data[0:6])
if cmd == ESP32_CMD_DATA_RX:
self.direction = 1
elif cmd == ESP32_CMD_DATA_TX:
self.direction = 0
return data[6:]
return None