Расшифровка обновлений одного популярного сотового модема: метод Дмитрия Склярова

fe744a01baec473fbf4c0b8abedc3083.png

Иногда хочется заглянуть в код прошивки какого-нибудь устройства. Но кроме самой прошивки, которая зашифрована, ничего нет. И как реверсеру с этим жить? В статье рассмотрена реальная ситуация, когда при помощи базовых знаний в computer science и логики удалось решить почти бесперспективную задачу.

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

1. Определение структуры


Начнем с определения структуры файлов прошивок. Есть три файла обновлений разных версий для одного модема:

  • v2.8_image.bin
  • v3.7_image.bin
  • v3.7.4_image.bin


При внимательном взгляде все три файла имеют внутри структуру, описываемую схемой TLV (Tag-Length-Value). Например, для v3.7.4_image.bin:

fd97a26ea2e14776864e12f769a8eb78.png

Все значения Little-endian, Tag имеет длину 16 бит, Length — 32 бита.
На первом уровне вложенности в файле присутствует только тег 0×7240, и данные для него занимают весь файл. На втором уровне вложенности (внутри данных тега 0×7240) расположен тег 0×0003 (0×0A байт), потом 0×0000 (0×4BDE0E байт), потом теги 0×0001и 0×0002 (на скриншоте не поместились). На третьем уровне вложенности (внутри данных тега 0×0003) инкапсулирован тег 0×0002, хранящий 4-байтовый номер версии файла 030704FF (если отбросить все FF, то получится 3.7.4).

Внутри остальных тегов, расположенных на втором уровне вложенности (теги 0×0000, 0×0001 и 0×0002), хранятся описания отдельных файлов, «упакованных» в один образ.
Для каждого файла указано имя (тег 0×0001), флаги (тег 0×0002), размер (тег 0×0003), некоторое 16-байтовое значение (тег 0×0004) и собственно данные файла (тег 0×0005).

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

10629df7164949909cf52e073cc40a63.png

Таким образом, из файлов прошивок можно извлечь зашифрованные данные для всех составляющих (CPUImage, AutoInstall и WebUI). Как оказалось, содержимое AutoInstall во всех трех версиях прошивки совпадает, внутренности WebUI одинаковы для v3.7 и v3.7.4, но отличаются в v2.8, а CPUImage уникален для каждой версии.

2. Догадки по алгоритмам


Тег 0×0004 на третьем уровне вложенности содержит 16-байтовый набор данных с высокой энтропией. Вполне возможно это значение хеша, а самый популярный 128-битовый хеш — MD5.

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

aa7c1f6eef78454b800a6a00f6d0b5b1.png

Однако если попытаться найти одинаковые последовательности в рамках одного файла — длинных повторов не встретится.

Подобный эффект возникает при использовании шифрования путем наложения константной гаммы очень большой длины. И самый популярный алгоритм шифрования, который именно так работает, — RC4.

3. Атака на потоковый шифр с константным ключом


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

Взяв файлы AutoInstall и WebUI, получим интересные результаты:

