Theft Protection and the CAN Gateway - krcenov/RNS510-Map-Viewer GitHub Wiki
This page covers the RNS510's real anti-theft/immobilizer mechanism —
spanning 2 separate processors (the VUCI/gateway ST10F276E chip and
the APPS PowerPC application) — and everything found investigating
it: a real ST10/C166 disassembler, a byte-level-confirmed EEPROM state
machine, and the real vehicle-pairing security design VW calls
"component protection." See
SWL Disc: Boot Chain and Build System
for how GATEWAY.FLI gets flashed, and
HOST BSP and Vehicle Coding for the
separate SetCoding/SetRegData coding-array mechanism this is
related to but distinct from. Reader/analysis code: research/ swl_5238_reader.py.
Status: real, complete picture at the STRING and (for one specific sub-mechanism) DISASSEMBLY level on both processors; the actual live PIN-comparison code path itself was searched for extensively and not located — recorded honestly as a real, multi-technique-confirmed wall, not a gap nobody tried to close.
VUCI\B101\RNSMIDEC\PROG\GATEWAY.FLI (884,736 bytes, the real
ST10F276E MOST-bus/CAN gateway firmware) is genuinely DIFFERENT
compiled content per market/disc (same size, different SHA256 across
builds — real per-vehicle-platform CAN routing tables baked in
alongside the shared logic, consistent with the per-platform
GWTABLE.FLI naming on the boot-chain page).
TheftProtection_* — not just a bare function name, a complete real
state machine: TheftProtection_Startup() (registers a MOST-bus
"Shadow" callback + a timer), TheftProtection_Timer(),
TheftProtection_Eeprom_Init()/_Update() (persists its own state to
EEPROM behind a verified signature — see below),
TheftProtection_Shadow_Callback_Button()/_Handle_FBlockAvail(),
TheftProtection_PrintStatus, setTheftProtection(), and a real,
complete one-line status dump:
THP: St=%d, t_cc=%ds, t_b=%ds, t_tu=%ds, c_r=%2d, c_uk=%2d, c_ud=%2d, last_st=0x%2x
(St=state, t_cc="ComfortCoding" timer, t_b=blocking timer,
t_tu=temp-unlock timer, c_r/c_uk/c_ud=3 counters, plausibly
Retry/UnlockKey/UnlockDiag). A parallel diagnostic OVERRIDE interface
exposes every one of these as a settable field — confirming the
guessed roles are real, not speculation from the format string alone:
"THP: Diag: Override CounterPINRetry %d",
"...Override Timer TempUnlock %d[s]", "...Override Timer Blocking %d[s]", "...Override Timer ConfortCoding %d[s]" (sic, a real typo
preserved in the firmware), "...Override Counter UnlockKey: %d",
"...Override Counter Diag %d", "...Override CurrentState %d",
"...Override ps_PIC_THP (theft detection) %d", "...Override ps_PIC_SFL (safe line cut) %d". A real retry-limit message: "THP: Cnt=%d, Limit=%d REACHED". A real persistent EEPROM record layout,
also dumped as one line: "THP: EEPROM: CS:0x%02x LS:0x%02x BT:%u UT:%u FUL:%2d/%2d PUL:%2d/%2d IP:%2d/%2d" (checksum, last-state,
block-time, unlock-time, 3 current/max counter pairs — exact field
meanings not decoded further, but the shape, a signed, versioned,
counter-limited persistent security record, is unambiguous).
ITD_TheftProtection_Unlock sits inside a much larger real ITD_*
("Interactive Test/Diagnostic", inferred) function family — ~50 real
names covering hardware test/calibration AND coding/security
(ITD_setCoding/ITD_readCoding/ITD_getTPFlags [TP=Theft
Protection]), gated by a real DiagS_SetAccessLayer function — a real
access-LAYER concept at the diagnostic-dispatch level, a plausible
real home for the vehicle SecurityAccess check this project searched
for and did not find in FHDD6.FLI's own 0-24MB region (see
HOST BSP and Vehicle Coding).
DiagS_SVDO_Key sits in the SAME service-name table as EELS_ ApplDiagReadDataByCommonIdentifier — a literal UDS/KWP2000
ReadDataByCommonIdentifier service-name convention, confirming this
whole area is a real diagnostic-service dispatch table. Its exact role
was not determined — no digit-count/PIN-format string was found
confirming or refuting a link to a 5-digit VCDS-style code.
A separate, real coding-synchronization protocol, distinct from
both TheftProtection_* and the APPS-side SetCoding/byte-array
mechanism: CodingShadow_* functions (CodingShadow_Check_Coding,
CodingShadow_Received_VZE_VZA/_SW_ID/_MFL_ONOFF_ID/_NAV_EXISTS/
_CAR_TYPE) driven by a real CONFIG_FKTID_* enum
(CONFIG_FKTID_AUTHENTICATE — a literal "authenticate" function ID —
plus _REGCHANGE/_REGDATA) and named CODING_*/CL_CODING_* items
(CODING_SW_ID, CODING_NAV_EXISTS, CODING_MFL_ONOFF_ID,
CODING_CAR_TYPE, CODING_VZE_VZA_ID — "VZE"/"VZA" not confidently
translated). A 2nd, parallel, gateway-side coding representation.
The APPS-side half of CONFIG_FKTID_AUTHENTICATE is now found and
disassembled, closing the cross-reference: WA/CTEST.OUT's real
configLogin() function builds a fixed-format record (a string
pointer/length, the real m_wHostDeviceId global, 4 literal constant
bytes, and the real address of a local callback,
configTestShadowSink, embedded as a function pointer) and transmits
it via cc_tla_most_send() — a genuine MOST-bus login/authenticate
message, not a local-only check. See
RNS510 Serial Console: Command Reference
for the full disassembly and the other 26 real, shell-callable
CTEST.OUT functions found alongside it (most previously undocumented
anywhere in this project).
Confirmed the EU and both North-America builds' GATEWAY.FLI all
carry the IDENTICAL set of 37 THP/TheftProtection strings — no
market-specific variation in this mechanism.
Capstone has no support for this chip's instruction set (CS_ARCH_*
lists PPC/ARM/x86/MIPS/etc, nothing C166/ST10/8051-family), and no
local objdump/binutils target exists either — but a real, open-source
tool does: hn/c166-dis (Perl,
GPLv3; a 3rd-party external tool, not vendored into this repo, same
treatment as pip install capstone). perl c166-dis.pl <file> <start_offset_hex> <length_hex>.
Immediately validated against known structure: disassembling from
file offset 0 decodes the file's own AA55AA55 magic (the SAME magic
already known from FHDD6.FLI and PQTABLE.FLI/SKTABLE.FLI — see
the boot-chain and
HOST BSP pages — confirming .FLI is a
shared generic Continental firmware wrapper across BOTH the PowerPC
processors AND this ST10 gateway chip, not PowerPC-specific) as
non-code, correctly, immediately followed by a real, textbook C166
interrupt vector table at file offset 0x20.
Address mapping confirmed SIMPLE — no unknown load-base recovery
needed, unlike FHDD6.FLI's own PowerPC region: file_offset = segment * 0x10000 + offset, verified by disassembling a real vector-
table target and getting back coherent, non-garbage C166 code: a
clean bit-extraction loop (MOVB/SHR/AND) feeding sequential
constant IDs into one shared reporting function via CALLS 0x05,0x8B24 — exactly a "for each status bit, report signal N"
diagnostic loop. That shared helper itself disassembles to a long,
coherent function using EXTP (C166's real paged-memory-extension
prefix) for indexed array lookups — real, generic, shared code, not
noise.
The string-to-code cross-reference — a real dispatcher found, an EEPROM signature confirmed at the disassembly level
Method that worked, after 2 that didn't: searching for
"ITD_TheftProtection_Unlock"'s own file offset as a literal pointer
value found nothing (0 hits one byte order, 23 hits the other — all
confirmed text coincidences inside unrelated strings). What worked:
target short, distinctive DEBUG/FORMAT strings instead of a bare job
name (loaded as a direct immediate right before a print call, the same
pattern already proven for PowerPC printf sites in CTEST.OUT) —
searching for MOV Rx,#imm16 (opcode 0xE6) loading the exact file
offset of 4 different "THP:"/"TheftProtection:" strings found real
hits, tightly clustered in one ~3.6KB region (file offset
0xA6480-0xA7100).
Disassembling that region (coherent throughout, no illegal-opcode
breaks) reveals a real diagnostic OVERRIDE-command dispatcher: each
handler loads its own name/format string via the same shared-reporter
pattern above — matching, by exact file-offset arithmetic, the
"CounterPINRetry"/"Timer TempUnlock"/etc. override names found by
string-scanning — then applies the override directly to internal state
(a single bit via BMOV, or a full EEPROM-style record write).
A real retry-limit constant, confirmed at the disassembly level:
MOVB RL1,[R12] ; load current counter byte
CMPB RL1,#0x0032 ; compare against 50 (0x32) decimal
JMPR CC_NC,0x000A65D8 ; if counter >= 50, take the "limit reached" path
...
ADDB RL1,#1 ; else increment
MOVB [R12],RL1 ; and store it back
— the "limit reached" branch leads, a few instructions later, into the
exact same shared reporter loading the file offset of "THP: Cnt=%d, Limit=%d REACHED", confirmed by direct address arithmetic. 50 is a
real, disassembly-confirmed retry limit (which specific counter —
PIN retry, unlock-key, unlock-diag — not individually pinned down; the
dispatcher services all of them through the same generic shape). A
sibling handler shows a 2nd, CONDITIONAL threshold: 0x64 (100) if a
helper-returned flag equals 1, else the same 0x32 (50).
The real EEPROM signature scheme, also confirmed at the disassembly level: after every state-changing write in this region, the code loads and stores a fixed 4-byte pair:
MOV R12,#0x5A55
MOV R13,#0xA5AA
MOV [R0],R12
MOV [R0+#0x0002],R13
0x5A55 XOR 0xA5AA == 0xFFFF exactly — a classic magic-value +
bitwise-inverted-magic-value pair, the real, concrete implementation
behind the already-known strings "TheftProtection_Eeprom_Init(): Signature invalid."/"...cannot be verified".
Honest scope: this is a real, disassembly-confirmed FACTORY/DEVELOPMENT diagnostic interface for directly overriding TheftProtection's internal counters/timers/state — genuinely useful new knowledge about this ECU's internals, but NOT the same code path as a real end user's VCDS-style Login/SecurityAccess PIN entry and comparison. That live check was not located.
Traced one level further back and found a whole FAMILY of small, per-override handler functions (each setting one internal constant/flag, then calling 1 of 2 shared apply-helpers) — clearly the real per-command handler family the override strings describe. Then tried, in order, to find what CALLS into this family:
- Literal jump-table search (consecutive handler addresses as adjacent 16-bit words, any order) — zero hits.
-
Backward disassembly for a
CMP+JMPRcompare-and-branch switch chain — found MORE independent tiny handler functions, no dispatch chain. -
Word-address hypothesis (
byte_offset/2table entries) — zero hits. - Low-byte-only table hypothesis — zero hits.
-
A blind full-file linear-sweep disassembly, grep for every
CALLIthe disassembler decoded — found a promising dense cluster, tested and REFUTED: disassembling backward shows it's really a 16-bit data table (a PWM/backlight calibration curve, matching this file's ownfrontIllum_printEepromInformationstrings), and the linear sweep had desynced upstream, decoding misaligned data bytes as plausible-lookingCALLImnemonics. A real, general lesson: a full-file blind sweep is not trustworthy on this architecture without first knowing real code/data boundaries — verify any full-sweep finding by disassembling backward from it first. - Order-independent literal-address search — every raw occurrence of 16 handler addresses, scanned for any 64-byte window containing 3+ distinct ones (tolerant of table reordering) — zero clusters anywhere in the 884,736-byte file. 6 of the 16 handler addresses have ZERO raw byte occurrences anywhere in the file except their own code location — not low, exactly zero. This closes the "literal address table" hypothesis completely, in any ordering.
Conclusion, definitively closed for this investigation: whatever selects these handlers is either computed via runtime arithmetic (a base plus a value derived from an incoming diagnostic request's own sub-function byte — information this project doesn't have) or reached via code not located in this disassembly. A real recursive-descent disassembler with symbolic register tracking would be the honest next step — substantial new tooling, not a quick follow-up search.
TpPvVerifyPin — the real APPS-side PIN-check code, located (strings), and the vehicle-pairing design
Confirmed, this project's own FHDD6.FLI (85,658,916 bytes) contains
TpPvVerifyPin verbatim, at file offset 82,555,321 (~82.6MB) —
inside the already-known 76-85.6MB VxWorks kernel/BSP region (see
Firmware Reversing), NOT the load-base-recovered
0-9MB native map/navicore code — real, linked, callable shell/console
code, just outside the region this project can currently disassemble
(a different linking unit; FHDD6_LOAD_BASE does not apply there, and
no separate base for this region was recovered). A wide net of strings
within ±15KB of this hit gives a complete, coherent picture:
-
"gsVuCTheftProtectionState : %02X"— theAPPSprocessor mirrors the SAMETheftProtectionstate already reverse-engineered inGATEWAY.FLIabove (VuC= the gateway chip)."LOCKED STATE FOR THEFT PROTECTION RECEIVED. PIN INPUT IS REQUESTED"— the real trigger: when the gateway reports its theft-protection state as locked,APPSasks the user for a PIN. Directly connects the 2 investigations —TpPvVerifyPinIS the real live PIN-check path. -
"comfortCoding : %d pinVerification : %d Seat target : %d"— one debug line naming both "comfortCoding" (matchesGATEWAY.FLI's ownt_cc/"ConfortCoding"field above) and "pinVerification" together — independent cross-confirmation of one distributed state machine spanning both chips, via the same MOST-bus "Shadow"/FBlock framework on both sides. -
The real hash-verification mechanism, named directly:
HWInfoGetPinHash()(a REFERENCE hash from "HWInfo" storage) andNFGetNandFlashId()(the unit's own NAND flash chip ID) are both called and logged around the verify path."entered PIN/Hash v[alid]"/"...sh inval[id]"are the real strings — "Hash valid" is a literal substring of the real"entered PIN/Hash valid"message (matching the community brute-force tool's own paraphrase, see below).HWInfois the same NOR-flash structured data area already found in theHOSTBSP (see HOST BSP and Vehicle Coding). -
A more specific, real security mechanism: CLUSTER-ID PAIRING. A
dense string cluster —
cCurrentClusterIdHash=,"hash written in NAND","ClusterID hash writing in NAND failed",CopyClusterIDFromNANDToNOR(),"Copying ClusterID from NAND to NOR"— shows the unit stores a HASH of the vehicle's INSTRUMENT CLUSTER's own unique ID, synced between NAND and NOR flash. This is the real, concrete implementation of VW Group's documented "component protection" anti-theft design: the head unit is cryptographically bound to a SPECIFIC vehicle's cluster, not just protected by a standalone PIN — moving the radio to a different car (different cluster ID) is what triggers the locked state in the first place, independent of whether the PIN itself is ever guessed. -
Real evidence of factory/regional default PINs:
ENABLE_DEFAULT_PIN_WITH_INITIAL_HASH(a real compile-time flag) and"Initial Hash found, Pin(JapNav) valid"(paired withTpPvJapNavPresent()/TpPvGetUniqueIdentifier()) — a real, build-specific default-PIN-with-pre-known-hash mechanism for at least the Japan-Navigation variant, not decoded further.
Not disassembled: the exact hash algorithm (PIN alone? PIN + NAND ID? PIN + ClusterID hash? some combination?) remains unknown at the byte/instruction level — only the real function/data names and their call relationships were established from strings.
A community Windows tool, RNS510_code_finder_ver20.exe/ver22.exe
(BASE/Other/rns_code_finders, BASE/), turned out to be a real
.NET/C# binary — decompiled cleanly with ilspycmd (pin version
9.1.0.7988; the tool's own latest-version install path is
currently broken upstream due to bad NuGet package metadata). A 2nd,
independent, Python implementation of the same idea,
rns510-code-finder-main/rns510_code_finder.py
(github.com/SlashGordon/rns510-code-finder, MIT-licensed), was found
separately and names the same real command directly in its own
source. Both confirm, from a working tool's own behavior rather than
static analysis, that TpPvVerifyPin is a real, LIVE, directly-
invocable command in a VxWorks target shell:
The real mechanism: opens a real Windows COM (serial) port at
115200 baud and, for each candidate PIN in a configurable range
(default/documented 0-1999 — a 2000-value keyspace, confirmed
independently by both tools), writes the literal ASCII line
TpPvVerifyPin(<pin>) directly to the port, then watches the response
for "Hash valid"/"Hash invalid" (the real, literal substrings of
FHDD6.FLI's own "entered PIN/Hash valid" message, above). A
separate "version" command's response is parsed for "Made on "/"Kernel:"/"VxWorks"/"tShell" — a real VxWorks target-shell
banner, confirming the shell is reachable live over this serial line.
A real, physical wiring diagram was recovered from the .NET tool
itself — its "How connect RNS510?" button shows a .resx-embedded
PNG (extracted by hand: ilspycmd doesn't decode resx Bitmap
blobs, but the base64 blob contains a plain embedded PNG findable by
its own file signature). Also saved to the main repo at
research/images/rns510_serial_debug_connector.png.

Shows a real 26-pin connector, part number N91-10930, wired to a
standard DB9: pin 14 = GND (explicitly labeled), 2 adjacent
unlabeled pins (same column, one row up) carry RXD/TXD to DB9 pins
2/3 — consistent with the 115200-baud serial config above. The exact
numbers of the 2 data pins are inferred from the diagram's own
row-numbering convention, not read off an explicit label — worth
re-verifying against real hardware before relying on them physically.
A newer build of the .NET tool (v22) adds 2 real operational facts: a startup warning, "Don't use USB TTL interface!" (implies real reliability problems with cheap USB-serial adapters on this port), and "Please wait ~1 min after boot RNS." before the shell responds — the real target shell is not available immediately at power-on.
BASE/Other/Setconfig-for-rns510.pdf is a real manual for "SetConfig
for RNS510", a commercial third-party coding/diagnostic tool (sold
with an FTDI cable). It independently corroborates this whole page
from a completely different angle — a working tool's UI against real
hardware:
- Its Coding tab lists "Theft protection" and "PIN
verification" as individually togglable, named coding options —
almost certainly further offsets in the same
SetCoding(0x400,...)byte array documented on the HOST BSP and Vehicle Coding page, alongside the already-known manufacturer/HDD/tuner/DAB/SDARS/MPEG offsets (exact offsets not identified). - Its Hardware tab (service-mode only) exposes a real 4-digit PIN code field with its own "Set" button — external confirmation a tool exists that can both read AND WRITE the theft-protection PIN, independent of the serial-shell mechanism above.
- Its Monitor tab shows a real captured debug log including
RTDD_PS_VuC_HB— a real "VuC" (gateway) heartbeat message, independently confirming the "VuC" terminology used throughout this page's ownGATEWAY.FLI/TpPvVerifyPinstring evidence.
Found in the same batch as the 2 PIN-finder tools, briefly (the real
BAP wire protocol they implement — frame framing, CAN IDs, function-ID
catalogs — is documented in full on the
HOST BSP and Vehicle Coding page):
rns510-online-dest-master/rns510-tv-input-master (real, working
SocketCAN BAP clients), rns510_dtv-master (a standalone Qt/GStreamer
DVB-T player — initially assumed to ride the same BAP mechanism, since
corrected: it actually uses its own, much simpler RS232 protocol, see
HOST BSP and Vehicle Coding), and
rns510_1020_1022_1024 — another real, complete SWL disc, the OLDEST
build this project had before the later BASE/ reorganization (see
SWL Disc: Boot Chain and Build System
for the even-older 900/1020_1022/1024 builds found after that
reorganization). Its VERSION.TXT reads (c) Siemens VDO AG Wetzlar
— NOT CONTINENTAL Wetzlar like every later disc — a direct,
disc-level confirmation of the Siemens-VDO-to-Continental corporate
lineage already inferred from the HOST BSP's own embedded copyright
string.
# C166/ST10 disassembly (GATEWAY.FLI):
perl c166-dis.pl GATEWAY.FLI 0xA6480 0xC80 # the override dispatcher region
# file_offset = segment*0x10000 + offset; AA55AA55 magic at offset 0 is non-code
# .NET decompile (rns_code_finders):
dotnet tool install -g ilspycmd --version 9.1.0.7988
ilspycmd -p -o out/ RNS510_code_finder_ver20.exe
Full narrative and every validation number: research/ swl_5238_reader.py's own module docstring — search for
"TheftProtection_* -- a complete, real", "A REAL, WORKING ST10/C166 disassembler", "The string-to-code cross-reference itself", "The real theft-protection PIN verification mechanism -- FOUND", and
"proves TpPvVerifyPin is a REAL, directly-callable".