суббота, 19 февраля 2011 г.

Arduino: делаем эмулятор iButton

 

Если после прочтения статьи про чтение кода ключа-таблетки iButton, Вам в голову пришла мысль, что можно сделать и обратную комбинацию – значит мы мыслим в одинаковом направлении :)

Сразу хочу сказать, что эта идея далеко не новая, и воспользовавшись поиском можно найти различные решения – примеры можно найти в конце статьи в ссылках.

Итак, что же нам нужно?
А нужно нам всего-навсего произвести симуляцию 1-Ware slave-устройства, выдавая себя за iButton :)

Для этого нужно вспомнить, что происходит в линии 1-Wire:
Сначала происходит

1. инициализация – ведущее устройство (домофон) подаёт импульс RESET, после него ведомое устройство (это мы) должно дать ответ PRESENCE (прижать линию к земле на 60 — 240 микросекунд)



Далее происходит сам обмен информации:
2. домофон выдает команду на чтение ПЗУ (ROM) – это должно быть 33h.
Информация, как мы помним, передаётся побайтно, бит за битом.
При этом «0» передаётся прижиманием линии к земле в течении всего тайм-слота (60 — 120 микросекунд)
А «1» передаётся кратковременным прижиманием (на 1-15микросекунд) и последующим отпусканием линии.
3. домофон выдерживает некоторое время и начинает посылать импульсы приема информации.
Т.к. в ответ ожидается 8 байт информации – будет 64 импульса (по одному импульсу для передачи каждого бита информации от нас).
Если мы хотим передать «0» — мы удерживаем линию на логическом нуле, а если хотим передать «1», то можем ничего не делать :)
Эмулируя ключ, мы должны сначала передать номер серии устройства 01h.
Затем собственно 6 байт номера ключа (начиная с младшего байта) и в самом конце – байт CRC-кода предыдущей информации.
Для ключа, рассмотренного в статье про iButton – это будет последовательность байт:
01h
41h
CEh
67h
0Fh
00h
00h

B6h

Попробуем это дело запрограммировать :)
Чтобы не бегать постоянно к домофону – начальную проверку можно попробовать сделать в протеусе :)
Поместим в проект две ардуины – в одну загрузим скетч работы с iButton, а во вторую будем грузить код нашего эмулятора :)



Кроме того, при симуляции схемы можно воспользоваться виртуальным инструментарием. Например, подключив в Протеусе на линию 1-Wire виртуальный осциллограф – можно вживую понаблюдать – как происходит обмен информацией :)



блок-схема скетча для эмуляции iButton будет такой



А если мы не знаем ключ – сможем ли мы его подобрать перебором?

Оценочно прикинем – сколько времени понадобится на подбор ключа :)
У моего ключа-таблетки номер
00000F67CE41
, что даёт нам возможность предположить, что пока для нумерации iButton-ов используются первые 4 байта номера :)
Вне всяких сомнений — это меньше 281 биллиона, которые можно спрятать в шести байтах :)
И даёт нам всего 4 миллиарда вариантов :)
0xFFFFFFFF = 4294967295

Прикинем — сколько времени займёт проверка одного ключа:
PRESENCE ~ 480 мкс
8 бит команды 120 мкс * 8
64 бита данных 120 мкс *64

Итого ~ 10 мс => 100 ключей в секунду (в датащите упоминается про 75 ключей в секунду)
Получается, что на полный перебор потребуется 497 дней :( Что-то долго :)

В интернете упоминается, что такой брутфорс (brute force) на домофонах не сработает и после трёх неправильных ключей домофон загудит и впадёт в ступор на 5 минут :)


Однако выход есть – дело в том, что на некоторых моделях домофонов в новой чистой памяти домофона (предназначенной для хранения ключей жильцов) все биты установлены в 1 (т.е. забиты FF-ами).
И если ключ будет выдавать тоже единицы, то программа подумает, что в чистой памяти тоже хранятся коды ключей и они совпадают с тем, что записано в нашем универсальном ключе и дверь откроется :)))

Это действительно дыра в программном обеспечении контроллера и есть во всех дешёвых контроллерах. Причём простая до смеху. Контроллер проверяет ключ (причём обычно только последнии 4 байта — контроллер то дешёвый) с записаными в ЭСПЗУ. А как выглядят пустые ячейки? То есть если записать ключ вида FFFFFF......FFFFFF что будет? Правильно, он уже везде прописан :) Подверженны все домофоны типа Цифрал ТС, Визит, Метаком


А где же скетч эмулятора?

