def main(): aparse = argparse.ArgumentParser( description="Connection initiator test script for Sniffle BLE5 sniffer" ) aparse.add_argument("-s", "--serport", default=None, help="Sniffer serial port name") args = aparse.parse_args() global hw hw = SniffleHW(args.serport) # set the advertising channel (and return to ad-sniffing mode) hw.cmd_chan_aa_phy(37, BLE_ADV_AA, 0) # pause after sniffing hw.cmd_pause_done(True) # Accept/follow connections hw.cmd_follow(True) # turn off RSSI filter hw.cmd_rssi() # Turn off MAC filter hw.cmd_mac() # initiator doesn't care about this setting, it always accepts aux hw.cmd_auxadv(False) # advertiser needs a MAC address hw.random_addr() # advertise roughly every 200 ms hw.cmd_adv_interval(200) # reset preloaded encrypted connection interval changes hw.cmd_interval_preload() # zero timestamps and flush old packets hw.mark_and_flush() # advertising and scan response data advData = bytes([ 0x02, 0x01, 0x1A, 0x02, 0x0A, 0x0C, 0x11, 0x07, 0x64, 0x14, 0xEA, 0xD7, 0x2F, 0xDB, 0xA3, 0xB0, 0x59, 0x48, 0x16, 0xD4, 0x30, 0x82, 0xCB, 0x27, 0x05, 0x03, 0x0A, 0x18, 0x0D, 0x18 ]) devName = b'NCC Goat' scanRspData = bytes([len(devName) + 1, 0x09]) + devName # now enter advertiser mode hw.cmd_advertise(advData, scanRspData) while True: msg = hw.recv_and_decode() print_message(msg)
def main(): aparse = argparse.ArgumentParser( description="Firmware reset utility for Sniffle BLE5 sniffer") aparse.add_argument("-s", "--serport", default="/dev/ttyACM0", help="Sniffer serial port name") args = aparse.parse_args() hw = SniffleHW(args.serport) # 5 resets seems to work more reliably than fewer print("Sending reset commands...") for i in range(5): hw.ser.write(b'@@@@@@@@\r\n') # command sync hw.cmd_reset() sleep(0.02) # try a flush, see if we get a marker back to prove firmware liveness hw.ser.write(b'@@@@@@@@\r\n') # command sync print("Trying a mark and flush to get things flowing...") hw.mark_and_flush() print("Reset success.")
def main(): aparse = argparse.ArgumentParser( description="Scanner utility for Sniffle BLE5 sniffer") aparse.add_argument("-s", "--serport", default="/dev/ttyACM0", help="Sniffer serial port name") aparse.add_argument("-c", "--advchan", default=37, choices=[37, 38, 39], type=int, help="Advertising channel to listen on") aparse.add_argument("-r", "--rssi", default=-80, type=int, help="Filter packets by minimum RSSI") aparse.add_argument("-e", "--extadv", action="store_const", default=False, const=True, help="Capture BT5 extended (auxiliary) advertising") aparse.add_argument( "-l", "--longrange", action="store_const", default=False, const=True, help="Use long range (coded) PHY for primary advertising") args = aparse.parse_args() # Sanity check argument combinations if args.longrange and not args.extadv: print("Long-range PHY only supported in extended advertising!", file=sys.stderr) return global hw hw = SniffleHW(args.serport) # set the advertising channel (and return to ad-sniffing mode) hw.cmd_chan_aa_phy(args.advchan, BLE_ADV_AA, 2 if args.longrange else 0) # only sniff advertisements (don't follow connections) hw.cmd_endtrim(0xB0) # configure RSSI filter hw.cmd_rssi(args.rssi) # turn off MAC address filtering hw.cmd_mac() # configure BT5 extended (aux/secondary) advertising hw.cmd_auxadv(args.extadv) # trap Ctrl-C signal.signal(signal.SIGINT, sigint_handler) print("Starting scanner. Press CTRL-C to stop scanning and show results.") while not done_scan: msg = hw.recv_and_decode() if isinstance(msg, DebugMessage): print(msg) elif isinstance(msg, PacketMessage): handle_packet(msg) print("\n\nScan Results:") for a in sorted(advertisers.keys(), key=lambda k: advertisers[k].rssi, reverse=True): print("=" * 80) print("AdvA: %s RSSI: %i Hits: %i" % (a, advertisers[a].rssi, advertisers[a].hits)) if advertisers[a].adv: print("\nAdvertisement:") print(advertisers[a].adv) else: print("\nAdvertisement: None") if advertisers[a].scan_rsp: print("\nScan Response:") print(advertisers[a].scan_rsp) else: print("\nScan Response: None") print("=" * 80, end="\n\n")
def main(): aparse = argparse.ArgumentParser( description="Connection initiator test script for Sniffle BLE5 sniffer" ) aparse.add_argument("-s", "--serport", default="/dev/ttyACM0", help="Sniffer serial port name") aparse.add_argument("-c", "--advchan", default=37, choices=[37, 38, 39], type=int, help="Advertising channel to listen on") aparse.add_argument("-r", "--rssi", default=-80, type=int, help="Filter packets by minimum RSSI") aparse.add_argument("-m", "--mac", default=None, help="Specify target MAC address") aparse.add_argument("-i", "--irk", default=None, help="Specify target IRK") aparse.add_argument( "-l", "--longrange", action="store_const", default=False, const=True, help="Use long range (coded) PHY for primary advertising") aparse.add_argument("-P", "--public", action="store_const", default=False, const=True, help="Supplied MAC address is public") args = aparse.parse_args() global hw hw = SniffleHW(args.serport) if args.mac is None and args.irk is None: print("Must specify target MAC address or IRK", file=sys.stderr) return if args.mac and args.irk: print("IRK and MAC filters are mutually exclusive!", file=sys.stderr) return if args.public and args.irk: print("IRK only works on RPAs, not public addresses!", file=sys.stderr) return # set the advertising channel (and return to ad-sniffing mode) hw.cmd_chan_aa_phy(args.advchan, BLE_ADV_AA, 2 if args.longrange else 0) # pause after sniffing hw.cmd_pause_done(True) # capture advertisements only hw.cmd_follow(False) # configure RSSI filter hw.cmd_rssi(args.rssi) if args.mac: try: macBytes = [int(h, 16) for h in reversed(args.mac.split(":"))] if len(macBytes) != 6: raise Exception("Wrong length!") except: print("MAC must be 6 colon-separated hex bytes", file=sys.stderr) return hw.cmd_mac(macBytes, False) else: hw.cmd_irk(unhexlify(args.irk), False) # initiator doesn't care about this setting, it always accepts aux hw.cmd_auxadv(False) # initiator needs a MAC address hw.random_addr() if args.irk: macBytes = get_mac_from_irk() # zero timestamps and flush old packets hw.mark_and_flush() # now enter initiator mode global _aa _aa = hw.initiate_conn(macBytes, not args.public) while True: msg = hw.recv_and_decode() print_message(msg)
def main(): aparse = argparse.ArgumentParser( description="Host-side receiver for Sniffle BLE5 sniffer") aparse.add_argument("-s", "--serport", default="/dev/ttyACM0", help="Sniffer serial port name") aparse.add_argument("-c", "--advchan", default=37, choices=[37, 38, 39], type=int, help="Advertising channel to listen on") aparse.add_argument("-p", "--pause", action="store_const", default=False, const=True, help="Pause sniffer after disconnect") aparse.add_argument("-r", "--rssi", default=-80, type=int, help="Filter packets by minimum RSSI") aparse.add_argument("-m", "--mac", default=None, help="Filter packets by advertiser MAC") aparse.add_argument( "-a", "--advonly", action="store_const", default=False, const=True, help="Sniff only advertisements, don't follow connections") aparse.add_argument("-e", "--extadv", action="store_const", default=False, const=True, help="Capture BT5 extended (auxiliary) advertising") aparse.add_argument( "-H", "--hop", action="store_const", default=False, const=True, help="Hop primary advertising channels in extended mode") aparse.add_argument( "-l", "--longrange", action="store_const", default=False, const=True, help="Use long range (coded) PHY for primary advertising") aparse.add_argument("-o", "--output", default=None, help="PCAP output file name") args = aparse.parse_args() # Sanity check argument combinations if args.hop and args.mac is None: print("Primary adv. channel hop requires a MAC address specified!", file=sys.stderr) return if args.longrange and not args.extadv: print("Long-range PHY only supported in extended advertising!", file=sys.stderr) return if args.longrange and args.hop: # this would be pointless anyway, since long range always uses extended ads print("Primary ad channel hopping unsupported on long range PHY!", file=sys.stderr) return global hw hw = SniffleHW(args.serport) # set the advertising channel (and return to ad-sniffing mode) hw.cmd_chan_aa_phy(args.advchan, BLE_ADV_AA, 2 if args.longrange else 0) # set whether or not to pause after sniffing hw.cmd_pause_done(args.pause) # set up endTrim if args.advonly: hw.cmd_endtrim(0xB0) else: hw.cmd_endtrim(0x10) # configure RSSI filter global _rssi_min _rssi_min = args.rssi hw.cmd_rssi(args.rssi) # disable 37/38/39 hop in extended mode unless overridden global _allow_hop3 if args.extadv and not args.hop: _allow_hop3 = False # configure MAC filter global _delay_top_mac if args.mac is None: hw.cmd_mac() elif args.mac == "top": hw.cmd_mac() _delay_top_mac = True else: try: macBytes = [int(h, 16) for h in reversed(args.mac.split(":"))] if len(macBytes) != 6: raise Exception("Wrong length!") except: print("MAC must be 6 colon-separated hex bytes", file=sys.stderr) return hw.cmd_mac(macBytes, _allow_hop3) # configure BT5 extended (aux/secondary) advertising hw.cmd_auxadv(args.extadv) global pcwriter if not (args.output is None): pcwriter = PcapBleWriter(args.output) while True: msg_type, msg_body = hw.recv_msg() print_message(msg_type, msg_body)
def main(): aparse = argparse.ArgumentParser( description="Scanner utility for Sniffle BLE5 sniffer") aparse.add_argument("-s", "--serport", default=None, help="Sniffer serial port name") aparse.add_argument("-c", "--advchan", default=37, choices=[37, 38, 39], type=int, help="Advertising channel to listen on") aparse.add_argument("-r", "--rssi", default=-128, type=int, help="Filter packets by minimum RSSI") aparse.add_argument( "-l", "--longrange", action="store_const", default=False, const=True, help="Use long range (coded) PHY for primary advertising") args = aparse.parse_args() global hw hw = SniffleHW(args.serport) # set the advertising channel (and return to ad-sniffing mode) hw.cmd_chan_aa_phy(args.advchan, BLE_ADV_AA, 2 if args.longrange else 0) # only sniff advertisements (don't follow connections) hw.cmd_follow(False) # configure RSSI filter hw.cmd_rssi(args.rssi) # turn off MAC address filtering hw.cmd_mac() # set a MAC address for ourselves hw.random_addr() # switch to active scanner mode hw.cmd_scan() # zero timestamps and flush old packets hw.mark_and_flush() # trap Ctrl-C signal.signal(signal.SIGINT, sigint_handler) print("Starting scanner. Press CTRL-C to stop scanning and show results.") while not done_scan: msg = hw.recv_and_decode() if isinstance(msg, DebugMessage): print(msg) elif isinstance(msg, PacketMessage): handle_packet(msg) print("\n\nScan Results:") for a in sorted(advertisers.keys(), key=lambda k: advertisers[k].rssi, reverse=True): print("=" * 80) print("AdvA: %s RSSI: %i Hits: %i" % (a, advertisers[a].rssi, advertisers[a].hits)) if advertisers[a].adv: print("\nAdvertisement:") print(advertisers[a].adv) else: print("\nAdvertisement: None") if advertisers[a].scan_rsp: print("\nScan Response:") print(advertisers[a].scan_rsp) else: print("\nScan Response: None") print("=" * 80, end="\n\n")
def main(): aparse = argparse.ArgumentParser(description="Host-side receiver for Sniffle BLE5 sniffer") aparse.add_argument("-s", "--serport", default="/dev/ttyACM0", help="Sniffer serial port name") aparse.add_argument("-c", "--advchan", default=40, choices=[37, 38, 39], type=int, help="Advertising channel to listen on") aparse.add_argument("-p", "--pause", action="store_const", default=False, const=True, help="Pause sniffer after disconnect") aparse.add_argument("-r", "--rssi", default=-80, type=int, help="Filter packets by minimum RSSI") aparse.add_argument("-m", "--mac", default=None, help="Filter packets by advertiser MAC") aparse.add_argument("-i", "--irk", default=None, help="Filter packets by advertiser IRK") aparse.add_argument("-a", "--advonly", action="store_const", default=False, const=True, help="Sniff only advertisements, don't follow connections") aparse.add_argument("-e", "--extadv", action="store_const", default=False, const=True, help="Capture BT5 extended (auxiliary) advertising") aparse.add_argument("-H", "--hop", action="store_const", default=False, const=True, help="Hop primary advertising channels in extended mode") aparse.add_argument("-l", "--longrange", action="store_const", default=False, const=True, help="Use long range (coded) PHY for primary advertising") aparse.add_argument("-q", "--quiet", action="store_const", default=False, const=True, help="Don't display empty packets") aparse.add_argument("-Q", "--preload", default=None, help="Preload expected encrypted " "connection parameter changes") aparse.add_argument("-o", "--output", default=None, help="PCAP output file name") args = aparse.parse_args() # Sanity check argument combinations if args.hop and args.mac is None and args.irk is None: print("Primary adv. channel hop requires a MAC address or IRK specified!", file=sys.stderr) return if args.longrange and not args.extadv: print("Long-range PHY only supported in extended advertising!", file=sys.stderr) return if args.longrange and args.hop: # this would be pointless anyway, since long range always uses extended ads print("Primary ad channel hopping unsupported on long range PHY!", file=sys.stderr) return if args.mac and args.irk: print("IRK and MAC filters are mutually exclusive!", file=sys.stderr) return if args.advchan != 40 and args.hop: print("Don't specify an advertising channel if you want advertising channel hopping!", file=sys.stderr) return global hw hw = SniffleHW(args.serport) # if a channel was explicitly specified, don't hop global _allow_hop3 if args.advchan == 40: args.advchan = 37 else: _allow_hop3 = False # set the advertising channel (and return to ad-sniffing mode) hw.cmd_chan_aa_phy(args.advchan, BLE_ADV_AA, 2 if args.longrange else 0) # set whether or not to pause after sniffing hw.cmd_pause_done(args.pause) # set up whether or not to follow connections hw.cmd_follow(not args.advonly) if args.preload: # expect colon separated triplets, separated by commas triplets = [] for tstr in args.preload.split(','): tsplit = tstr.split(':') tup = (int(tsplit[0]), int(tsplit[1]), int(tsplit[2])) triplets.append(tup) hw.cmd_interval_preload(triplets) else: # reset preloaded encrypted connection interval changes hw.cmd_interval_preload() # configure RSSI filter global _rssi_min _rssi_min = args.rssi hw.cmd_rssi(args.rssi) # disable 37/38/39 hop in extended mode unless overridden if args.extadv and not args.hop: _allow_hop3 = False # configure MAC filter global _delay_top_mac if args.mac is None and args.irk is None: hw.cmd_mac() elif args.irk: hw.cmd_irk(unhexlify(args.irk), _allow_hop3) elif args.mac == "top": hw.cmd_mac() _delay_top_mac = True else: try: macBytes = [int(h, 16) for h in reversed(args.mac.split(":"))] if len(macBytes) != 6: raise Exception("Wrong length!") except: print("MAC must be 6 colon-separated hex bytes", file=sys.stderr) return hw.cmd_mac(macBytes, _allow_hop3) # configure BT5 extended (aux/secondary) advertising hw.cmd_auxadv(args.extadv) # zero timestamps and flush old packets hw.mark_and_flush() global pcwriter if not (args.output is None): pcwriter = PcapBleWriter(args.output) while True: msg = hw.recv_and_decode() print_message(msg, args.quiet)