KopherBit
Diagnostics

UDS Reflash Sequence: 0x10 → 0x27 → 0x31 → 0x34 / 0x36 / 0x37 End-to-End

A step-by-step walkthrough of the full UDS (ISO 14229) reflash sequence — session control, Security Access, erase routines, RequestDownload / TransferData / RequestTransferExit, ECU Reset, and common NRCs.

Why a reflash needs this full sequence

ECU reflashing is not “write a binary into flash.” A bootloader has to confirm the tool is authorised, the application is shut down cleanly, the flash region is erased, the address range and size are negotiated, every payload chunk arrives in order, and the final image passes a CRC check. UDS (ISO 14229) standardises this whole flow as a series of service IDs, riding on top of ISO-TP (ISO 15765-2), so different OEMs, tools, and ECUs can interoperate. This article walks the exact order KITE Reflasher executes on the wire: 0x10 → 0x27 → 0x31 → 0x34 / 0x36 / 0x37 → 0x31 → 0x11.

0x10 Diagnostic Session Control: from default to programming

The first step is to switch into a session that has flashing privileges. Common practice is to enter the extended diagnostic session (0x03) first to do prep work (e.g. silence non-diagnostic communication), then move to the programming session (0x02), at which point the ECU jumps to the bootloader.

# enter extended session
Tx: 02 10 03 00 00 00 00 00
Rx: 06 50 03 00 32 01 F4 00      ; P2=50ms, P2*=5000ms

# enter programming session
Tx: 02 10 02 00 00 00 00 00
Rx: 06 50 02 00 32 01 F4 00

After the switch, KITE Reflasher starts a periodic TesterPresent (0x3E) to keep P2 from timing out.

0x27 Security Access: seed → DLL → key

In programming session, the bootloader rejects writes until Security Access succeeds. The handshake is: tool requests a seed (0x27 01), passes the seed to an external DLL that computes the key, then sends the key back via 0x27 02.

# request seed
Tx: 02 27 01 00 00 00 00 00
Rx: 06 67 01 11 22 33 44 00      ; seed = 11 22 33 44

# (host) compute_key_from_dll(seed) → key
# send key
Tx: 06 27 02 AA BB CC DD 00
Rx: 02 67 02 00 00 00 00 00

KITE Reflasher loads the DLL with libloading and supports two export conventions: KopherBit computeKeyFromSeed and Vector CANoe GenerateKeyEx. Most OEMs ship one or the other.

0x31 Routine Control: pre-programming, erase, check, CRC

0x31 is one SID with many uses. A bootloader typically registers four to six routine identifiers (RIDs):

  • Pre-programming — gives the application a chance to shut down / save NV.
  • Dependency check — verifies hardware and bootloader version compatibility.
  • Erase memory — the tool passes a start address and length to erase.
  • Check programming dependencies — image integrity check after writing.
  • CRC check — CRC compare on the downloaded segment.

Erase example (subfunction 01 = startRoutine, RID FF 00, parameters address[4] | size[4]):

Tx: 0F 31 01 FF 00 00 02 00 00 00 00 80 00 00 00 00 00
Rx: 04 71 01 FF 00 00 00 00      ; routine done, no result data

Erase takes time, so the bootloader may answer 0x78 (RequestCorrectlyReceived-ResponsePending) to buy more time. The tool must not give up — it has to wait for the final positive response.

0x34 RequestDownload: address, size, format identifier

Each segment download is negotiated by 0x34, which carries:

  • dataFormatIdentifier — high nibble = compression, low nibble = encryption (often 0x00).
  • addressAndLengthFormatIdentifier — high nibble = size length, low nibble = address length. 0x44 is common (4 + 4 bytes).
  • memoryAddress (4 bytes) + memorySize (4 bytes).
# write 0x00010000 bytes at 0x00020000
Tx: 0B 34 00 44 00 02 00 00 00 01 00 00
Rx: 04 74 20 04 02 00            ; lengthFormatId=0x20, maxBlockLen=0x0402

The maxNumberOfBlockLength in the response tells the tool the upper bound on each TransferData chunk (including SID and BSC). KITE Reflasher slices the payload accordingly.

0x36 TransferData: block sequence counter and chunking

Every chunk goes out via 0x36, and the first byte after the SID is the block sequence counter (BSC). It starts at 01, increments by 1 per chunk, and wraps to 00 on overflow.

