Участвуя последнее время в разных интересных проектах, возникла задачка альтернативного управления продуктом 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 мы начали изучать как устроен протокол.
Схема подключения внешних устройств довольно тривиальна.
Можно подключить:
- РУ — радиопульт
- ПДУ — пульт дистанционного управления
- ДКЗП — Датчик контроля зоны прохода
- Сирену
- до 8 замков PERCo-CL201
- табло системного времени PERCo-AU05
Никаких других устройств подключить нельзя.
Протокол Perco является закрытым, но есть описание часов PERCo-AU05, которое легко гуглится в сети.
Данная картинка из описания это единственное упоминание протокола PERCo найденное в сети.
Отлично. Кусок протокола есть, значит можно посмотреть, что там бегает. Подключаем RS485 к турникету и смотрим.
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, вторая часть — это ответ устройства к которому относилась команда. Данный ответ имеет переменную длину и заканчивается контрольной суммой всего пакета.
В реальности сессия «контроллер-считыватель» выглядит вот так:
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’].
Таким образом считыватель говорит контроллеру, что он «живой».
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’, ‘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 внешних замков.
После окончания инициализации всех доступных устройств, контроллер на считывателях сбрасывает индикацию
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 — красный
После инициализации считывателей, контроллер еще раз проверяет их состояние
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
ссылка на оригинал статьи https://habrahabr.ru/post/277405/
Добавить комментарий