Реверс протокола СКУД RS485 от Perco. Берегите линии своих СКУД от вторжения

от автора

Участвуя последнее время в разных интересных проектах, возникла задачка альтернативного управления продуктом Perco Электронная проходная KT02.3. Данный продукт является законченным решением и не подразумевает использование в составе других систем СКУД, а также какого-либо вторжения в свою среду управления. Но как говорится в поговорке
«Возможно все! На невозможное просто требуется больше времени» (С) Дэн Браун

Как и что из этой затеи получилось, читайте под катом

Основное описание системы можно прочитать вот из этого документа.

Остановимся на внешних интерфейсах системы:

Поддерживает подключение по интерфейсу RS-485 следующих устройств:
• до 8-ми контроллеров замка PERCo-CL201 (контроллер CL201 имеет встроенный считыватель и обеспечивает управление одним замком);
• табло системного времени PERCo-AU05
• картоприемник PERCo-IC02.1 (схему подключения см. в описании PERCo-IC02.)

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

Интерфейс связи с ПК и другими контроллерами системы S-20 – Ethernet (обеспечивается
поддержка стека протоколов TCP/IP (ARP, IP, ICMP, TCP, UDP, DHCP)).

Управляется через своё приложение, доступное для скачивания с сайта. Так же имеет очень хитрый SDK, закрываемый NDA.

«Поставка SDK предусматривает подписание с заинтересованной стороной соглашения о неразглашении конфиденциальной информации и осуществляется бесплатно.»

Так как ни схемы турникета ни доступа к SDK у меня не было, вооружившись паяльником и трансивером RS485 мы начали изучать как устроен протокол.

Схема подключения внешних устройств довольно тривиальна.
image
Можно подключить:

  • РУ — радиопульт
  • ПДУ — пульт дистанционного управления
  • ДКЗП — Датчик контроля зоны прохода
  • Сирену
  • до 8 замков PERCo-CL201
  • табло системного времени PERCo-AU05

Никаких других устройств подключить нельзя.
Протокол Perco является закрытым, но есть описание часов PERCo-AU05, которое легко гуглится в сети.

image

Данная картинка из описания это единственное упоминание протокола PERCo найденное в сети.
Отлично. Кусок протокола есть, значит можно посмотреть, что там бегает. Подключаем RS485 к турникету и смотрим.

Первичный дамп

