Предисловие
Эта история началась с того, что нам как-то понадобилось прочесть/записать микросхему FLASH памяти типа SPANSION S29GL512 в корпусе TSOP56. До этого времени мы успешно эксплуатировали программатор XELTEK SuperPro 500P. Но, к сожалению, установленная в нем сокета с 48-ю контактами не позволяла этого сделать даже с применением переходника. Само собой, что и в программе на PC для этого программатора поддержка таких микросхем отсутствовала.
Пора обновляться, решили мы, и приобрели (не сочтите за рекламу) программатор того же производителя модели SuperPro 6100, в котором используются сменные адаптеры с различными сокетами. Также отдельно был приобретен необходимый, как нам тогда казалось, адаптер модели CX1011 с сокетой TSOP56 (в комплекте с программатором шел адаптер с сокетой ZIF48).
Условимся в дальнейшем называть программу программатора просто софтом, потому что «программа программатора» звучит не очень эстетично.
После получения всего этого мы собрали конструктор из программатора с адаптером и попытались начать с ним работу.
Но после того, как в софте была выбрана нужная микросхема, программатор несколько раз пропищал и на его дисплее отобразилась загадочная надпись «ERROR CODE: 1D». Софт тоже выругался сообщением, что данный адаптер для программирования этой микросхемы не подходит. Как же так!? Адаптер с программатором благополучно поженились, микросхема с сокетой тоже, а вот злобный производитель решил, что этого недостаточно и пользователь, в дополнение к новому программатору, непременно должен приобрести еще и «новый» адаптер DX1011. За который, кстати, на сайте производителя хотят около трехсот вечнозеленых. Ну да ладно, в нашем случае мы его купили по ошибке, хотя приобретать «новый» никакого желания все же не было. Но ведь у кого то он мог сохраниться от старого программатора, а кто-то, возможно, захотел бы изготовить его самостоятельно. Так что именно для этой аудитории данная статья.
При подготовке этого материала мы не ставили целью научить читателя пользоваться различными программами и аппаратными приспособлениями, которыми пришлось воспользоваться на этом пути. Поэтому не будем засорять статью описанием тонкостей работы со всем этим инструментарием, оставив его за кадром. Но если кому-то будут интересны подробности — с удовольствием постараемся их прокомментировать.
Здесь в самый раз привести текст отказа об ответственности, смысл которого заключается в том, что автор не несёт никакой ответственности за любое использование вами материалов данной статьи. Иными словами — только вы сами будете отвечать за свои действия, на которые вас может подвигнуть ознакомление с этим материалом.
Предварительное обследование
Обзор адаптера позволил обнаружить на нем восьминогую микросхему с надписью AE801, документация на которую в интернете, естественно, отсутствовала. Эта микросхема хорошо видна в правом углу на фотографии адаптера CX1011 выше.
Хотя кое-какую информацию об этой микросхеме все же удалось найти. На сайте XELTEK в разделе FAQ в ответе на вопрос: «Какая разница между программаторами SuperPro 6000 и 6100?» присутствует любопытная фраза: «Адаптеры серии CX (для программаторов 6000) и DX (для программаторов 6100) абсолютно одинаковы за исключением ID чипа, который в адаптерах серии DX использует усовершенствованную защиту.» Стало ясно, что эта микросхема — некий ID чип, который используется программатором для идентификации установленного в него адаптера. Вот только про какую защиту идет речь: от кого или от чего нужно защищать адаптер? Ну конечно же от нас с вами!
Эта защита подобна той, которая сейчас повсеместно используется в картриджах для принтеров, чтобы их не заправляли повторно, а покупали новые. Но здесь то ситуация несколько иная. Вы на свои кровные приобретаете недешевый адаптер и надеетесь, что будете пользоваться им вечно. Не тут-то было! Производитель об этом предусмотрительно позаботился и, уподобившись самому Создателю, решил искусственно ограничить адаптеру срок его бренной жизни. В подтверждение чего, в софте обнаруживаются следующие многозначительные сообщения:
— «Socket adapter end-of-life is near. Please prepare to replace with a new one.»
— «Adaptor usage beyond its cycle life, please change.»
Как вам нравится этот перл: «Срок жизни адаптера подходит к концу»? И особенно фраза «replace with a new one», так ласкающая слух отдела продаж.
Почему-то на сайте XELTEK на странице с адаптерами об их бренности — ни слова! Перефразируя одну известную поговорку про козу и баян так и хочется сказать: «А нафига адаптеру защита»?
Но как бы то ни было, потребность все же прочесть/записать микросхему FLASH памяти (о чем, надо сказать, мы уже подзабыли, увлекшись всем остальным) и непреодолимое желание во что бы то ни стало облегчить участь несчастного, обреченного на неминуемую гибель «больного» адаптера, заставило нас двигаться дальше.
Подготовка к лечению
Условимся в дальнейшем называть PC, на котором работает софт для программатора — хостом.
Для обмена данными между хостом и программатором используется интерфейс USB, а в нем несколько USB каналов (Pipes). По одному из USB каналов хост отправляет в программатор команды, по другому шлет в него данные, по следующему получает данные из программатора.
Нужно было идентифицировать интерфейс для обмена данными между AE801 и самим программатором. Тут производитель не стал мудрствовать, использовав старый добрый UART со скоростью около 10500 бод. У микросхемы AE801, которая по сути является восьминогим микроконтроллером, ножки 1 и 4 соединены с «землей», на ножку 8 подается питание +5V, ножка 3 используется для двунаправленной передачи данных по UART, ножка 7 используется, как положительный CS, а на ножку 6, одновременно с появлением единицы на ножке 7 подается тактовая частота 4 MHz. Из известных нам микроконтроллеров о восьми ногах для данной распиновки лучше всего подходит ATMEL серий ATtiny25, 45, 85. Правда, используемый для них корпус SOIC8 несколько шире, чем нужно.
При исследовании обмена данными между AE801 на адаптере и программатором обнаружилась одна интересная вещь: при выборе какой-либо микросхемы в софте, еще до того, как произойдет выбор микросхемы AE801 на адаптере, на ее ножке 3 появляются некие пакеты данных. Получалось, что есть еще до начала общения с микросхемой из адаптера программатор общается еще с кем-то. В последствии обнаружилось, что в самом программаторе установлена такая же микросхема AE801, сидящая на той же двунаправленной шине данных, что и AE801 из адаптера. И служит она тем же целям — защите от клонирования, но теперь уже самого программатора.
Далее софт на хосте был модифицирован, чтобы сохранять в лог файл процедуру обмена данными между ним и программатором. Из полученного лога стало совершенно понятно, что ничего не понятно. Обмен данными был каким-то образом зашифрован и зашумлен мусором. Видимо в этом и заключалось то самое «усовершенствование защиты». Но одно стало очевидным — адаптер сообщает программатору название своей модели прямо в текстовом виде и свой возраст, чтобы можно было своевременно отправить его на заслуженный, по мнению XELTEK, отдых. Все остальное — мусор.
Путем дальнейшего изучения выяснилось, что алгоритм защиты писал человек очень далекий от данной темы. Так называемая «усовершенствованная защита» не выдерживает никакой критики.
Однако, в лоб в логе разобраться не удалось и стало очевидным, что теперь придется перемещаться внутрь самого программатора. Его электронная начинка выглядит, как слоеный пирог, в виде множества отдельных плат, соединенных вместе стойками и разъемами.
Одна из этих плат (назовем ее, по аналогии с PC, «материнской») содержит ARM микроконтроллер ATMEL AT91SAM9G20, SDRAM, SPI FLASH с прошивкой, тот самый ID чип AE801 с моделью программатора и его серийным номером, микросхему USB контроллера и ряд других микросхем.
Еще на одной плате установлена микросхема XILINX, которая вместе с остальными платами с транзисторными сборками управляет ножками микросхемы на подключенном к программатору адаптере во время процедур чтения/записи и т.д.
Вся прошивка ARM микроконтроллера содержится во внешней микросхеме SPI FLASH, что, конечно же, тоже никак не способствует усилению защиты. Обычно загрузчик прячут во внутренней памяти микроконтроллера, недоступной для чтения, а прошивки или обновления размещаемые снаружи, шифруют. Но разработчики из XELTEK решили пойти другим путем, заморочившись с проприетарными чипами AE801, оставив прошивку ARM-а на растерзание. Чем мы, собственно, и занялись далее.
Идея состояла в том, чтобы дизассемблировать «usb» файл алгоритма программатора и затем собрать проект в одной из ARM IDE, дабы потом через JTAG, имея исходник на ассемблере, изучать работу программатора в части его общения с микросхемой AE801.
На материнской плате весьма кстати было предусмотрено место под разъем JTAG. Для удобства отладки разъем для подключения JTAG был смонтирован на боку пластикового корпуса программатора и соединен с материнской платой плоским шлейфом.
В итоге получился эдакий Development Kit.
Так как у нас имелся JTAG OLIMEX ARM-USB-OCD, сохранившийся от какого-то старого проекта, выбор ARM IDE пал на IAR Embedded Workbench, потому что в нем поддерживается отладка через GDB сервер. Впрочем, настройка этого самого GDB сервера оказалась задачей весьма заковыристой. И на то, чтобы собранный в итоге из ассемблерного исходника проект начал грузиться в SDRAM программатора и появилась возможность его отладки, пришлось потратить какое-то время.
После того, как все наконец заработало из под отладчика, некоторые самые «интересные» методы алгоритма были переписаны на С, чтобы в них было удобнее разбираться. Таким образом, постепенно удалось накопить информацию, позволившую разобраться в механизме работы программатора, который описан ниже в несколько упрощенном виде.
При включении питания программатора, в ARM микроконтроллере начинает работу его встроенный загрузчик (условно нулевой), который загружает из SPI FLASH другой загрузчик (назовем его первым) в свое внутреннее ОЗУ, ремапит это ОЗУ в адрес 0 и передает управление первому загрузчику. Начав работу, первый загрузчик настраивает в ARM-е SDRAM контроллер, подключая таким образом внешнее ОЗУ по адресу 0x20000000 и переписывает туда другой загрузчик (назовем его вторым) из той же SPI FLASH и передает управление ему. Второй загрузчик настраивает внешнюю микросхему USB контроллера для общения с хостом, выводит на LCD экранчик строки с названием программатора и версией прошивки, общается со своим собственным ID чипом AE801 и в итоге попадает в цикл обработки команд от хоста.
Для работы с каждым типом микросхем памяти создана своя прошивка для ARM микроконтроллера, называемая алгоритмом. Файлы алгоритмов имеют расширение «usb» и хранятся на хосте в каталоге «algoX» с установленной программой. В каталоге «lib» находятся прошивки для микросхемы XILINX и файлы с расширением «wls», в которых содержится информация о соответствии нужного алгоритма каждой, поддерживаемой программатором микросхеме, тип необходимого для этой микросхемы адаптера, а также некоторые дополнительные параметры для каждой микросхемы. Например, напряжение ее питания, напряжения программирования и т.п.
После того, как в софте будет выбрана какая-либо микросхема, необходимо убедиться, что в программаторе установлен соответствующий ей адаптер. Для этого хост отправляет в программатор команду — запрос типа установленного в нем адаптера. Программатор запрашивает у микросхемы AE801 из адаптера его тип и специальным образом сформированный ответ отправляет обратно в хост. Софт на хосте сравнивает тип адаптера из файла «wls» с типом адаптера из ответа и в случае их совпадения отправляет в программатор по адресу 0x20020000 очередной загрузчик (назовем его третьим) — файл «loader.bin», находящийся в каталоге «bin». После передачи управления третьему загрузчику хост с его помощью загружает по адресу 0x20000000 соответствующий данному типу микросхемы алгоритм (файл «usb»), прошивку для XILINX и в итоге передает управление загруженному алгоритму. После некоторых настроек железа алгоритм, в свою очередь, снова попадает в цикл обработки команд от хоста. Промежуточный третий загрузчик нужен только для того, чтобы рабочий алгоритм каждый раз грузился по одному и тому же адресу 0x20000000.
При выборе в софте другой микросхемы вся описанная выше процедура повторяется, начиная с запроса хостом типа установленного в программаторе адаптера.
После того, как загруженный в программатор алгоритм переходит в цикл обработки команд от хоста, все дальнейшие действия активизируются только этими командами. Иными словами, хост становится ведущим, а программатор ведомым устройством. Микросхема же AE801 из адаптера является ведомой по отношению к программатору и после подачи на ее ножку 7 (CS) активного высокого уровня, а на ножку 6 тактовой частоты 4 MHz также переходит в цикл обработки команд от программатора. Ножка 3 микросхемы AE801 изначально настраивается на прием команд от программатора. После отправки очередной команды, программатор настраивает линию данных на прием, а AE801, соответственно, свою ножку 3 на передачу и отправляет данные программатору. Таким образом происходит обмен данными между AE801 в адаптере и программатором. Обмен данными между внутренней AE801 на материнской плате и программатором происходит точно таким же образом.
Лечение
Итак, мы непосредственно подошли к рассмотрению механизма защиты адаптеров серии DX.
Как упоминалось ранее, дабы не перегружать статью лишними подробностями, приведем здесь лишь квинтэссенцию пройденного пути — описание процесса обмена данными между программатором и ID чипом AE801.
Терминология, используемая в данном описании:
- Термин «ножка» относится к соответствующим выводам микросхемы AE801.
- Счет байт — начиная с единицы.
- Каждый раз, программатор, отправив в микросхему определенное количество байт, переводит линию данных на прием, а микросхема, получив эти данные, переключает ножку 3 на передачу и т.д. Поэтому описание этих переключений и упоминание ножки 3 приводиться не будут.
- Повествование ведется от лица микросхемы AE801.
1. При наличии на ножке 7 неактивного низкого уровня, ножка 3 настроена на ввод и мы находимся либо в сбросе, либо в состоянии сна.
2. Когда программатор подаст на ножку 7 активный высокий уровень (на ножку 6 при этом подается тактовая частота 4 MHz), мы должны передать в программатор 6 случайных байт.
Известно, что для использования алгоритма генерации случайных, а точнее, псевдослучайных чисел из стандартной библиотеки С необходимо сначала вызвать команду srand(), передав ей в качестве входного аргумента некое число — соль, используя которое каждый вызов команды rand() будет возвращать следующее значение из определенной этим числом псевдослучайной последовательности. В PC в качестве источника энтропии для srand() обычно используется time(), но в простых микроконтроллерах, как правило, нет часов реального времени и эту задачу можно решить, например, сохраняя каждый раз после сброса новое значение для srand() в EEPROM. Скорее всего, именно так это и было реализовано в AE801. По большому счету, нам вообще нет надобности генерить случайные числа, но решение этой задачи показалось интересным.
Мы реализовали прототип AE801 на микроконтроллере фирмы Microchip (в тот момент он просто оказался под рукой), который работал от своего собственного внутреннего генератора на частоте 8 Mhz и по отношению к нему внешняя частота 4 MHz, поступающая на ножку 6, оказалась приемлемым (и, пожалуй, единственным) источником энтропии.
Ножка 6 в программе использовалась, как цифровой вход и с нее в цикле начитывалось 16 бит, необходимых для передачи в метод srand().
3. Получаем из программатора 9 байт и подтверждаем их получение отправкой в программатор полученного шестым байта.
4. Получаем из программатора 5 байт. Первый из полученных пяти байт отправляем в программатор как есть, а оставшиеся 4-е шифруем по алгоритму RSA в 4-е 16-и битных слова и отправляет полученные 8 байт в программатор. Причем, для каждого из полученных после шифрования слов сначала отправляется старший байт, а затем младший. В завершение, еще раз отправляем в программатор первый из полученных пяти байт. Итого 10 байт.
В качестве алгоритма шифрования использован RSA с очень короткими ключами 63, 847 и 2773. Кому интересно, могут найти информацию об этом, например, ЗДЕСЬ.
В прошивке алгоритма для вычисления RSA используется цикл из 2773-х итераций. Но так как входным числом для шифрования является байт, а выходным — слово из двух байт, для упрощения вычисления RSA была сформирована таблица из 256-и слов, что заняло лишних 512 байт программной памяти.
{
0x0000, 0x0001, 0x03d0, 0x0640, 0x059d, 0x045f, 0x0191, 0x020b, 0x0863, 0x0209, 0x0933, 0x0489, 0x017f, 0x0168, 0x00d8, 0x0717,
0x0741, 0x0315, 0x040d, 0x0368, 0x0984, 0x084f, 0x06d8, 0x0832, 0x08b2, 0x0602, 0x07aa, 0x06a4, 0x0044, 0x086f, 0x08da, 0x07cd,
0x067f, 0x099f, 0x0797, 0x0086, 0x0ab4, 0x0088, 0x09e6, 0x07c5, 0x0433, 0x082a, 0x06d4, 0x00d0, 0x06f8, 0x029d, 0x0496, 0x04f5,
0x0525, 0x06ef, 0x037f, 0x02ad, 0x0606, 0x0133, 0x03b2, 0x0573, 0x0a1d, 0x017d, 0x09ad, 0x0674, 0x05ff, 0x0745, 0x097a, 0x02d9,
0x0373, 0x02f3, 0x09a6, 0x0402, 0x0969, 0x05be, 0x01c5, 0x0ab7, 0x042c, 0x074c, 0x0965, 0x047d, 0x0989, 0x0a81, 0x00a4, 0x00fd,
0x03ee, 0x099c, 0x0695, 0x05ee, 0x028d, 0x0435, 0x0243, 0x07df, 0x09d1, 0x07ee, 0x0509, 0x09b9, 0x023f, 0x02c0, 0x06fa, 0x098d,
0x05d5, 0x0499, 0x0800, 0x016f, 0x0019, 0x0410, 0x010b, 0x01ab, 0x07ea, 0x036f, 0x0094, 0x004e, 0x0a64, 0x06b9, 0x0abe, 0x051a,
0x0295, 0x0078, 0x0112, 0x06a8, 0x08dd, 0x06e9, 0x04d7, 0x08c3, 0x02e4, 0x00f3, 0x0015, 0x09e7, 0x0967, 0x06e2, 0x0650, 0x0508,
0x0882, 0x0028, 0x07f3, 0x00b7, 0x03d7, 0x0504, 0x0143, 0x0016, 0x0995, 0x08b5, 0x0437, 0x0763, 0x04c5, 0x0234, 0x04c7, 0x07da,
0x09bd, 0x027e, 0x051b, 0x01c0, 0x052a, 0x0544, 0x046c, 0x078c, 0x0199, 0x0299, 0x04b6, 0x094a, 0x07d3, 0x053c, 0x0083, 0x017b,
0x00d6, 0x077f, 0x090b, 0x0475, 0x00ab, 0x09cc, 0x0312, 0x0603, 0x0907, 0x07fa, 0x00b9, 0x0909, 0x0889, 0x0360, 0x0247, 0x00cc,
0x054c, 0x0213, 0x054e, 0x0a44, 0x0767, 0x07b0, 0x0074, 0x087b, 0x041e, 0x098a, 0x087d, 0x03ab, 0x069c, 0x06cc, 0x0604, 0x095f,
0x053f, 0x0954, 0x02da, 0x06d1, 0x08f0, 0x09bf, 0x01db, 0x039e, 0x08a8, 0x0ac5, 0x007a, 0x0222, 0x0a8f, 0x042f, 0x0322, 0x01f0,
0x00e3, 0x00f7, 0x0417, 0x09aa, 0x00fc, 0x077a, 0x04e9, 0x0a21, 0x0278, 0x06f7, 0x07ef, 0x08e7, 0x09cd, 0x04aa, 0x0739, 0x09e3,
0x0708, 0x0a72, 0x028e, 0x04a6, 0x04c0, 0x021b, 0x081d, 0x05c5, 0x069a, 0x05bc, 0x06ca, 0x00eb, 0x00ec, 0x0a9b, 0x04f7, 0x07a2,
0x04ec, 0x0338, 0x05b7, 0x0459, 0x043d, 0x02f5, 0x0284, 0x023b, 0x01f5, 0x0979, 0x01c4, 0x027b, 0x0868, 0x043c, 0x0397, 0x048f
};
5. Получаем из программатора 1 байт и запоминаем его, как маркер. После этого отправляем в программатор случайные 4-е байта. Программатор, получив эти 4 байта, точно так же, как и мы в п.4 шифрует их по тому-же самому алгоритму RSA и отправляет нам получившееся 8 байт в том же порядке (сначала старший, потом младший байт из каждого слова). Получив эти 8 байт мы должны проверить, справился ли программатор с поставленной задачей и в случае положительного ответа отправляем ему полученный ранее маркер.
6. Получаем из программатора 1 байт и снова запоминаем его, как маркер. Затем формируем в массиве, скажем, buf11 — 11 случайных байт и сохраняем его для последующих действий. Затем хитрым образом отправляем эти 11 байт в программатор. Хитрость заключается в том, что мы должны отправить в программатор 88 байт, в которые будут упакованы сформированные нами 11, причем младший бит маркера используется для инвертирования передаваемых 11-и байт. После отправки в программатор 88-и байт, отправляем в него полученный маркер.
byte _mark; // Полученный из программатора байт.
byte _buf11[11]; // Массив для 11-и случайных байт.
for( i=0; i<11; i++ )
{
byte bt = rand();
_buf11[i] = bt;
if( _mark & 1 )
bt = ~bt;
// Упаковываем один байт в 8.
//
for( j=0; j<8; j++ )
{
byte rnd = rand();
byte msk = ( 1 << j );
if( bt & msk )
rnd |= msk;
else
rnd &= ~msk;
// Отправляем байт rnd с упакованным в него очередным битом в программатор.
//
ОтправкаБайта( rnd );
}
}
// Отправляем в программатор маркер.
//
ОтправкаБайта( _mark );
7. Получаем из программатора 1 байт и отправляем в него массив из 133 байт, состоящий из четырех частей.
Первая часть — 48 байт с информацией о типе адаптера и его «возрасте» была взята из родного адаптера «DX0001», в которой текст «DX0001» был заменен на «DX1011».
{
'D', 'X', '1', '0', '1', '1', 0xff, 0xff, 0xff, 0xff, '1', '6', '0', '6', '2', '4',
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x50, 0x00, 0xff, 0xff, 0x00, 0x00, 0x0c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
Вторая часть — 16 случайных байт.
Третья часть — 22 байта, специальным образом сформированные из сохранных ранее в buf11 11-и байт.
byte _buf22[22];
const byte _hash[] =
{
0x58,0x3E,0x09,0x2D,0xFD,0x45,0x68,0x10,0xD5,0x25,0xC7,0xB8,0x28,0x93,0x75,0xDE,
0x57,0x02,0x1E,0x24,0x36,0xB6,0x3A,0x59,0x11,0x6E,0x58,0x91,0xAE,0x53,0x69,0xDF,
0x44,0xA4,0x8B,0xFB,0x76,0x91,0x59,0x3C,0x30,0xB9,0xDA,0x21,0xD8,0x05,0xB4,0x16,
0x4C,0x05,0x78,0x8D,0xB5,0x1C,0x41,0x63,0x4C,0xBE,0xA6,0xCC,0x65,0xB8,0x38,0x1D,
0xE7,0xC6,0xC9,0x19,0xB7,0x73,0xB2,0x7D,0xCD,0x54,0xDC,0xFE,0x67,0x5E,0x79,0x68,
0xB8,0x77,0x73,0x37,0xC8,0x56,0xA2,0x4D,0x9B,0x86,0x56,0x3F,0x26,0x39,0xDE,0xF6,
0xA8,0x13,0xB4,0xBA,0x19,0xDE,0xDF,0x08,0x64,0x2A,0x9F,0xA4,0x3E,0xEE,0x90,0x5B,
0xF0,0xF3,0xC6,0x5F,0x1F,0x84,0x87,0xA3,0x94,0x0D,0x04,0x92,0xDC,0x3C,0xD0,0x6A,
0xD6,0x9B,0xA9,0xED,0x02,0xB0,0xB3,0xBB,0xF3,0x17,0x04,0x93,0x8F,0x18,0x22,0x9B,
0x33,0x0F,0x2A,0x4C,0x72,0x1A,0x0F,0xC2,0x3E,0x4C,0x77,0xAA,0xF2,0x04,0xDC,0x60,
0x68,0x81,0x7B,0x7C,0x60,0xE7,0xD3,0x61,0x3A,0xDA,0x69,0x4A,0x14,0x5A,0xB7,0x31,
0x9F,0xB5,0x60,0x61,0xB4,0x2D,0x80,0x10,0xCF,0x16,0x6B,0xF1,0x08,0x81,0xDA,0x12,
0xA6,0x46,0xF2,0xA2,0x14,0x68,0xAA,0x48,0x94,0x8B,0x9D,0xE3,0xD0,0xFB,0x84,0x74,
0x1C,0x3C,0x94,0x5A,0x3F,0xF0,0x37,0x8C,0xD9,0x7E,0xA7,0x38,0xA4,0xB5,0xA7,0x25,
0x65,0x15,0x7F,0xE5,0x3B,0xD1,0x14,0x1E,0xD3,0xA8,0x47,0x2E,0xD8,0xEB,0xB0,0xAE,
0x4F,0x34,0xF4,0x52,0xC7,0x23,0x9D,0x60,0x98,0x1E,0x2C,0xFC,0xF2,0x96,0xB7,0x83
};
void PrepareBuf22()
{
byte i, j, bt, bt1, bt2, bt3;
for( i=0; i<4; i++ )
_buf22[i] = rand();
bt2 = (_buf22[1] ^ _buf22[2] ) & 0xEF;
bt3 = _buf22[1] ^ _buf22[3];
_buf22[4] = (byte)((_buf22[0] + _buf22[1] + _buf22[2]) ^ _buf22[3]);
_buf22[1] ^= ( 1 << (_buf22[2] & 7) );
bt = _buf22[2];
bt1 = _buf22[0];
i = 0;
while( i < 10 )
{
bt += _buf11[i++];
bt1 += _buf11[i++];
}
_buf22[5] = bt;
_buf22[6] = bt1;
// ----------------------------------------------------------------
_buf22[7+11] = 0;
_buf22[7+12] = 0;
for( i=0; i<10; i++ )
{
_buf22[7+11] += _buf11[i];
_buf22[7+12] ^= _buf11[i];
}
_buf22[7+11] ^= _buf11[9];
_buf22[7+12] ^= _buf11[10];
_buf22[7+13] = (_buf22[7+11] + _buf11[10]) ^ _buf22[7+12];
_buf22[7+14] = (_buf22[7+13] + _buf11[9]) ^ _buf22[7+12];
for( i=0; i<15; i++ )
{
bt = (_buf22[7+i] ^ _hash[bt2+i] );
bt = (bt << 2) | (bt >> 6);
bt ^= bt3;
_buf22[7+i] = bt;
}
}
Четвертая часть — 47 случайных байт.
8. После передачи массива из 133 байт микросхема должна либо перейти в сон до следующего появления активного высокого уровня на ножке 7, либо она автоматически перейдет в состояние сброса, в случае, если ножка 7 является сигналом сброса. И далее все повторяется, начиная с пункта 2.
Выписка выздоровевшего больного
В конце концов адаптер CX1011 был благополучно поставлен на ноги и получил вторую жизнь в новом программаторе.
Надеемся, материал данной статьи поможет читателям благополучно оживить и другие «старые» адаптеры, а возможно, изготовить свои собственные.
Автор: beketata