суббота, 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. Я обычно с кулака открываю, если чо.

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

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

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