14:04:08 :: [‘0xaa’, ‘0x05’, ‘0x8c’, ‘0x04’, ‘0x01’, ‘0x01’, ‘0x98’, ‘0xfe’]
14:04:08 :: [‘0xaa’, ‘0x25’, ‘0x8c’, ‘0x04’, ‘0x01’, ‘0x01’, ‘0x19’, ‘0x39’]
14:04:08 :: [‘0xaa’, ‘0x45’, ‘0x8c’, ‘0x04’, ‘0x01’, ‘0x01’, ‘0x99’, ‘0x31’]
14:04:09 :: [‘0xaa’, ‘0x65’, ‘0x8c’, ‘0x04’, ‘0x01’, ‘0x01’, ‘0x18’, ‘0xf6’]
14:04:09 :: [‘0xaa’, ‘0x85’, ‘0x8c’, ‘0x04’, ‘0x01’, ‘0x01’, ‘0x99’, ‘0x20’]
14:04:09 :: [‘0xaa’, ‘0xa5’, ‘0x8c’, ‘0x04’, ‘0x01’, ‘0x01’, ‘0x18’, ‘0xe7’]
14:04:09 :: [‘0xaa’, ‘0xc5’, ‘0x8c’, ‘0x04’, ‘0x01’, ‘0x01’, ‘0x98’, ‘0xef’]
14:04:10 :: [‘0xaa’, ‘0xe5’, ‘0x8c’, ‘0x04’, ‘0x01’, ‘0x01’, ‘0x19’, ‘0x28’]
14:04:10 :: [‘0xaa’, ‘0x05’, ‘0x1a’, ‘0xff’, ‘0xa4’, ‘0xde’]
14:04:10 :: [‘0xaa’, ‘0x25’, ‘0x1a’, ‘0xff’, ‘0xa5’, ‘0x14’]
14:04:10 :: [‘0xaa’, ‘0x45’, ‘0x1a’, ‘0xff’, ‘0xa5’, ‘0x0a’]
14:04:10 :: [‘0xaa’, ‘0x65’, ‘0x1a’, ‘0xff’, ‘0xa4’, ‘0xc0’]
14:04:10 :: [‘0xaa’, ‘0x85’, ‘0x1a’, ‘0xff’, ‘0xa5’, ‘0x36’]
14:04:11 :: [‘0xaa’, ‘0xa5’, ‘0x1a’, ‘0xff’, ‘0xa4’, ‘0xfc’]
14:04:11 :: [‘0xaa’, ‘0xc5’, ‘0x1a’, ‘0xff’, ‘0xa4’, ‘0xe2’]
14:04:11 :: [‘0xaa’, ‘0xe5’, ‘0x1a’, ‘0xff’, ‘0xa5’, ‘0x28’]
14:04:11 :: [‘0xaa’, ‘0x01’, ‘0x1a’, ‘0xff’, ‘0xe5’, ‘0x1f’, ‘0x7f’, ‘0xa4’]
14:04:11 :: [‘0xaa’, ‘0x01’, ‘0x48’, ‘0x04’, ‘0xff’, ‘0x00’, ‘0xff’, ‘0x6f’, ‘0x60’, ‘0xfe’, ‘0x59’]
14:04:11 :: [‘0xaa’, ‘0x01’, ‘0xa8’, ‘0x07’, ‘0x01’, ‘0x01’, ‘0xff’, ‘0x01’, ‘0x01’, ‘0xff’, ‘0x01’, ‘0x01’, ‘0xff’, ‘0x44’, ‘0xc2’, ‘0xff’, ‘0xd1’]
14:04:11 :: [‘0xaa’, ‘0x21’, ‘0x1a’, ‘0xff’, ‘0xe4’, ‘0xd5’, ‘0x66’, ‘0x64’]
14:04:11 :: [‘0xaa’, ‘0x21’, ‘0x48’, ‘0x04’, ‘0xff’, ‘0x00’, ‘0xff’, ‘0x68’, ‘0x00’, ‘0xe7’, ‘0x99’]
14:04:11 :: [‘0xaa’, ‘0x21’, ‘0xa8’, ‘0x07’, ‘0x01’, ‘0x01’, ‘0xff’, ‘0x01’, ‘0x01’, ‘0xff’, ‘0x01’, ‘0x01’, ‘0xff’, ‘0xc5’, ‘0x7d’, ‘0xe6’, ‘0x11’]
14:04:11 :: [‘0xaa’, ‘0x02’, ‘0x1a’, ‘0xff’, ‘0x15’, ‘0x1f’]
14:04:11 :: [‘0xaa’, ‘0x22’, ‘0x1a’, ‘0xff’, ‘0x14’, ‘0xd5’]
14:04:11 :: [‘0xaa’, ‘0x04’, ‘0x38’, ‘0x34’, ‘0x02’, ‘0x11’, ‘0x83’, ‘0xfd’]
14:04:11 :: [‘0xaa’, ‘0x01’, ‘0x1b’, ‘0x0f’, ‘0xe4’, ‘0xcb’, ‘0xbe’, ‘0x64’]
14:04:11 :: [‘0xaa’, ‘0x01’, ‘0x48’, ‘0x02’, ‘0x00’, ‘0xff’, ‘0xff’, ‘0x1e’, ‘0x28’, ‘0xfe’, ‘0x59’]
14:04:11 :: [‘0xaa’, ‘0x21’, ‘0x1b’, ‘0x0f’, ‘0xe5’, ‘0x01’, ‘0xa7’, ‘0xa4’]
14:04:11 :: [‘0xaa’, ‘0x21’, ‘0x48’, ‘0x02’, ‘0x00’, ‘0xff’, ‘0xff’, ‘0x19’, ‘0x48’, ‘0xe7’, ‘0x99’]
14:04:11 :: [‘0xaa’, ‘0x04’, ‘0x38’, ‘0x34’, ‘0x02’, ‘0x11’, ‘0x83’, ‘0xfd’]
14:04:11 :: [‘0xaa’, ‘0x04’, ‘0x38’, ‘0x34’, ‘0x02’, ‘0x11’, ‘0x83’, ‘0xfd’]
14:04:11 :: [‘0xaa’, ‘0x04’, ‘0x38’, ‘0x34’, ‘0x02’, ‘0x11’, ‘0x83’, ‘0xfd’]
14:04:11 :: [‘0xaa’, ‘0x04’, ‘0x38’, ‘0x34’, ‘0x02’, ‘0x11’, ‘0x83’, ‘0xfd’]
14:04:11 :: [‘0xaa’, ‘0x05’, ‘0x04’, ‘0x00’]
14:04:11 :: [‘0xaa’, ‘0x01’, ‘0x01’, ‘0x0e’, ‘0x10’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x88’, ‘0x22’, ‘0xf5’]
14:04:11 :: [‘0xaa’, ‘0x01’, ‘0x09’, ‘0x3e’, ‘0x69’, ‘0x3e’, ‘0x69’]
14:04:11 :: [‘0xaa’, ‘0x01’, ‘0x05’, ‘0x16’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0xf2’, ‘0x7a’]
14:04:12 :: [‘0xaa’, ‘0x01’, ‘0x1a’, ‘0xff’, ‘0xe5’, ‘0x1f’, ‘0x7f’, ‘0xa4’]
14:04:12 :: [‘0xaa’, ‘0x01’, ‘0x48’, ‘0x02’, ‘0x00’, ‘0xff’, ‘0xff’, ‘0x1e’, ‘0x28’, ‘0xfe’, ‘0x59’]
14:04:12 :: [‘0xaa’, ‘0x21’, ‘0x01’, ‘0x07’, ‘0x10’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0xfb’, ‘0x65’]
14:04:12 :: [‘0xaa’, ‘0x21’, ‘0x09’, ‘0x27’, ‘0xa9’, ‘0x27’, ‘0xa9’]
14:04:12 :: [‘0xaa’, ‘0x21’, ‘0x05’, ‘0x4e’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0xf1’, ‘0x6e’]
14:04:12 :: [‘0xaa’, ‘0x21’, ‘0x1a’, ‘0xff’, ‘0xe4’, ‘0xd5’, ‘0x66’, ‘0x64’]
14:04:12 :: [‘0xaa’, ‘0x21’, ‘0x48’, ‘0x02’, ‘0x00’, ‘0xff’, ‘0xff’, ‘0x19’, ‘0x48’, ‘0xe7’, ‘0x99’]
14:04:12 :: [‘0xaa’, ‘0x01’, ‘0x01’, ‘0x4f’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x34’, ‘0x24’]
14:04:12 :: [‘0xaa’, ‘0x01’, ‘0x05’, ‘0x40’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x11’, ‘0x24’]
14:04:12 :: [‘0xaa’, ‘0x21’, ‘0x01’, ‘0x2e’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0xe7’, ‘0xe0’]
14:04:12 :: [‘0xaa’, ‘0x21’, ‘0x05’, ‘0x2d’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x02’, ‘0xdf’]

