[Перевод] Чтение и запись физической памяти по протоколу UDS

В данном тексте я произвел перевод UDS стандарта ISO-14229 с английского на русский. А конкретнее ту его часть, которая рассказывает про то как читать и писать память в микроконтроллере.
UDS протокол позволяет читать и писать физическую память на микроконтроллере. Для этого в протокол заложены такие пакеты

Название пакета

SID

Аргументы

PosPesp

RequestUpload

0x35

2

0x75

TransferData

0x36

2

0x76

RequestTransferExit

0x37

0

0x77

RequestDownload

0x34

2

0x74

В тексте я расскажу как пользоваться этими пакетами.

Постановка задачи
Разработать программный механизм, который позволит программе UDS клиента читать и писать физическую память на UDS сервере.

Определение


Массив
- непрерывная последовательность байт. Массив обладает размером. Структура - набор переменных разного типа данных Пакет - бинарная структура в массиве
Физическая память (ОЗУ, RAM) — это реальное аппаратное хранилище компьютера, куда загружаются и временно хранятся программы и данные, которые процессор использует прямо сейчас. Это оперативное запоминающее устройство (ОЗУ), которое позволяет быстро выполнять задачи, но его объем ограничен и требует управления для изоляции процессов и предотвращения конфликтов.
Upload - процесс переноса данных от ECU в LapTop. По сути upload это чтение.
DownLoad - процесс переноса данных от LapTop в ECU. DownLoad это запись данных в ECU.
UDS cервис - это просто входной пакет, на который спецификацией определён ответный пакет.
Сервер - это ECU. ECU - электронная плата с микроконтроллером, которая решает какую-либо задачу внутри автомобиля. Это может быть плата управления автоматической коробкой переключения передач (АКПП), контроллер бензиновых (или газовых) форсунок, телематический блок, плата управления батареей BMS, плата управления подвеской, плата электро-усилителя руля (Electric Power Steering ), плата управления обогревом и конидционером, плата управления тяговым инвертором, сигнализация, плата управления парковочной видеокамерой заднего вида, плата управления подушкой безопасности, приборная панель, плата управления АБС, плата управления ESP, кузовная электроника (габаритные огни), парковочный радар, радар круиз контроля, мультимедийная система (infotainment ), плата управления подогревом сидений. Всё что угодно.
Клиент - LapTop. Тестировочное оборудование с программой UDS-клиента внутри. Мастер UDS сети.
Сессия - это процесс, деятельность, сеанс, совещание, заседание. Сессии имеют свойство повторяются. Сессии всегда ограничены во времени.
Seed - номер пароля или своеобразный логин, выраженный в виде натурального числа.
Key - своеобразный пароль выраженный в виде натурального числа.

Реализация

В протоколе UDS нельзя просто так взять и прочитать память. UDS протокол обладает много эшелонированной защитой от не санкционных действий извне. Неправильная загрузка программ или данных на ECU потенциально может привести к повреждению электроники или других компонентов транспортного средства, а также поставить под угрозу соответствие транспортного средства стандартам безопасности или защиты. Поэтому операции с памятью проворачиваются в три этапа.

Фаза 1: Переключиться в диагностическую сессию programmming

Перед тем как начать пользоваться пакетами чтения памяти надо переключить сессию. С default на prog. Иначе прилетит негативный код serviceNotSupportedInActiveSession. Делается это пакетом DiagnosticSessionControl.

На стороне клиента в логе это выглядит так

0.606,128,N,[UdsClient],UDS_CLIENT1,DiagnosticSessionControl:0x2=Prog

0.608,129,D,[UdsServerLL],LapTop->ECU,1002

0.614,130,D,[UdsServerLL],ECU->LapTop:500209C401F4

0.664,131,N,[UdsClient],DiagSessionCtrlPosResp:[,SID:0x50,

                                                 SessionType:2=Prog,

                                                 [,P2ServerMax:2500 ms,

                                                   P2*ServerMax:5000 ms]]

Фаза 2: Получить права доступа

Затем протокол UDS ограничивает доступ к сервисам не только сессиями, но и своеобразными паролями. Иначе прилетит негативный код securityAccessDenied. Для ввода пароля существует отдельные пакеты группы SecurityAccess (0x27).

В логе это выглядит так

1.619,133,D,[UdsServerLL],LapTop->ECU,2701

1.624,134,D,[UdsServerLL],ECU->LapTop:6701FFFE4A27

