Beispiel #1
0
    def pp_bt_profile_descp_list(self, seq:ElementTree.Element):
        '''Parse and print BluetoothProfileDescriptorList (0x0009).
        
        seq - Example:
                  <sequence>
                      <sequence>
                          <uuid value="0x1108" />
                          <uint16 value="0x0102" />
                      </sequence>
                  </sequence>
        '''
        profiles = seq.findall('./sequence')
        for profile in profiles:
            uuid = profile.find('./uuid').attrib['value']
            print('\t'+uuid+':', end=' ')
            try:
                uuid = int(uuid[2:], base=16)
            except ValueError:
                pass

            try:
                if 'Profile' in service_cls_profile_ids[uuid]['Allowed Usage']:
                    name = service_cls_profile_ids[uuid]['Name']
                    print(green(name), end=' ')
                    # print('\t\t', service_cls_profile_ids[uuid]['Specification'])
                else:
                    print(red('unknown'), end=' ')
                version = int(profile.find('./uint16').attrib['value'][2:], base=16)
                print(green('v%d.%d'%(version>>8, version&0xFF)))
            except KeyError:
                print(red('unknown'))
Beispiel #2
0
    def pp_service_cls_list(self, seq:ElementTree.Element):
        '''Parse and print ServiceClassIDList (0x0001).
        
        seq - Example:
                  <sequence>
                      <uuid value="0x110e" />
                      <uuid value="0x110f" />
                  </sequence>
        '''
        uuids = seq.findall('./uuid')
        for uuid in uuids:
            uuid = uuid.attrib['value']
            print('\t'+uuid+':', end=' ')
            
            try:
                uuid = int(uuid[2:], base=16)
            except ValueError: # Full UUID
                pass

            self.service_clses.append(uuid)

            try:
                if 'Service Class' in service_cls_profile_ids[uuid]['Allowed Usage']:
                    name = service_cls_profile_ids[uuid]['Name']
                    print(green(name))
                else:
                    print(red('unknown'))
            except KeyError:
                if uuid == 0x1800:
                    print(green('Generic Access'))
                elif uuid == 0x1801:
                    print(green('Generic Attribute'))
                else:
                    print(red('unknown'))
Beispiel #3
0
def parse_cmdline() -> dict:
    args = docopt(__doc__, version="v" + VERSION, options_first=True)
    logger.debug("parse_cmdline, args: {}".format(args))

    try:
        if args['-m'] is not None:
            args['-m'] = args['-m'].lower()

            if (args['-m'] == 'sdp'
                    or args['-m'] == 'gatt') and args['BD_ADDR'] is None:
                raise ValueError(
                    "The argument BD_ADDR is {}, please provide it.".format(
                        args['BD_ADDR']))

        args['--inquiry-len'] = int(args['--inquiry-len'])
        args['--timeout'] = int(args['--timeout'])
        args['--sort'] = args['--sort'].lower()

        if args['--channel'] is not None:
            args['--channel'] = [int(n) for n in args['--channel'].split(',')]
            args['--channel'] = set(args['--channel'])
            if args['--channel'].issubset({37, 38, 39}):
                args['--channel'] = list(args['--channel'])
            else:
                raise ValueError("Invalid channel {}, ".format(args['--channel']) \
                    + "must a subset of {37, 38, 39}")

        if args['BD_ADDR'] is not None:
            args['BD_ADDR'] = args['BD_ADDR'].lower()
            if not valid_bdaddr(args['BD_ADDR']):
                raise ValueError("Invalid BD_ADDR: " + red(args['BD_ADDR']))

        if args['--scan-type'] is not None:
            args['--scan-type'] = args['--scan-type'].lower()
            if args['--scan-type'] not in ('active', 'passive'):
                raise ValueError("Invalid scan type %s, " % \
                    red(args['--scan-type']) + "must be active or passive.")

        if args['--addr-type'] is not None:
            args['--addr-type'] = args['--addr-type'].lower()
            if args['--addr-type'] not in ('public', 'random'):
                raise ValueError("Invalid address type %s, " % \
                    args['--addr-type'] + "must be public or random.")

        if args['--io-capability'] not in [
                'DisplayOnly', 'DisplayYesNo', 'KeyboardOnly',
                'NoInputNoOutput', 'KeyboardDisplay', 'KeyboardOnly'
        ]:
            raise ValueError("Invalid IO capability %s" %
                             args['--io-capability'])
    except ValueError as e:
        logger.error(str(e))
        exit(1)

    return args
Beispiel #4
0
    def pp_supported_formats_list(self, val: ElementTree.Element):
        """
        val - sequence
        """
        fid_descps = {
            0x01: "vCard 2.1",
            0x02: "vCard 3.0",
            0x03: "vCal 1.0",
            0x04: "iCal 2.0",
            0x05: "vNote",
            0x06: "vMessage",
            0xFF: "Any type of object"
        }

        format_ids = val.findall('./uint8')
        for format_id in format_ids:
            try:
                id_val = int(format_id.attrib['value'], base=16)
                print('\t0x%02x' % id_val,
                      ': ',
                      green(fid_descps[id_val]),
                      sep='')
            except KeyError as e:
                print('\t0x%02x' % id_val, ': ', red('unknown'), sep='')
                continue
            except Exception as e:
                logger.warning('{}'.format(e))
                print(WARNING_INDENT + format_id.attrib['value'])
Beispiel #5
0
def pp_page_scan_repetition_mode(val):
    print(val, end=' ')
    if val == 0x00:
        print('(R0)')
    elif val == 0x01:
        print('(R1)')
    elif val == 0x02:
        print('(R2)')
    else:
        print(red('RFU'))
Beispiel #6
0
 def pp_supported_features(self, val: int):
     '''Parse and print SupportedFeatures service attribute.
 
     val - Value of SupportedFeatures, Uint16
     '''
     print('\t0x%04X' % val)
     for i in range(len(self.supported_features_bitmap)):
         feature_name = self.supported_features_bitmap[i]
         print('\t\t' + (green(feature_name) if val >> i
                         & 0x0001 else red(feature_name)))
Beispiel #7
0
def bdaddr_to_company_name(addr: str):
    company_id = addr.replace(':', '-').upper()[0:8]

    logger.debug("bdaddr_to_company_name(), addr: " + addr)
    logger.debug(company_id)

    try:
        return blue(oui_company_names[company_id])
    except KeyError:
        return red('Unknown')
Beispiel #8
0
 def pp_map_supported_features(cls, val: int):
     '''Parse and print MapSupportedFeatures (MAP v1.2 and later).
     
     val - Value of MapSupportedFeatures, uint32
     '''
     print('\t0x%08X'%val)
     for i in range(len(cls.map_supported_features_bitmap)):
         feature_name = cls.map_supported_features_bitmap
         print('\t\t', end=' ')
         if i < 23:
             print(green(feature_name) if val >> i & 0x01 else red(feature_name))
         else:
             print(val >> i & 0x01, 'RFU')
 def pp_supported_msg_types(self, val: int):
     '''Parse and print SupportedMessageTypes service attribute.
     
     val - Value of SupportedMessageTypes, uint8
     '''
     print('\t0x%02X' % val)
     for i in range(len(self.supported_msg_types_bitmap)):
         type_name = self.supported_msg_types_bitmap[i]
         print('\t\t', end=' ')
         if i < 5:
             print(green(type_name) if val >> i & 0x01 else red(type_name))
         else:
             print(val >> i & 0x01, 'RFU')
Beispiel #10
0
def pp_ext_lmp_features(ext_lmp_features:bytes, page_num:int):
    '''Parse and print Extended LMP Features

    ext_lmp_features -- when page_num is 0, 8 bytes;
                        when page_num is 1, 1 bytes;
                        when page_num is 2, 2 bytes.
    '''
    if page_num == 0:
        print('Page 0')
        pp_lmp_features(ext_lmp_features)
    elif page_num == 1:
        b = ext_lmp_features[0]
        print('Page 1')
        print('    Secure Simple Pairing (Host Support):', green('True') if b & 0x01 else red('False'))
        print('    LE Supported (Host):', green('True') if (b >> 1) & 0x01 else red('False'))
        print('    Simultaneous LE and BR/EDR to Same Device Capable (Host):', green('True') if (b >> 2) & 0x01 else red('False'))
        print('    Secure Connections (Host Support):', green('True') if (b >> 3) & 0x01 else red('False'))
    elif page_num == 2:
        print('Page 2')
        for i in range(0, 2):
            b = ext_lmp_features[i]
            if i == 0:
                print('    Connectionless Slave Broadcast - Master Operation:', green('True') if b & 0x01 else red('False'))
                print('    Connectionless Slave Broadcast - Slave Operation:', green('True') if (b >> 1) & 0x01 else red('False'))
                print('    Synchronization Train:', green('True') if (b >> 2) & 0x01 else red('False'))
                print('    Synchronization Scan:', green('True') if (b >> 3) & 0x01 else red('False'))
                print('    HCI_Inquiry_Response_Notification event: ', green('True') if (b >> 4) & 0x01 else red('False'))
                print('    Generalized interlaced scan:', green('True') if (b >> 5) & 0x01 else red('False'))
                print('    Coarse Clock Adjustment:', green('True') if (b >> 6) & 0x01 else red('False'))
                print('    Reserved for future use:', green('True') if (b >> 7) & 0x01 else red('False'))
            elif i == 1:
                print('    Secure Connections (Controller Support):', green('True') if b & 0x01 else red('False'))
                print('    Ping:', green('True') if (b >> 1) & 0x01 else red('False'))
                print('    Slot Availability Mask:', green('True') if (b >> 2) & 0x01 else red('False'))
                print('    Train nudging:', green('True') if (b >> 3) & 0x01 else red('False'))
    else:
        print(WARNING, 'Unknown page number', page_num)
