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()
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
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))
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()