00 UDS nedir — ISO 14229
UDS (Unified Diagnostic Services), ISO 14229-1 standardıyla tanımlanmış araç diagnostics protokolüdür. Tüm büyük OEM'ler (BMW, Mercedes, VW, Toyota, Ford) bu standardı uygular.
UDS'nin temel işlevleri şunlardır:
- ECU'ya bağlan ve teşhis session'ı aç
- Hata kodlarını (DTC) oku ve sil
- ECU parametrelerini oku/yaz (VIN, kalibrasyon değerleri)
- Güvenlik erişimi ile yazılım güncellemesi yap (flash)
- Rutin testleri çalıştır (sensör kalibrasyonu, aktuatör testi)
┌──────────────────────────────────────────────────────────┐
│ UDS Kullanım Alanı │
├──────────────────────────────────────────────────────────┤
│ Servis Merkezi │ Araç bakımı, DTC okuma/silme │
│ OEM Test Hattı │ Üretim sonu kalibrasyon, flash │
│ OTA Güncelleme │ DoIP + UDS ile uzaktan flash │
│ Araç Geliştirme │ ECU kalibrasyonu, değer yazma │
│ Araştırma │ Araç güvenlik analizi │
└──────────────────────────────────────────────────────────┘
Temel Kavramlar
ISO 14229 çok bölümlüdür: -1 uygulama katmanı (bu rehber), -2 session ve transport, -3 UDS on CAN, -5 UDS on Internet. DoIP (ISO 13400) ile birlikte UDS, Ethernet üzerinden de çalışır.
01 OSI katman modeli
UDS, uygulama katmanı protokolüdür. Altındaki katmanlar farklı fiziksel ortamlara göre değişebilir — CAN veya Ethernet.
┌─────────────────────────────────────────────────────────────┐
│ Uygulama Katmanı │ UDS (ISO 14229-1) │
│ │ Service: 0x22 ReadDataByIdentifier │
├─────────────────────┼─────────────────────────────────────┤
│ Transport Katmanı │ ISO-TP (ISO 15765-2) │
│ │ Single/First/Consecutive Frame │
├──────────┬──────────┴─────────────────────────────────────┤
│ CAN yolu│ Ağ Katmanı │ IPv4 / IPv6 │
│ │ Veri Katmanı │ CAN 2.0B / CAN FD │
│ │ │ │
│ IP yolu │ Transport │ TCP (DoIP) │
│ │ Ağ │ IPv4 / IPv6 │
│ │ Veri │ Ethernet │
└──────────┴───────────────┴─────────────────────────────────┘
ISO-TP (ISO 15765-2)
CAN frame'i maksimum 8 byte taşır. UDS mesajları daha büyük olabileceğinden ISO-TP segmentasyon/yeniden birleştirme yapar:
# Tester → ECU (CAN ID: 0x7DF broadcast veya 0x7E0 + ECU offset)
# Single Frame: PCI=0x03 (3 byte), SID=0x22, DID=0xF190
CAN ID: 0x7DF
Data: 03 22 F1 90 00 00 00 00
# ECU → Tester (First Frame: toplam 20 byte VIN yanıtı)
CAN ID: 0x7E8
Data: 10 14 62 F1 90 31 47 31 # FF: len=0x14=20, SID+0x40=0x62
# Tester → ECU (Flow Control: devam et)
CAN ID: 0x7DF
Data: 30 00 00 00 00 00 00 00 # FC: BS=0 (sınırsız), STmin=0
# ECU → Tester (Consecutive Frame 1)
CAN ID: 0x7E8
Data: 21 46 4A 31 32 33 34 35 # CF sn=1
# ECU → Tester (Consecutive Frame 2)
CAN ID: 0x7E8
Data: 22 36 37 38 39 30 31 32 # CF sn=2, VIN tamamlandı
02 Service ID tablosu
UDS, her biri bir araç diagnostics işlevi için ayrılmış Service ID'lerden oluşur. Yanıtlar her zaman SID + 0x40 şeklindedir; hata yanıtı 0x7F SID NRC formatındadır.
Negatif Yanıt Kodları (NRC)
# Format: 7F [SID] [NRC]
# Örnek: 0x27 SecurityAccess'e hatalı key gönderildi
7F 27 35 # NRC 0x35 = invalidKey
# Yaygın NRC kodları
# 0x10 — generalReject
# 0x11 — serviceNotSupported
# 0x12 — subFunctionNotSupported
# 0x13 — incorrectMessageLengthOrInvalidFormat
# 0x22 — conditionsNotCorrect (wrong session)
# 0x24 — requestSequenceError (wrong order)
# 0x31 — requestOutOfRange (invalid DID)
# 0x33 — securityAccessDenied
# 0x35 — invalidKey
# 0x36 — exceededNumberOfAttempts
# 0x37 — requiredTimeDelayNotExpired
# 0x78 — requestCorrectlyReceivedResponsePending (ECU meşgul, bekle)
03 Session yönetimi
UDS üç temel session türü tanımlar. Her session farklı bir yetki seviyesini ve erişilebilir servisleri kapsar.
Session Zamanlama Parametreleri
# 1. Default → Extended Session aç
# İstek: 10 03
# Yanıt: 50 03 00 32 01 F4 (P2=50ms, P2*=500ms)
# 2. Session canlı tut (her 4.5 saniyede bir)
# İstek: 3E 00 (TesterPresent, suppress response)
# Yanıt: 7E 00 (ya da suppress bit ile yanıt yok)
# 3. Default Session'a dön
# İstek: 10 01
# Yanıt: 50 01 ...
# 4. ECU Reset
# İstek: 11 01 (Hard Reset)
# Yanıt: 51 01 (sonra ECU yeniden başlar)
Extended veya Programming session'da çalışırken S3 timer'ı aşmamaya dikkat et. Script içinde TesterPresent (0x3E 0x80) mesajlarını periyodik gönder. 0x80 sub-function "suppressPosRspMsgIndicationBit" anlamına gelir — yanıt istemediğini belirtir ve ağ trafiğini azaltır.
04 SecurityAccess — seed/key mekanizması
Hassas ECU işlemleri (yazılım güncelleme, kalibrasyon yazma) güvenlik kilidi arkasında bulunur. SecurityAccess (0x27) servisi challenge-response yöntemiyle bu kilidi açar.
Seed/Key Akışı
Tester ECU
│ │
│── 27 01 (requestSeed, level 1) ────────►│
│ │ random seed üret
│◄─ 67 01 [seed bytes] ───────────────────│
│ │
│ key = f(seed, secret_algorithm) │
│ │
│── 27 02 [key bytes] ────────────────────►│
│ key doğrula│
│◄─ 67 02 (accessGranted) ────────────────│
│ │
│ Artık kısıtlı servisler kullanılabilir │
Security Level'lar
Her OEM farklı security level'lar tanımlar. Genel kural:
- Odd sub-function (0x01, 0x03, 0x05...): seed iste
- Even sub-function (0x02, 0x04, 0x06...): key gönder
- Level 0x01/0x02: geliştirici erişimi
- Level 0x11/0x12: ECU supplier erişimi
- Level 0x21/0x22: OEM erişimi (üretim hattı)
Brute Force Koruması
ECU, ardışık yanlış key denemelerini sayar. Genellikle 3 yanlış denemeden sonra:
- Belirli süre (delay timer) yeni seed talebi reddeder (NRC 0x37)
- Bazı ECU'larda kalıcı kilitleme gerektirir servis merkezi müdahalesi
def calculate_key(seed: bytes, secret: int = 0xABCD1234) -> bytes:
"""
Gerçek araçlarda bu algoritma OEM tarafından gizli tutulur.
Tersine mühendislik veya NDA ile temin edilir.
"""
seed_int = int.from_bytes(seed, 'big')
key_int = seed_int ^ secret
key_int = ((key_int << 3) | (key_int >> 29)) & 0xFFFFFFFF
return key_int.to_bytes(4, 'big')
# Kullanım:
seed = bytes.fromhex('12 34 56 78'.replace(' ', ''))
key = calculate_key(seed)
print(f"Seed: {seed.hex()}, Key: {key.hex()}")
Gerçek araçlarda SecurityAccess algoritması OEM sırrıdır. GDPR ve araç güvenliği yasaları kapsamında bu algoritmanın izinsiz tersine mühendisliği yasal sorun yaratabilir. Araştırma ortamında kendi test ECU'nuzla çalışın.
05 DID — Data Identifier okuma/yazma
DID (Data Identifier), ECU içindeki veri nesnelerini tanımlayan 2 byte'lık anahtarlardır. ISO 14229 bazı standart DID'leri tanımlar; OEM'ler kendi özel DID'lerini ekler.
Standart DID'ler
Tek istek — birden fazla DID
# Tek DID: VIN oku
# İstek: 22 F1 90
# Yanıt: 62 F1 90 [17 byte VIN ASCII]
# İki DID aynı anda: VIN + SW Version
# İstek: 22 F1 90 F1 01
# Yanıt: 62 F1 90 [17 byte] F1 01 [N byte versiyon]
# Örnek yanıt (ASCII VIN: "1G1JC5444Y7123456")
62 F1 90 \
31 47 31 4A 43 35 34 34 \ # 1G1JC544
34 59 37 31 32 33 34 35 \ # 4Y712345
36 # 6
DID Yazma — WriteDataByIdentifier (0x2E)
# Önce Extended Session ve SecurityAccess gerekli
# İstek: 2E F1 90 [17 byte VIN]
# Yanıt: 6E F1 90 (başarılı)
# Hata: 7F 2E 22 (conditionsNotCorrect — wrong session)
# Yeni VIN: "WBAC5441X1LB12345"
2E F1 90 \
57 42 41 43 35 34 34 31 \ # WBAC5441
58 31 4C 42 31 32 33 34 \ # X1LB1234
35 # 5
06 DTC okuma ve yönetimi
0x19 ReadDTCInformation servisi, ECU'da kayıtlı arıza kodlarını (DTC) çeşitli kriterlere göre sorgular. DTC statusByte, arızanın mevcut durumunu kodlar.
0x19 Sub-function'ları
statusByte Anatomisi
Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
──────────────────────────────────────────────────────
WIR TNASF TNCSLC PDTC CDTC PDTCSNC TFTOC TF
│ │ │ │ │ │ │ │
│ │ │ │ │ │ │ └─ testFailed (şu an arıza var)
│ │ │ │ │ │ └─ testFailedThisOperationCycle
│ │ │ │ │ └─ pendingDTC (bu sürüş döngüsünde başarısız)
│ │ │ │ └─ confirmedDTC (yeterince tekrar etti)
│ │ │ └─ pendingDTCnotConfirmed
│ │ └─ testNotCompletedSinceLastClear
│ └─ testNotCompletedThisOperationCycle
└─ warningIndicatorRequested (MIL lambası)
# Tüm aktif (testFailed) DTC'leri iste: statusMask = 0x01
# İstek: 19 02 01
# Yanıt: 59 02 FF [DTC1 3 byte] [status1] [DTC2 3 byte] [status2] ...
# Yanıt parse:
# 59 02 FF — SID+0x40, sub-func, DTC format
# 00 12 34 09 — DTC: P0x1234, status: 0x09 (TF=1, confirmed=1)
# 00 45 67 0F — DTC: P0x4567, status: 0x0F
# Tüm DTC'leri sil
# İstek: 14 FF FF FF
# Yanıt: 54
DTC Formatı
ISO 15031-6 DTC formatı: 3 byte. İlk 2 bit domain kodunu, geri kalanı arıza numarasını belirler.
def decode_dtc(dtc_bytes: bytes) -> str:
domain = {0: 'P', 1: 'C', 2: 'B', 3: 'U'}
high = dtc_bytes[0]
code = ((high & 0x3F) << 8) | dtc_bytes[1]
d = domain[(high >> 6) & 0x03]
return f"{d}{code:04X}"
# Örnek: 0x001234 → P1234
print(decode_dtc(bytes([0x00, 0x12, 0x34]))) # P1234
print(decode_dtc(bytes([0x40, 0x45, 0x67]))) # C4567
07 python-udsoncan ile UDS istemcisi
python-udsoncan, UDS protokolünü Python'da kolayca kullanmanızı sağlayan bir kütüphanedir. ISO-TP transport katmanı için python-can veya doipclient ile çalışır.
Kurulum
pip install udsoncan python-can
# DoIP transport için:
pip install doipclient
CAN üzerinde UDS — tam örnek
import udsoncan
from udsoncan.connections import PythonIsoTpConnection
from udsoncan.client import Client
import isotp
import can
# CAN bus ve ISO-TP bağlantısı kur (SocketCAN: vcan0)
bus = can.interface.Bus(channel='vcan0', bustype='socketcan')
tp_addr = isotp.Address(isotp.AddressingMode.Normal_11bits,
txid=0x7E0, rxid=0x7E8)
stack = isotp.CanStack(bus=bus, address=tp_addr)
conn = PythonIsoTpConnection(stack)
config = udsoncan.configs.default_client_config.copy()
config['request_timeout'] = 5
with Client(conn, config=config) as client:
# 1. Extended Session aç
client.change_session(
udsoncan.services.DiagnosticSessionControl.Session.extendedDiagnosticSession)
print("Extended session açıldı")
# 2. SecurityAccess — seed iste, key hesapla, gönder
result = client.request_seed(0x01)
seed = result.service_data.seed
key = calculate_key(seed) # OEM'e özgü algoritma
client.send_key(0x02, key)
print("SecurityAccess başarılı")
# 3. VIN oku
result = client.read_data_by_identifier([0xF190])
vin = result.service_data.values[0xF190].decode('ascii')
print(f"VIN: {vin}")
# 4. DTC listesini oku
result = client.get_dtc_by_status_mask(0xFF)
for dtc_entry in result.service_data.dtcs:
print(f"DTC: {dtc_entry.id:06X} Status: {dtc_entry.status.get_byte():02X}")
# 5. DTC'leri temizle
client.clear_dtc(0xFFFFFF)
print("DTC'ler temizlendi")
# 6. ECU soft reset
client.ecu_reset(udsoncan.services.ECUReset.ResetType.softReset)
TesterPresent döngüsü
import threading, time
def keep_session_alive(client, interval=4.5):
while not stop_event.is_set():
try:
client.tester_present(suppress_positive_response=True)
except:
pass
time.sleep(interval)
stop_event = threading.Event()
tp_thread = threading.Thread(target=keep_session_alive,
args=(client,), daemon=True)
tp_thread.start()
# ... UDS işlemler ...
stop_event.set()
08 Pratik: ECU tester ve yazılım indirme
Raspberry Pi + MCP2515 SPI-CAN modülü ile gerçek CAN bus'a bağlan, VIN oku ve UDS 0x34/36/37 servisleriyle yazılım indirme akışını uygula.
Donanım Kurulumu
# /boot/config.txt eklemeleri
dtparam=spi=on
dtoverlay=mcp2515-can0,oscillator=8000000,interrupt=25
# CAN interface'i başlat
sudo ip link set can0 up type can bitrate 500000
ip -details link show can0
# Test: candump ile CAN trafiği izle
candump can0
Yazılım İndirme Akışı (0x34/36/37)
Tester ECU
│ │
│── 10 02 (Programming Session) ─────────►│
│◄─ 50 02 ────────────────────────────────│
│ │
│── 27 11 (Seed Request) ─────────────────►│
│◄─ 67 11 [seed] ─────────────────────────│
│── 27 12 [key] ─────────────────────────►│
│◄─ 67 12 ────────────────────────────────│
│ │
│── 34 00 44 [addr:4] [len:4] ────────────►│ RequestDownload
│◄─ 74 20 [maxBlockLen:2] ────────────────│ maxBlockSize
│ │
│── 36 01 [block data] ──────────────────►│ TransferData blk 1
│◄─ 76 01 ────────────────────────────────│
│── 36 02 [block data] ──────────────────►│ TransferData blk 2
│◄─ 76 02 ────────────────────────────────│
│ ... (tüm bloklar) ... │
│── 37 (RequestTransferExit) ────────────►│
│◄─ 77 ───────────────────────────────────│
│── 31 01 FF 01 (CheckProgrammingDep.) ──►│ Opsiyonel
│◄─ 71 01 FF 01 ──────────────────────────│
│── 11 01 (ECU Hard Reset) ──────────────►│
import udsoncan
from udsoncan.client import Client
def flash_firmware(client, firmware_path: str, flash_addr: int):
with open(firmware_path, 'rb') as f:
firmware = f.read()
# Programming session
client.change_session(
udsoncan.services.DiagnosticSessionControl.Session.programmingSession)
# Security access
seed = client.request_seed(0x11).service_data.seed
client.send_key(0x12, calculate_key(seed))
# RequestDownload: mem addr 4 byte, len 4 byte
mem_addr = udsoncan.MemoryLocation(
address=flash_addr,
memorysize=len(firmware),
address_format=32, memorysize_format=32)
result = client.request_download(
memory_location=mem_addr,
dfi=udsoncan.DataFormatIdentifier(0x00)) # no compression
max_block = result.service_data.max_length
print(f"Max blok boyutu: {max_block} byte")
# TransferData bloklar halinde
block_seq = 1
offset = 0
while offset < len(firmware):
chunk = firmware[offset:offset + max_block - 2]
client.transfer_data(block_seq, chunk)
offset += len(chunk)
block_seq = (block_seq % 0xFF) + 1
print(f" {offset}/{len(firmware)} byte aktarıldı")
# Transfer tamamla
client.request_transfer_exit()
print("Flash tamamlandı, ECU resetleniyor...")
# ECU reset
client.ecu_reset(udsoncan.services.ECUReset.ResetType.hardReset)
# Örnek kullanım:
# flash_firmware(client, 'firmware_v2.bin', 0x08000000)
CANdela Studio (Vector Informatik), araç OEM'lerinin UDS servis tanımlarını (.cdd dosyası) oluşturmak için kullandığı ticari araçtır. python-udsoncan, .cdd dosyalarından otomatik DID/DTC yapılarını yükleyebilir. Açık kaynak alternatif: diag-gui veya python-uds.