1.674,135,N,[UdsClient],Seed:0xFFFE4A27

1.724,136,N,[UdsClient],UDS_CLIENT1,SecurityAccess,SendKey:0xFEEDFACE

  

1.726,137,D,[UdsServerLL],LapTop->ECU,2702FEEDFACE

1.728,138,D,[UdsServerLL],ECU->LapTop:6702

1.774,139,I,[UdsClient],UnLocked!

Сервис SecurityAccess позволяет клиенту разблокировать функции/сервисы с ограниченным доступом. Цель данного пакета — предоставить средства доступа к данным и/или диагностическим услугам, доступ к которым ограничен по соображениям безопасности или защиты. Диагностические услуги, включающие загрузку/выгрузку программ или данных на сервер и чтение определенных ячеек памяти с сервера, относятся к ситуациям, когда может потребоваться доступ с целью обеспечения безопасности. Концепция безопасности использует связь между начальным значением и ключом.

Последовательность разблокировки:

1–Клиент запрашивает у ECU начальное значение (seed). По сути это номер пароля.
2–ECU отвечает положительным ответом, содержащим случайно сгенерированное значение начального значения (seed).
3–И клиент, и ECU вычисляют значение ключа на основе этого начального значения (используя секретный алгоритм).
4–Клиент отправляет вычисленный ключ на ECU .
5–ECU проверяет подлинность клиента, сравнивая полученный ключ со своим собственным вычисленным ключом. Если они совпадают, клиенту предоставляется доступ к защищенной функциональности соответствующего уровня безопасности.

Значение параметра подфункции 'requestSeed' всегда должно быть нечетным числом, а соответствующее значение параметра подфункции 'sendKey' для того же уровня безопасности должно равняться значению параметра подфункции 'requestSeed' плюс один.

В любой момент времени может быть активен только один уровень безопасности. Например, если активен уровень безопасности, связанный с requestSeed 0x03, и запрос тестировщика успешно разблокирует уровень безопасности, связанный с requestSeed 0x01, то в этот момент будет разблокирована только защищенная функциональность, поддерживаемая уровнем безопасности, связанным с requestSeed 0x01. Любая дополнительная защищенная функциональность, которая ранее была разблокирована уровнем безопасности, связанным с requestSeed 0x03, больше не будет активна. Нумерация уровней безопасности является произвольной и не подразумевает какой-либо связи между уровнями.

Клиент должен запросить у ECU «разблокировку», отправив сообщение SecurityAccess «requestSeed». ECU должен ответить, отправив «начальное значение» с помощью сообщения SecurityAccess «requestSeed» (положительный ответ). Затем клиент должен ответить, отправив ECU номер «ключа» с помощью соответствующего сообщения SecurityAccess «sendKey». ECU должен сравнить этот «ключ» с одним из тех, что хранятся/вычисляются внутри системы. Если два числа совпадают, ECU должен разрешить («разблокировать») доступ клиента к определенным службам/данным и указать это с помощью сообщения SecurityAccess «sendKey» (положительный ответ).

Если присланный ключ и вычисленный локально ключ не совпадают, это считается ложной попыткой доступа. Недействительный ключ требует от клиента начать все сначала с помощью сообщения SecurityAccess «requestSeed» (см. Приложение I для получения дополнительной информации о деталях обработки доступа к безопасности).

Если ECU поддерживает безопасность, но запрошенный уровень безопасности уже разблокирован при получении сообщения SecurityAccess «requestSeed», этот ECU должен ответить положительным сообщением SecurityAccess «requestSeed» со значением начального числа, равным нулю (0). ECU никогда не должен отправлять начальное число, равное нулю, для заданного уровня безопасности, который в данный момент заблокирован. Клиент должен использовать этот метод для определения того, заблокирован ли ECU для определенного уровня безопасности, проверяя наличие ненулевого начального числа.

Для корректного ответа на сообщение «requestSeed» службы SecurityAccess от клиента после включения/перезагрузки ECU и после определенного количества ложных попыток доступа (см. подробное описание ниже) может потребоваться задержка, специфичная для производителя транспортного средства. Если этот таймер задержки поддерживается, то должна активироваться задержка после достижения указанного производителем транспортного средства количества ложных попыток доступа или когда сервер включается/перезагружается, и ранее выполненная служба SecurityAccess завершилась с ошибкой из-за одной ложной попытки доступа. В случае, если ECU поддерживает этот таймер задержки, то после успешного выполнения службы SecurityAccess «sendKey» внутренняя информация о таймере задержки, вызванная ECU при включении/перезагрузке, должна быть удалена ECU . В случае, если ECU поддерживает этот таймер задержки и не может определить, завершилась ли с ошибкой ранее выполненная служба SecurityAccess до включения/перезагрузки, то таймер задержки должен оставаться активным после включения/перезагрузки. Задержка необходима только в том случае, если ECU заблокирован при включении/перезагрузке. Производитель транспортного средства должен выбрать, поддерживается ли таймер задержки.