В принципе вообще непонятно, как это работает. Кто отправитель, кто получатель? Где что, чем терминируется? Просто поток каких-то бинарных данных данных.
Провозившись со снятыми дампами несколько дней, я понял, что «дьявол сидит в деталях».
Получилась следующая структура байт внутри пакетов для считывателей:

  • 1. 0xAA — код начала команды
  • 2. 0x[02][12] — идентификатор считывателя
  • 3. 0x0[15] — код команды
  • какие-то данные
  • контрольная сумма CRC16

Что же дальше?
Кто это отправляет? Что из этого ответ?
Оказалось, что всё намного хитрее, чем мы привыкли видеть в сессионных протоколах.
Для понимания этого пришлось подключить считыватели и контроллер в разрыв через два конвертора RS485.
Так вот, на самом деле пакет состоит из двух частей. Первая часть — это команда контроллера, всегда начинающаяся с 0xAA, вторая часть — это ответ устройства к которому относилась команда. Данный ответ имеет переменную длину и заканчивается контрольной суммой всего пакета.
В реальности сессия «контроллер-считыватель» выглядит вот так:

разделенный дамп сессии

cntrler:[‘0xaa’, ‘0x01’, ‘0x1a’, ‘0xff’, ‘0xe5’, ‘0x1f’]
readers:[‘0x7f’, ‘0xa4’]
cntrler:[‘0xaa’, ‘0x01’, ‘0x48’, ‘0x04’, ‘0xff’, ‘0x00’, ‘0xff’, ‘0x6f’, ‘0x60’]
readers:[‘0xfe’, ‘0x59’]
cntrler:[‘0xaa’, ‘0x01’, ‘0xa8’, ‘0x07’, ‘0x01’, ‘0x01’, ‘0xff’, ‘0x01’, ‘0x01’, ‘0xff’, ‘0x01’, ‘0x01’, ‘0xff’, ‘0x44’, ‘0xc2’]
readers:[‘0xff’, ‘0xd1’]
cntrler:[‘0xaa’, ‘0x21’, ‘0x1a’, ‘0xff’, ‘0xe4’, ‘0xd5’]
readers:[‘0x66’]
readers:[‘0x64’]
cntrler:[‘0xaa’, ‘0x21’, ‘0x48’, ‘0x04’, ‘0xff’, ‘0x00’, ‘0xff’, ‘0x68’, ‘0x00’]
readers:[‘0xe7’]
readers:[‘0x99’]
cntrler:[‘0xaa’, ‘0x21’, ‘0xa8’, ‘0x07’, ‘0x01’, ‘0x01’, ‘0xff’, ‘0x01’, ‘0x01’, ‘0xff’, ‘0x01’, ‘0x01’, ‘0xff’, ‘0xc5’, ‘0x7d’]
readers:[‘0xe6’, ‘0x11’]
cntrler:[‘0xaa’, ‘0x02’, ‘0x1a’, ‘0xff’, ‘0x15’, ‘0x1f’]
cntrler:[‘0xaa’, ‘0x02’, ‘0x1a’, ‘0xff’, ‘0x15’, ‘0x1f’]
cntrler:[‘0xaa’, ‘0x22’, ‘0x1a’, ‘0xff’, ‘0x14’, ‘0xd5’]
cntrler:[‘0xaa’, ‘0x22’, ‘0x1a’, ‘0xff’, ‘0x14’, ‘0xd5’]
cntrler:[‘0xaa’, ‘0x04’, ‘0x38’, ‘0x37’, ‘0x2e’, ‘0x11’, ‘0x6f’, ‘0x3d’]
cntrler:[‘0xaa’, ‘0x01’, ‘0x1b’, ‘0x0f’, ‘0xe4’, ‘0xcb’]
readers:[‘0xbe’]
readers:[‘0x64’]
cntrler:[‘0xaa’, ‘0x01’, ‘0x48’, ‘0x02’, ‘0x00’, ‘0xff’, ‘0xff’, ‘0x1e’, ‘0x28’]
readers:[‘0xfe’, ‘0x59’]
cntrler:[‘0xaa’, ‘0x21’, ‘0x1b’, ‘0x0f’, ‘0xe5’, ‘0x01’]
readers:[‘0xa7’, ‘0xa4’]
cntrler:[‘0xaa’, ‘0x21’, ‘0x48’, ‘0x02’, ‘0x00’, ‘0xff’, ‘0xff’, ‘0x19’, ‘0x48’]
readers:[‘0xe7’]
readers:[‘0x99’]
cntrler:[‘0xaa’, ‘0x04’, ‘0x38’, ‘0x37’, ‘0x2e’, ‘0x11’, ‘0x6f’, ‘0x3d’]
cntrler:[‘0xaa’, ‘0x04’, ‘0x38’, ‘0x37’, ‘0x2e’, ‘0x11’, ‘0x6f’, ‘0x3d’]
cntrler:[‘0xaa’, ‘0x04’, ‘0x38’, ‘0x37’, ‘0x2e’, ‘0x11’, ‘0x6f’, ‘0x3d’]
cntrler:[‘0xaa’, ‘0x04’, ‘0x38’, ‘0x37’, ‘0x2e’, ‘0x11’, ‘0x6f’, ‘0x3d’]
cntrler:[‘0xaa’, ‘0x05’, ‘0x04’, ‘0x00’]
cntrler:[‘0xaa’, ‘0x01’, ‘0x01’]
readers:[‘0x0f’, ‘0x10’, ‘0x00’]
readers:[‘0x00’, ‘0x00’, ‘0x00’]
readers:[‘0x00’, ‘0x00’, ‘0x00’]
readers:[‘0x00’, ‘0xfb’, ‘0x30’]
cntrler:[‘0xaa’, ‘0x01’, ‘0x09’, ‘0x3e’, ‘0x69’]
readers:[‘0x3e’, ‘0x69’]
cntrler:[‘0xaa’, ‘0x01’, ‘0x05’]
readers:[‘0x4b’]
readers:[‘0x00’, ‘0x00’, ‘0x00’]
readers:[‘0x00’, ‘0x00’, ‘0x00’]
readers:[‘0x00’, ‘0x00’, ‘0x00’]
readers:[‘0x60’, ‘0xc1’]
cntrler:[‘0xaa’, ‘0x01’, ‘0x1a’, ‘0xff’, ‘0xe5’, ‘0x1f’]
readers:[‘0x7f’, ‘0xa4’]
cntrler:[‘0xaa’, ‘0x01’, ‘0x48’, ‘0x02’, ‘0x00’, ‘0xff’, ‘0xff’, ‘0x1e’, ‘0x28’]
readers:[‘0xfe’, ‘0x59’]
cntrler:[‘0xaa’, ‘0x21’, ‘0x01’]
readers:[‘0x47’]
readers:[‘0x10’, ‘0x00’, ‘0x00’]
readers:[‘0x00’, ‘0x00’, ‘0x00’]
readers:[‘0x00’, ‘0x00’, ‘0x00’]
readers:[‘0xf9’, ‘0xb1’]