Beispiel #11
0
def parse_cmdline() -> dict:
    args = docopt(__doc__, version='v0.4.1', options_first=True)
    logger.debug("ui.parse_cmdline, args: {}".format(args))

    args['-m'] = args['-m'].lower()
    args['--inquiry-len'] = int(args['--inquiry-len'])
    args['--timeout'] = int(args['--timeout'])
    args['--sort'] = args['--sort'].lower()

    if args['--channel'] is not None:
        args['--channel'] = [int(n) for n in args['--channel'].split(',')]
        args['--channel'] = set(args['--channel'])
        if args['--channel'].issubset({37, 38, 39}):
            args['--channel'] = list(args['--channel'])
        else:
            raise ValueError("Invalid channel {}, ".format(args['--channel']) \
                + "must a subset of {37, 38, 39}")

    if args['BD_ADDR'] is not None:
        args['BD_ADDR'] = args['BD_ADDR'].lower()
        if not valid_bdaddr(args['BD_ADDR']):
            raise ValueError("Invalid BD_ADDR: " + red(args['BD_ADDR']))

    if args['--scan-type'] is not None:
        args['--scan-type'] = args['--scan-type'].lower()
        if args['--scan-type'] not in ('active', 'passive'):
            raise ValueError("Invalid scan type %s, " % \
                red(args['--scan-type']) + "must be active or passive.")

    if args['--addr-type'] is not None:
        args['--addr-type'] = args['--addr-type'].lower()
        if args['--addr-type'] not in ('public', 'random'):
            raise ValueError("Invalid address type %s, " % \
                args['--addr-type'] + "must be public or random.")

    return args
Beispiel #12
0
def pp_le_features(features:bytes):
    """
    features - LE LL features. The Bluetooth specification calls this FeatureSet.
    待处理 Valid from Controller to Controller, Masked to Peer, Host Controlled
    """
    for i in range(8):
        b  = features[i]
        if i == 0:
            print('    LE Encryption:', green('True') if b & 0x01 else red('False'))
            print('    Connection Parameters Request Procedure:', green('True') if (b >> 1) & 0x01 else red('False'))
            print('    Extended Reject Indication:', green('True') if (b >> 2) & 0x01 else red('False'))
            print('    Slave-initiated Features Exchange:', green('True') if (b >> 3) & 0x01 else red('False'))
            print('    LE Ping: ', green('True') if (b >> 4) & 0x01 else red('False'))
            print('    LE Data Packet Length Extension:', green('True') if (b >> 5) & 0x01 else red('False'))
            print('    LL Privacy:', green('True') if (b >> 6) & 0x01 else red('False'))
            print('    Extended Scanner Filter Policies:', green('True') if (b >> 7) & 0x01 else red('False'))
        elif i == 1:
            print('    LE 2M PHY:', green('True') if b & 0x01 else red('False'))
            print('    Stable Modulation Index - Transmitter:', green('True') if (b >> 1) & 0x01 else red('False'))
            print('    Stable Modulation Index - Receiver:', green('True') if (b >> 2) & 0x01 else red('False'))
            print('    LE Coded PHY:', green('True') if (b >> 3) & 0x01 else red('False'))
            print('    LE Extended Advertising:', green('True') if (b >> 4) & 0x01 else red('False'))
            print('    LE Periodic Advertising:', green('True') if (b >> 5) & 0x01 else red('False'))
            print('    Channel Selection Algorithm #2:', green('True') if (b >> 6) & 0x01 else red('False'))
            print('    LE Power Class 1:', green('True') if (b >> 7) & 0x01 else red('False'))
        elif i == 2:
            print('    Minimum Number of Used Channels Procedure:', green('True') if b & 0x01 else red('False'))
            print('    Connection CTE Request:', green('True') if (b >> 1) & 0x01 else red('False'))
            print('    Connection CTE Response:', green('True') if (b >> 2) & 0x01 else red('False'))
            print('    Connectionless CTE Transmitter:', green('True') if (b >> 3) & 0x01 else red('False'))
            print('    Connectionless CTE Receiver:', green('True') if (b >> 4) & 0x01 else red('False'))
            print('    Antenna Switching During CTE Transmission (AoD):', green('True') if (b >> 5) & 0x01 else red('False'))
            print('    Antenna Switching During CTE Reception (AoA):', green('True') if (b >> 6) & 0x01 else red('False'))
            print('    Receiving Constant Tone Extensions:', green('True') if (b >> 7) & 0x01 else red('False'))
        elif i == 3:
            print('    Periodic Advertising Sync Transfer - Sender:', green('True') if b & 0x01 else red('False'))
            print('    Periodic Advertising Sync Transfer - Recipient:', green('True') if (b >> 1) & 0x01 else red('False'))
            print('    Sleep Clock Accuracy Updates:', green('True') if (b >> 2) & 0x01 else red('False'))
            print('    Remote Public Key Validation:', green('True') if (b >> 3) & 0x01 else red('False'))
            print('    Connected Isochronous Stream - Master:', green('True') if (b >> 4) & 0x01 else red('False'))
            print('    Connected Isochronous Stream - Slave:', green('True') if (b >> 5) & 0x01 else red('False'))
            print('    Isochronous Broadcaster:', green('True') if (b >> 6) & 0x01 else red('False'))
            print('    Synchronized Receiver:', green('True') if (b >> 7) & 0x01 else red('False'))
        elif i == 4:
            print('    Isochronous Channels (Host Support):', green('True') if b & 0x01 else red('False'))
            print('    LE Power Control Request:', green('True') if (b >> 1) & 0x01 else red('False'))
            print('    LE Power Change Indication:', green('True') if (b >> 2) & 0x01 else red('False'))
            print('    LE Path Loss Monitoring:', green('True') if (b >> 3) & 0x01 else red('False'))
Beispiel #13
0
    def scan(self, bdaddr, addr_type, include_descriptor: bool):
        """
        bdaddr - Remote BD_ADDR
        """
        def run_mainloop():
            mainloop.run()

        mainloop_thread = threading.Thread(target=run_mainloop, args=[])
        mainloop_thread.start()

        try:
            try:
                target = Peripheral(bdaddr,
                                    iface=self.devid,
                                    addrType=addr_type)
            except BTLEDisconnectError as e:
                logger.error("BTLEDisconnectError")
                print(ERROR_INDENT, e, sep='')
                return

            services = target.getServices()
            print("Number of services: %s\n\n" % len(services))

            # Show service
            for service in services:
                logger.debug('Start handle: {}'.format(service.hndStart))
                logger.debug('End handle: {}'.format(service.hndEnd))

                try:
                    characteristics = []
                    characteristics = service.getCharacteristics()
                except BTLEException as e:
                    logger.warning("BTLEException")
                    print(WARNING_INDENT, e, sep='')
                    # continue

                print(
                    blue('Service'), '(0x%04x - 0x%04x, %s characteristics)' %
                    (service.hndStart, service.hndEnd, len(characteristics)))
                print(
                    indent + 'Handle: 0x%04x' % service.hndStart
                )  # ", "\"attr handle\" by using gatttool -b <BD_ADDR> --primary
                print(indent + 'Type: (May be primary service 0x2800)')
                print(indent + 'Value (Service UUID): ',
                      blue(str(service.uuid).replace(sig_uuid_suffix, '')),
                      end=' ')
                try:
                    print(
                        '(' +
                        services_spec['0x' +
                                      ("%s" %
                                       service.uuid)[4:8].upper()]['Name'] +
                        ')', '\x1B[0m')
                except KeyError:
                    print('(' + red('unknown') + ')', '\x1B[0m')
                print(
                    indent +
                    'Permission: Read Only, No Authentication, No Authorization\n'
                )

                # Show characteristic
                for characteristic in characteristics:
                    descriptors = []
                    # 对每个 characteristic 都获取 descriptor 会很耗时
                    # 有些设备会因此断开连接。于是这里提供了一个是否获取 descriptor 的选项
                    if include_descriptor:
                        descriptors = characteristic.getDescriptors()

                    try:
                        print(indent + yellow('Characteristic'),
                              '(%s descriptors)' % len(descriptors))
                        #print('-'*8)
                        print(indent * 2 + 'Handle: %#06x' %
                              (characteristic.getHandle() - 1))
                        print(indent * 2 + 'Type: 0x2803 (Characteristic)')
                        print(indent * 2 + 'Value:')
                        print(indent * 3 + 'Characteristic properties:',
                              green(characteristic.propertiesToString()))
                        print(indent * 3 +
                              'Characteristic value handle: %#06x' %
                              characteristic.getHandle())
                        print(
                            indent * 3 + 'Characteristic UUID: ',
                            green(
                                str(characteristic.uuid).replace(
                                    sig_uuid_suffix, '')),
                            end=' '
                        )  # This UUID is also the type field of characteristic value declaration attribute.
                        try:
                            print('(' + characteristics_spec['0x' + (
                                "%s" %
                                characteristic.uuid)[4:8].upper()]['Name'] +
                                  ')')
                        except KeyError:
                            print('(' + red('unknown') + ')')
                        print(
                            indent * 3 +
                            'Permission: Read Only, No Authentication, No Authorization'
                        )

                        if characteristic.supportsRead():
                            print(indent + yellow('Characteristic value'))
                            print(indent * 2 + 'Handle:',
                                  green('%#06x' % characteristic.getHandle()))
                            print(
                                indent * 2 + 'Type:',
                                str(characteristic.uuid).replace(
                                    sig_uuid_suffix, ''))
                            print(indent * 2 + 'Value:',
                                  green(str(characteristic.read())))
                            print(
                                indent * 2 +
                                'Permission: Higher layer profile or implementation-specific'
                            )
                    except BTLEException as e:
                        print('        ' + str(e))

                    # Show descriptor
                    for descriptor in descriptors:
                        try:
                            print(indent + yellow('Descriptor'))
                            print(indent * 2 + 'Handle:',
                                  green('%#06x' % descriptor.handle))
                            print(indent * 2 + 'Type:',
                                  str(descriptor.uuid).replace(
                                      sig_uuid_suffix, ''),
                                  end=' ')
                            try:
                                print('(' + descriptors_spec['0x' + (
                                    "%s" %
                                    descriptor.uuid)[4:8].upper()]['Name'] +
                                      ')')
                            except KeyError:
                                print('(Unknown descriptor)')
                            print(indent * 2 + 'Value:',
                                  green(str(descriptor.read())))
                            print(indent * 2 + 'Permissions:')
                        except BTLEException as e:
                            print(indent * 2 + str(e))
                    print()
                print()

            # Set remote device untursted
            output = subprocess.check_output(' '.join(
                ['bluetoothctl', 'untrust', bdaddr]),
                                             stderr=STDOUT,
                                             timeout=60,
                                             shell=True)
            logger.info(output.decode())

            # output = subprocess.check_output(
            #     ' '.join(['sudo', 'systemctl', 'stop', 'bluetooth.service']),
            #     stderr=STDOUT, timeout=60, shell=True)

            # output = subprocess.check_output(
            #     ' '.join(['sudo', 'rm', '-rf', '/var/lib/bluetooth/' + \
            #               self.hci_bdaddr + '/' + bdaddr.upper()]),
            #     stderr=STDOUT, timeout=60, shell=True)

            # output = subprocess.check_output(
            #     ' '.join(['sudo', 'systemctl', 'start', 'bluetooth.service']),
            #     stderr=STDOUT, timeout=60, shell=True)
        finally:
            if self.agent_registered:
                self.agent_mgr_1_iface.UnregisterAgent(
                    ObjectPath(self.bluescan_agent.path))
                logger.info('Unregistered Agent object')

                mainloop.quit()
