86890704fd
todo: add documentation & wireshark dissector
217 lines
6.5 KiB
Python
Executable file
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')
|