Фаза 3: Чтение физической памяти

Направление передачи данных определяется сервисом RequestUpload. В ответ на пакет RequestUpload микроконтроллер высылает maxNumberOfBlockLength. Это максимальная длина в байтах сообщения TransferData, которую способен обработать этот микроконтроллер. Этот параметр позволяет клиенту адаптироваться к размеру буфера отправки сервера до того, как сервер начнет передачу данных клиенту.

Сервис TransferData (0x36) используется клиентом для передачи данных либо от клиента к серверу (загрузка), либо от сервера к клиенту (выгрузка).

Если клиент инициировал RequestUpload, данные включаются в параметр(ы) transferResponseParameter в сообщении(ях) ответа TransferData.

Запрос к сервису TransferData включает blockSequenceCounter для улучшения обработки ошибок в случае сбоя сервиса TransferData во время последовательности нескольких запросов TransferData. blockSequenceCounter сервера должен быть инициализирован единицей при получении сообщения запроса RequestDownload (0x34) или RequestUpload (0x35). Это означает, что первое сообщение запроса TransferData (0x36), следующее за сообщением запроса RequestDownload (0x34) или RequestUpload (0x35), начинается с параметра blockSequenceCounter, равного единице.

Вот так выглядит UDS Upload лог со стороны UDS клиента. Тут происходит чтение 64 байт по адресу 0x004821a0.

1.858,I,[UdsClient],UdsClient1,RequestUpload,Addr:0x004821a0,Size:64 Byte

1.861,D,[UdsClient],ComposeTxFrame,RequestUpload,request,Addr:0x004821a0,Size:64 Byte

1.864,D,[UdsClient],SendDataTo:[UdsServer1],Data:[35 00 24 004821A0 0040]

1.869,D,[UdsServerLL],LapTop->ECU,size:9,data:350024004821A00040

1.884,N,[UdsServerTranf],Address:0x004821a0,size:64 Byte

1.889,D,[UdsServerLL],ECU->LapTop:size:4,data:75200082

1.931,D,[UdsClient],UDS_CLIENT_1,ExpSid:0x75,RxData:75200082

1.971,N,[UdsClient],RequestUploadPosResp:0x,SID:0x75,ParamLen:{,ParamSize:2 Byte,Res:0},Param:0082,MaxNumOfBlkLen:130 Byte

1.978,N,[UdsClient],max_number_of_block_length:130 byte

1.984,N,[UdsClient],UDS_CLIENT1,TransferData,AskData,blockSequenceCounter:1

1.991,D,[UdsClient],ComposeTxFrame,TransferData,request,blockSequenceCounter:1

2.012,D,[UdsServerLL],LapTop->ECU,size:2,data:3601

2.089,D,[UdsServerLL],ECU->LapTop:size:66,data:7601000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F

2.136,D,[UdsClient],RxDataChank,size:64,data:000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F

2.151,I,[UdsClient],DataRx,Done,size:64,data:000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F

2.194,D,[UdsServerLL],LapTop->ECU,size:1,data:37

2.212,D,[UdsServerLL],ECU->LapTop:size:1,data:77

2.237,D,[UdsClient],DataMoveDone!

Фаза 4: Запись физической памяти

Аналогично можно и читать физическую память. Отличается только тем что диалог начинается с пакета RequestDownload, а пакеты TransferData в запросах инкапсулируют массивы с данными.

В логе клиента это выглядит так.

1.711,+1,193,I,[UdsClient],UDS_CLIENT1,DownLoad,Address:0x004821A0,size:16,data:[ABCDEF98765432108855556677889933]

1.715,+4,194,D,[UdsClient],ComposeTxFrame,RequestDownload,request,Addr:0x004821a0,Size:16 Byte

1.723,+2,197,D,[UdsServerLL],LapTop->ECU,size:9,data:34 00 24 004821A0 0010

1.738,+2,202,N,[UdsServerTranf],Address:0x004821a0,size:16 Byte

