Exemplo n.º 1
0
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
Exemplo n.º 2
0
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
Exemplo n.º 3
0
    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
Exemplo n.º 4
0
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
Exemplo n.º 5
0
    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()
Exemplo n.º 6
0
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
Exemplo n.º 7
0
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