Beispiel #14
0
    def __init__(self, record_xml:str):
        self.attrs = {
            self.HID_DEVICE_RELEASE_NUMBER: {
                'Name': 'HIDDeviceReleaseNumber (Deprecated)',
                'Parser': lambda val: print('\t0x%04X'%val)},
            self.HID_PARSER_VERSION: {
                # HIDP v1.1.1, 5.3.4.2 HIDParserVersion
                # 
                # Example
                #     <attribute id="0x0201">
                #         <uint16 value="0x0111" />
                #     </attribute>
                'Name': 'HIDParserVersion',
                'Parser': lambda val: print('\t0x%04x:'%val, 
                    green('USB HID specification ' + 'v' + \
                    ('%04x'%val)[:2].lstrip('0') + '.' + ('%04x.'%val)[2] + '.' + \
                    ('%04x.'%val)[3]))},

            self.HID_DEVICE_SUBCLASS: {
                # HIDP v1.1.1, 5.3.4.3 HIDDeviceSubclass
                #
                # Example
                #     <attribute id="0x0203">
                #         <uint8 value="0x00" />
                #     </attribute>
                'Name': 'HIDDeviceSubclass',
                'Parser': lambda val: print('\t0x%02X'%val)},
            self.HID_COUNTRY_CODE: {
                'Name': 'HIDCountryCode',
                'Parser': lambda val: print('\t0x%02X'%val)},
            self.HID_VIRTUAL_CABLE: {
                'Name': 'HIDVirtualCable',
                'Parser': lambda val: print(green('\tTrue') if val == 'true' \
                    else red('\tFalse'))},
            self.HID_RECONNECT_INITIATE:{
                'Name': 'HIDReconnectInitiate',
                'Parser': lambda val: print(green('\tTrue') if val == 'true' \
                    else red('\tFalse'))},
            self.HID_DESCRIPTOR_LIST:{
                'Name': 'HIDDescriptorList',
                'Parser': lambda val: print('\t', val)},
            self.HID_LANGID_BASE_LIST:{
                'Name': 'HIDLANGIDBaseList',
                'Parser': lambda val: print('\t', val)},
            self.HID_SDP_DISABLE:{
                'Name': 'HIDSDPDisable (Deprecated)',
                'Parser': lambda val: print(green('\tTrue') if val == 'true' \
                    else red('\tFalse'))},
            self.HID_BATTERY_POWER:{
                'Name': 'HIDBatteryPower',
                'Parser': lambda val: print(green('\tTrue') if val == 'true' \
                    else red('\tFalse'))},
            self.HID_REMOTE_WAKE:{
                'Name': 'HIDRemoteWake', 
                'Parser': lambda val: print(green('\tTrue') if val == 'true' \
                    else red('\tFalse'))},
            self.HID_PROFILE_VERSION:{
                'Name': 'HIDProfileVersion',
                'Parser': lambda val: print('\t0x%04X'%val)},
            self.HID_SUPERVISION_TIMEOUT:{
                'Name': 'HIDSupervisionTimeout',
                'Parser': lambda val: print('\t0x%04X'%val)},
            self.HID_NORMALLY_CONNECTABLE:{
                'Name': 'HIDNormallyConnectable',
                'Parser': lambda val: print(green('\tTrue') if val == 'true' \
                    else red('\tFalse'))},
            self.HID_BOOT_DEVICE: {
                'Name': 'HIDBootDevice',
                'Parser': lambda val: print(green('\tTrue') if val == 'true' \
                    else red('\tFalse'))},
            self.HID_SSR_HOST_MAX_LATENCY:{
                'Name': 'HIDSSRHostMaxLatency',
                'Parser': lambda val: print('\t0x%04X'%val)},
            self.HID_SSR_HOST_MIN_TIMEOUT:{
                'Name': 'HIDSSRHostMinTimeout',
                'Parser': lambda val: print('\t0x%04X'%val)
            },
            # HIDSSRHostMinTimeout 0x0211-0x03FF
            # Available for HID Language Strings 0x0400-0xFFFF
        }

        super().__init__(record_xml)