Разберем некоторые комбинации.

cntrler:[‘0xaa’, ‘0x01’, ‘0x1a’, ‘0xff’, ‘0xe5’, ‘0x1f’]
readers:[‘0x7f’, ‘0xa4’]

Контроллер посылает команду ‘0x1a’ для считывателя с идентификатором ‘0x01’ и данными ‘0xFF’, на что считыватель отвечает каким-то кодом. Казалось бы «Вот! оно! бери и делай», ан нет.
В инструкции написано, что последние два байта пакета это контрольная сумма всего пакета за исключением кода команды, по алгоритму CRC16. Cчитаем CRC16 от [‘0x01’, ‘0x1A’, ‘0xFF’] на калькуляторе и получаем 0xE01A, что никак не сходится с 0x1FE5. Оказывается доблестные разработчики PERCo сделали небольшую защиту или от помех в линии, или от таких как я 😉
Дело в том что 0x1FE5, это 0xE01A xor 0xFFFF и об этом естественно нигде не написано (см. мануал выше).
Итак, с пакетом от контроллера всё более менее понятно, что же такое прислал считыватель с адресом 0x01?
Перебирая данные внутри пакета от контроллера и пошагово считая CRC16 оказалось, что ответ считывателя [‘0x7f’, ‘0xa4’] это контрольная сумма второго и третьего байта [‘0x01’, ‘0x1A’].
Таким образом считыватель говорит контроллеру, что он «живой».