Tx: ... 36 01  <payload bytes>
Rx: 02 76 01 00

Tx: ... 36 02  <payload bytes>
Rx: 02 76 02 00
...

If the ECU echoes a BSC the tool didn’t expect, a chunk was lost — re-send with the original BSC. A single chunk is naturally split into ISO-TP CFs (Consecutive Frames); the tool’s ISO-TP layer handles flow control (stmin / BS) automatically.

0x37 RequestTransferExit: end this segment (optionally with CRC)

When the segment’s payload is fully sent, send 0x37 to close it. 0x37 can carry an optional transferRequestParameter — a common practice is to pass a tool-side CRC-32 so the bootloader can verify integrity.

# without parameter
Tx: 01 37 00 00 00 00 00 00
Rx: 01 77 00 00 00 00 00 00

# with CRC-32
Tx: 05 37 12 34 56 78 00 00 00
Rx: 01 77 00 00 00 00 00 00

To write multiple sectors, loop back to 0x34 and renegotiate.

0x11 ECU Reset: apply the new firmware

After every segment is written and the final 0x31 CRC + dependency checks pass, send 0x11 01 to trigger a hard reset. The bootloader hands control back to the application and the reflash is done.

Tx: 02 11 01 00 00 00 00 00
Rx: 02 51 01 00 00 00 00 00

Role of HEX / S-record / VBF in this sequence

What the tool sees in HEX, S-record, or VBF is not “a single linear binary” — it is a list of address-tagged blocks (records). Before the 0x34 phase, KITE Reflasher does a memory-aware merge:

  • Parse HEX ULBA + data records, or S-record S1/S2/S3, or VBF erase regions + data blocks.
  • Use the user’s MemoryConfigPanel sector layout to merge contiguous records into a single block per sector.
  • Pad gaps with 0xFF (Pad flag) so the download stage does not over-fragment.
  • Write the merged binary as Intel HEX as an audit artifact.

The result: each 0x34 corresponds to one merged sector; 0x36 simply slices that sector’s bytes to maxBlockLen.

How KITE Reflasher composes this on the sequencer

In UdsSequencerPanel, the engineer drops in steps:

  1. Diagnostic Session Control (0x03)
  2. Diagnostic Session Control (0x02)
  3. Security Access (0x27 level=01) — automatically calls compute_key_from_dll
  4. Routine Control (0x31 startRoutine, RID=Pre-programming)
  5. Routine Control (0x31 startRoutine, RID=EraseMemory, params=addr|size)
  6. Download Step — load .hex/.vbf, pick blocks, the tool expands this into 0x34 → 0x36×N → 0x37
  7. Routine Control (0x31 startRoutine, RID=CheckProgrammingDependencies)
  8. Routine Control (0x31 startRoutine, RID=CRCCheck)
  9. ECU Reset (0x11 01)

Each step shows idle / running / success / error in the UI, and the run-loop is abortable. The whole sequence saves as .udss so an EOL fixture can replay it later.

Common NRCs

When something fails, the bootloader returns 7F <SID> <NRC>. The ones you’ll meet most:

NRCNameUsually means
0x10generalRejectbad service combination
0x11serviceNotSupportedtool sent a SID the ECU didn’t implement
0x12subFunctionNotSupportedsession or routine subfunction does not exist
0x22conditionsNotCorrectsent 0x34 without entering programming session
0x33securityAccessDeniedwrong key from DLL, or wrong security level
0x35invalidKeyseed → key computation was wrong
0x37requiredTimeDelayNotExpiredpost-failure delay after consecutive bad keys
0x71transferDataSuspendeda previous 0x36 was not fully received
0x72generalProgrammingFailureflash hardware write error
0x78requestCorrectlyReceivedResponsePendinglong operation (erase / CRC) in progress, keep waiting

0x78 is not an error — it is “wait for me.” The tool must reset its P2 timer and keep listening for the final positive response. KITE Reflasher’s ISO-TP layer handles this automatically and does not abort the sequence.

Closing

The thing to internalise is not any single SID, but the dependency between steps: you can’t reach 0x27 without 0x10, you can’t reach 0x31 without 0x27, and 0x34 without an erase routine returns NRC 0x22. Capturing the sequence as .udss and ASC means bring-up engineers and EOL validation share one ground truth.