tags: DoIP, UDS, 0x27, Security Access, 0x31, Routine Control, Seed and Key, DevBJ
안녕하세요, DevBJ입니다! 😎
[👉 2편]에서 Wireshark로 패킷을 까보며 디버깅 전투력을 올리셨나요?
📌 [DoIP 마스터 가이드 시즌 2: 실전 심화편 목차]
[IT/자동차] - [DoIP 마스터 가이드 시즌2 - 1편] 파이썬(Python)으로 직접 짜보는 DoIP 파서와 TCP 프레이밍 구현
[IT/자동차] - [DoIP 마스터 가이드 시즌2 - 2편] Wireshark 실전! 패킷 덤프(PCAP)로 뜯어보는 DoIP 흐름과 디버깅
[IT/자동차] - [DoIP 마스터 가이드 시즌2 - 3편] UDS 딥다이브! 악명 높은 보안 접속(0x27)과 루틴 제어(0x31) 해부
[IT/자동차] - [DoIP 마스터 가이드 시즌2 - 4편] 실전 엣지 케이스! "왜 이 상황에서만 통신이 죽을까? (完)
그렇다면 이제 본격적인 UDS(Unified Diagnostic Services) 비즈니스 로직의 깊은 심연으로 들어갈 차례입니다.
단순히 정보만 읽어오는 0x22 (ReadDataByIdentifier) 서비스는 별로 어렵지 않습니다. 개발자들을 진짜 미치게 만드는 주범은 복잡한 상태 머신(State Machine)을 타야 하는 '보안 접속(0x27)'과 차량의 특정 동작을 제어하는 '루틴 제어(0x31)'입니다.
오늘은 이 두 녀석의 통신 시퀀스와 실무에서 밥 먹듯이 발생하는 삽질 포인트들을 아주 집요하게 파헤쳐 보겠습니다.
1. 통곡의 벽: Security Access (0x27)
ECU의 중요한 메모리를 쓰거나, 펌웨어를 올리기 전에는 반드시 이 보안 접속(Security Access)을 거쳐 권한을 획득해야 합니다. 이 과정은 일명 'Seed & Key (시드 앤 키)' 알고리즘이라는 핑퐁 구조로 진행됩니다.
📝 정상적인 0x27 통신 시퀀스 (Level 1 권한 획득 예시)
- 테스터:
27 01(나 Level 1 보안 해제할래! Seed 좀 줘!) - ECU:
67 01 [Seed 4 bytes](그래, 이 난수(Seed) 풀어봐!) - 테스터: (내부 암호화 알고리즘으로 Seed를 비비적거려 Key 생성)
- 테스터:
27 02 [Key 4 bytes](정답은 이거야! Key 받아라!) - ECU:
67 02(오 정답! 이제 너에게 권한을 주노라.)
구조는 간단해 보이지만, 실무에서는 이 짧은 핑퐁 사이에 온갖 지뢰가 숨어 있습니다.
🚨 실전 삽질 케이스 연구 (0x27)
- 지뢰 1: 타임아웃(Timeout) 초과에 의한 초기화
테스터가 ECU로부터 Seed를 받고 나서, PC 내부에서 암호화 알고리즘을 계산하느라 시간이 지체되었습니다. 그사이 ECU는 "얘 왜 답이 없어?" 하고 보안 타이머(Security Delay Timer)를 만료시키고 초기화해버립니다. 뒤늦게 테스터가 Key를 보내봤자 ECU는7F 27 24 (Request Sequence Error)를 뱉어버립니다.
[해결책]: Seed 수신부터 Key 송신까지의 흐름을 멈춤(Block) 없이 비동기나 독립 스레드로 신속하게 처리해야 합니다. - 지뢰 2: 눈치 없는 Tester Present (0x3E)의 난입
Seed를 받고 Key를 보내기 직전, 백그라운드 스레드에서 돌던 '세션 유지용 Tester Present(0x3E)' 패킷이 쏙 끼어들어갔습니다.
ECU 상태 머신 입장에서는 "0x27 요청 중인데 갑자기 0x3E? 시퀀스 위반!" 이라고 판단하고 쿨하게 세션을 리셋해버립니다. 역시나7F 27 24가 발생합니다.
[해결책]: 0x27 인증 트랜잭션이 진행되는 동안에는 백그라운드 Tester Present 송신을 일시 정지(Pause) 시키는 동기화(Mutex/Lock) 처리가 필수입니다. - 지뢰 3: 진짜 알고리즘이 틀렸을 때
7F 27 35 (Invalid Key)가 떨어졌다면, 정말 내가 만든 암호화 로직(RSA, AES 등 OEM마다 다름)이나 바이트 오더(Endian)가 틀린 것입니다. OEM에서 제공한 DLL이나 C 코드를 래핑(Wrapping)해서 쓸 때 포인터나 타입 변환이 잘못되지 않았는지 확인하세요.
2. 마음대로 켰다 껐다: Routine Control (0x31)
차량의 와이퍼를 강제로 돌리거나, 자체 테스트(Self-test)를 수행하거나, 특정 메모리를 지울(Erase) 때 사용하는 것이 바로 0x31입니다.
루틴 제어는 단순히 요청 한 번으로 끝나는 게 아니라 시작(Start) -> 정지(Stop) -> 결과 요청(Request Results)의 서브 펑션(Sub-function)으로 쪼개져 있습니다.
📝 0x31 통신 시퀀스 (루틴 ID 0xDF01 - 특정 센서 초기화 예시)
- 시작:
31 01 DF 01(01: Start) - 응답:
71 01 DF 01 [루틴 상태 정보](시작했음!) - (...작업 대기 또는 정지 요청...)
- 결과 요청:
31 03 DF 01(03: Request Results) - 응답:
71 03 DF 01 [최종 결과 데이터](완료됐고 결과는 이거임!)
🚨 실전 삽질 케이스 연구 (0x31)
- 지뢰 1: 사전 조건 미달 (Conditions Not Correct)
주행 중에 와이퍼 강제 제어 루틴을 시작(31 01)하려 하면, 안전상의 이유로 ECU가 가차 없이7F 31 22 (Conditions Not Correct)를 던집니다. 루틴 제어는 차량의 속도, 기어 상태, 시동 상태 등에 극도로 민감합니다. 차량의 물리적 상태(Pre-condition)가 스펙에 맞는지 먼저 확인하세요. - 지뢰 2: 옵션 레코드(Option Record) 누락
어떤 루틴은 시작할 때 부가적인 파라미터(예: 얼마나 오래 동작할지, 어떤 채널을 쓸지)를 뒤에 붙여줘야 합니다.31 01 DF 01 [파라미터 바이트]구조인데, 이 파라미터를 빼먹고 던지면 ECU는 길이가 안 맞는다며7F 31 13 (Incorrect Message Length)을 뱉습니다. 스펙 문서의 Routine Control Data 구조를 꼼꼼히 봐야 합니다.
📝 3편 마무리
UDS의 0x27과 0x31은 단순히 메시지를 주고받는 행위를 넘어, 차량 ECU 내부의 '상태 머신(State Machine)'을 테스터와 동기화시키는 고도의 작업입니다. 여기서 발생하는 에러들은 통신 불량이 아니라 철저한 논리적 위반인 경우가 많으므로, 디버깅할 때 로직의 순서와 타이밍을 최우선으로 살펴봐야 합니다.
자, 이제 UDS의 핵심 로직까지 마스터했습니다.
마지막 대미를 장식할 4편에서는, 개발자들을 그야말로 멘붕의 늪으로 빠뜨리는 기상천외한 네트워크 엣지 케이스(Edge Cases)들을 다뤄보겠습니다. "대체 이 상황에선 왜 통신이 죽는 거야?"라는 질문에 명쾌한 답을 드리겠습니다.
👉 [다음 글 보기: 시즌2 - 4편. 실전 엣지 케이스! "왜 이 상황에서만 통신이 죽을까?"]
로직 구현하시다 막히시면 언제든 SOS 쳐주세요! DevBJ였습니다.
'IT > 자동차' 카테고리의 다른 글
| DoIP 차량 진단 프로토콜 총정리 (1) | 2026.08.24 |
|---|---|
| [DoIP 마스터 가이드 시즌2 - 4편] 실전 엣지 케이스! "왜 이 상황에서만 통신이 죽을까? (完) (0) | 2026.08.19 |
| [DoIP 마스터 가이드 시즌2 - 2편] Wireshark 실전! 패킷 덤프(PCAP)로 뜯어보는 DoIP 흐름과 디버깅 (0) | 2026.08.19 |
| [DoIP 마스터 가이드 시즌2 - 1편] 파이썬(Python)으로 직접 짜보는 DoIP 파서와 TCP 프레이밍 구현 (0) | 2026.08.19 |
| [DoIP 마스터 가이드 5편] 실무 끝판왕! 펌웨어 전송과 다중 ECU 게이트웨이 라우팅 완벽 해부 (完) (0) | 2026.08.18 |
