UDS Negative Response Codes erklärt für Tester
Was eine negative UDS-Antwort (0x7F) am Prüfstand verrät: der Aufbau, die Codes, die bei Enumeration und SecurityAccess zählen, und wie du sie liest.
Tom Zaubermann · Veröffentlicht · 8 Min. Lesezeit
Eine negative UDS-Antwort ist das ECU, das dir sagt, dass es nicht tut, worum du gebeten hast, und warum. Die Antwort besteht immer aus drei Bytes: 7F, dem Service-Identifier (SID) des abgelehnten Requests und einem Negative Response Code (NRC). Richtig gelesen bilden diese Codes das ECU für dich ab, denn ein ECU, das präzise ablehnt, beschreibt zugleich seine eigene Struktur, eine Antwort nach der anderen.
Der Aufbau, Byte für Byte
Sende einen Request und du bekommst eines von drei Ergebnissen. Eine positive Antwort beginnt mit dem SID plus 0x40: ein Request 22 F1 90 (ReadDataByIdentifier, DID 0xF190), beantwortet mit 62 F1 90 ..., ist ein Erfolg. Eine negative Antwort sieht so aus:
7F 22 31
│ │ └─ NRC: requestOutOfRange
│ └──── abgelehnter SID: 0x22 ReadDataByIdentifier
└─────── Negative-Response-Service-Identifier
Das dritte Ergebnis ist Stille, die auf CAN meist bedeutet, dass der ISO-TP-Frame (ISO 15765-2) nie akzeptiert wurde, die Arbitration-ID falsch ist oder das ECU beschäftigt ist. Stille ist kein NRC, und du solltest sie anders behandeln als eine ausdrückliche Ablehnung.
Die Codes mit dem meisten Informationsgehalt
ISO 14229-1 Anhang A definiert die volle Tabelle. Während eines Scans erledigt eine Handvoll Codes die meiste Arbeit:
| Code | ISO-Name | Was er dem Tester sagt |
|---|---|---|
0x10 | generalReject | Das ECU lehnt ohne Grund ab. Manche Implementierungen antworten das auf alles Unverstandene, was Struktur verbirgt. |
0x11 | serviceNotSupported | Der Service existiert auf diesem ECU gar nicht. |
0x12 | subFunctionNotSupported | Der Service existiert, die Sub-Funktion nicht. |
0x13 | incorrectMessageLengthOrInvalidFormat | Länge oder Format falsch. Nützliches Signal beim Fuzzing. |
0x22 | conditionsNotCorrect | Der Service existiert, aber Vorbedingungen fehlen (falscher Zustand, Drehzahl, Spannung). |
0x31 | requestOutOfRange | Der Parameter, meist ein DID oder RID, ist unbekannt oder out of range. |
0x33 | securityAccessDenied | Der Service ist hinter SecurityAccess (0x27) gesperrt. |
0x35 | invalidKey | Falscher Key an SecurityAccess gesendet. |
0x36 | exceedNumberOfAttempts | Zu viele falsche Keys; der Lockout hat ausgelöst. |
0x37 | requiredTimeDelayNotExpired | Du musst warten, bevor du es erneut versuchst. |
0x7E | subFunctionNotSupportedInActiveSession | Sub-Funktion existiert, nicht in dieser Session. |
0x7F | serviceNotSupportedInActiveSession | Service existiert, nicht in dieser Session. |
0x78 | requestCorrectlyReceived-ResponsePending | Kein Fehler: auf die echte Antwort warten. |
“Nicht hier” von “nicht jetzt” unterscheiden
Die nützlichste Unterscheidung während der Enumeration ist die zwischen einem Service, der nicht existiert, und einem, der existiert, aber hinter einer Session oder SecurityAccess gesperrt ist. Die Codes machen das explizit.
0x11serviceNotSupported: der SID fehlt. Hör auf, ihn zu proben.0x7FserviceNotSupportedInActiveSession: der SID ist da, du bist in der falschen Session. Sende nach10 03(Extended) oder, vorsichtig,10 02(Programming) erneut.0x33securityAccessDenied: der SID ist da und in dieser Session erreichbar, braucht aber zuerst eine entsperrte SecurityAccess-Stufe.
Ein RoutineControl-Probe, der 7F 31 33 zurückgibt, ist also keine Sackgasse. Es ist die Bestätigung, dass 0x31 hier existiert und dass etwas Schützenswertes hinter 0x27 sitzt. Dieses eine Byte lenkt den ganzen Test auf das Seed/Key-Tor.
Response Pending, und die ECUs, die es missbrauchen
0x78 ist das ECU, das um mehr Zeit bittet. Das korrekte Tester-Verhalten ist, den P2*-Timer zurückzusetzen und weiter auf eine positive Antwort oder einen anderen NRC zu warten. Byte für Byte:
tx 31 01 FF 00 RoutineControl startRoutine 0xFF00
rx 7F 31 78 Response Pending, weiter warten
rx 7F 31 78 arbeitet noch
rx 71 01 FF 00 finale positive Antwort
Zwei Muster sind selbst Findings. Ein ECU, das 0x78 sendet und nie nachliefert, hängt jeden Tester, der das Protokoll einhält. Ein ECU, das einen endlosen Strom von 0x78 sendet, lässt sich nutzen, um eine Session, sogar eine Programming-Session, viel länger offen zu halten als vorgesehen.
NRCs bei SecurityAccess nutzen
Die SecurityAccess-Codes bilden eine kleine Zustandsmaschine, die du beobachten kannst. Ein gesundes Tor läuft durch sie hindurch:
tx 27 01 requestSeed Level 1
rx 67 01 A1 B2 Seed zurückgegeben
tx 27 02 00 00 sendKey (falsch)
rx 7F 27 35 invalidKey
...wiederholen...
rx 7F 27 36 exceedNumberOfAttempts (Lockout ausgelöst)
tx 27 02 00 00 sendKey
rx 7F 27 37 requiredTimeDelayNotExpired
Ein ECU, das endlos 0x35 antwortet und nie zu 0x36 und 0x37 eskaliert, hat keinen Attempt-Counter und keine Verzögerung. Das ist ein schwaches Tor, und die NRC-Spur ist der Nachweis.
Wo die Codes in die Irre führen
Nicht jedes ECU nutzt die Tabelle sauber. Manche antworten 0x10 generalReject auf alles Unbekannte und löschen so den Unterschied zwischen “nicht unterstützt” und “nicht in dieser Session”. Manche geben 0x22 conditionsNotCorrect zurück, wo 0x31 korrekt wäre. Eine einzelne Antwort ist daher ein schwacher Nachweis. Die verlässliche Methode ist statistisch: jeden SID über alle entdeckten Sessions proben und die Response-Codes vergleichen, sodass auch ein falsch beschriftetes ECU seine echte Karte offenlegt. Genau das macht die Enumeration von AutoST, indem sie jeden Service aus seinem Antwortmuster klassifiziert statt einer einzigen Antwort zu trauen, und unser freier UDS-NRC-Decoder löst einen einzelnen Code auf, wenn du nur den Namen brauchst.
FAQ
Häufig gestellte Fragen
Wie ist eine negative UDS-Antwort aufgebaut?
Aus drei Bytes: 0x7F, dem Service-Identifier des abgelehnten Requests und dem Negative Response Code. Ein Tester, der 22 F1 90 sendet und 7F 22 31 erhält, lernt, dass der Service existiert, der Data Identifier aber out of range ist.
Ist Response Pending (0x78) ein Fehler?
Nein. requestCorrectlyReceived-ResponsePending sagt dem Tester, dass das ECU mehr Zeit braucht, also verlängert der Tester seinen P2*-Timeout und wartet. Ein ECU, das 0x78 sendet und dann nie antwortet, ist selbst ein Finding, weil es den Tester hängen lässt.
Warum antworten ECUs auf einen unbekannten DID mit 0x31 statt 0x7F?
requestOutOfRange (0x31) ist die korrekte Antwort auf einen Data Identifier, den das ECU nicht unterstützt. serviceNotSupportedInActiveSession (0x7F) heißt, der ganze Service ist in der aktuellen Session nicht verfügbar. Beides zu verwechseln ist häufig und erschwert die Enumeration.