Beispiel #15
0
def pp_lmp_features(lmp_features: bytes):
    '''Parse and print LMP Features
    
    lmp_features -- 8 bytes
    '''
    for i in range(8):
        b = lmp_features[i]
        if i == 0:
            print('    3 slot packets:',
                  green('True') if b & 0x01 else red('False'))
            print('    5 slot packets:',
                  green('True') if (b >> 1) & 0x01 else red('False'))
            print('    Encryption:',
                  green('True') if (b >> 2) & 0x01 else red('False'))
            print('    Slot offset:',
                  green('True') if (b >> 3) & 0x01 else red('False'))
            print('    Timing accuracy:',
                  green('True') if (b >> 4) & 0x01 else red('False'))
            print('    Role switch:',
                  green('True') if (b >> 5) & 0x01 else red('False'))
            print('    Hold mode:',
                  green('True') if (b >> 6) & 0x01 else red('False'))
            print('    Sniff mode:',
                  green('True') if (b >> 7) & 0x01 else red('False'))
        elif i == 1:
            print('    Previously used:',
                  green('True') if b & 0x01 else red('False'))
            print('    Power control requests:',
                  green('True') if (b >> 1) & 0x01 else red('False'))
            print('    Channel quality driven data rate (CQDDR):',
                  green('True') if (b >> 2) & 0x01 else red('False'))
            print('    SCO link:',
                  green('True') if (b >> 3) & 0x01 else red('False'))
            print('    HV2 packets:',
                  green('True') if (b >> 4) & 0x01 else red('False'))
            print('    HV3 packets:',
                  green('True') if (b >> 5) & 0x01 else red('False'))
            print('    μ-law log synchronous data:',
                  green('True') if (b >> 6) & 0x01 else red('False'))
            print('    A-law log synchronous data:',
                  green('True') if (b >> 7) & 0x01 else red('False'))
        elif i == 2:
            print('    CVSD synchronous data:',
                  green('True') if b & 0x01 else red('False'))
            print('    Paging parameter negotiation:',
                  green('True') if (b >> 1) & 0x01 else red('False'))
            print('    Power control:',
                  green('True') if (b >> 2) & 0x01 else red('False'))
            print('    Transparent synchronous data:',
                  green('True') if (b >> 3) & 0x01 else red('False'))
            print('    Flow control lag:', (b & 0x70) >> 4)
            print('    Broadcast Encryption:',
                  green('True') if (b >> 7) & 0x01 else red('False'))
        elif i == 3:
            print('    Reserved for future use:',
                  green('True') if b & 0x01 else red('False'))
            print('    Enhanced Data Rate ACL 2 Mb/s mode:',
                  green('True') if (b >> 1) & 0x01 else red('False'))
            print('    Enhanced Data Rate ACL 2 Mb/s mode:',
                  green('True') if (b >> 2) & 0x01 else red('False'))
            print('    Enhanced inquiry scan:',
                  green('True') if (b >> 3) & 0x01 else red('False'))
            print('    Interlaced inquiry scan:',
                  green('True') if (b >> 4) & 0x01 else red('False'))
            print('    Interlaced page scan:',
                  green('True') if (b >> 5) & 0x01 else red('False'))
            print('    RSSI with inquiry results:',
                  green('True') if (b >> 6) & 0x01 else red('False'))
            print('    Extended SCO link (EV3 packets):',
                  green('True') if (b >> 7) & 0x01 else red('False'))
        elif i == 4:
            print('    EV4 packets:',
                  green('True') if b & 0x01 else red('False'))
            print('    EV5 packets:',
                  green('True') if (b >> 1) & 0x01 else red('False'))
            print('    Reserved for future use:',
                  green('True') if (b >> 2) & 0x01 else red('False'))
            print('    AFH capable slave:',
                  green('True') if (b >> 3) & 0x01 else red('False'))
            print('    AFH classification slave:',
                  green('True') if (b >> 4) & 0x01 else red('False'))
            print('    BR/EDR Not Supported:',
                  green('True') if (b >> 5) & 0x01 else red('False'))
            print('    LE Supported (Controller):',
                  green('True') if (b >> 6) & 0x01 else red('False'))
            print('    3-slot Enhanced Data Rate ACL packets:',
                  green('True') if (b >> 7) & 0x01 else red('False'))
        elif i == 5:
            print('    5-slot Enhanced Data Rate ACL packets:',
                  green('True') if b & 0x01 else red('False'))
            print('    Sniff subrating:',
                  green('True') if (b >> 1) & 0x01 else red('False'))
            print('    Pause encryption:',
                  green('True') if (b >> 2) & 0x01 else red('False'))
            print('    AFH capable master:',
                  green('True') if (b >> 3) & 0x01 else red('False'))
            print('    AFH classification master:',
                  green('True') if (b >> 4) & 0x01 else red('False'))
            print('    Enhanced Data Rate eSCO 2 Mb/s mode:',
                  green('True') if (b >> 5) & 0x01 else red('False'))
            print('    Enhanced Data Rate eSCO 3 Mb/s mode:',
                  green('True') if (b >> 6) & 0x01 else red('False'))
            print('    3-slot Enhanced Data Rate eSCO packets:',
                  green('True') if (b >> 7) & 0x01 else red('False'))
        elif i == 6:
            print('    Extended Inquiry Response:',
                  green('True') if b & 0x01 else red('False'))
            print(
                '    Simultaneous LE and BR/EDR to Same Device Capable (Controller):',
                green('True') if (b >> 1) & 0x01 else red('False'))
            print('    Reserved for future use:',
                  green('True') if (b >> 2) & 0x01 else red('False'))
            print('    Secure Simple Pairing (Controller Support):',
                  green('True') if (b >> 3) & 0x01 else red('False'))
            print('    Encapsulated PDU:',
                  green('True') if (b >> 4) & 0x01 else red('False'))
            print('    Erroneous Data Reporting:',
                  green('True') if (b >> 5) & 0x01 else red('False'))
            print('    Non-flushable Packet Boundary Flag:',
                  green('True') if (b >> 6) & 0x01 else red('False'))
            print('    Reserved for future use:',
                  green('True') if (b >> 7) & 0x01 else red('False'))
        elif i == 7:
            print('    HCI_Link_Supervision_Timeout_Changed event:',
                  green('True') if b & 0x01 else red('False'))
            print('    Variable Inquiry TX Power Level:',
                  green('True') if (b >> 1) & 0x01 else red('False'))
            print('    Enhanced Power Control:',
                  green('True') if (b >> 2) & 0x01 else red('False'))
            print('    Reserved for future use:',
                  green('True') if (b >> 3) & 0x01 else red('False'))
            print('    Reserved for future use:',
                  green('True') if (b >> 4) & 0x01 else red('False'))
            print('    Reserved for future use:',
                  green('True') if (b >> 5) & 0x01 else red('False'))
            print('    Reserved for future use:',
                  green('True') if (b >> 6) & 0x01 else red('False'))
            print('    Extended features:',
                  green('True') if (b >> 7) & 0x01 else red('False'))
Beispiel #16
0
def attr_permissions2str(permissions: dict):
    permi_str = ''

    read_flags = []
    write_flags = []

    if permissions is None:
        return red("Unknown")

    if permissions['read']['enable']:
        permi_str += 'Read'

        if permissions['read']['authen'] or permissions['read'][
                'author'] or permissions['read']['higher']:
            permi_str += "("

            if permissions['read']['authen']:
                read_flags.append("authen")

            if permissions['read']['author']:
                read_flags.append("author")

            if permissions['read']['higher']:
                read_flags.append("higher")

            permi_str += ' '.join(read_flags)
            permi_str += ")"
        elif not (permissions['write']['enable'] or permissions['encrypt']
                  or permissions['higher']):
            return "Read Only, No Authentication, No Authorization"

        permi_str += ' '

    if permissions['write']['enable']:
        permi_str += 'Write'

        if permissions['write']['authen'] or permissions['write'][
                'author'] or permissions['write']['higher']:
            permi_str += "("

            if permissions['write']['authen']:
                write_flags.append("authen")

            if permissions['write']['author']:
                write_flags.append("author")

            if permissions['write']['higher']:
                write_flags.append("higher")

            permi_str += ' '.join(write_flags)
            permi_str += ")"

        permi_str += ' '

    if permissions['encrypt']:
        permi_str += 'Encrypt'
        permi_str += ' '

    if permissions['higher']:
        permi_str += 'Higher layer profile or implementation specific'
        permi_str += ' '

    return permi_str
Beispiel #17
0
class AGServiceRecord(ServiceRecord):
    '''Autio Gateway Service Record
    
    See HFP Specification v1.8, Table 5.3: Service Record for the AG
    '''
    service_clses = [{
        'UUID': 0x111F,
        'name': 'HandsfreeAudioGateway'
    }, {
        'UUID': 0x1203,
        'name': 'GenericAudio'
    }]

    NETWORK = 0x0301
    SUPPORTED_FEATURES = 0x0311

    network = {
        0x01: green('Ability to reject a call'),
        0x00: red('No ability to reject a call')
    }

    # See HFP Specification v1.8, Table 5.4: "SupportedFeatures" attribute bit
    # mapping for the AG
    supported_features_bitmap = {
        0: 'Three-way calling',  # LSB
        1: 'EC and/or NR function',
        2: 'Voice recognition function',
        3: 'In-band ring tone capability',
        4: 'Attach a phone number to a voice tag',
        5: 'Wide band speech',
        6: 'Enhanced Voice Recognition Status',
        7: 'Voice Recognition Text'
    }

    def __init__(self, record_xml: str):
        '''
        record_xml - Service record XML for Autio Gateway
        '''
        self.attrs = {
            self.NETWORK: {
                'Name': 'Network',
                'Parser': self.pp_network
            },
            self.SUPPORTED_FEATURES: {
                'Name': 'SupportedFeatures',
                'Parser': self.pp_supported_features
            },
        }

        super().__init__(record_xml)

    def pp_network(self, val: int):
        '''Parse and print Network service attribute.

        val - Value of Network, Uint8
        '''
        print('\t0x%02X' % val)
        print('\t\t' + self.network[val])

    def pp_supported_features(self, val: int):
        '''Parse and print SupportedFeatures service attribute.

        val  - Value of SupportedFeatures, Uint16
        '''
        print('\t0x%04X' % val)
        for i in range(len(self.supported_features_bitmap)):
            feature_name = self.supported_features_bitmap[i]
            print('\t\t' + (green(feature_name) if val >> i
                            & 0x0001 else red(feature_name)))