00000000: EB 3C 90 6D 6B 64 6F 73 66 73 00 00 02 04 01 00  л<ђmkdosfs  ☻
00000010: 02 00 02 F8 0F F8 03 00 20 00 40 00 00 00 00 00  ☻ ☻ш☼ш   @
00000020: 00 00 00 00 00 00 29 6E 1F 3B 15 47 43 54 2D 4C        )n▼;§GCT-L
00000030: 54 45 20 20 20 20 46 41 54 31 32 20 20 20 0E 1F  TE    FAT12   ♫▼
00000040: BE 5B 7C AC 22 C0 74 0B 56 B4 0E BB 07 00 CD 10  ѕ[|¬"Аt♂Vґ♫»• Н►
00000050: 5E EB F0 32 E4 CD 16 CD 19 EB FE 54 68 69 73 20  ^лр2дН▬Н↓люThis
00000060: 69 73 20 6E 6F 74 20 61 20 62 6F 6F 74 61 62 6C  is not a bootabl
00000070: 65 20 64 69 73 6B 2E 20 20 50 6C 65 61 73 65 20  e disk.  Please
00000080: 69 6E 73 65 72 74 20 61 20 62 6F 6F 74 61 62 6C  insert a bootabl
00000090: 65 20 66 6C 6F 70 70 79 20 61 6E 64 0D 0A 70 72  e floppy and♪◙pr
000000A0: 65 73 73 20 61 6E 79 20 6B 65 79 20 74 6F 20 74  ess any key to t
000000B0: 72 79 20 61 67 61 69 6E 20 2E 2E 2E 20 0D 0A 00  ry again ... ♪◙
000000C0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
...
00008800: 02 43 44 30 30 31 01 00 00 20 00 20 00 20 00 20  ☻CD001
00008810: 00 20 00 20 00 20 00 20 00 20 00 20 00 20 00 20 


4. Получение начала гаммы


Современные сотовые модемы любят при подключении создавать виртуальный CD-ROM, с которого можно установить драйверы или сопутствующее ПО. Вероятно, и тут использована та же идея.

Правда, при подключении модема к современным операционным системам (Windows 7/8, Linux, MacOS X) этот CD-ROM или не появляется вообще, или присутствует всего долю секунды, после чего исчезает. Пришлось найти ноутбук 2002 года с Windows XP, где при подключении модема CD-ROM тоже исчезал вскоре после появления, но это занимало целых 5 секунд. За это время можно было успеть прочитать все сектора логического тома и получить образ размером 606 208 == 0x94000 байт, что соответствует размеру файла AutoInstall. И значение MD5 от прочитанного образа оказалось равно 897279F34B7629801D839A3E18DA0345, что соответствует значению тега 0×0004 для этого файла.

Теперь остается поXORить прочитанный образ с содержимым AutoInstall и получить первые 600 килобайт гаммы. Этой гаммой мы можем расшифровать начала файлов CPUImage и WebUI (имеющих длину 4 971 976 и 2 093 056 байт соответственно).

5. Реконструкция образа FDD


Если расшифровать начало файла WebUI (первые 606 208 байт), а остальное заполнить нулями, и проинтерпретировать все как образ FAT, будет видна структура файловой системы и даже доступно содержимое некоторых файлов:

          Name           | Size |  Date  |Time
bru                      |Folder|31.05.12|22:17
cgi-bin                  |Folder|31.05.12|22:17
cors                     |Folder|31.05.12|22:17
css                      |Folder|31.05.12|22:17
eng                      |Folder|31.05.12|22:17
img                      |Folder|31.05.12|22:17
js                       |Folder|31.05.12|22:17
ru                       |Folder|31.05.12|22:17
name.html                |  2248|31.05.12|22:17
easyXDM.js               |101924|31.05.12|22:17
easyXDM.debug.js         |113900|31.05.12|22:17
easyXDM.min.js           | 19863|31.05.12|22:17
easyXDM.Widgets.js       | 11134|31.05.12|22:17
easyXDM.Widgets.debug.js | 11134|31.05.12|22:17
easyXDM.Widgets.min.js   |  3114|31.05.12|22:17
json2.js                 | 17382|31.05.12|22:17
easyxdm.swf              |  1758|31.05.12|22:17
MIT-license.txt          |  1102|31.05.12|22:17


Если при подключенном модеме зайти браузером на веб-интерфейс по адресу http://<имя хоста для модема>/dir, то будет видна в точности такая же файловая система, и любой файл можно будет скачать.

Таким образом, чтобы восстановить образ диска WebUI, надо разложить скачанные через веб-интерфейс файлы по тем местам, где они должны быть в соответствии с данными в boot-секторе, таблице FAT и описаниях директорий. Единственная сложность — папка «ru» в корне. Кластер с описанием файлов в этой папке не попадает в первые 606 208 байт, и его содержимое надо воссоздавать вручную.

Согласно информации из веб-интерфейса, в директории «ru» должны быть следующие файлы:

          Name           | Size |  Date  |Time
Manualupdate.html        |  3981|31.05.12|22:17
Index.html               |  5327|31.05.12|22:17
Network.html             |  3328|31.05.12|22:17


К счастью, в корне есть папка «eng», в которой располагаются файлы с такими же именами и датами создания. И чтобы получить правильные данные для папки «ru» надо всего лишь исправить:

  • номер стартового кластера текущей директории,
  • размер каждого файла,
  • номера стартовых кластеров каждого файла.


Номер кластера директории «ru» (0×213) можно узнать из корневой директории.
Размеры файлов (3981==0xF8D, 5327==0×14CF и 3328==0xD00 соответственно) узнаем из веб-интерфейса.

Номера стартовых кластеров для файлов придется угадывать, но это не очень сложно. Согласно информации в boot-секторе, каждый кластер занимает 4 сектора или 2048 байт. Для директории «ru» достаточно одного кластера, для файлов Manualupdate.html и Network.html — двух, и для Index.html понадобится три кластера. Поскольку кластеры при записи на пустой диск обычно выделяются последовательно, файлы будут начинаться в кластерах 0×214, 0×216 и 0×219 соответственно. Восстановленные данные для директории «ru» будут выглядеть так:

00000000: 2E 20 20 20 20 20 20 20 20 20 20 10 00 00 2C AA  .          ►  ,к
00000010: BF 40 BF 40 00 00 2C AA BF 40 13 02 00 00 00 00  ┐@┐@  ,к┐@☻
00000020: 2E 2E 20 20 20 20 20 20 20 20 20 10 00 00 2C AA  ..         ►  ,к
00000030: BF 40 BF 40 00 00 2C AA BF 40 00 00 00 00 00 00  ┐@┐@  ,к┐@
00000040: 42 68 00 74 00 6D 00 6C 00 00 00 0F 00 56 FF FF  Bh t m l   ☼ V  
00000050: FF FF FF FF FF FF FF FF FF FF 00 00 FF FF FF FF                  
00000060: 01 6D 00 61 00 6E 00 75 00 61 00 0F 00 56 6C 00  m a n u a ☼ Vl
00000070: 75 00 70 00 64 00 61 00 74 00 00 00 65 00 2E 00  u p d a t   e .
00000080: 4D 41 4E 55 41 4C 7E 31 48 54 4D 20 00 00 2C AA  MANUAL~1HTM   ,к
00000090: BF 40 BF 40 00 00 2C AA BF 40 14 02 8D 0F 00 00  ┐@┐@  ,к┐@¶☻Н☼
000000A0: 41 69 00 6E 00 64 00 65 00 78 00 0F 00 33 2E 00  Ai n d e x ☼ 3.
000000B0: 68 00 74 00 6D 00 6C 00 00 00 00 00 FF FF FF FF  h t m l         
000000C0: 49 4E 44 45 58 7E 31 20 48 54 4D 20 00 00 2C AA  INDEX~1 HTM   ,к
000000D0: BF 40 BF 40 00 00 2C AA BF 40 16 02 CF 14 00 00  ┐@┐@  ,к┐@▬☻╧¶
000000E0: 41 6E 00 65 00 74 00 77 00 6F 00 0F 00 98 72 00  An e t w o ☼ Шr
000000F0: 6B 00 2E 00 68 00 74 00 6D 00 00 00 6C 00 00 00  k . h t m   l
00000100: 4E 45 54 57 4F 52 7E 31 48 54 4D 20 00 00 2C AA  NETWOR~1HTM   ,к
00000110: BF 40 BF 40 00 00 2C AA BF 40 19 02 00 0D 00 00  ┐@┐@  ,к┐@↓☻ ♪
00000120: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00


Аккуратно собрав реконструированную папку «ru» и содержимое всех файлов, полученных из веб-интерфейса, в образ диска (с учетом того, что первый кластер соответствует сектору 0×23), мы получаем plain-text-версию файла WebUI, значение MD5 для которого совпадает с 48D1C3194E45472D28ABFBEB6BBF1CC6 из заголовка обновления.

Теперь у нас есть расшифрованные файлы AutoInstall и WebUI, и мы знаем первые 2 093 056 байт гаммы.

6. Заглянем в CPUImage

После того как мы расшифровали первые 2 мегабайта CPUImage, наконец имеет смысл запустить дизассемблер. После определения системы команд процессора (ARM Little-Endian), базового адреса загрузки (необходимо выкинуть первые 0×34C байт файла) и локализации места расшифровки обновлений можно увидеть вот такой код:

ROM:0008ADD0 loc_8ADD0 
ROM:0008ADD0                 LDR             R1, =byte_2ADC60
ROM:0008ADD4                 LDRB            R2, [R1,R0]
ROM:0008ADD8                 LDRB            R1, [R4]
ROM:0008ADDC                 ADD             R0, R0, #1
ROM:0008ADE0                 ADD             R2, R2, R1
ROM:0008ADE4                 ADD             R2, R2, R6
ROM:0008ADE8                 AND             R6, R2, #0xFF
ROM:0008ADEC                 LDRB            R2, [R10,R6]
ROM:0008ADF0                 STRB            R2, [R4],#1
ROM:0008ADF4                 STRB            R1, [R10,R6]
ROM:0008ADF8                 MOV             R1, #0x15
ROM:0008ADFC                 BL              sub_27C0EC
ROM:0008AE00                 SUBS            R11, R11, #1
ROM:0008AE04                 AND             R0, R1, #0xFF
ROM:0008AE08                 BNE             loc_8ADD0


Это не что иное, как загрузка ключа шифрования, расположенного по адресу 0×2ADC60 и имеющего длину 0×15 байт, в алгоритм RC4. Однако 0x2ADC60==2’808’928, то есть ключ, располагается за пределами области, для которой нам известна гамма.

Правда, у нас целых три версии обновлений. Вдруг в v3.7 или v2.8 ключ в более удачном месте? Увы, в более ранних прошивках адрес расположения ключа будет 0x2AD70C и 0x2A852C, что тоже за пределами расшифрованной области :(

7. И снова XOR


Если поXORить между собой CPUImage от обновлений v3.7 и v3.7.4, то по адресу 0x34C + 0x2AD70C == 0x2ADA58 мы обнаружим строчку «SungKook «James» Shin», которая и является тем самым ключом RC4, на котором зашифрованы все файлы, входящие в обновления.

Остается убедиться, что гамма на выходе RC4, инициализированного этим ключом, совпадает с той, что мы получили ранее из AutoInstall и WebUI, и что MD5 от расшифрованного CPUImage совпадает со значением из заголовка обновления.

Теперь можно приступать к анализу собственно прошивки, но это уже совсем другая история.

Автор: Дмитрий Скляров, руководитель отдела исследований приложений Positive Technologies

© Habrahabr.ru