Example #1
0
 def scan(self):
     try:
         self.supervisor = CLIAccessAddressSniffer(verbose=self.verbose,
                                                   devices=self.device)
     except DeviceError as error:
         print(
             '[!] Please connect a compatible Micro:Bit in order to use BtleJack'
         )
         exit(-1)
Example #2
0
    def follow(self, deviceAddress):
        if deviceAddress is not None:
            # address is okay, feed our sniffer
            try:
                self.supervisor = CLIConnectionSniffer(deviceAddress,
                                                       output=self.output,
                                                       verbose=self.verbose,
                                                       devices=self.device)

            except SnifferUpgradeRequired as su:
                print(
                    "[i] Quitting, please upgrade your sniffer firmware (-i option if you are using a Micro:Bit)"
                )
            except DeviceError as error:
                print(
                    '[!] Please connect a compatible Micro:Bit in order to use BtleJack'
                )
                sys.exit(-1)
        else:
            print('[!] Wrong Bluetooth Address format: %s' %
                  self.access_address)
Example #3
0
    def sniff(self):
        if self.chm is not None:
            chm = int(self.chm, 16)
        else:
            chm = None
        if self.crc is not None:
            crc = int(self.crc, 16)
        else:
            crc = None

        if self.hop is not None:
            hop = self.hop
        else:
            hop = None
        try:
            cached_parameters = BtlejackSession.get_instance().find_connection(
                self.access_address)
            if cached_parameters is not None:
                # override parameters with those stored in cache
                for param in cached_parameters:
                    if param == 'crcinit':
                        crc = cached_parameters[param]
                creation_date = datetime.datetime.fromtimestamp(
                    cached_parameters['start']).strftime('%Y-%m-%d %H:%M:%S')
                print('[i] Using cached parameters (created on %s)' %
                      creation_date)

            try:
                self.supervisor = CLIConnectionRecovery(self.access_address,
                                                        channel_map=chm,
                                                        hijack=self.hijack,
                                                        jamming=self.jamming,
                                                        hop_interval=hop,
                                                        crc=crc,
                                                        output=self.output,
                                                        verbose=self.verbose,
                                                        devices=self.device,
                                                        timeout=self.timeout)
            except SnifferUpgradeRequired as su:
                print("[i] Quitting, please upgrade your sniffer firmware")

        except DeviceError as error:
            print(
                '[!] Please connect a compatible Micro:Bit in order to use BtleJack'
            )
            exit(-1)