Beispiel #18
0
    def print(self):
        for dev_info in self.devices_info:
            print('Addr:       ', blue(dev_info.addr), 
                  "("+bdaddr_to_company_name(dev_info.addr)+")" if dev_info.addr_type == 'public' else "")
            print('Addr type:  ', blue(dev_info.addr_type))
            print('Connectable:', 
                green('True') if dev_info.connectable else red('False'))
            print("RSSI:        {} dBm".format(dev_info.rssi))
            print("General Access Profile:")
            
            # TODO: Unify the gap type name parsings of BR and LE
            for ad in dev_info.ad_structs:
                try:
                    type_names = gap_type_names[ad.type]
                except KeyError:
                    type_names = "0x{:02X} ".format(ad.type)+"("+ red("Unknown")+")"

                print(INDENT+"{}: ".format(type_names), end='')
                # print(INDENT+"0x{:02X} ({}): ".format(ad.type, type_names), end='')
                
                # Parses AD structure based on https://www.bluetooth.com/specifications/specs/
                # -> Core Specification Supplement
                if ad.type == COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS or \
                    ad.type == INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS:
                    print()
                    for uuid in ad.value.split(','):
                        if len(uuid) == 36:
                            # 这里拿到的是完整的 128-bit uuid,但我们需要 16-bit uuid。
                            print(INDENT*2 + blue("0x"+uuid[4:8].upper()))
                        else:
                            print(INDENT*2 + blue(uuid))
                elif ad.type == COMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS or \
                    ad.type == INCOMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS:
                    print()
                    for uuid in ad.value.split(','): 
                        if len(uuid) == 36:
                            # 这里拿到的是完整的 128-bit uuid,但我们需要 32-bit uuid。
                            print(INDENT*2 + blue("0x"+uuid[0:8].upper()))
                        else:
                            print(INDENT*2 + blue(uuid))
                elif ad.type == COMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS or \
                    ad.type == INCOMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS:
                        print()
                        for uuid in ad.value.split(','): 
                            print(INDENT*2 + blue(uuid).upper())
                elif ad.type == SERVICE_DATA_16_BIT_UUID:
                    print()
                    print(INDENT*2 + "UUID: 0x{}".format(ad.value[0:2*2].upper()))
                    print(INDENT*2 + "Data:", ad.value[2*2:])
                elif ad.type == SERVICE_DATA_32_BIT_UUID:
                    print()
                    print(INDENT*2 + "UUID: {}".format(ad.value[0:4*2].upper()))
                    print(INDENT*2 + "Data:", ad.value[4*2:])
                elif ad.type == SERVICE_DATA_128_BIT_UUID:
                    print()
                    print(INDENT*2 + "UUID: {}".format(ad.value[0:16*2].upper()))
                    print(INDENT*2 + "Data: ", ad.value[16*2:])
                elif ad.type == FLAGS:
                    print()
                    try:
                        value = bytes.fromhex(ad.value)
                        print(INDENT*2 + "LE Limited Discoverable Mode\n" if value[0] & 0x01 else "", end="")
                        print(INDENT*2 + "LE General Discoverable Mode\n" if value[0] & 0x02 else "", end="")
                        print(INDENT*2 + "BR/EDR Not Supported\n" if value[0] & 0x04 else "", end="") # Bit 37 of LMP Feature Mask Definitions (Page 0)
                        print(INDENT*2 + "Simultaneous LE + BR/EDR to Same Device Capable (Controller)\n" if value[0] & 0x08 else "", end="") # Bit 49 of LMP Feature Mask Definitions (Page 0)
                        print(INDENT*2 + "Simultaneous LE + BR/EDR to Same Device Capable (Host)\n" if value[0] & 0x10 else "", end="") # Bit 66 of LMP Feature Mask Definitions (Page 1)
                    except (ValueError, IndexError) as e:
                        logger.debug("LeDevicesScanResult.print(), parse ad.type == FLAGS")
                        print(ad.value, "("+red("Raw")+")")
                elif ad.type == MANUFACTURER_SPECIFIC_DATA:
                    value = bytes.fromhex(ad.value)
                    company_id = int.from_bytes(value[0:2], 'little', signed=False)
                    try:
                        company_name = blue(company_names[company_id])
                    except KeyError:
                        company_name = red("Unknown")
                    
                    if len(value) >= 2:
                        print()
                        print(INDENT*2+"Company ID:", '0x{:04X} ({})'.format(company_id,company_name))
                        try:
                            
                            print(INDENT*2+'Data:      ', ''.join(["{:02X}".format(b) for b in value[2:]]))
                        except IndexError:
                            print(INDENT*2+'Data:', None)
                    else:
                        print(value)
                elif ad.type == TX_POWER_LEVEL:
                    value = int.from_bytes(bytes.fromhex(ad.value), 'little', signed=True)
                    print(value, "dBm", "(pathloss {} dBm)".format(value - dev_info.rssi))
                else:
                    print(ad.value)

            print()  
            print() # Two empty lines before next LE device information
Beispiel #19
0
    def pp_attr(self, attr:ElementTree.Element):
        '''Parse and print the service attribute.

        attr - Example:
                   <attribute id="0x????">
                       ... ... 
                   </attribute>
        '''
        attr_id = int(attr.attrib['id'][2:], base=16)
        val_type = attr.find('./').tag
        if 'uint' in val_type:
            val = int(attr.find('./'+val_type).attrib['value'][2:], base=16)
        elif val_type == 'sequence':
            val = attr.find('./'+val_type)
        else:
            val = attr.find('./'+val_type).attrib['value']

        # Parse and print three universal attribute ID offsets
        for base in self.attr_id_bases:    
            offset = attr_id - base
            try:
                print('0x%04x:'%attr_id, 
                    self.universal_attr_offsets[offset]['Name'], 
                    '(%s)'%val_type)
            
                self.universal_attr_offsets[offset]['Parser'](val)
                return
            except KeyError:
                continue

        try:
            # The code for parsing a service attribute is divided into two parts.
            # One of the part is parsing the universal attribute and the other part is 
            # parsing the non universal attribute. This is the first part.
            print('0x%04x:'%attr_id, self.universal_attrs[attr_id]['Name'], 
                '(%s)'%val_type)
            self.universal_attrs[attr_id]['Parser'](val)
        except KeyError:
            # The code that parses a service attribute is divided into two parts.
            # One of the part is parsing the universal attribute and the other part is 
            # parsing the non universal attribute. This is the second part.
            from .ag_service_record import AGServiceRecord
            from .hf_service_record import HFServiceRecord
            from .hid_service_record import HIDServiceRecord
            from .mce_service_record import MCEServiceRecord
            from .mse_service_record import MSEServiceRecord
            from .op_service_record import ObjPushServiceRecord

            # Guess if the attribute is one of the universal attribute offsets.
            offset = attr_id - 0x0100
            try:
                print('0x%04x:'%attr_id, 
                    self.universal_attr_offsets[offset]['Name'], '(guess)', 
                    '(%s)'%val_type)
                self.universal_attr_offsets[offset]['Parser'](val)
                return
            except KeyError:
                pass

            if self.service_clses[0] == HFServiceRecord.service_clses[0]['UUID']:
                hfsr = HFServiceRecord(self.record_xml)
                print('0x%04x:'%attr_id, hfsr.attrs[attr_id]['Name'], 
                    '(%s)'%val_type)
                hfsr.attrs[attr_id]['Parser'](val)
            elif self.service_clses[0] == AGServiceRecord.service_clses[0]['UUID']:
                agsr = AGServiceRecord(self.record_xml)
                print('0x%04x:'%attr_id, agsr.attrs[attr_id]['Name'], 
                    '(%s)'%val_type)
                agsr.attrs[attr_id]['Parser'](val)
            elif self.service_clses[0] == MSEServiceRecord.service_clses[0]['UUID']:
                msesr = MSEServiceRecord(self.record_xml)
                print('0x%04x:'%attr_id, msesr.attrs[attr_id]['Name'], 
                    '(%s)'%val_type)
                msesr.attrs[attr_id]['Parser'](val)
            elif self.service_clses[0] == MCEServiceRecord.service_clses[0]['UUID']:
                mcesr = MCEServiceRecord(self.record_xml)
                print('0x%04x:'%attr_id, mcesr.attrs[attr_id]['Name'], 
                    '(%s)'%val_type)
                mcesr.attrs[attr_id]['Parser'](val)
            elif self.service_clses[0] == HIDServiceRecord.service_clses[0]['UUID']:
                hidsr = HIDServiceRecord(self.record_xml)
                print('0x%04x:'%attr_id, hidsr.attrs[attr_id]['Name'], 
                    '(%s)'%val_type)
                hidsr.attrs[attr_id]['Parser'](val)
            elif self.service_clses[0] == ObjPushServiceRecord.service_clses[0]['UUID']:
                opsr = ObjPushServiceRecord(self.record_xml)
                print('0x%04x:'%attr_id, opsr.attrs[attr_id]['Name'], '(guess)', 
                    '(%s)'%val_type)
                opsr.attrs[attr_id]['Parser'](val)
            else:
                print('0x%04x:'%attr_id, red('unknown'))
                for elem in list(attr):
                    s = [i.strip() for i in ElementTree.tostring(elem).decode().strip().replace('\t', '').split('\n')]
                    # print('DEBUG', s)
                    for i in s:
                        print('\t' + i)
