etc.git


etc

~ scripts and random things
git clone https://github.com/djnnvx/etc.git
log | files | refs | archive

uds_protection_map.py (5329B)

      1 #!/usr/bin/env python3
      2 """
      3   0x33 securityAccessDenied          -> SecurityAccess 0x27
      4   0x34 authenticationRequired        -> Authentication 0x29
      5   0x7F serviceNotSupportedInSession  -> session gated
      6   0x7E subFunctionNotSupportedInSess -> session gated (subfunction)
      7   0x13 incorrectMessageLength        -> REACHABLE, no gate hit
      8   0x22 conditionsNotCorrect          -> reached handler, precondition unmet
      9   0x11 serviceNotSupported           -> not implemented
     10   non-standard codes                 -> vendor filter, never reaches the handler
     11 
     12   sudo ./uds_protection_map.py -t 127.0.0.1
     13   sudo ./uds_protection_map.py -t 127.0.0.1 --ta 0x0040 --all-sessions
     14 """
     15 import argparse, socket, struct, sys
     16 
     17 V = 0x03
     18 GATE = {
     19     0x33: ('SecurityAccess 0x27', 'gated'),
     20     0x34: ('Authentication 0x29', 'gated'),
     21     0x7F: ('session', 'gated'),
     22     0x7E: ('session (subfunction)', 'gated'),
     23     0x13: ('none, reachable', 'OPEN'),
     24     0x12: ('none, subfunc invalid', 'OPEN'),
     25     0x31: ('none, out of range', 'OPEN'),
     26     0x22: ('precondition', 'reached'),
     27     0x24: ('request sequence', 'reached'),
     28     0x11: ('not implemented', 'absent'),
     29     0xF0: ('vendor-specific', 'FILTERED'),
     30 }
     31 NAMES = {0x10: 'DiagnosticSessionControl', 0x11: 'ECUReset', 0x14: 'ClearDiagnosticInformation',
     32          0x19: 'ReadDTCInformation', 0x22: 'ReadDataByIdentifier', 0x23: 'ReadMemoryByAddress',
     33          0x24: 'ReadScalingDataByIdentifier', 0x27: 'SecurityAccess', 0x28: 'CommunicationControl',
     34          0x29: 'Authentication', 0x2A: 'ReadDataByPeriodicIdentifier',
     35          0x2C: 'DynamicallyDefineDataIdentifier', 0x2E: 'WriteDataByIdentifier',
     36          0x2F: 'InputOutputControlByIdentifier', 0x31: 'RoutineControl', 0x34: 'RequestDownload',
     37          0x35: 'RequestUpload', 0x36: 'TransferData', 0x37: 'RequestTransferExit',
     38          0x38: 'RequestFileTransfer', 0x3D: 'WriteMemoryByAddress', 0x3E: 'TesterPresent',
     39          0x83: 'AccessTimingParameter', 0x84: 'SecuredDataTransmission',
     40          0x85: 'ControlDTCSetting', 0x86: 'ResponseOnEvent', 0x87: 'LinkControl'}
     41 
     42 
     43 class Session:
     44     def __init__(self, host, sa, ta, timeout):
     45         self.sa, self.ta, self.timeout = sa, ta, timeout
     46         self.s = socket.create_connection((host, 13400), timeout)
     47         self.s.settimeout(timeout)
     48         self.s.sendall(self._h(0x0005, struct.pack('>HB', sa, 0) + b'\0' * 4))
     49         self.s.recv(4096)
     50 
     51     @staticmethod
     52     def _h(pt, pl=b''):
     53         return struct.pack('>BBHI', V, 0xFF ^ V, pt, len(pl)) + pl
     54 
     55     def _rd(self):
     56         h = self.s.recv(8)
     57         if len(h) < 8:
     58             return None, b''
     59         _, _, pt, ln = struct.unpack('>BBHI', h)
     60         b = b''
     61         while len(b) < ln:
     62             c = self.s.recv(ln - len(b))
     63             if not c:
     64                 break
     65             b += c
     66         return pt, b
     67 
     68     def uds(self, payload):
     69         self.s.sendall(self._h(0x8001, struct.pack('>HH', self.sa, self.ta) + payload))
     70         while True:
     71             try:
     72                 pt, b = self._rd()
     73             except socket.timeout:
     74                 return None
     75             if pt == 0x0007:                      # AliveCheckRequest
     76                 self.s.sendall(self._h(0x0008, struct.pack('>H', self.sa)))
     77                 continue
     78             if pt == 0x8002:
     79                 continue
     80             if pt == 0x8003:
     81                 return None
     82             if pt != 0x8001:
     83                 continue
     84             r = b[4:]
     85             if len(r) >= 3 and r[0] == 0x7F and r[2] == 0x78:
     86                 continue
     87             return r
     88 
     89 
     90 def sweep(sess, label):
     91     print(f'\n=== {label}')
     92     print(f'  {"SID":5} {"service":34} {"NRC":5} {"verdict":10} gate')
     93     buckets = {}
     94     for sid in range(0x100):
     95         r = sess.uds(bytes([sid]))
     96         if r is None:
     97             continue
     98         if r[0] == 0x7F and len(r) >= 3:
     99             nrc = r[2]
    100             gate, verdict = GATE.get(nrc, (f'unknown 0x{nrc:02X}', '?'))
    101         else:
    102             nrc, gate, verdict = 0, 'accepted bare SID', 'OPEN'
    103         if verdict == 'absent':
    104             continue
    105         buckets.setdefault(verdict, []).append(sid)
    106         print(f'  0x{sid:02X}  {NAMES.get(sid, ""):34} 0x{nrc:02X}  {verdict:10} {gate}')
    107     print(f'\n  summary for {label}:')
    108     for v in ('OPEN', 'reached', 'gated', 'FILTERED'):
    109         if v in buckets:
    110             print(f'    {v:9} {len(buckets[v]):3}  ' + ' '.join(f'{x:02X}' for x in buckets[v]))
    111     return buckets
    112 
    113 
    114 def main():
    115     a = argparse.ArgumentParser()
    116     a.add_argument('-t', '--target', default='127.0.0.1')
    117     a.add_argument('-s', '--sa', default='0x0EF4')
    118     a.add_argument('-d', '--ta', default='0x0010')
    119     a.add_argument('--timeout', type=float, default=3.0)
    120     a.add_argument('--all-sessions', action='store_true', help='also probe in extended session')
    121     o = a.parse_args()
    122 
    123     sess = Session(o.target, int(o.sa, 0), int(o.ta, 0), o.timeout)
    124     print(f'[*] target {o.target} ta={o.ta}, bare-SID probe (always invalid length, never executes)')
    125     sweep(sess, 'default session (10 01)')
    126 
    127     if o.all_sessions:
    128         r = sess.uds(b'\x10\x03')
    129         if r and r[0] == 0x50:
    130             sweep(sess, 'extended session (10 03)')
    131             sess.uds(b'\x10\x01')
    132         else:
    133             print('\n[-] could not enter extended session')
    134     return 0
    135 
    136 
    137 if __name__ == '__main__':
    138     sys.exit(main())