Инициализация

cntrler:[‘0xaa’, ‘0x01’, ‘0x48’, ‘0x04’, ‘0xff’, ‘0x00’, ‘0xff’, ‘0x6f’, ‘0x60’]
readers:[‘0xfe’, ‘0x59’]
cntrler:[‘0xaa’, ‘0x01’, ‘0xa8’, ‘0x07’, ‘0x01’, ‘0x01’, ‘0xff’, ‘0x01’, ‘0x01’, ‘0xff’, ‘0x01’, ‘0x01’, ‘0xff’, ‘0x44’, ‘0xc2’]
readers:[‘0xff’, ‘0xd1’]

Дальше всё так же. Команда заканчивающаяся CRC16 и CRC16 от второго и третьего байта команды.

Остановимся подробнее на адресации внешних устройств.

адресация внешних замков

[‘0xaa’, ‘0x05’, ‘0x1a’, ‘0xff’, ‘0xa4’, ‘0xde’]
[‘0xaa’, ‘0x25’, ‘0x1a’, ‘0xff’, ‘0xa5’, ‘0x14’]
[‘0xaa’, ‘0x45’, ‘0x1a’, ‘0xff’, ‘0xa5’, ‘0x0a’]
[‘0xaa’, ‘0x65’, ‘0x1a’, ‘0xff’, ‘0xa4’, ‘0xc0’]
[‘0xaa’, ‘0x85’, ‘0x1a’, ‘0xff’, ‘0xa5’, ‘0x36’]
[‘0xaa’, ‘0xa5’, ‘0x1a’, ‘0xff’, ‘0xa4’, ‘0xfc’]
[‘0xaa’, ‘0xc5’, ‘0x1a’, ‘0xff’, ‘0xa4’, ‘0xe2’]
[‘0xaa’, ‘0xe5’, ‘0x1a’, ‘0xff’, ‘0xa5’, ‘0x28’]