1.743,+2,204,D,[UdsServerLL],ECU->LapTop:size:4,data:74200082

1.789,+3,206,D,[UdsClient],UDS_CLIENT_1,ExpSid:0x74,RxData:74200082

1.793,+4,207,D,[UdsClient],UDS_CLIENT_1,RxPositiveResponse:[RequestDownload]

1.801,+8,208,N,[UdsClient],RequestPosRespDown:[,SID:0x74,ParamLen:{,ParamSize:2 Byte,Res:0},Param:0082,MaxNumOfBlkLen:130 Byte]

1.810,+9,209,N,[UdsClient],tx_max_number_of_block_length:130 byte

1.836,+21,211,N,[UdsClient],Client1,TransferData,TxData,blockSequenceCounter:1

1.843,+7,212,D,[UdsClient],ComposeTxFrame,TransferData,request,blockSequenceCounter:1,size:16,data:ABCDEF98765432108855556677889933

1.865,+6,215,D,[UdsServerLL],LapTop->ECU,size:18,data:36 01 ABCDEF98765432108855556677889933

1.880,+3,218,D,[UdsServerTranf][loadStatus:LD_IDLE,Dir:DOWNLOAD,memStartAddress:0x004821a0,memSize:16,DatCRC32:0xb2aa7578,memIndex:0,requestTime:0,memIndexPrev:0,blockSeqCntr:1,blockSeqCntrPrev:1,len:0,isMemOpOk:0,data:,crc32:0xB2AA7578,,Size:512,]

1.903,+4,220,N,[UdsServerTranf],DOWNLOAD,Len:16

1.916,+13,221,D,[UdsServerTranf] [loadStatus:LD_DONE,Dir:DOWNLOAD,memStartAddress:0x004821a0,

                                  memSize:16,DatCRC32:0x315ef3a4,memIndex:0,

                                  requestTime:1899,memIndexPrev:0,blockSeqCntr:1,

                                  blockSeqCntrPrev:1,len:16,

                                  isMemOpOk:1,

                                  data:crc32:0x315EF3A4,,Size:512]

1.949,+4,223,D,[UdsServerLL],ECU->LapTop:size:2,data:7601

1.959,+5,225,D,[UdsClient],UDS_CLIENT_1,ExpSid:0x76,RxData:7601

1.965,+6,226,D,[UdsClient],UDS_CLIENT_1,RxPositiveResponse:[TransferData]

1.980,+4,229,I,[UdsClient],DataTx,Done,size:16,data:ABCDEF98765432108855556677889933

2.004,+17,231,N,[UdsClient],UDS_CLIENT1,RequestTransferExit

2.013,+5,233,D,[UdsClient],SendDataTo:[UdsServer1],Data:[37]

2.022,+4,235,D,[UdsServerLL],LapTop->ECU,size:1,data:37

2.041,+5,239,D,[UdsServerLL],ECU->LapTop:size:1,data:77

Итог

Удалось научиться настраивать сессию, проходить проверку безопасности чтобы читать и писать память по UDS протоколу.

UDS больше похож не на протокол, а на набор протоколов. Для каждого сервиса предусмотрена своя уникальная бинарная структура пакета, как для запроса, так и для ответа.

Ссылки


Вопросы
--Зачем в UDS протоколе потребовался отдельный сложных механизм TransferData, если уже были простые атомарные пакеты ReadMemoryByAddress ( 0x23) и WriteMemoryByAddress (0x3D) ?

--Какие еще прикладные протоколы могут быть в автомобиле кроме UDS?

--Зачем в 2006 понадобился автомобильный протокол UDS, если тремя годами ранее уже в 2003 был автомобильный протокол XCP? И UDS и XCP могут читать и писать произвольную память, оба могут обновлять прошивку.

--Чем протокол UDS отличается от протокола OBD-II?

--Существует ли какая-нибудь готовая клиентская Windows утилита (c GUI или консольный вариант), которая опрашивает ECU по CAN через UDS протокол? Чтобы прочитать стандартные в UDS DID параметры. Такие как VIN номер, серийный номер ECU, дату производства ECU, дату программирования ECU, название производителя ECU и прочее.

--Можно ли по стандарту протокола UDS начать читать и писать память одновременно в полном дуплексе? То есть сразу отправить RequestDownload и RequestUpLoad. 

Информация на этой странице взята из источника: https://habr.com/ru/articles/992542/