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

217 lines
6.5 KiB
Python
Executable file

#!/usr/bin/python3
import os
import sys
import _ctypes
import ctypes
import _thread
import colorama
import subprocess
import random
import signal
from threading import Thread
from time import sleep
from colorama import Fore, init
sys.path.insert(0, os.getcwd() + '/libs')
from scapy.layers.bluetooth import HCI_Hdr, BT_ACL_Hdr, HCI_PHDR_Hdr, BT_LMP, BT_Baseband
from scapy.utils import wrpcap, PcapWriter
from ESP32BTDriver import ESP32BTDriver
init(autoreset=True)
class SnifferBREDR:
TAG = 'Sniffer'
working_dir = None
packets = []
wireshark_process = None
fifo_file = '/tmp/fifocap.fifo'
pcap_filename = 'capture_bluetooth.pcap'
save_pcap = False
pcap_fifo_writer = None
pcap_writer = None
wireshark_started = False
driver = None # type: ESP32BTDriver
run_driver = True
serial_port = None
serial_baud = None
serial_thread = None
bridge_hci = True
bt_program = None
bt_program_thread = None
bt_program_run = True
bt_program_process = None
# BT Vars
tx_packets = 0
rx_packets = 0
remote_address = b'a8:96:75:25:c2:ac'
# Constructor
def __init__(self,
serial_port=None,
serial_baud=4000000,
start_wireshark=False,
save_pcap=True,
pcap_filename=None,
bridge_hci=True,
bt_program=None):
self.serial_port = serial_port
self.serial_baud = serial_baud
self.save_pcap = save_pcap
self.bridge_hci = bridge_hci
if pcap_filename:
self.pcap_filename = pcap_filename
if bt_program:
self.bt_program = bt_program
if start_wireshark:
try:
os.remove(self.fifo_file)
except:
pass
os.mkfifo(self.fifo_file)
try:
self.l('[!] Starting Wireshark...')
self.wireshark_process = subprocess.Popen(
['wireshark', '-k', '-i', self.fifo_file])
self.pcap_fifo_writer = PcapWriter(self.fifo_file, sync=True)
self.wireshark_started = True
except Exception as e:
self.error('Wireshark could not start: ' + str(e))
if save_pcap:
self.pcap_writer = PcapWriter(self.pcap_filename, sync=True)
if sys.platform == 'linux':
os.system('chmod o+rw ' + self.pcap_filename)
def signal_handler(self, signal, frame):
self.error('You pressed Ctrl+C - or killed me with -2')
exit(0)
# sys.exit(0)
# Logs
def l(self, msg):
print(Fore.YELLOW + '[' + self.TAG + '] ' + msg)
def error(self, msg):
print(Fore.RED + '[Error:' + self.TAG + '] ' + msg)
# Main functions
def start(self):
if self.bridge_hci or self.bt_program is None:
self.driver = ESP32BTDriver(self.serial_port, self.serial_baud)
self.driver.enable_sniffing(1)
self.driver.disable_poll_null(1)
print(Fore.GREEN + 'ESP32BT driver started on ' +
self.serial_port + '@' + str(self.serial_baud))
self.serial_thread = Thread(target=self.uart_rx_handler)
self.serial_thread.daemon = True
self.serial_thread.start()
if self.bt_program is not None:
self.bt_program_thread = Thread(target=self.bt_program_handler)
self.bt_program_thread.daemon = True
self.bt_program_thread.start()
@staticmethod
def skip_slashes(summary_text, idx):
return '/'.join(summary_text.split('/')[idx:])
def bt_program_handler(self):
if self.bridge_hci:
p_name = self.driver.serial_bridge_name
else:
p_name = self.serial_port
print('Starting ' + self.bt_program + ' -u ' + p_name)
process = subprocess.Popen([self.bt_program, '-u', p_name],
# stdin=subprocess.PIPE,
# stdout=subprocess.PIPE,
# stderr=subprocess.PIPE
)
self.bt_program_process = process
while self.bt_program_run:
sleep(1)
rc = process.poll()
return rc
def uart_rx_handler(self):
while self.run_driver:
# Receive packet from the NRF52 Dongle
data = self.driver.raw_receive()
if data:
# Decode Bluetooth Low Energy Data
pkt = BT_Baseband(data)
if pkt:
summary = pkt.summary()
direction = self.driver.direction
if direction == 1:
# print('R:' + summary)
self.log_rx(summary)
self.rx_packets += 1
elif direction == 0:
if BT_LMP in pkt:
# print('T:' + summary)
pkt = BT_ACL_Hdr(data)
self.log_tx(summary)
self.tx_packets += 1
# self.update_summary(self.tx_packets, self.rx_packets)
# Pipe/Save pcap
hci_pkt = HCI_PHDR_Hdr(
direction=direction) / HCI_Hdr(type=8) / pkt
if self.wireshark_started is True:
self.pcap_fifo_writer.write(hci_pkt)
if self.save_pcap is True:
self.pcap_writer.write(hci_pkt)
@staticmethod
def decode_address(addr):
return bytes.fromhex(''.join(addr.split(':')))
def log_tx(self, log_message):
print(Fore.CYAN + 'TX --> ' + log_message)
def log_rx(self, log_message):
print(Fore.GREEN + 'RX <-- ' + log_message)
def update_summary(self, tx_pkts, rx_pkts):
self.log_summary('TX packets: ' + str(tx_pkts))
self.log_summary('RX Packets: ' + str(rx_pkts))
self.log_summary('BT Clock: ' + str(self.driver.event_counter))
Sniffer = SnifferBREDR(serial_port='/dev/ttyUSB1',
serial_baud=4000000,
start_wireshark=False,
bt_program='./bin/spp_counter',
)
Sniffer.start()
try:
while True:
sleep(1)
except KeyboardInterrupt:
if Sniffer.save_pcap:
print(Fore.GREEN + 'Capture saved on capture_bluetooth.pcap')
if Sniffer.bt_program_process is not None:
Sniffer.bt_program_process.kill()
print(Fore.YELLOW + 'BT Program finished')