Example #4
0
def main():
    """
    Btlejack main routine for CLI
    """
    parser = ArgumentParser('btlejack')
    parser.add_argument('-d',
                        '--device',
                        dest='devices',
                        action='append',
                        type=str,
                        help='Micro:Bit device serial port')

    parser.add_argument('-5',
                        '--ble5',
                        dest='v5',
                        action='store_true',
                        default=False,
                        help='Force BLE v5 mode')

    parser.add_argument('-s',
                        '--scan-connections',
                        dest='scan_aa',
                        action='store_true',
                        default=False,
                        help='Scan for active BLE connections')

    parser.add_argument('-f',
                        '--follow',
                        dest='follow',
                        type=str,
                        help='Follow an active connection')

    parser.add_argument(
        '-c',
        '--connreq',
        dest='connreq',
        type=str,
        help='Sniff new BTLE connections on multiple channels if possible')

    parser.add_argument('-m',
                        '--channel-map',
                        dest='chm',
                        type=str,
                        default=None,
                        help='Set channel map')

    parser.add_argument('-p',
                        '--hop-interval',
                        dest='hop',
                        type=int,
                        default=None,
                        help='Set hop interval')

    parser.add_argument('-v',
                        '--verbose',
                        dest='verbose',
                        action='store_true',
                        default=False,
                        help='Enable verbose mode')

    parser.add_argument('-o',
                        '--output',
                        dest='output',
                        default=None,
                        help='Output file')

    parser.add_argument('-w',
                        '--wireshark-fifo',
                        dest='output_fifo',
                        default=None,
                        help='Fifo path (for wireshark)')

    parser.add_argument(
        '-x',
        '--output-format',
        dest='output_format',
        default='pcap',
        help='PCAP output format: `ll_phdr`, `nordic` or `pcap`')

    parser.add_argument(
        '-j',
        '--jamming',
        dest='jamming',
        default=False,
        action='store_true',
        help=
        'Jam an active connection (only performed in conjunction with -f option)'
    )

    parser.add_argument(
        '-t',
        '--hijack',
        dest='hijack',
        default=False,
        action='store_true',
        help=
        'Hijack an active connection (only performed in conjunction with -f option)'
    )

    parser.add_argument('-k', '--crc', dest='crc', help='CRCInit value')

    parser.add_argument('-z',
                        '--clear',
                        action='store_true',
                        dest='flush',
                        default=False,
                        help='Clear stored connections parameters')

    parser.add_argument(
        '-i',
        '--install',
        action='store_true',
        dest='install',
        help='Install latest version of firmware on every sniffer')

    parser.add_argument('-n',
                        '--timeout',
                        dest='timeout',
                        default=0,
                        type=int,
                        help='Channel map recovery timeout')

    args = parser.parse_args()
    supervisor = None

    print('BtleJack version %s' % VERSION)
    print('')

    # upgrade sniffers
    if args.install:
        # retrieve the embedded firmware version
        _dir, _filename = os.path.split(__file__)
        fw_path = os.path.join(_dir, "data", "btlejack-fw.hex")

        if os.name == 'posix':
            mount_output = check_output('mount').splitlines()
            mounted_volumes = [x.split()[2] for x in mount_output]
            flashed = 0
            for volume in mounted_volumes:
                if re.match(b'.*MICROBIT[0-9]*$', volume):
                    print('[i] Flashing %s ...' % volume.decode('ascii'))
                    path = os.path.join(volume.decode('ascii'), 'fw.hex')
                    fw = open(fw_path, 'r').read()
                    # copy our firmware on it
                    with open(path, 'wb') as output:
                        output.write(fw.encode('ascii'))
                    flashed += 1
            if flashed > 0:
                print('[i] Flashed %d devices' % flashed)
            else:
                print(
                    '[i] No sniffer found, make sure all your devices are mounted as mass storage devices before flashing.'
                )
            sys.exit(1)
        else:
            print(
                '[!] This feature does not support your operating system, sorry.'
            )

    if args.flush:
        try:
            BtlejackSession.get_instance().clear()
            BtlejackSession.get_instance().save()
            print('[i] Stored connections cleared')
        except BtlejackSessionError as error:
            pass
    else:
        try:
            BtlejackSession.get_instance().load()
        except BtlejackSessionError as error:
            print('[!] Cannot load connections cache')

    # Create output if required
    if args.output is not None or args.output_fifo is not None:
        if args.output_format.lower() not in ['ll_phdr', 'nordic', 'pcap']:
            print(
                '[!] Unknown specified output format (%s). Supported formats are: ll_phdr, nordic, pcap'
                % args.output_format)
            sys.exit(-1)
        if args.output_format.lower().strip() == 'nordic':
            output = PcapNordicTapWriter(args.output, args.output_fifo)
        elif args.output_format.lower().strip() == 'll_phdr':
            output = PcapBlePHDRWriter(args.output, args.output_fifo)
        else:
            print('[i] No output format supplied, pcap format will be used')
            output = PcapBleWriter(args.output, args.output_fifo)
    else:
        output = None

    if args.scan_aa:
        try:
            supervisor = CLIAccessAddressSniffer(verbose=args.verbose,
                                                 devices=args.devices)
        except DeviceError as error:
            print(
                '[!] Please connect a compatible Micro:Bit in order to use BtleJack'
            )
            sys.exit(-1)

    elif args.follow is not None:
        aa = int(args.follow, 16)
        if args.chm is not None:
            chm = int(args.chm, 16)
        else:
            chm = None
        if args.crc is not None:
            crc = int(args.crc, 16)
        else:
            crc = None

        if args.hop is not None:
            hop = args.hop
        else:
            hop = None
        try:
            cached_parameters = BtlejackSession.get_instance().find_connection(
                aa)
            if cached_parameters is not None:
                # override parameters with those stored in cache
                for param in cached_parameters:
                    if param == 'crcinit':
                        crc = cached_parameters[param]
                creation_date = datetime.datetime.fromtimestamp(
                    cached_parameters['start']).strftime('%Y-%m-%d %H:%M:%S')
                print('[i] Using cached parameters (created on %s)' %
                      creation_date)

            try:
                #print('ble v5: %s' % args.v5)
                supervisor = CLIConnectionRecovery(aa,
                                                   channel_map=chm,
                                                   hijack=args.hijack,
                                                   jamming=args.jamming,
                                                   hop_interval=hop,
                                                   crc=crc,
                                                   output=output,
                                                   verbose=args.verbose,
                                                   devices=args.devices,
                                                   timeout=args.timeout,
                                                   v5=args.v5)
            except SnifferUpgradeRequired as su:
                print(
                    "[i] Quitting, please upgrade your sniffer firmware (-i option if you are using a Micro:Bit)"
                )

        except DeviceError as error:
            print(
                '[!] Please connect a compatible Micro:Bit in order to use BtleJack'
            )
            sys.exit(-1)

    elif args.connreq is not None:
        # Support magic word "any" and "*" as wildcards
        if args.connreq.lower() == 'any':
            args.connreq = 'ff:ff:ff:ff:ff:ff'
        bd_addr_int = bd_address_to_int(args.connreq)
        if bd_addr_int is not None:
            # address is okay, feed our sniffer
            try:
                supervisor = CLIConnectionSniffer(bd_addr_int,
                                                  output=output,
                                                  verbose=args.verbose,
                                                  devices=args.devices)
            except SnifferUpgradeRequired as su:
                print(
                    "[i] Quitting, please upgrade your sniffer firmware (-i option if you are using a Micro:Bit)"
                )
            except DeviceError as error:
                print(
                    '[!] Please connect a compatible Micro:Bit in order to use BtleJack'
                )
                sys.exit(-1)
        else:
            print('[!] Wrong Bluetooth Address format: %s' % args.connreq)
    elif not args.flush and not args.install:
        parser.print_help()
        sys.exit(-2)

    try:
        # install a handler in case CTRL-C is pressed
        def ctrlc_handler(signum, frame):
            if output is not None:
                print('[i] Stopping capture process ...')
                output.close()
            raise ForcedTermination()

        signal.signal(signal.SIGINT, ctrlc_handler)

        if supervisor is not None:
            while True:
                supervisor.process_packets()
    except ForcedTermination as e:
        print('[i] Quitting')
    except IOError as io_error:
        print('[!] File access/write error occured, quitting.')
    except FifoError as fifo_err:
        print('[!] An error occured while accessing fifo.')
