def auth_scan(u, auth_range, key_func=None, delay=None): log.debug('Starting auth scan for range: {}'.format(auth_range)) u.c.placeCanBookmark('auth_scan({}, key_func={}, delay={})'.format( auth_range, key_func, delay)) auth_levels = {} for i in auth_range: if key_func: secret = '' else: secret = key_func(i) log.detail('Trying auth level {}: secret \'{}\''.format(i, secret)) u.c.placeCanBookmark('SecurityAccess({}, {})'.format(i, repr(secret))) resp = try_auth(u, i, secret) if resp is not None: log.debug('auth {}: {}'.format(i, resp)) if 'resp' in resp: log.msg('SECURITY {}: {}'.format(i, resp['resp'].hex())) else: log.msg('SECURITY {}: {}'.format(i, err_str(resp['err']))) auth_levels[i] = resp if delay: time.sleep(delay) return auth_levels
def did_read_scan(u, did_range, delay=None): log.debug('Starting DID read scan for range: {}'.format(did_range)) u.c.placeCanBookmark('did_read_scan({}, delay={})'.format( did_range, delay)) dids = {} for i in did_range: log.detail('Trying DID read {}'.format(hex(i))) u.c.placeCanBookmark('ReadDID({})'.format(hex(i))) resp = try_read_did(u, i) if resp is not None: log.debug('DID {}: {}'.format(hex(i), resp)) if 'resp' in resp: printable_did = ''.join([ x if x in string.printable else '' for x in resp['resp'][3:] ]) log.msg('DID {}: {} ({})'.format(did_str(i), resp['resp'], printable_did)) else: log.msg('DID {}: ERR {}'.format(did_str(i), err_str(resp['err']))) dids[i] = resp if delay: time.sleep(delay) return dids
def _try_key(self, u, auth_level, key): resp = utils.try_auth(u, auth_level, key) if resp is not None and 'err' in resp: # Attempt to handle a few common errors if 'err' in resp and resp['err'] == 0x36: # 0x36:'ExceedNumberOfAttempts' # log.detail('Retrying session {}, auth {}, length {}'.format(sess, lvl, keylen)) resp = utils.try_auth(u, auth_level, key) log.detail('resp: {}'.format(resp)) elif 'err' in resp and resp['err'] == 0x37: # 0x37:'RequiredTimeDelayNotExpired' time.sleep(1.0) # log.detail('Retrying session {}, auth {}, length {}'.format(sess, lvl, keylen)) resp = utils.try_auth(u, auth_level, key) log.detail('resp: {}'.format(resp)) return resp
def did_write_scan(u, did_range, write_data, delay=None): log.debug('Starting DID write scan for range: {}'.format(did_range)) u.c.placeCanBookmark('did_write_scan({}, write_data={}, delay={})'.format( did_range, write_data, delay)) dids = {} for i in did_range: log.detail('Trying DID write {}'.format(hex(i))) u.c.placeCanBookmark('WriteDID({})'.format(hex(i))) resp = try_write_did(u, i, write_data) if resp is not None: log.detail('DID {}: {}'.format(hex(i), resp)) if 'resp' in resp: log.msg('DID {}: {}'.format(did_str(i), resp['resp'].hex())) else: log.msg('DID {}: {}'.format(did_str(i), err_str(resp['err']))) dids[i] = resp if delay: time.sleep(delay) return dids
def key_length_scan(self, len_range, rescan=False): log.msg('{} starting key/seed length scan {}'.format( self._addr, len_range)) self.c.placeCanBookmark('canmap key_length_scan({}, delay={})'.format( len_range, self._delay)) arb, resp, ext = self._addr u = self._scancls(self.c, arb, resp, extflag=ext, verbose=False, timeout=self._timeout) u.StartTesterPresent(request_response=False) # I can't think of a good way to turn this into a more generic utility # function for sess in self._sessions: # Skip session 1 if sess == 1: continue with utils.new_session(u, sess, True): for lvl in self._sessions[sess]['auth']: if not self._found_key_len(sess, lvl) or rescan: # TODO: For now, we delete the old scan data, not sure how # best to track to new vs. old key key scans otherwise self._sessions[sess]['auth'][lvl] = {'seeds': []} log.msg( '{} session {} auth {} starting key length scan'. format(self._addr, sess, lvl)) for keylen in len_range: key = '\x00' * keylen log.detail( 'Trying session {}, auth {}, key \'{}\''. format(sess, lvl, key)) self.c.placeCanBookmark( 'SecurityAccess({}, {})'.format( sess, repr(key))) resp = self._try_key(u, lvl, key) self._sessions[sess]['auth'][lvl].update(resp) self._sessions[sess]['auth'][lvl]['seeds'].append( dict(u.seed)) if 'resp' in resp: # Get the key attempted from the recorded seed data # in case the scanning class modified it log.msg( 'Session {}, auth {} key found! secret {} (seed {})' .format(sess, lvl, repr(u.seed['secret']), repr(u.seed['seed']))) break elif resp['err'] == 0x35: log.debug( 'Session {}, auth {} length found! {} (seed {})' .format(sess, lvl, len(u.seed['secret']), repr(u.seed['seed']))) break if self._delay: time.sleep(self._delay) # Ensure tester present is not being sent anymore u.StopTesterPresent()
def try_session_scan( u, session_range, prereq_sessions, found_sessions, delay=None, # noqa: C901 recursive_scan=True, try_ecu_reset=True, try_sess_ctrl_reset=True): log.debug('Starting session scan for range: {}'.format(session_range)) u.c.placeCanBookmark('session_scan({}, delay={})'.format( session_range, delay)) sessions = {} for i in session_range: # If this session matches any one of the prereqs, or one of the # sessions already found, skip it if i in prereq_sessions or i in found_sessions: continue log.detail('Trying session {}'.format(i)) u.c.placeCanBookmark('DiagnosticSessionControl({})'.format(i)) # Enter any required preq sessions try: for prereq in prereq_sessions: enter_session(u, prereq) except uds.UDSTimeout as e: log.detail( 'SESSION ({}) TIMEOUT: Can\'t enter prereqs, stopping session scan' .format(prereq_sessions)) return sessions except uds.NegativeResponseException as e: # 0x7f:'ServiceNotSupportedInActiveSession' if e.neg_code == 0x7f: log.detail( 'SESSION ({}): Can\'t enter prereqs, stopping session scan' .format(prereq_sessions)) return sessions else: raise e resp = try_session(u, i) if resp is not None: resp['prereqs'] = list(prereq_sessions) log.debug('session {}: {}'.format(i, resp)) if 'resp' in resp: log.msg('SESSION {}: {} ({})'.format(i, resp['resp'].hex(), prereq_sessions)) else: log.msg('SESSION {}: {} ({})'.format(i, err_str(resp['err']), prereq_sessions)) sessions[i] = resp # Only bother with this if a successful response was received if resp and 'resp' in resp: if try_ecu_reset: try: u.EcuReset() # Small delay to allow for the reset to complete time.sleep(0.2) except uds.NegativeResponseException as e: # 0x11:'ServiceNotSupported' # 0x22:'ConditionsNotCorrect' if e.neg_code in [0x11, 0x22]: try_ecu_reset = False elif try_sess_ctrl_reset: try: # Try just changing back to session 1 new_session(u, 1) except uds.NegativeResponseException: # The default method to try returning to session 1 is EcuReset, if # EcuReset doesn't work (or isn't enabled), then try using the # DiagnosticSessionControl message to return to session 1, if that # doesn't work then we can't attempt recursive session scanning try_sess_ctrl_reset = False # Extra delay between attempts if configured if delay: time.sleep(delay) # For each session found re-scan for new sessions that can be entered from # those, but only if we have a valid reset method: if recursive_scan and (try_ecu_reset or try_sess_ctrl_reset): subsessions = {} for sess in sessions: # Only attempt this with sessions that we got a successful response # for if 'msg' in sessions[sess]: log.debug('Scanning for sessions from session {} ({})'.format( sess, prereq_sessions)) prereqs = prereq_sessions + [sess] subsessions.update( try_session_scan(u, session_range, prereqs, sessions.keys(), delay=delay, recursive_scan=recursive_scan, try_ecu_reset=try_ecu_reset, try_sess_ctrl_reset=try_sess_ctrl_reset)) sessions.update(subsessions) return sessions
def ecu_session_scan( c, arb_id_range, ext=0, session=1, udscls=None, timeout=3.0, # noqa: C901 delay=None, verbose_flag=False): scan_type = '' if ext: scan_type = ' ext' if udscls is None: udscls = UDS log.debug('Starting{} Session ECU scan for range: {}'.format( scan_type, arb_id_range)) c.placeCanBookmark( 'ecu_session_scan({}, ext={}, session={}, timeout={}, delay={})'. format(arb_id_range, ext, session, timeout, delay)) ecus = [] possible_ecus = [] for i in arb_id_range: tester_id = uds.ARBID_CONSTS[ext]['tester'] if i == tester_id: # Skip i == 0xF1 because in that case the sender and receiver IDs # are the same log.detail( 'Skipping {} in ext ECU scan: invalid ECU address'.format( hex(tester_id))) continue arb_id, resp_id = gen_arbids(i, ext) addr = ECUAddress(arb_id, resp_id, ext) u = udscls(c, addr.tx_arbid, addr.rx_arbid, extflag=addr.extflag, verbose=verbose_flag, timeout=timeout) log.detail('Trying {}'.format(addr)) try: start_index = u.c.getCanMsgCount() with new_session(u, session) as msg: if msg is not None: log.debug('{} session {}: {}'.format( addr, session, repr(msg))) log.msg('found {}'.format(addr)) ecus.append(addr) else: tx_msg, responses = find_possible_resp( u, start_index, arb_id, uds.SVC_DIAGNOSTICS_SESSION_CONTROL, session, timeout) if responses: log.warn( 'Possible non-standard responses for {} found:'. format(hex(addr.tx_arbid))) for rx_arbid, msg in responses: log.warn('{}: {}'.format(hex(rx_arbid), msg.hex())) possible_ecus.append(ECUAddress(arb_id, rx_arbid, ext)) except uds.NegativeResponseException as e: log.debug('{} session {}: {}'.format(addr, session, e)) log.msg('found {}'.format(addr)) # If a negative response happened, that means an ECU is present # to respond at this address. ecus.append(addr) if delay: time.sleep(delay) # Double check any non-standard responses that were found if possible_ecus: log.detail('Retrying possible non-standard ECU addresses') for addr in possible_ecus: # if the TX ID is the OBD2 request ID, skip it if addr.tx_arbid == uds.ARBID_CONSTS[addr.extflag]['obd2_broadcast']: log.detail('Skipping OBD2 broadcast address ECU {}'.format(addr)) continue u = udscls(c, addr.tx_arbid, addr.rx_arbid, extflag=addr.extflag, verbose=verbose_flag, timeout=timeout) log.detail('Trying {}'.format(addr)) try: with new_session(u, session) as msg: if msg is not None: log.debug('{} session {}: {}'.format( addr, session, repr(msg))) log.msg('found {}'.format(addr)) ecus.append(addr) except uds.NegativeResponseException as e: log.debug('{} session {}: {}'.format(addr, session, e)) log.msg('found {}'.format(addr)) # If a negative response happened, that means an ECU is present # to respond at this address. ecus.append(addr) if delay: time.sleep(delay) return ecus