2021-09-06 01:45:23 +00:00
|
|
|
#!./runtime/install/bin/python3
|
2021-09-01 02:01:05 +00:00
|
|
|
|
2021-08-31 11:51:03 +00:00
|
|
|
import os
|
|
|
|
import sys
|
|
|
|
import _ctypes
|
|
|
|
import ctypes
|
|
|
|
import _thread
|
|
|
|
import colorama
|
|
|
|
import subprocess
|
|
|
|
import random
|
|
|
|
import signal
|
2021-09-06 01:45:23 +00:00
|
|
|
import click
|
2021-08-31 11:51:03 +00:00
|
|
|
from threading import Thread
|
|
|
|
from time import sleep
|
|
|
|
from colorama import Fore, init
|
|
|
|
|
|
|
|
sys.path.insert(0, os.getcwd() + '/libs')
|
|
|
|
|
2021-09-06 01:45:23 +00:00
|
|
|
from scapy.layers.bluetooth import HCI_Hdr, ESP32_BREDR, BT_Baseband, BT_ACL_Hdr, BT_LMP, HCI_PHDR_Hdr
|
2021-08-31 11:51:03 +00:00
|
|
|
from scapy.utils import wrpcap, PcapWriter
|
|
|
|
from ESP32BTDriver import ESP32BTDriver
|
|
|
|
|
|
|
|
|
|
|
|
init(autoreset=True)
|
|
|
|
|
|
|
|
|
|
|
|
class SnifferBREDR:
|
|
|
|
TAG = 'Sniffer'
|
|
|
|
working_dir = None
|
|
|
|
wireshark_process = None
|
2021-09-06 01:45:23 +00:00
|
|
|
pcap_fifo_filename = '/tmp/fifocap.fifo'
|
|
|
|
pcap_filename = 'logs/capture_bluetooth.pcapng'
|
2021-08-31 11:51:03 +00:00
|
|
|
save_pcap = False
|
|
|
|
pcap_fifo_writer = None
|
|
|
|
pcap_writer = None
|
2021-09-06 01:45:23 +00:00
|
|
|
|
|
|
|
show_summary = True
|
|
|
|
start_wireshark = False
|
2021-08-31 11:51:03 +00:00
|
|
|
wireshark_started = False
|
2021-09-06 01:45:23 +00:00
|
|
|
host_bdaddr = None
|
2021-08-31 11:51:03 +00:00
|
|
|
|
|
|
|
driver = None # type: ESP32BTDriver
|
2021-09-06 01:45:23 +00:00
|
|
|
driver_run = False
|
2021-08-31 11:51:03 +00:00
|
|
|
serial_port = None
|
|
|
|
serial_baud = None
|
|
|
|
serial_thread = None
|
|
|
|
bridge_hci = True
|
|
|
|
bt_program = None
|
|
|
|
bt_program_thread = None
|
2021-09-06 01:45:23 +00:00
|
|
|
bt_program_run = False
|
2021-08-31 11:51:03 +00:00
|
|
|
bt_program_process = None
|
2021-09-06 01:45:23 +00:00
|
|
|
# program parameters
|
|
|
|
bt_bdaddr = None
|
2021-08-31 11:51:03 +00:00
|
|
|
|
|
|
|
# BT Vars
|
|
|
|
tx_packets = 0
|
|
|
|
rx_packets = 0
|
|
|
|
|
|
|
|
# Constructor
|
|
|
|
def __init__(self,
|
|
|
|
serial_port=None,
|
2021-09-06 01:45:23 +00:00
|
|
|
serial_baud=921600,
|
|
|
|
show_summary=True,
|
2021-08-31 11:51:03 +00:00
|
|
|
start_wireshark=False,
|
|
|
|
save_pcap=True,
|
|
|
|
pcap_filename=None,
|
|
|
|
bridge_hci=True,
|
2021-09-06 01:45:23 +00:00
|
|
|
bt_program=None,
|
|
|
|
target_bdaddress=None,
|
|
|
|
host_bdaddr='E0:D4:E8:19:C7:68'):
|
2021-08-31 11:51:03 +00:00
|
|
|
|
2021-09-06 01:45:23 +00:00
|
|
|
self.show_summary = show_summary
|
|
|
|
self.start_wireshark = start_wireshark
|
2021-08-31 11:51:03 +00:00
|
|
|
self.serial_port = serial_port
|
|
|
|
self.serial_baud = serial_baud
|
|
|
|
self.save_pcap = save_pcap
|
|
|
|
self.bridge_hci = bridge_hci
|
2021-09-06 01:45:23 +00:00
|
|
|
self.bt_bdaddr = target_bdaddress
|
|
|
|
self.host_bdaddr = host_bdaddr
|
2021-08-31 11:51:03 +00:00
|
|
|
|
|
|
|
if pcap_filename:
|
|
|
|
self.pcap_filename = pcap_filename
|
|
|
|
|
|
|
|
if bt_program:
|
|
|
|
self.bt_program = bt_program
|
|
|
|
|
2021-09-06 01:45:23 +00:00
|
|
|
if self.start_wireshark:
|
2021-08-31 11:51:03 +00:00
|
|
|
try:
|
2021-09-06 01:45:23 +00:00
|
|
|
os.remove(self.pcap_fifo_filename)
|
2021-08-31 11:51:03 +00:00
|
|
|
except:
|
|
|
|
pass
|
2021-09-06 01:45:23 +00:00
|
|
|
os.mkfifo(self.pcap_fifo_filename)
|
2021-08-31 11:51:03 +00:00
|
|
|
try:
|
|
|
|
self.l('[!] Starting Wireshark...')
|
|
|
|
self.wireshark_process = subprocess.Popen(
|
2021-09-06 01:45:23 +00:00
|
|
|
['wireshark', '-k', '-i', self.pcap_fifo_filename])
|
|
|
|
self.pcap_fifo_writer = PcapWriter(
|
|
|
|
self.pcap_fifo_filename, sync=True)
|
2021-08-31 11:51:03 +00:00
|
|
|
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)
|
|
|
|
|
|
|
|
# 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)
|
2021-09-06 01:45:23 +00:00
|
|
|
self.driver.set_bdaddr(self.host_bdaddr)
|
|
|
|
|
2021-08-31 11:51:03 +00:00
|
|
|
print(Fore.GREEN + 'ESP32BT driver started on ' +
|
|
|
|
self.serial_port + '@' + str(self.serial_baud))
|
|
|
|
|
2021-09-06 01:45:23 +00:00
|
|
|
self.driver_run = True
|
2021-08-31 11:51:03 +00:00
|
|
|
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:
|
2021-09-06 01:45:23 +00:00
|
|
|
self.bt_program_run = True
|
2021-08-31 11:51:03 +00:00
|
|
|
self.bt_program_thread = Thread(target=self.bt_program_handler)
|
|
|
|
self.bt_program_thread.daemon = True
|
|
|
|
self.bt_program_thread.start()
|
|
|
|
|
|
|
|
def bt_program_handler(self):
|
|
|
|
if self.bridge_hci:
|
|
|
|
p_name = self.driver.serial_bridge_name
|
|
|
|
else:
|
|
|
|
p_name = self.serial_port
|
|
|
|
|
2021-09-06 01:45:23 +00:00
|
|
|
p_args = [self.bt_program, '-u', p_name, '-a', str(self.bt_bdaddr)]
|
|
|
|
print('Starting ' + str(p_args))
|
|
|
|
process = subprocess.Popen(p_args)
|
2021-08-31 11:51:03 +00:00
|
|
|
self.bt_program_process = process
|
|
|
|
|
|
|
|
while self.bt_program_run:
|
|
|
|
sleep(1)
|
|
|
|
|
|
|
|
rc = process.poll()
|
|
|
|
return rc
|
|
|
|
|
|
|
|
def uart_rx_handler(self):
|
2021-09-06 01:45:23 +00:00
|
|
|
while self.driver_run:
|
|
|
|
# Receive packet from the ESP32 Board
|
|
|
|
data = self.driver.receive()
|
|
|
|
if data is not None:
|
2021-08-31 11:51:03 +00:00
|
|
|
# Decode Bluetooth Low Energy Data
|
2021-09-06 01:45:23 +00:00
|
|
|
pkt = ESP32_BREDR(data)
|
2021-08-31 11:51:03 +00:00
|
|
|
if pkt:
|
2021-09-06 01:45:23 +00:00
|
|
|
summary = pkt[BT_Baseband].summary()
|
2021-08-31 11:51:03 +00:00
|
|
|
direction = self.driver.direction
|
|
|
|
if direction == 1:
|
2021-09-06 01:45:23 +00:00
|
|
|
if self.show_summary:
|
|
|
|
self.log_rx(summary)
|
2021-08-31 11:51:03 +00:00
|
|
|
self.rx_packets += 1
|
|
|
|
elif direction == 0:
|
2021-09-06 01:45:23 +00:00
|
|
|
if self.show_summary:
|
|
|
|
self.log_tx(summary)
|
2021-08-31 11:51:03 +00:00
|
|
|
self.tx_packets += 1
|
|
|
|
|
2021-09-06 01:45:23 +00:00
|
|
|
# Pipe / Save pcap
|
2021-08-31 11:51:03 +00:00
|
|
|
hci_pkt = HCI_PHDR_Hdr(
|
2021-09-06 01:45:23 +00:00
|
|
|
direction=direction) / HCI_Hdr() / pkt
|
2021-08-31 11:51:03 +00:00
|
|
|
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)
|
|
|
|
|
|
|
|
def log_tx(self, log_message):
|
|
|
|
print(Fore.CYAN + 'TX --> ' + log_message)
|
|
|
|
|
|
|
|
def log_rx(self, log_message):
|
|
|
|
print(Fore.GREEN + 'RX <-- ' + log_message)
|
|
|
|
|
|
|
|
|
2021-09-06 01:45:23 +00:00
|
|
|
# Defaults
|
|
|
|
serial_port = '/dev/ttyUSB0'
|
|
|
|
serial_baud = 921600
|
|
|
|
|
|
|
|
|
|
|
|
@click.command()
|
|
|
|
@click.option('--port', default=serial_port,
|
|
|
|
help='Serial port name (/dev/ttyUSBx for Linux)')
|
|
|
|
@click.option('--host', default='E0:D4:E8:19:C7:68', help='BDAddress of local host (default: E0:D4:E8:19:C7:68)')
|
|
|
|
@click.option('--target', help='BDAddress of remote target (ex: a8:96:75:25:c2:ac)')
|
|
|
|
@click.option('--live-wireshark', is_flag=True,
|
|
|
|
help='Opens Wireshark live session')
|
|
|
|
@click.option('--live-terminal', is_flag=True,
|
|
|
|
help='Show a summary of each packet on terminal')
|
|
|
|
@click.option('--bridge-only', is_flag=True,
|
|
|
|
help='Starts the HCI bridge without connecting any BT Host stack')
|
|
|
|
def sniffer(port, host, target, live_wireshark, live_terminal, bridge_only):
|
|
|
|
|
|
|
|
bt_program = None
|
|
|
|
host_bdaddress = None
|
|
|
|
target_bdaddress = None
|
|
|
|
bd_role_master = False
|
|
|
|
|
|
|
|
if target:
|
|
|
|
# Check BDAddress format
|
|
|
|
if ':' in target and (len(target.split(':')) == 6) and (len(target) == 17):
|
|
|
|
target_bdaddress = target.lower()
|
|
|
|
else:
|
|
|
|
raise ValueError("Incorrect BDAddress format")
|
|
|
|
|
|
|
|
if host:
|
|
|
|
# Check BDAddress format
|
|
|
|
if ':' in host and (len(host.split(':')) == 6) and (len(host) == 17):
|
|
|
|
host_bdaddress = host.lower()
|
|
|
|
else:
|
|
|
|
raise ValueError("Incorrect BDAddress format")
|
|
|
|
|
|
|
|
if (live_terminal or live_wireshark) and not bridge_only:
|
|
|
|
bd_role_master = True if target else False
|
|
|
|
bt_program = (
|
|
|
|
'./host_stack/sdp_rfcomm_query' if bd_role_master else './host_stack/spp_counter')
|
|
|
|
else:
|
|
|
|
print(Fore.YELLOW + '[!] Bridge will start without BT host stack')
|
|
|
|
|
|
|
|
print('Using options:\n\
|
|
|
|
Serial Port: %s\n\
|
|
|
|
Serial Baud: %d\n\
|
|
|
|
BT Host Program: %s\n\
|
|
|
|
Host BDAddress: %s\n\
|
|
|
|
Target BDAddress: %s' % (port, serial_baud, bt_program, host_bdaddress, target_bdaddress))
|
|
|
|
|
|
|
|
Sniffer = SnifferBREDR(serial_port=port,
|
|
|
|
serial_baud=serial_baud,
|
|
|
|
show_summary=live_terminal,
|
|
|
|
start_wireshark=live_wireshark,
|
|
|
|
bt_program=bt_program,
|
|
|
|
target_bdaddress=target)
|
|
|
|
Sniffer.start()
|
|
|
|
|
|
|
|
try:
|
|
|
|
while True:
|
|
|
|
sleep(1)
|
2021-08-31 11:51:03 +00:00
|
|
|
|
2021-09-06 01:45:23 +00:00
|
|
|
except KeyboardInterrupt:
|
|
|
|
if Sniffer.save_pcap:
|
|
|
|
print(Fore.GREEN + 'Capture saved on logs/capture_bluetooth.pcapng')
|
2021-08-31 11:51:03 +00:00
|
|
|
|
2021-09-06 01:45:23 +00:00
|
|
|
if Sniffer.bt_program_process is not None:
|
|
|
|
Sniffer.bt_program_process.kill()
|
|
|
|
print(Fore.YELLOW + 'BT Program finished')
|
2021-08-31 11:51:03 +00:00
|
|
|
|
|
|
|
|
2021-09-06 01:45:23 +00:00
|
|
|
if __name__ == '__main__':
|
|
|
|
sniffer()
|