Example #5
0
def main():
    """
    Btlejack main routine for CLI
    """
    parser = ArgumentParser('btlejack')
    parser.add_argument('-d',
                        '--device',
                        dest='devices',
                        action='append',
                        type=str,
                        help='Micro:Bit device serial port')

    parser.add_argument('-5',
                        '--ble5',
                        dest='v5',
                        action='store_true',
                        default=False,
                        help='Force BLE v5 mode')

    parser.add_argument('-s',
                        '--scan-connections',
                        dest='scan_aa',
                        action='store_true',
                        default=False,
                        help='Scan for active BLE connections')

    parser.add_argument('-f',
                        '--follow',
                        dest='follow',
                        type=str,
                        help='Follow an active connection')

    parser.add_argument(
        '--sniff_adv',
        '--sniff_advertisements',
        dest='sniff_advertisements',
        type=str,
        help=
        'Sniff advertisements tips to use it: --sniff_adv=any (otherwise you can look at the source)'
    )

    parser.add_argument('--jam_adv',
                        '--jam_advertisements',
                        dest='jam_advertisements',
                        type=str,
                        help='Jam advertisements')

    parser.add_argument(
        '--policy_type',
        '--filtering_policy_type',
        dest='policy_type',
        type=str,
        help=
        'Defines the type of filtering policy (blacklist or whitelist) to use')

    parser.add_argument(
        '--accept_invalid_crc',
        dest='accept_invalid_crc',
        action='store_true',
        default=False,
        help='Indicates if the invalid packets are accepted or dropped')

    parser.add_argument('--raw',
                        dest='raw',
                        default=False,
                        action='store_true',
                        help='Displays the frames as a succession of bytes.')

    parser.add_argument('--channel',
                        dest='channel',
                        type=int,
                        default=37,
                        help='Set channel')

    parser.add_argument(
        '-c',
        '--connreq',
        dest='connreq',
        type=str,
        help='Sniff new BTLE connections on multiple channels if possible')

    parser.add_argument('-m',
                        '--channel-map',
                        dest='chm',
                        type=str,
                        default=None,
                        help='Set channel map')

    parser.add_argument('-p',
                        '--hop-interval',
                        dest='hop',
                        type=int,
                        default=None,
                        help='Set hop interval')

    parser.add_argument('-v',
                        '--verbose',
                        dest='verbose',
                        action='store_true',
                        default=False,
                        help='Enable verbose mode')

    parser.add_argument('-o',
                        '--output',
                        dest='output',
                        default=None,
                        help='Output file')

    parser.add_argument('-w',
                        '--wireshark-fifo',
                        dest='output_fifo',
                        default=None,
                        help='Fifo path (for wireshark)')

    parser.add_argument(
        '-x',
        '--output-format',
        dest='output_format',
        default='pcap',
        help='PCAP output format: `ll_phdr`, `nordic` or `pcap`')

    parser.add_argument(
        '-j',
        '--jamming',
        dest='jamming',
        default=False,
        action='store_true',
        help=
        'Jam an active connection (only performed in conjunction with -f option)'
    )

    parser.add_argument(
        '-t',
        '--hijack',
        dest='hijack',
        default=False,
        action='store_true',
        help=
        'Hijack an active connection (only performed in conjunction with -f option)'
    )

    parser.add_argument('-k', '--crc', dest='crc', help='CRCInit value')

    parser.add_argument('-z',
                        '--clear',
                        action='store_true',
                        dest='flush',
                        default=False,
                        help='Clear stored connections parameters')

    parser.add_argument(
        '-i',
        '--install',
        action='store_true',
        dest='install',
        help='Install latest version of firmware on every sniffer')

    parser.add_argument('-n',
                        '--timeout',
                        dest='timeout',
                        default=0,
                        type=int,
                        help='Channel map recovery timeout')

    args = parser.parse_args()
    supervisor = None

    print('BtleJack version %s' % VERSION)
    print('')

    # upgrade sniffers
    if args.install:
        # retrieve the embedded firmware version
        _dir, _filename = os.path.split(__file__)
        fw_path = os.path.join(_dir, "data", "btlejack-fw.hex")

        if os.name == 'posix':
            mount_output = check_output('mount').splitlines()
            mounted_volumes = [x.split()[2] for x in mount_output]
            flashed = 0
            for volume in mounted_volumes:
                if re.match(b'.*MICROBIT[0-9]*$', volume):
                    print('[i] Flashing %s ...' % volume.decode('ascii'))
                    path = os.path.join(volume.decode('ascii'), 'fw.hex')
                    fw = open(fw_path, 'r').read()
                    # copy our firmware on it
                    with open(path, 'wb') as output:
                        output.write(fw.encode('ascii'))
                    flashed += 1
            if flashed > 0:
                print('[i] Flashed %d devices' % flashed)
            else:
                print(
                    '[i] No sniffer found, make sure all your devices are mounted as mass storage devices before flashing.'
                )
            sys.exit(1)
        else:
            print(
                '[!] This feature does not support your operating system, sorry.'
            )

    if args.flush:
        try:
            BtlejackSession.get_instance().clear()
            BtlejackSession.get_instance().save()
            print('[i] Stored connections cleared')
        except BtlejackSessionError as error:
            pass
    else:
        try:
            BtlejackSession.get_instance().load()
        except BtlejackSessionError as error:
            print('[!] Cannot load connections cache')

    # Create output if required
    if args.output is not None or args.output_fifo is not None:
        if args.output_format.lower() not in ['ll_phdr', 'nordic', 'pcap']:
            print(
                '[!] Unknown specified output format (%s). Supported formats are: ll_phdr, nordic, pcap'
                % args.output_format)
            sys.exit(-1)
        if args.output_format.lower().strip() == 'nordic':
            output = PcapNordicTapWriter(args.output, args.output_fifo)
        elif args.output_format.lower().strip() == 'll_phdr':
            output = PcapBlePHDRWriter(args.output, args.output_fifo)
        else:
            print('[i] No output format supplied, pcap format will be used')
            output = PcapBleWriter(args.output, args.output_fifo)
    else:
        output = None

    if args.scan_aa:
        try:
            supervisor = CLIAccessAddressSniffer(verbose=args.verbose,
                                                 devices=args.devices)
        except DeviceError as error:
            print(
                '[!] Please connect a compatible Micro:Bit in order to use BtleJack'
            )
            sys.exit(-1)

    elif args.follow is not None:
        aa = int(args.follow, 16)
        if args.chm is not None:
            chm = int(args.chm, 16)
        else:
            chm = None
        if args.crc is not None:
            crc = int(args.crc, 16)
        else:
            crc = None

        if args.hop is not None:
            hop = args.hop
        else:
            hop = None
        try:
            cached_parameters = BtlejackSession.get_instance().find_connection(
                aa)
            if cached_parameters is not None:
                # override parameters with those stored in cache
                for param in cached_parameters:
                    if param == 'crcinit':
                        crc = cached_parameters[param]
                creation_date = datetime.datetime.fromtimestamp(
                    cached_parameters['start']).strftime('%Y-%m-%d %H:%M:%S')
                print('[i] Using cached parameters (created on %s)' %
                      creation_date)

            try:
                #print('ble v5: %s' % args.v5)
                supervisor = CLIConnectionRecovery(aa,
                                                   channel_map=chm,
                                                   hijack=args.hijack,
                                                   jamming=args.jamming,
                                                   hop_interval=hop,
                                                   crc=crc,
                                                   output=output,
                                                   verbose=args.verbose,
                                                   devices=args.devices,
                                                   timeout=args.timeout,
                                                   v5=args.v5)
            except SnifferUpgradeRequired as su:
                print(
                    "[i] Quitting, please upgrade your sniffer firmware (-i option if you are using a Micro:Bit)"
                )

        except DeviceError as error:
            print(
                '[!] Please connect a compatible Micro:Bit in order to use BtleJack'
            )
            sys.exit(-1)

    elif args.connreq is not None:
        # Support magic word "any" and "*" as wildcards
        if args.connreq.lower() == 'any':
            args.connreq = 'ff:ff:ff:ff:ff:ff'
        bd_addr_int = bd_address_to_int(args.connreq)
        if bd_addr_int is not None:
            # address is okay, feed our sniffer
            try:
                supervisor = CLIConnectionSniffer(bd_addr_int,
                                                  output=output,
                                                  verbose=args.verbose,
                                                  devices=args.devices)
            except SnifferUpgradeRequired as su:
                print(
                    "[i] Quitting, please upgrade your sniffer firmware (-i option if you are using a Micro:Bit)"
                )
            except DeviceError as error:
                print(
                    '[!] Please connect a compatible Micro:Bit in order to use BtleJack'
                )
                sys.exit(-1)
        else:
            print('[!] Wrong Bluetooth Address format: %s' % args.connreq)

    elif args.sniff_advertisements is not None:
        """
        Btlejack allows to use a filtering policy in order to accept or drop specific advertisements.
        It may be useful in order to focus on a specific device, if you want to focus on a specific behaviour, etc.
	The policy can provide a whitelist mode (the rules define the allowed frames) or a blacklist mode
        (the rules define the dropped frames). By default, the whitelist mode is in use, but you can easily change it with
        the --policy_type parameter :
        $ btlejack --policy_type=blacklist --sniff_adv=<rules>

        Multiple rules can be provided, separated by commas: 
        $ btlejack --sniff_adv=<rule1>,<rule2>,<rule3>

        First of all, if you want to accept every received frame, you can use "any" or "FF:FF:FF:FF:FF:FF" :
        $ btlejack --sniff_adv=any

        You may also want to focus on specific devices by providing their BD addresses :
        $ btlejack --sniff_adv=11:22:33:44:55:66
        $ btlejack --sniff_adv=11:22:33:44:55:66,aa:bb:cc:dd:ee:ff

        If you want to focus on some specific type of frames, you can provide their respective name :
        $ btlejack --sniff_adv=SCAN_REQ,SCAN_RSP
        $ btlejack --sniff_adv=CONNECT_REQ

        You can also provide a raw pattern as a *limited* regular expression to match a part of the link layer frame. 
        You can use ? to ignore a specific symbol, and * to specify that some information are missing at the beginning
        of the frame :
        $ btlejack --sniff_adv=?5??665544332211          (matches the CONNECT_REQ transmitted by 11:22:33:44:55:66)
        $ btlejack --sniff_adv=*69546167                 (matches the frames containing the string "iTag")

        If you know where is the position of the pattern in the packet, you can provide it using the syntax <pattern>:<position>:
        $ btlejack --sniff_adv=665544332211aabbccddeeff:2 (matches the SCAN_REQ and CONNECT_REQ transmitted by 11:22:33:44:55:66
                                                           to aa:bb:cc:dd:ee:ff)

        The channel can be provided using --channel=37. If multiple sniffers are found, they are set to different channels to
        monitor every advertisements channels.
 
        """

        result = {"policy_type": "whitelist", "rules": []}

        pattern_list = args.sniff_advertisements.split(",")

        adv_types = {
            "ADV_IND": {
                "position": 0,
                "pattern": b"\x00",
                "mask": b"\x0F"
            },
            "ADV_DIRECT_IND": {
                "position": 0,
                "pattern": b"\x01",
                "mask": b"\x0F"
            },
            "ADV_NONCONN_IND": {
                "position": 0,
                "pattern": b"\x02",
                "mask": b"\x0F"
            },
            "SCAN_REQ": {
                "position": 0,
                "pattern": b"\x03",
                "mask": b"\x0F"
            },
            "SCAN_RSP": {
                "position": 0,
                "pattern": b"\x04",
                "mask": b"\x0F"
            },
            "CONNECT_REQ": {
                "position": 0,
                "pattern": b"\x05",
                "mask": b"\x0F"
            },
            "ADV_SCAN_IND": {
                "position": 0,
                "pattern": b"\x06",
                "mask": b"\x0F"
            }
        }

        # For every pattern in the rule's list :
        for pattern in pattern_list:
            # If pattern is "any" or "FF:FF:FF:FF:FF:FF", the policy type is "blacklist" with no rules.
            if pattern == "any" or pattern.lower() == "ff:ff:ff:ff:ff:ff":
                result["policy_type"] = "blacklist"
                result["rules"] = []
                break
            elif re.match(
                    "^([a-fA-F0-9][a-fA-F0-9]:){5}[a-fA-F0-9][a-fA-F0-9]$",
                    pattern):
                # If pattern is a BD address, add a rule matching the pattern anywhere in the frame.
                result["rules"].append({
                    "pattern":
                    bytes.fromhex(''.join([i for i in pattern.split(":")
                                           ][::-1])),
                    "mask":
                    b"\xFF" * 6,
                    "position":
                    0xFF
                })
            elif re.match("^[a-fA-F0-9\?]*:[0-9]+$", pattern) or re.match(
                    "^(\*)?[a-fA-F0-9\?]*(\*)?$", pattern):
                # If a regexp-like pattern is provided, generate the corresponding rule.
                if ":" in pattern:
                    position = int(pattern.split(":")[1])
                    pattern = pattern.split(":")[0]
                else:
                    if "*" == pattern[0]:
                        position = 0xFF
                    else:
                        position = 0
                value = ""
                mask = ""
                for char in pattern:
                    if char == "?":
                        value += "0"
                        mask += "0"
                    elif char != "*":
                        value += char
                        mask += "f"

                if len(value) % 2 == 0 and len(mask) == len(value):
                    value = bytes.fromhex(value)
                    mask = bytes.fromhex(mask)
                    result["rules"].append({
                        "pattern": value,
                        "mask": mask,
                        "position": position
                    })
            elif pattern in adv_types.keys():
                # If pattern is a type, use the corresponding rule in adv_types.
                result["rules"].append(adv_types[pattern])

            # Set the policy according to the --policy_type parameter
            result["policy"] = "whitelist" if args.policy_type is None else (
                args.policy_type if args.policy_type in (
                    "blacklist", "whitelist") else "whitelist")

        try:
            # Instanciate the supervisor
            supervisor = CLIAdvertisementsSniffer(
                verbose=args.verbose,
                devices=args.devices,
                output=output,
                channel=args.channel,
                policy=result,
                accept_invalid_crc=args.accept_invalid_crc,
                display_raw=args.raw)
        except DeviceError as error:
            print(
                '[!] Please connect a compatible Micro:Bit in order to use BtleJack'
            )
            sys.exit(-1)

    elif args.jam_advertisements is not None:
        """
        Btlejack allows to reactively jam some advertisements frames according to a specific pattern in the Link Layer frame.
        If you want to reactively jam the advertisements transmitted by a specific target, you can provide its BD address :
        $ btlejack --jam_adv=11:22:33:44:55:66

        If you want to jam a specific pattern, use the syntax <pattern>:<position>. For example, if you want to jam the frames 
        containing aabbcc at the third position, use the following request :
        $ btlejack --jam_adv=aabbcc:3

        Please note that this feature is still experimental. If you want to stop the reactive jamming, you need to reset the 
        device using the physical button (TODO : bugfix). 
        """
        pattern = args.jam_advertisements
        if re.match("^([a-fA-F0-9][a-fA-F0-9]:){5}[a-fA-F0-9][a-fA-F0-9]$",
                    pattern):
            # If the argument provided is an address, generate the corresponding pattern at position 2.
            position = 2
            pattern = bytes.fromhex(pattern.replace(":", ""))[::-1]
        elif re.match("^[a-fA-F0-9]*:[0-9]+$", pattern):
            # If the argument provided is a pattern, use it directly.
            position = int(pattern.split(":")[1])
            pattern = bytes.fromhex(pattern.split(":")[0])
        else:
            print("[!] Incorrect pattern, exiting ...")
            sys.exit(-3)

        try:
            # Instanciate the supervisor
            supervisor = CLIAdvertisementsJammer(verbose=args.verbose,
                                                 devices=args.devices,
                                                 output=output,
                                                 channel=args.channel,
                                                 pattern=pattern,
                                                 position=position)
        except DeviceError as error:
            print(
                '[!] Please connect a compatible Micro:Bit in order to use BtleJack'
            )
            sys.exit(-1)

    elif not args.flush and not args.install:
        print('BtleJack version %s' % VERSION)
        print('')
        parser.print_help()
        sys.exit(-2)

    try:
        # install a handler in case CTRL-C is pressed
        def ctrlc_handler(signum, frame):
            if output is not None:
                print('[i] Stopping capture process ...')
                output.close()
            raise ForcedTermination()

        signal.signal(signal.SIGINT, ctrlc_handler)

        if supervisor is not None:
            while True:
                supervisor.process_packets()
    except ForcedTermination as e:
        print('[i] Quitting')
    except IOError as io_error:
        print('[!] File access/write error occured, quitting.')
    except FifoError as fifo_err:
        print('[!] An error occured while accessing fifo.')