Ниже приводится набросок такого скетча – вам остаётся только освежить в памяти – что происходит на линии 1-Wire и наполнить кодом функции:
void wire_send_byte(byte dsbyte)
byte wire_read_byte()
void wire_write0(void)
void wire_write1(void)
или же полностью переписать скетч самостоятельно ;)

Набросок скетча (ВНИМАНИЕ – СКЕТЧ НЕ ДОПИСАН ДО КОНЦА):


  1. /*
  2. * Эмулируем работу ключа-таблетки iButton типа DS1990A
  3. */
  4. int onepinio = 10; // порт для подключения 1-Wire
  5. // данные ключа  - номер серии, 6 байт номера, байт CRC
  6. byte key_data[8] = {0x1, 0x41, 0xCE, 0x67, 0x0F, 0x00, 0x00, 0xB6};
  7. void setup()
  8. {
  9.         Serial.begin(9600);
  10. }
  11. void loop()
  12. {
  13.     // считываем импульс RESET из линии
  14.     //
  15.     wire_read_RESET();
  16.        
  17.     //
  18.     // выдаём в линию импульс PRESENCE
  19.     //
  20.     wire_PRESENCE();
  21.        
  22.        
  23.     //
  24.     // считываем команду от домофона
  25.     //
  26.     int b = wire_read_byte();
  27.        
  28.        
  29.     //
  30.     // что за команду считали? :)
  31.     //
  32.     if(b==0xF0) // SEARCH ROM
  33.     {
  34.         //
  35.         // начинаем двубитную передачу :(
  36.         //
  37.         wire_send_duos_bits();
  38.     }
  39.     else
  40.     {
  41.         if(b==0x33) // READ ROM
  42.         {
  43.             //
  44.             // отправляем данные iButton :)
  45.             //
  46.             wire_send_data();
  47.         }
  48.     }
  49.     delay(300);
  50. }
  51. //
  52. // ждём пока линия не прижмётся к земле
  53. //
  54. void wire_wait()
  55. {
  56.     pinMode(onepinio, INPUT);
  57.     while(digitalRead(onepinio))
  58.     {
  59.         delayMicroseconds(1);
  60.     }
  61. }
  62. //
  63. // двубитное общение с ведущим устройством - см. AN187
  64. //
  65. void wire_send_duos_bits()
  66. {
  67. }
  68. //
  69. // считываем импульс RESET из линии
  70. //
  71. void wire_read_RESET()
  72. {
  73.     pinMode(onepinio, INPUT);
  74.     while(digitalRead(onepinio))
  75.     {
  76.         delayMicroseconds(1);
  77.     }
  78.     while(!digitalRead(onepinio))
  79.     {
  80.         delayMicroseconds(1);
  81.     }
  82. }
  83. //
  84. // выдаём в линию 1-Wire сигнал PRESENCE
  85. // для этого нужно прижать линию к земле на 60-240 микросекунд, а затем отпустить
  86. //
  87. void wire_PRESENCE()
  88. {
  89.     pinMode(onepinio, OUTPUT);
  90.     digitalWrite(onepinio,LOW);         // прижимаем линию к земле
  91.     delayMicroseconds(120);             // 60-240 микросекунд
  92.     pinMode(onepinio, INPUT);           // отключаемся
  93.     delayMicroseconds(450);
  94. }
  95. //
  96. // выдача в линию данных iButton
  97. //
  98. void wire_send_data()
  99. {
  100.     for(int i=0;i<8;i++)
  101.         wire_send_byte(key_data[i]);
  102. }
  103. //
  104. // выдача в линию байта данных
  105. //
  106. void wire_send_byte(byte dsbyte)
  107. {
  108. }
  109. //
  110. // считываем байт данных из линии
  111. //
  112. byte wire_read_byte()
  113. {
  114. }
  115. //
  116. // пишем бит "0" в линию
  117. //
  118. void wire_write0(void)
  119. {
  120. }
  121. //
  122. // пишем бит "1" в линию
  123. //
  124. void wire_write1(void)
  125. {
  126. }

13 комментариев:

  1. давно хотел узнать, как это всё работает.

    ОтветитьУдалить
  2. Ждите, скоро будут еще статьи по схожих темах.

    ОтветитьУдалить
  3. годная статья. спасибо, пригодится.

    ОтветитьУдалить
  4. Я обычно с кулака открываю, если чо.

    ОтветитьУдалить
  5. Слишком сложно, попроще никак?

    ОтветитьУдалить
  6. 2Magnolia-Fan: чтоб попроще получилось, придется растянуть на десяток статей :-(

    ОтветитьУдалить