Beispiel #20
0
def pp_adv_phych_pdu(pdu: bytes, ch: int) -> list:
    '''Parse and print advertising physical channel PDU

    ref 
    BLUETOOTH CORE SPECIFICATION Version 5.2 | Vol 6, Part B page 2871, 
    2.3 ADVERTISING PHYSICAL CHANNEL PDU

    Advertising physical channel PDU
    +------------------+
    | Header | Payload |
    |--------|---------|
    | 16 b   | 1-255 B |
    +------------------+

    Header
    +-------------------------------------------------+
    | PDU Type | RFU | ChSel | TxAdd | RxAdd | Length |
    |----------|-----|-------|-------|-------|--------|
    | 4 b      | 1 b | 1 b   | 1 b   | 1 b   | 8 b    |
    +-------------------------------------------------+
    '''
    header = pdu[:2]
    payload = pdu[2:]

    pdu_type = (header[0] & PDU_TYPE_MSK) >> PDU_TYPE_POS
    rfu = (header[0] & RFU_MSK) >> RFU_POS
    ch_sel = (header[0] & CH_SEL_MSK) >> CH_SEL_POS
    tx_add = (header[0] & TX_ADD_MSK) >> TX_ADD_POS
    rx_add = (header[0] & RX_ADD_MSK) >> RX_ADD_POS

    addrs = []

    print("[{}] ".format(ch), end='')
    if pdu_type == ADV_IND:
        adv_a = payload[:6][::-1]
        addrs = [{
            'BD_ADDR': adv_a,
            'type': 'public' if tx_add == 0b0 else 'random'
        }]

        print("[{}]".format(blue('ADV_IND')))
        print("{} AdvA: {}".format('public' if tx_add == 0b0 else 'random',
                                   ':'.join('%02X' % b for b in adv_a)))
        # print("AdvData:", payload[6:])
    elif pdu_type == ADV_DIRECT_IND:
        adv_a = payload[:6][::-1]
        target_a = payload[6:][::-1]
        addrs = [
            {
                'BD_ADDR': adv_a,
                'type': 'public' if tx_add == 0b0 else 'random'
            },
            {
                'BD_ADDR': target_a,
                'type': 'public' if rx_add == 0b0 else 'random'
            },
        ]
        print("[{}]".format(blue('ADV_DIRECT_IND')))
        print("{} AdvA: {}".format('public' if tx_add == 0b0 else 'random',
                                   ':'.join('%02X' % b for b in adv_a)))
        print("{} TargetA: {}".format('public' if rx_add == 0b0 else 'random',
                                      ':'.join('%02X' % b for b in target_a)))
    elif pdu_type == ADV_NONCONN_IND:
        adv_a = payload[:6][::-1]
        addrs = [{
            'BD_ADDR': adv_a,
            'type': 'public' if tx_add == 0b0 else 'random'
        }]
        print("[{}]".format(red('ADV_NONCONN_IND')))
        print("{} AdvA: {}".format('public' if tx_add == 0b0 else 'random',
                                   ':'.join('%02X' % b for b in adv_a)))
        # print("AdvData:", payload[6:])
    elif pdu_type == ADV_SCAN_IND:
        adv_a = payload[:6][::-1]
        addrs = [{
            'BD_ADDR': adv_a,
            'type': 'public' if tx_add == 0b0 else 'random'
        }]
        print("[{}]".format(blue('ADV_SCAN_IND')))
        print("{} AdvA: {}".format('public' if tx_add == 0b0 else 'random',
                                   ':'.join('%02X' % b for b in adv_a)))
        # print("AdvData:", payload[6:])
    elif pdu_type == ADV_EXT_IND:
        print("[{}]".format(yellow('ADV_EXT_IND')))
        print("raw: {}".format(payload))
    elif pdu_type == SCAN_REQ:
        scan_a = payload[:6][::-1]
        adv_a = payload[6:][::-1]
        addrs = [
            {
                'BD_ADDR': scan_a,
                'type': 'public' if tx_add == 0b0 else 'random'
            },
            {
                'BD_ADDR': adv_a,
                'type': 'public' if rx_add == 0b0 else 'random'
            },
        ]
        print("[{}]".format(blue('SCAN_REQ')))
        print("{} ScanA: {}".format('public' if tx_add == 0b0 else 'random',
                                    ':'.join('%02X' % b for b in scan_a)))
        print("{} AdvA: {}".format('public' if rx_add == 0b0 else 'random',
                                   ':'.join('%02X' % b for b in adv_a)))
    elif pdu_type == SCAN_RSP:
        adv_a = payload[:6][::-1]
        addrs = [{
            'BD_ADDR': adv_a,
            'type': 'public' if tx_add == 0b0 else 'random'
        }]
        print("[{}]".format(blue('SCAN_RSP')))
        print("{} AdvA: {}".format('public' if tx_add == 0b0 else 'random',
                                   ':'.join('%02X' % b for b in adv_a)))
        # print("ScanRspData:", payload[6:])
    elif pdu_type == CONNECT_IND:
        init_a = payload[:6]
        adv_a = payload[6:12]
        print('init_a:', ':'.join('%02x' % b for b in init_a))
        print('adv_a:', ':'.join('%02x' % b for b in adv_a))
        addrs = [
            {
                'BD_ADDR': init_a,
                'type': 'public' if tx_add == 0b0 else 'random'
            },
            {
                'BD_ADDR': adv_a,
                'type': 'public' if rx_add == 0b0 else 'random'
            },
        ]
        print("[{}]".format(green('CONNECT_IND')))
        print("{} InitA: {}".format('public' if tx_add == 0b0 else 'random',
                                    ':'.join('%02X' % b for b in init_a)))
        print("{} AdvA: {}".format('public' if rx_add == 0b0 else 'random',
                                   ':'.join('%02X' % b for b in adv_a)))
        # print("LLData:", payload[12:])
    else:
        logger.warning("Unknown PDU type 0x%02x'%pdu_type")

    return addrs
Beispiel #21
0
def pp_ext_inquiry_rsp(ext_inq_rsp):
    '''Parse and print Extended Inquiry Response (240 octets)

    https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile/
    '''
    print('Extended inquiry response: ', end='')
    if ext_inq_rsp[0] == 0:
        print(red('None'))
        return

    print()

    while ext_inq_rsp[0] != 0:
        length = ext_inq_rsp[0]
        data = ext_inq_rsp[1:1 + length]
        data_type = data[0]
        ext_inq_rsp = ext_inq_rsp[1 + length:]
        print('\t', end='')
        if data_type == COMPLETE_16_BIT_SERVICE_CLS_UUID_LIST:
            print(gap_type_name_pairs[data_type])
            if length - 1 >= 2:
                eir_data = data[1:]
                if len(eir_data) % 2 != 0:
                    print('\t\t' +
                          blue('Invalid EIR data length: %d' % len(eir_data)))
                    continue

                for i in range(0, len(eir_data), 2):
                    uuid = int.from_bytes(eir_data[i:i + 2],
                                          byteorder='little')
                    print('\t\t0x%04x ' % uuid, end='')
                    try:
                        print(blue(service_cls_profile_ids[uuid]['Name']))
                    except KeyError as e:
                        print(red('unknown'))
            else:
                print('\t\t' + red('None'))
        elif data_type == COMPLETE_32_BIT_SERVICE_CLS_UUID_LIST:
            print(gap_type_name_pairs[data_type])
            if length - 1 >= 4:
                eir_data = data[1:]
                if len(eir_data) % 4 != 0:
                    logger.info('\t\tInvalid EIR data length: {} {}'.format(
                        len(eir_data), eir_data))
                    continue
                for i in range(0, len(eir_data), 4):
                    uuid = int.from_bytes(eir_data[i:i + 4],
                                          byteorder='little')
                    print('\t\t0x%08x ' % uuid)
            else:
                print('\t\t' + red('None'))
        elif data_type == COMPLETE_128_BIT_SERVICE_CLS_UUID_LIST:
            print(gap_type_name_pairs[data_type])
            if length - 1 >= 16:
                eir_data = data[1:]
                if len(eir_data) % 16 != 0:
                    logger.info('\t\tInvalid EIR data length: {} {}'.format(
                        len(eir_data), eir_data))
                    continue
                for i in range(0, len(eir_data), 16):
                    uuid = int.from_bytes(eir_data[i:i + 16],
                                          byteorder='little')
                    uuid_str = '%032X' % uuid
                    print('\t\t', end='')
                    print(
                        blue('-'.join([
                            uuid_str[:8], uuid_str[8:12], uuid_str[12:16],
                            uuid_str[16:20], uuid_str[20:32]
                        ])))
            else:
                print('\t\t' + red('None'))
        elif data_type == SHORTENED_LOCAL_NAME or \
            data_type == COMPLETE_LOCAL_NAME:
            print(gap_type_name_pairs[data_type] + ':',
                  blue(data[1:].decode()))
        elif data_type == TX_POWER_LEVEL:
            print(
                gap_type_name_pairs[data_type] + ':',
                blue(
                    str(int.from_bytes(data[1:], byteorder='little')) +
                    ' dBm'))
        else:
            try:
                print(gap_type_name_pairs[data_type])
            except KeyError as e:
                print(red('Unknown, 0x%02x' % data_type))
            print('\t\t', data[1:], sep='')