Как видно из дампа, все внешние замки имеют битовую адресацию, отсюда и получается ограничение в 8 внешних замков.
После окончания инициализации всех доступных устройств, контроллер на считывателях сбрасывает индикацию

Сброс индикации

cntrler:[‘0xaa’, ‘0x01’, ‘0x1b’, ‘0x0f’, ‘0xe4’, ‘0xcb’]
readers:[‘0xbe’, ‘0x64’]
cntrler:[‘0xaa’, ‘0x01’, ‘0x48’, ‘0x02’, ‘0x00’, ‘0xff’, ‘0xff’, ‘0x1e’, ‘0x28’]
readers:[‘0xfe’, ‘0x59’]
cntrler:[‘0xaa’, ‘0x21’, ‘0x1b’, ‘0x0f’, ‘0xe5’, ‘0x01’]
readers:[‘0xa7’, ‘0xa4’]
cntrler:[‘0xaa’, ‘0x21’, ‘0x48’, ‘0x02’, ‘0x00’, ‘0xff’, ‘0xff’, ‘0x19’, ‘0x48’]
readers:[‘0xe7’, ‘0x99’]

Команда контроллера 0x1B резетит считыватель, а команда [‘0x48’, lamp] зажигает лампочку, где lamp имеет значения.

  • 0x01 — зеленый
  • 0x02 — оранжевый
  • 0x04 — красный

После инициализации считывателей, контроллер еще раз проверяет их состояние

Опрос состояния