Example #6
0
class bleSniffer(Sniffer):
    # def __init__(self, output, output_format, devices=None, crc=None, chm=None,
    #              hop=None, verbose=True, timeout=0, hijack=None, jamming=None):
    def __init__(self, options):
        name, self.device, self.nbpkts, self.access_address, self.type = options
        if name is None:
            name = 'btle'

        Sniffer.__init__(self, name)
        #elf.devices, self.nbpkts, self.access_address, self.type = options
        # self.devices, self.crc, self.chm, self.hop, self.verbose, self.timeout, self.hijack, self.jamming, output, output_format = options
        #self.devices = devices
        self.crc = None
        self.chm = None
        self.hop = None
        self.verbose = False
        self.timeout = None
        self.hijack = None
        self.jamming = None
        self.supervisor = None
        # self.crc = crc
        # self.chm = chm
        # self.hop = hop
        # self.verbose = verbose
        # self.timeout = timeout
        # self.hijack = hijack
        # self.jamming = jamming

        self.redirect = io.StringIO()

        self.output = None

    # This function is similar to the scan-connections option of the original btlejack project
    def scan(self):
        try:
            self.supervisor = CLIAccessAddressSniffer(verbose=self.verbose,
                                                      devices=self.device)
        except DeviceError as error:
            print(
                '[!] Please connect a compatible Micro:Bit in order to use BtleJack'
            )
            exit(-1)

    def setSupervisor(self):

        output = '/home/jnt/ble-lived.pcap'
        output_format = 'll_phdr'

        if output_format.lower().strip() == 'nordic':
            self.output = PcapNordicTapWriter(output)
        elif output_format.lower().strip() == 'll_phdr':
            self.output = PcapBlePHDRWriter(output)
        else:
            self.output = PcapBleWriter(output)

        if self.type == 'sniff':
            self.sniff()
        else:
            self.follow(int(self.access_address.replace(':', ''), 16))

    # This function is similar to the follow option of the original btlejack project
    def sniff(self):
        if self.chm is not None:
            chm = int(self.chm, 16)
        else:
            chm = None
        if self.crc is not None:
            crc = int(self.crc, 16)
        else:
            crc = None

        if self.hop is not None:
            hop = self.hop
        else:
            hop = None
        try:
            cached_parameters = BtlejackSession.get_instance().find_connection(
                self.access_address)
            if cached_parameters is not None:
                # override parameters with those stored in cache
                for param in cached_parameters:
                    if param == 'crcinit':
                        crc = cached_parameters[param]
                creation_date = datetime.datetime.fromtimestamp(
                    cached_parameters['start']).strftime('%Y-%m-%d %H:%M:%S')
                print('[i] Using cached parameters (created on %s)' %
                      creation_date)

            try:
                self.supervisor = CLIConnectionRecovery(self.access_address,
                                                        channel_map=chm,
                                                        hijack=self.hijack,
                                                        jamming=self.jamming,
                                                        hop_interval=hop,
                                                        crc=crc,
                                                        output=self.output,
                                                        verbose=self.verbose,
                                                        devices=self.device,
                                                        timeout=self.timeout)
            except SnifferUpgradeRequired as su:
                print("[i] Quitting, please upgrade your sniffer firmware")

        except DeviceError as error:
            print(
                '[!] Please connect a compatible Micro:Bit in order to use BtleJack'
            )
            exit(-1)

    def follow(self, deviceAddress):
        if deviceAddress is not None:
            # address is okay, feed our sniffer
            try:
                self.supervisor = CLIConnectionSniffer(deviceAddress,
                                                       output=self.output,
                                                       verbose=self.verbose,
                                                       devices=self.device)

            except SnifferUpgradeRequired as su:
                print(
                    "[i] Quitting, please upgrade your sniffer firmware (-i option if you are using a Micro:Bit)"
                )
            except DeviceError as error:
                print(
                    '[!] Please connect a compatible Micro:Bit in order to use BtleJack'
                )
                sys.exit(-1)
        else:
            print('[!] Wrong Bluetooth Address format: %s' %
                  self.access_address)

    def run(self):
        try:
            self.setSupervisor()
            if self.supervisor is not None:
                # print("Interepcetion in progress")
                while True:
                    with redirect_stdout(self.redirect):
                        if self.terminated():
                            if self.output is not None:

                                self.output.close()
                            raise ForcedTermination()

                        self.supervisor.process_packets()
        except ForcedTermination as e:
            print('[i] Quitting')