Beispiel #22
0
def pp_smp_pkt(pkt: bytes):
    logger.debug("pp_smp_pkt(), pkt: {}".format(pkt))
    code = pkt[0]

    if code == btsmp.CmdCode.PAIRING_RESPONSE:
        print(blue("Pairing Response"))
        iocap, oob, auth_req, max_enc_key_size, init_key_distr, rsp_key_distr = struct.unpack(
            'BBBBBB', pkt[1:])
        print("    IO Capability: 0x%02x - %s" %
              (iocap, green(IOCapability[iocap].hname)))
        print("    OOB data flag: 0x%02x - %s" % (oob, OOBDataFlag[oob].hname))
        print("    AuthReq: 0x%02x" % auth_req)
        bonding_flag = (auth_req & BONDING_FLAGS_MSK) >> BONDING_FLAGS_POS
        mitm = (auth_req & MITM_MSK) >> MITM_POS
        sc = (auth_req & SC_MSK) >> SC_POS
        keypress = (auth_req & KEYPRESS_MSK) >> KEYPRESS_POS
        ct2 = (auth_req & CT2_MSK) >> CT2_POS
        rfu = (auth_req & AUTHREQ_RFU_MSK) >> AUTHREQ_RFU_POS
        print("        Bonding Flag:      0b{:02b} - {}".format(
            bonding_flag, BondingFlag[bonding_flag].hname))
        print("        MitM:              {}".format(
            green("True") if mitm else red("False")))
        print("        Secure Connection: {}".format(
            green("True") if sc else red("False")))
        print("        Keypress:          {}".format(
            green("True") if keypress else red("False")))
        print("        CT2:               {}".format(
            green("True") if ct2 else red("False")))
        print("        RFU:               0b{:02b}".format(rfu))
        print("    Maximum Encryption Key Size: %d" % max_enc_key_size)
        print("    Initiator Key Distribution: 0x%02x" % init_key_distr)
        enckey = (init_key_distr & ENCKEY_MSK) >> ENCKEY_POS
        idkey = (init_key_distr & IDKEY_MSK) >> IDKEY_POS
        signkey = (init_key_distr & SIGNKEY_MSK) >> SIGNKEY_POS
        linkkey = (init_key_distr & LINKKEY_MSK) >> LINKKEY_POS
        print("        EncKey:  {}".format(
            green("True") if enckey else red("False")))
        print("        IdKey:   {}".format(
            green("True") if idkey else red("False")))
        print("        SignKey: {}".format(
            green("True") if signkey else red("False")))
        print("        LinkKey: {}".format(
            green("True") if linkkey else red("False")))
        print("        RFU:     0b{:04b}".format(
            (init_key_distr
             & INIT_RESP_KEY_DIST_RFU_MSK) >> INIT_RESP_KEY_DIST_RFU_POS))
        print("    Responder Key Distribution: 0x%02x" % rsp_key_distr)
        enckey = (rsp_key_distr & ENCKEY_MSK) >> ENCKEY_POS
        idkey = (rsp_key_distr & IDKEY_MSK) >> IDKEY_POS
        signkey = (rsp_key_distr & SIGNKEY_MSK) >> SIGNKEY_POS
        linkkey = (rsp_key_distr & LINKKEY_MSK) >> LINKKEY_POS
        print("        EncKey:  {}".format(
            green("True") if enckey else red("False")))
        print("        IdKey:   {}".format(
            green("True") if idkey else red("False")))
        print("        SignKey: {}".format(
            green("True") if signkey else red("False")))
        print("        LinkKey: {}".format(
            green("True") if linkkey else red("False")))
        print("        RFU:     0b{:04b}".format(
            (rsp_key_distr
             & INIT_RESP_KEY_DIST_RFU_MSK) >> INIT_RESP_KEY_DIST_RFU_POS))
    elif code == btsmp.CmdCode.PAIRING_FAILED:
        reason = pkt[1]
        print(red("Pairing Failed"))
        print("    Reason: 0x%02x (%s)" %
              (reason, PairingFailedReason[reason].hname))
        print("            %s" % PairingFailedReason[reason].desc)
    else:
        logger.warning("unknown SMP packet: {}".format(pkt))
Beispiel #23
0
    def scan_devs(self, timeout=8, scan_type='active', sort='rssi'):
        """LE devices scanning

        scan_type  - Indicate the type of LE scan:active, passive, adv or 
                     features.
        """
        if scan_type == 'adv':
            return

        scanner = Scanner(self.devid).withDelegate(LEDelegate())
        #print("[Debug] timeout =", timeout)

        # scan() 返回的 devs 是 dictionary view。
        if scan_type == 'active':  # Active scan 会在 LL 发送 SCAN_REQ PDU
            logger.warning(
                'Before doing an active scan, make sure you spoof your BD_ADDR.'
            )
            logger.info('LE active scanning on %s with timeout %d sec\n' % \
                (blue('hci%d'%self.devid), timeout))
            devs = scanner.scan(timeout)
        elif scan_type == 'passive':
            logger.info('LE passive scanning on %s with timeout %d sec\n' % \
                (blue('hci%d'%self.devid), timeout))
            devs = scanner.scan(timeout, passive=True)
        else:
            logger.error('Unknown LE scan type')
            return

        if sort == 'rssi':
            devs = list(devs)  # 将 dictionary view 转换为 list
            devs.sort(key=lambda d: d.rssi)

        for dev in devs:
            print('Addr:       ', blue(dev.addr.upper()))
            print('Addr type:  ', blue(dev.addrType))
            print('Connectable:',
                  green('True') if dev.connectable else red('False'))
            print("RSSI:        %d dB" % dev.rssi)
            print("General Access Profile:")
            for (adtype, desc, val) in dev.getScanData():
                # 打印当前 remote LE dev 透露的所有 GAP 数据(AD structure)。
                #
                # 如果 bluepy.scan() 执行的是 active scan,那么这些 GAP 数据
                # 可能同时包含 AdvData 与 ScanRspData。其中 AdvData 由 remote LE
                # dev 主动返回,ScanRspData 由 remote BLE dev 响应 SCAN_REQ 返回。
                #
                # 虽然 LL 分开定义了 Advertising PDUs (ADV_IND, ADV_DIRECT_IND...)
                # 和 Scanning PDUs (SCAN_REQ, SCAN_RSP)。但它们分别包含的 AdvData
                # 与 ScanRspData 到了 HCI 层都被放在了 HCI_LE_Advertising_Report
                # event 中。HCI_LE_Advertising_Report 的 Event_Type 标识了这些数
                # 据具体来源于哪个 LL 层的 PDU。另外 ScanRspData 与 AdvData 的格式
                # 完全相同,都是 GAP 协议标准定义的 AD structure。
                #
                # 在 LL 定义的 Advertising PDUs 中 ADV_DIRECT_IND 一定不会包含
                # AdvData。其余的 ADV_IND,ADV_NONCONN_IND 以及 ADV_SCAN_IND 都
                # 可能包含 AdvData。
                #
                # 另外 getScanData() 返回的 desc 还可以通过 ScanEntry.getDescription()
                # 单独获取;val 还可以通过 ScanEntry.getValueText() 单独获取;
                # adtype 表示当前一条 GAP 数据(AD structure)的类型。
                print('\t' + desc + ': ', end='')
                if adtype == COMPLETE_16_BIT_SERVICE_CLS_UUID_LIST:
                    print()
                    for uuid in val.split(','):
                        if len(uuid) == 36:
                            # 这里拿到的是完整的 128-bit uuid,但我们需要 16-bit uuid。
                            print('\t\t' + blue(uuid[4:8]))
                        else:
                            print('\t\t' + blue(uuid))
                    continue
                elif adtype == COMPLETE_32_BIT_SERVICE_CLS_UUID_LIST:
                    print()
                    for uuid in val.split(','):
                        if len(uuid) == 36:
                            # 这里拿到的是完整的 128-bit uuid,但我们需要 32-bit uuid。
                            print('\t\t' + blue(uuid[0:8]))
                        else:
                            print('\t\t' + blue(uuid))
                    continue
                elif adtype == MANUFACTURER_SPECIFIC_DATA:
                    val = bytes.fromhex(val)
                    if len(val) > 2:
                        print()
                        print(
                            '\t\tCompany ID:', '0x%04x' %
                            int.from_bytes(val[0:2], 'little', signed=False))
                        print('\t\tData:', val[2:])
                    else:
                        print(val)
                    continue
                print(val)
            print("\n")
Beispiel #24
0
    def print(self):
        if self.addr is None or self.addr_type is None:
            return

        print("Number of services: {}".format(len(self.services)))
        print()
        print()  # Two empty lines before Service Group

        # Prints each service group
        for service in self.services:
            uuid_str_for_show = self.uuid2str_for_show(service.declar.value)

            try:
                service_name = green(ServiceUuids[service.declar.value].name)
            except KeyError:
                service_name = red("Unknown")

            print(
                blue("Service"),
                "(0x{:04x} - 0x{:04x}, {} characteristics)".format(
                    service.start_handle, service.end_handle,
                    len(service.get_characts())))
            print(INDENT + blue("Declaration"))
            print(INDENT + "Handle: 0x{:04x}".format(service.start_handle))
            print(INDENT + "Type:   {:04X} ({})".format(
                service.declar.type.int16, service.declar.type.name))
            print(INDENT + "Value:  {} ({})".format(green(uuid_str_for_show),
                                                    service_name))
            print(INDENT + "Permissions:", service.declar.permissions_desc)
            print()  # An empty line before Characteristic Group

            # Prints each Gharacteristic group
            for charact in service.characts:
                uuid_str_for_show = self.uuid2str_for_show(
                    charact.declar.value.uuid)

                # if type(uuid) is int:
                #     uuid = "0x{:04X}".format(uuid)

                # value_declar_uuid = charact.value_declar.type
                # if type(value_declar_uuid) is int:
                #     value_declar_uuid = "0x{:04X}".format(value_declar_uuid)

                # if charact.declar.value.uuid != charact.value_declar.type:
                #     pass
                #     logger.warning("charact.declar.value['UUID'] != charact.value_declar.type")

                try:
                    charact_name = green(
                        GattAttrTypes[charact.declar.value.uuid].name)
                    # charact_name = green(charact_names[charact.declar.value.uuid])
                except KeyError:
                    charact_name = red("Unknown")

                print(INDENT + yellow("Characteristic"),
                      '({} descriptors)'.format(len(charact.descriptors)))
                print(INDENT * 2 + yellow("Declaration"))
                print(INDENT * 2 +
                      "Handle: 0x{:04x}".format(charact.declar.handle))
                print(INDENT * 2 + "Type:   {:04X} ({})".format(
                    charact.declar.type.int16, charact.declar.type.name))
                print(INDENT * 2 + "Value:")
                print(INDENT * 3 + "Properties: {}".format(
                    green(', '.join(charact.declar.get_property_names()))))
                print(INDENT * 3 + "Handle:    ",
                      green("0x{:04x}".format(charact.declar.value.handle)))
                print(INDENT * 3 + "UUID:       {} ({})".format(
                    green(uuid_str_for_show), charact_name))
                print(INDENT * 2 + "Permissions: {}\n".format(
                    charact.declar.permissions_desc))

                if charact.value_declar is not None:
                    try:
                        value_declar_name = GattAttrTypes[
                            charact.value_declar.type].name
                    except KeyError:
                        value_declar_name = "Unknown"

                    type_str_for_show = self.uuid2str_for_show(
                        charact.value_declar.type)

                    error = charact.value_declar.get_read_error()
                    if error != None:
                        value_print = red(error.desc)
                    elif charact.value_declar.value is None:
                        value_print = red('Unknown')
                    else:
                        value_print = green(str(charact.value_declar.value))

                    print(INDENT * 2 + yellow("Value declaration"))
                    print(
                        INDENT * 2 +
                        "Handle: 0x{:04x}".format(charact.value_declar.handle))
                    print(INDENT * 2 + "Type:   {} ({})".format(
                        type_str_for_show, value_declar_name))
                    print(INDENT * 2 + "Value:  {}".format(value_print))
                    print(INDENT * 2 + "Permissions: {}\n".format(
                        charact.value_declar.permissions_desc))

                # Prints each Characteristic Descriptor
                for descriptor in charact.get_descriptors():
                    uuid = descriptor.type
                    if type(uuid) is int:
                        uuid = "0x{:04X}".format(uuid)

                    error = descriptor.get_read_error()
                    if error != None:
                        value_print = red(error.desc)
                    else:
                        if descriptor.value is None:
                            value_print = red('Unknown')
                        else:
                            value_print = green(str(descriptor.value))

                    print(INDENT * 2 + yellow("Descriptor"))
                    print(INDENT * 2 + "Handle: {}".format(
                        green('0x{:04x}'.format(descriptor.handle))))
                    print(INDENT * 2 + "Type:   {} ({})".format(
                        green("{:04X}".format(descriptor.type.int16)),
                        yellow(descriptor.type.name)))
                    print(INDENT * 2 + "Value: ", value_print)
                    print(INDENT * 2 + "Permissions: {}\n".format(
                        descriptor.permissions_desc))