cntrler:[‘0xaa’, ‘0x01’, ‘0x01’]
readers:[‘0x0e’, ‘0x10’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x88’, ‘0x22’, ‘0xf5’]
cntrler:[‘0xaa’, ‘0x01’, ‘0x09’, ‘0x3e’, ‘0x69’]
readers:[‘0x3e’, ‘0x69’]
cntrler:[‘0xaa’, ‘0x01’, ‘0x05’]
readers:[‘0x16’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0xf2’, ‘0x7a’]
cntrler:[‘0xaa’, ‘0x01’, ‘0x1a’, ‘0xff’, ‘0xe5’, ‘0x1f’]
readers:[‘0x7f’, ‘0xa4’]
cntrler:[‘0xaa’, ‘0x01’, ‘0x48’, ‘0x02’, ‘0x00’, ‘0xff’, ‘0xff’, ‘0x1e’, ‘0x28’]
readers:[‘0xfe’, ‘0x59’]
cntrler:[‘0xaa’, ‘0x21’, ‘0x01’]
readers:[‘0x07’, ‘0x10’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0xfb’, ‘0x65’]
cntrler:[‘0xaa’, ‘0x21’, ‘0x09’, ‘0x27’, ‘0xa9’]
readers:[‘0x27’, ‘0xa9’]
cntrler:[‘0xaa’, ‘0x21’, ‘0x05’]
readers:[‘0x4e’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0xf1’, ‘0x6e’]
cntrler:[‘0xaa’, ‘0x21’, ‘0x1a’, ‘0xff’, ‘0xe4’, ‘0xd5’]
readers:[‘0x66’, ‘0x64’]
cntrler:[‘0xaa’, ‘0x21’, ‘0x48’, ‘0x02’, ‘0x00’, ‘0xff’, ‘0xff’, ‘0x19’, ‘0x48’]
readers:[‘0xe7’, ‘0x99’]

и запускает генератор опроса состояния считывателей и замков.
Опрос считывателей происходит 3 раза в секунду каждый.
А теперь начинается самое интересное.
На запрос состояния, считыватель ДОПОЛНЯЕТ команду контроллера данными из своего буфера, считает CRC16 xor 0xFFFF и выдаёт данные в канал связи.
Рассмотрим пакет ответа считывателя:
Пустой пакет: readers:[‘0x4e’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0xf1’, ‘0x6e’]
Пакет с картой: readers:[‘0x45’, ‘0x40’, ‘0x5d’, ‘0x7a’, ‘0x07’, ‘0x00’, ‘0x04’, ‘0x00’, ‘0x00’, ‘0x00’, ‘0xbb’, ‘0x9d’]

  • 1й байт это номер пакета, который вычисляется путём приращения к предыдущему значению случайного числа из диапазона от 1 до 15, после чего берется значение по модулю 79(0x4F)
  • 2й байт это состояние считывателя. Если там 0x00, то значит буфер считывателя уже прочитан контроллером, если там 0x40, то значит в буфере имеется карта.
  • с 3го по 6 байт помещается код считанной карты.
  • в 7 байте всегда живёт цифра 4. Остальные байты я не разбирал.

Если у контроллера присутствует приложенная карта, то контроллер выдаёт команду на считыватель о принятии кода карты, зажигает зеленую лампу и открывает механизм прохода. По истечении времени ожидания или при проходе пользователя механизм прохода закрывается и состояние считывателя сбрасывается в исходное. При этом опрос остальных устройств не прекращается.

А теперь собственно, что подразумевалось в заголовке про «Берегите линии своих СКУД от вторжения»?

Написанный мной на языке питон перехватчик позволяет захватить управление СКУД PERCo в любой точке магистрали RS485 и отследив карты на которые турникет выдаёт разрешение прохода, прерывать передачу данных от считывателя к контроллеру турникета с базой валидных ключей, открывать любые устройства подключенные к магистрали данных. При этом «левые» карты прикладываемые к считывателям системы, могут заменяться на «валидные» и обратно. Сняв дамп блока инициализации и прокрутив его обратно в линию можно эмулировать как сам контроллер, так и считыватели, что открывает просто безграничные возможности для управления системой.

Так что «Берегите линии своих СКУД от вторжения» 🙂

PS: скрипты выкладывать не буду 😛

© Aborche 2016
Aborche

ссылка на оригинал статьи https://habrahabr.ru/post/277405/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *