Переходник sata usb

Находка № 3

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

Обработчик команды из регистра с адресом 0x800F

Понимая, что где-то обязательно должны обрабатываться SCSI-команды, я стал среди них искать байты, с которыми на рисунке выше сравнивается содержимое регистра с адресом 0x800F. Оказалось, что первые четыре ветки проверяют команды Read(10), Write(10), Read(16), Write(16). Сомнений в том, что это обработчик команд SCSI, больше не осталось. Далее я просмотрел функцию, которая вызывается в случае, если пришедшая команда не Read/Write (u_Switch). Она, в зависимости от байта в регистре с адресом 0x16A (значение взято из 0x800F), считывает адрес, на который мы попадем при выходе их этой функции. Это похоже на .

Switch SCSI-команд

Так как я уже определил байт, с которым сравниваю пришедшую в адаптер SCSI-команду, то быстро расставил соответствие адресов по командам. Так, например, на рисунке выше видно, что если бы в регистре с адресом 0x16A оказался байт 0x1A, то после выхода из функции u_Switch мы бы перешли по адресу 0x1B85. Интересно, что не все байты, с которыми происходит сравнение в u_Switch, были определены в стандарте SCSI. То есть адаптер может обработать байты 0хЕ6 или 0xDF, но они не закреплены стандартом.

Как видим, адаптер может выполнять кастомные команды и для них есть функции-обработчики.

Страница 13 из Universal Serial Bus Mass Storage Class

Обратим внимание на смещение 0x0F относительно адреса 0x8000. Перед обработчиком именно из регистра с адресом 0x800F считывается команда SCSI

Если внимательно прочитать таблицу на рисунке выше, то можно увидеть, что в Command Block Wrapper (CBW) поле CBWCB тоже имеет смещение 0x0F. Получается, что адреса RAM-памяти ASM1051, начиная с 0x8000, могут быть USB-буфером, как показано в таблице ниже.

Адрес памяти Описание
0х8000-0х8003 dCBWSignature (USBC – в случае получения пакета)
0х8004-0х8007 dCBWTag
0х8008-0х800B dCBWDataTransferLength
0х800C bmdCBWFlag
0х800D bCBWLUN
0х800E bCBWCBLength
0х800F-0х801F CBWCB – SCSI-команда и ее параметры

На рисунке ниже показан участок кода, где происходит сравнение со строкой USBC (такой должна быть сигнатура dCBWSignature) и расположена предполагаемая сигнатура с адреса 0х8000. Думаю, этого достаточно, чтобы убедиться в том, что USB-буфер расположен в RAM-памяти начиная с 0х8000.

Проверка поля dCBWSignature на совпадение со строкой USBC

Выводы:

  1. МК ASM1051 умеет обрабатывать не только SCSI-команды, которые описаны в стандарте.
  2. Начальный адрес USB-буфера – 0х8000. SCSI-команда располагается в регистре с адресом 0х800F, значит дальше будут приходящие данные/аргументы команд.

Находка № 1

Интересно, что в ответ на мой запрос INQUIRY (SCSI-команда) я получил ответ, содержащий две строки, которые мы видели в начале памяти ПЗУ. Я, конечно, тут же изменил эти строчки в памяти эмулятора, ожидая при запросе INQUIRY увидеть то, что я написал. Такая наивная мечта быстро рухнула. Теперь же в ответ на команду я видел другую строку, ASM1051 не запрашивал большую часть памяти у ПЗУ. МК читал только первые 0х80 байт и все. В архитектуре 8051 может использоваться масочная (аппаратная) прошивка, видимо, ASM1051 начинал загружаться с нее.

Так стало понятно, что первые 0х80 байт действительно важны, а менять их просто так не получится. Я решил подробнее изучить запросы, которые делает МК по SPI до загрузки кода.

SPI-запрос данных в ПЗУ

Интересным показались два запроса двух байтов. Поиск в IDA 0х00, 0х80 и 0хEB дал огромное количество результатов, которые анализировать я не стал, а вот байт 0х5А попадался реже.

Сравнение с байтом 0х5А. Подсчет checksum-8

Буквально шестой клик привел меня к участку кода, изображенному на рисунке выше. Видно, что значение из регистра с адресом 0х807E сравнивается с 0х5А. Потом считывается checksum-8 для значений, расположенных с адреса 0х8004 до 0х807E. Далее значение по адресу 0х807F сравнивается с полученной ранее суммой.

Начало памяти в ПЗУ

Такие смещения напомнили начало дампа памяти из ПЗУ. На рисунке выше видно, что в адресе 0х7Е находится байт 0x5A. А если посчитать checksum-8 для байтов с позиции 0х04 до 0х7E, то получим 0хА7, а такое значение как раз лежит по адресу 0х7F.

Аналогичным образом удалось найти и подсчет чек-суммы для байтов с адреса 0х0082 до 0х807F (видимо это весь код), которая сверяется с байтом по адресу 0х8083. А по адресу 0х8082 снова лежит значение 0х5А.

Да, это уже чуть сложнее, чем просто менять строчки в памяти. Их я тоже поменял, но и чек-суммы для нового файла посчитал и записал в нужные места. После этого в ответ на SCSI-команду INQUIRY я увидел свои строчки.

Выводы:

  1. Во время загрузки ASM1051 пытается загрузить код из ПЗУ.
  2. Первым делом ASM1051 сравнивает байт checksum-8 с адреса 0х04 до 0х7E со значением по адресу 0х7F.
  3. Если сравнение чек-суммы для преамбулы проведено успешно, то можно считать ее для «кода» (адреса с 0х0082 до 0х807F). Эту сумму ASM1051 сравнивает со значением по адресу 0х8083 и проверяет, что по адресу 0х8082 находится байт 0х5А.
  4. Если все проверки верны, то ASM1051 загружается из ПЗУ, в противном случае использует масочную прошивку.

Находка № 6

Аппаратный регистр, найденный во время исследования обработчика SCSI-команды MODE SENSE, очень похож на GPIO. Чтобы это подтвердить, я решил прикасаться к ножкам ASM1051 резистором под напряжением и читать значение регистра (SCSI-командой 0хЕ4) с адресом 0хС884. Для этого я написал скрипт на Python, использующий кастомные SCSI-команды, который мониторит значение в регистре 0хС884 и выводит его на ПК.

Биты 0xC884 7 6 5 4 3 2 1
Ножка ASM1051 37 9 10 45 44

После проведения подобного эксперимента я составил таблицу, в которой отобразил, какие биты в регистре 0хС884 изменялись при касании ножек ASM1051 резистором. Получается, что исследуемый регистр тесно связан с GPIO, но попытка записи в него (SCSI-командой 0хЕ5) успехом не увенчалась – значение не менялось.

Тогда я решил, что этот регистр либо только для чтения, либо где-то запрещается в него запись на аппаратном уровне. Если бы, например, ножки МК были изначально настроены только на чтение, тогда, наверное, и запись в регистр 0хС884 могла бы быть недоступна.

В общем, для того чтобы найти регистры, связанные с GPIO, я пробежался по коду начальной инициализации МК. Выписал все регистры, адреса которых близки к 0хС884. Таких у меня получилось около 10. Напоминаю, что десятая ножка МК подключена к светодиоду на плате, она соответствует второму биту в регистре 0хС884. Это сократило список из десяти подозреваемых регистров до одного – 0хС880, так как в нем устанавливается второй бит во время инициализации МК (видимо, пин настраивается как выход). Факт интересный, но мое предположение, что регистр 0хС880 отвечает за настройку направления порта (вход/выход), а регистр 0хС884 содержит значение порта, нужно все-таки проверить.

Во время работы адаптера я записал единички в 0хС880 на места тех битов, которые в регистре 0хС884 связаны с ножками МК. После этого изменение регистра 0хС884 стало возможным. К тому же его изменение соответствовало логическим уровням на ножках ASM1051.

Вывод:

Есть возможность использовать GPIO на ASM1051. Регистр с адресом 0хС880 содержит настройку вход/выход для порта I/O. Регистр 0хС884 содержит значение порта I/O.

Находка № 2

Во время рассмотрения и комментирования функций я обнаружил, что очень часто в коде используется функция PRINTF (я так назвал ее). Интересно в ней то, что перед ее вызовом в регистр R7 записывается печатный символ.

Функция PRINTF в IDA Pro

Сама функция была представлена на рисунке выше. Давайте разберемся с ней. Первым делом нужно переместить значение из регистра с адресом 0x7F6 в аккумулятор. Если там оказался ноль, то выходим из функции. Самое интересное происходит, если там окажется не ноль. Тогда значение регистра R7 перемещается в регистр с адресом 0xC001, а, как мы помним, перед вызовом этой функции в R7 записывается печатный символ. Далее следует проверка, равно ли значение в R7 коду символа «.» или «-», если нет, то выходим из функции. А вот если сравнение оказалось удачным, то функция берет значение из регистра с адресом 0x16A и перемещает его в 0xC001, но делает это хитро. Например, вместо байта 0х41 (символ «А» в ASCII) функция переместит в 0хС001 байт 0х34 (символ «4» в ASCII), а потом 0х31 (символ «1» в ASCII). Снова выходим из функции.

Я выяснил, что проверка в начале функции не может быть пройдена, так как регистр с адресом 0x7F6 инициализируется нулем, потом в коде не изменяется. То есть данная функция отключена программистом, хотя и осталась скомпилированной. То, что в регистр 0хС001 только записываются байты (а иногда по два подряд), натолкнуло на мысль, что это, скорее всего, аппаратный регистр.

Все это напоминает UART. Чтобы выяснить, так ли это, необходимо сделать следующее:

  1. Определить ножки на ASM1051, куда выведен UART.
  2. Определить параметры UART (скорость, четность, количество стоп-битов).
  3. Хорошо бы включить UART в коде (судя по всему, он выключен).

Выглядит все достаточно просто: можно по очереди прикасаться к ножкам логическим анализатором и искать ту, на которой будет виден момент посылки UART. При наличии сигнала скорость можно определить по времени импульсов. С остальными параметрами тоже все понятно, достаточно лишь увидеть момент посылки байта на анализаторе.

Чтобы «включить» данную функцию, можно записать нули вместо первых трех строк, где выполняется проверка значения в регистре с адресом 0х7F6. Для этого еще раз открываю прошивку в WinHex.

Выделены шесть байтов, которые нужно обнулить

В редакторе меняю нужные шесть байтов на нули. Теперь прошивка готова и ее можно загрузить в эмулятор ПЗУ. Если предположить, что функция для вывода байтов в UART включена, а ее вызов расположен очень часто по всему коду, то можно ожидать, что байты должны «полететь» из UART при работе адаптера. Надеюсь увидеть трейсер, который сигнализирует байтами в UART о том, какая часть кода исполняется.

Как я уже писал выше, для того чтобы найти нужные ножки Rx и Tx, можно смотреть логическим анализатором по очереди все. Однако я предположил, что Rx и Tx на ASM1051 находятся там же, где и у ASM1053 – ножки 40 и 41 соответственно. Прикладываю щуп анализатора к пину 41 (предполагаемый Tx) и вижу что-то похожее на искомый сигнал:

Временная диаграмма с ножки 41 – Tx

Для того чтобы подключить преобразователь USB-UART и наблюдать в терминале приходящие печатные символы, пришлось припаять два тонких проводка прямо на плату адаптера и закрепить термоклеем.

Два проводка припаяны к RX и TX

Немного изучил диаграмму с рисунка «Временная диаграмма с ножки 41 – Tx»: время одного импульса, судя по всему, – 1 мкс, а для шести бит – 6,3 мкс. Пересчитав значение в боды, получил около 950000 бод, ближайшая стандартная скорость UART – 921600 бод. Думаю, такое расхождение получается из-за погрешности измерения логическим анализатором, я взял не самый достойный аппарат, а китайского «малыша». После установки параметров в окне программы Terminal 1.9b я смог наблюдать приходящие байты с МК ASM1051 во время его работы.

Окно программы Terminal 1.9b во время работы адаптера

Вывод:

В МК ASM1051 есть аппаратный модуль UART. Регистр для отправки данных имеет адрес 0хС001. Скорость передачи данных – 921600 бод. Имеется один стоп-бит. Ножка 41 – Tx, а 40 – Rx (хотя это не точно).

Находка № 5. Продолжение

После исследования GPIO-регистров стало ясно, что 45-я ножка МК связана с первым битом в регистре с адресом 0хС884. Именно первый бит из этого регистра влияет на значение бита WP, который отправляется по USB. Теоретически можно попробовать менять значение на 45-й ножке МК и ожидать, что запись на HDD, подключенный через адаптер к ПК, будет невозможна.

Попытка изменить текстовый файл на HDD, после подачи логической единички на 45-ю ножку МК

Проверить это было совсем просто. Тем же резистором я подтянул к GND 45-ю ножку МК и попробовал открыть файл, внести изменения и сохранить на HDD. Результат представлен на рисунке выше.

Вывод

Воздействием на 45-ю ножку ASM1051 можно включать и выключать защиту от записи на HDD.

Разработка собственного устройства

На этом этапе я закончил исследование USB-SATA-адаптера. Во время реверса ПО была найдена возможность удобного обновления прошивки ASM1051. Также я узнал, какие чек-суммы нужно посчитать, чтобы МК принял новую прошивку. Мне стало известно, как настраивать и управлять GPIO. А самое интересное – на ASM1051 есть ножка, при воздействии на которую можно защитить от записи HDD. Учитывая, что профильное направление, по которому я учусь («Проектирование и технология производства электронной аппаратуры»), тесно связано с разработкой электроники, было решено, что часть стажировки я могу посвятить разработке собственного USB-SATA-адаптера на основе ASM1051.

Признаюсь, что на первой итерации разработки платы была допущена ошибка с footprint для ASM1051, так как я взял размеры из datasheet на ASM1053. Он оказался немного меньше МК, но для проверки работы устройства я припаял ASM1051 на коротких проводках.

ASM1051 припаян на проводках к плате

Старшие коллеги мне напомнили, что следовало бы для начала создать 3D-модель платы, а затем собрать образец.

3D-модель разработанной платы и фотография собранного образца

В данное устройство была добавлена кнопка с фиксацией для управления функцией WP. Неиспользуемые GPIO ASM1051 выведены на внешний разъем так же, как и UART. Кроме того, добавлена кнопка отключения питания для SATA, чтобы была возможность перезагрузки HDD. Неудобный и вечно вываливающийся разъем USB 3.0 Micro-B заменен на более современный Type-C. Некоторым HDD может быть мало питания по USB, а для HDD 3.5″ необходимо +12 В, поэтому на разработанной плате есть возможность подключать внешнее питание от 12 до 21 В. Ниже представлена фотография собранного устройства.

Фотография разработанного устройства в корпусе

Заключение

В завершение этого наивного для профессионалов повествования, хотелось бы подвести небольшой итог моей стажировки.

За очень короткое время мне удалось немного проникнуть за кулисы реверс-инжиниринга, область не очень освещенную, но жутко интересную. Большим плюсом стажировки был дружный и высококвалифицированный коллектив «НТЦ «Вулкан», который всегда мог помочь и пролить свет на интересующие темы. На меня было потрачено много сил и времени, за что всем причастным выражаю огромные благодарности.

Как мне кажется, я прошел все (ну, или почти все) этапы исследования embedded-устройств. Конечно, мне помогали, направляли и наставляли намного более опытные исследователи. Все время меня не покидало чувство, что знаю я мало, а необходимо знать все, наверное, именно поэтому направление реверса стало столь желанным для меня.

За время стажировки я прочитал много документов, стандартов и datasheets, пытался выцепить важные вещи, чтобы их применять

Но уже на второй неделе я понял, что в реверсе важно ВСЕ!

Raccoon Security – специальная команда экспертов НТЦ «Вулкан» в области практической информационной безопасности, криптографии, схемотехники, обратной разработки и создания низкоуровневого программного обеспечения.Мы постоянно проводим такие индивидуальные стажировки и будем благодарны, если вы поделитесь ссылкой на эту статью с теми, кому это может быть интересно.Оставить заявку на прохождение стажировки можно тут.

Находка № 4

Зная, что МК может обработать нестандартные команды, захотелось узнать, что же они делают. Большинство из них мне достаточно быстро покорилось. Не буду приводить исследование кода этих команд, так как это долго и может быть материалом для отдельной статьи с названием «Ассемблер – это просто», опишу полученные результаты в таблице ниже.

SCSI-команда Описание команды
0хЕ0 Позволяет прочитать первые 0х80 байт из ПЗУ. В дальнейшем эту часть памяти я буду называть преамбулой (да, те самые 0x80 байт, в которых есть строчки и )
0хЕ1 Записывает первые 0х80 байт в ПЗУ
0хЕ3 Записывает в память ПЗУ с 0х80 адреса любое количество байтов. В качестве аргумента (как оказалось) передается размер посылки
0хЕ4 Позволяет прочитать блок байтов RAM-памяти ASM1051. В качестве аргумента принимает начальный адрес и количество байт, которое читаем
0хЕ5 Записывает один байт в RAM по адресу
0хЕ7 Читает последний принятый пакет в ATA-буфер
0хЕ8 Перезагружает устройство

Признаюсь, что не все команды я разгадал путем чтения функций в IDA. Уткнувшись во время исследования в стену, вспомнил, что видел ПО и много прошивок для ASM1051, когда искал документацию на него. С использованием найденного софта можно обновить прошивку и перезагрузить устройство. Поэтому я решил, что самое время воспользоваться Device Monitoring Studio и посмотреть, что же отправляет ПК на адаптер во время обновления.

Таким образом, удалось понять, как происходит процесс обновления прошивки: сначала отправляется преамбула (командой 0xE1), далее командой 0хЕ3 записывается код, затем все это шлифуется перезагрузкой (команда 0хЕ8). Для быстрого и удобного обновления я написал скрипт на Python, который вставляет в преамбулу нужные строки, затем считывает чек-суммы и обновляет устройство. Теперь мне уже не нужен эмулятор, я получил возможность заливать прошивку в ASM1051 через USB, можно вернуть родное ПЗУ на плату.

Выводы

Чтобы обновить прошивку, необходимо выполнить последовательно три SCSI-команды: 0xE1, 0хЕ3 и 0хЕ8.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Adblock
detector