Beispiel #25
0
    def scan(self, timeout=8, scan_type='active', sort='rssi'):
        '''        
        scan_type
            指定执行的 LE scan,是 active scan 还是 passive scan。
        '''
        scanner = Scanner(self.devid).withDelegate(LEDelegate())
        #print("[Debug] timeout =", timeout)

        # scan() 返回的 devs 是 dictionary view。
        if scan_type == 'active':  # Active scan 会在 LL 发送 SCAN_REQ PDU
            print(
                WARNING,
                'Before doing an active scan, make sure you spoof your BD_ADDR.'
            )
            print(
                INFO,
                "LE active scanning on \x1B[1;34mhci%d\x1B[0m with timeout %d sec\n"
                % (self.devid, timeout))
            devs = scanner.scan(timeout)
        elif scan_type == 'passive':
            print(
                "LE passive scanning on \x1B[1;34mhci%d\x1B[0m with timeout %d sec\n"
                % (self.deivd, timeout))
            devs = scanner.scan(timeout, passive=True)
        else:
            print(ERROR, "Unknown LE scan type.")
            return

        if sort == 'rssi':
            devs = list(devs)  # 将 dictionary view 转换为 list
            devs.sort(key=lambda d: d.rssi)

        for dev in devs:
            print('Addr:       ', blue(dev.addr.upper()))
            print('Addr type:  ', blue(dev.addrType))
            print('Connectable:',
                  green('True') if dev.connectable else red('False'))
            print("RSSI:        %d dB" % dev.rssi)
            print("General Access Profile:")
            for (adtype, desc, val) in dev.getScanData():
                # 打印当前 remote LE dev 透露的所有 GAP 数据(AD structure)。
                #
                # 如果 bluepy.scan() 执行的是 active scan,那么这些 GAP 数据
                # 可能同时包含 AdvData 与 ScanRspData。其中 AdvData 由 remote LE
                # dev 主动返回,ScanRspData 由 remote BLE dev 响应 SCAN_REQ 返回。
                #
                # 虽然 LL 分开定义了 Advertising PDUs (ADV_IND, ADV_DIRECT_IND...)
                # 和 Scanning PDUs (SCAN_REQ, SCAN_RSP)。但它们分别包含的 AdvData
                # 与 ScanRspData 到了 HCI 层都被放在了 HCI_LE_Advertising_Report
                # event 中。HCI_LE_Advertising_Report 的 Event_Type 标识了这些数
                # 据具体来源于哪个 LL 层的 PDU。另外 ScanRspData 与 AdvData 的格式
                # 完全相同,都是 GAP 协议标准定义的 AD structure。
                #
                # 在 LL 定义的 Advertising PDUs 中 ADV_DIRECT_IND 一定不会包含
                # AdvData。其余的 ADV_IND,ADV_NONCONN_IND 以及 ADV_SCAN_IND 都
                # 可能包含 AdvData。
                #
                # 另外 getScanData() 返回的 desc 还可以通过 ScanEntry.getDescription()
                # 单独获取;val 还可以通过 ScanEntry.getValueText() 单独获取;
                # adtype 表示当前一条 GAP 数据(AD structure)的类型。
                print('\t' + desc + ': ', end='')
                if adtype == COMPLETE_16_BIT_SERVICE_CLS_UUID_LIST:
                    print()
                    for uuid in val.split(','):
                        print('\t\t' + blue(uuid))
                    continue
                print(val)
            print("\n")
Beispiel #26
0
    def scan(self, bdaddr, addr_type, include_descriptor: bool):
        #print("[Debug] target_addr:", self.target_bdaddr)
        #print("[Debug] iface:", self.iface)
        #print("[Debug] addr_type:", self.addr_type)
        target = Peripheral(bdaddr, iface=self.devid, addrType=addr_type)
        #print("[Debug] target", target)

        services = target.getServices()
        print("Number of services: %s\n\n" % len(services))

        # Show service
        for service in services:
            characteristics = service.getCharacteristics()

            print(blue('Service'),
                  '(%s characteristics)' % len(characteristics))
            print(
                '\tHandle:", "\"attr handle\" by using gatttool -b <BD_ADDR> --primary'
            )
            print('\tType: (May be primary service 0x2800)')
            print('\tValue (Service UUID): ', blue(str(service.uuid)), end=' ')
            try:
                print(
                    '(' +
                    services_spec['0x' +
                                  ("%s" % service.uuid)[4:8].upper()]['Name'] +
                    ')', '\x1B[0m')
            except KeyError:
                print('(' + red('unknown') + ')', '\x1B[0m')
            print(
                '\tPermission: Read Only, No Authentication, No Authorization\n'
            )

            # Show characteristic
            for characteristic in characteristics:
                descriptors = []
                # 对每个 characteristic 都获取 descriptor 会很耗时
                # 有些设备会因此断开连接。于是这里提供了一个是否获取 descriptor 的选项
                if include_descriptor:
                    descriptors = characteristic.getDescriptors()

                try:
                    print(yellow('\tCharacteristic'),
                          '(%s descriptors)' % len(descriptors))
                    #print('-'*8)
                    print('\t\tHandle: %#06x' %
                          (characteristic.getHandle() - 1))
                    print('\t\tType: 0x2803 (tCharacteristic)')
                    print('\t\tValue:')
                    print('\t\t\tCharacteristic properties:',
                          green(characteristic.propertiesToString()))
                    print('\t\t\tCharacteristic value handle: %#06x' %
                          characteristic.getHandle())
                    print(
                        '\t\t\tCharacteristic UUID: ',
                        green(str(characteristic.uuid)),
                        end=' '
                    )  # This UUID is also the type field of characteristic value declaration attribute.
                    try:
                        print('(' + characteristics_spec['0x' + (
                            "%s" % characteristic.uuid)[4:8].upper()]['Name'] +
                              ')')
                    except KeyError:
                        print('(' + red('unknown') + ')')
                    print(
                        '\t\tPermission: Read Only, No Authentication, No Authorization'
                    )

                    if characteristic.supportsRead():
                        print(yellow('\tCharacteristic value'))
                        print('\t\tHandle:',
                              green('%#06x' % characteristic.getHandle()))
                        print('\t\tType:', characteristic.uuid)
                        print('\t\tValue:', green(str(characteristic.read())))
                        print(
                            '\t\tPermission: Higher layer profile or implementation-specific'
                        )
                except BTLEException as e:
                    print('        ' + str(e))

                # Show descriptor
                for descriptor in descriptors:
                    try:
                        print('\tDescriptor')
                        print('\t\tHandle:',
                              green('%#06x' % descriptor.handle))
                        print('\t\tType:', descriptor.uuid, end=' ')
                        try:
                            print('(' + descriptors_spec['0x' + (
                                "%s" % descriptor.uuid)[4:8].upper()]['Name'] +
                                  ')')
                        except KeyError:
                            print('(Unknown descriptor)')
                        print('\t\tValue:', descriptor.read())
                        print('\t\tPermissions:')
                    except BTLEException as e:
                        print('\t\t' + str(e))
                print()
            print()