Защита ПО процессора S805-B

Речь пойдёт о способе защиты програмного обеспечения, который реализован в самом процессоре. В качества экспериментов я выбрал мультимедиа приставку Comigo Quattro. Цель — запустить своё ядро линукс.

Краткий обзор

На первый взгляд ПО приставки является клоном Android. Доступ по ssh закрыт. Работает только с действующим абонементом от дистрибьютора. Стоковая прошивка полностью шифрована. Всё глухо, как в танке.

Подготовка

Первым делом я подробно ознакомился с техническим описанием процессора. Порадовали меня две вещи: у процессора был интерфейс UART и он поддерживал загрузку системы с разных источников (USB, SD и т.д.), которая могла настраиваться с наружи при помощи конфигурационного регистра. На борту приставки также имелось место для mSD карты, но разьём был не припаян. Для того, чтобы найти пятачки конфигурационного регистра и пины UART мне пришлось спаять процессор и прозвонить дорожки. Когда все контакты были найдены я подключил адаптер UART и запустил приставку. На экрана высветился U-Boot. Загрузка системы прерывалась нажатием клавиш и U-Boot выходил в режим ввода, но ни на какие клавиши, кроме enter никак не реагировал. И эта лазейка тоже была закрыта.

U-boot
QA5:A;SVN:B72;POC:17F;STS:0;BOOT:0;INIT:0;READ:0;CHECK:0;PASS:0;
no sdio debug board detected
TE : 1583722
BT : 19:30:15 Apr 13 2015
PMU:NONE
##### VDDEE voltage = 0x044c

CPU clock is 792MHz

DDR mode: 32 bit mode
DDR size: 1GB
DDR check: Pass!
DDR clock: 792MHz with 2T mode
DDR pll bypass: Disabled
DDR init use : 13644 us

HHH
Boot From SDIO C
SD_boot_type: 00000002
card_type: 00000003
0x0000009f
Aml log : M8-R2048 TPL pass!
ucl decompress...pass
0x12345678
Boot from internal device 1st tSD/fSD on SDIO C

TE : 1867612

System Started


==================COMIGO BOOTLOADER==================
==========12fc92:S:Enc (Apr 13 2015 - 19:29:55)==========

clr h-ram
DRAM:  1 GiB
relocation Offset is: 2febc000
show partition table:
part: 0, name :       logo, size : 800000
part: 1, name : recovery_bak, size : 1000000
part: 2, name :   recovery, size : 1000000
part: 3, name :       boot, size : 1000000
part: 4, name :     system, size : 32000000
part: 5, name :       data, size : 8c000000
part: 6, name :      cache, size : 20800000
part: 7, name :    sec_gpt, size : end
aml_card_type=0x100
MMC:   out reg=c1108058,value=fffcf800
out reg=c1108058,value=fffcfa00
[mmc_register] add mmc dev_num=0, port=1, if_type=6
[mmc_register] add mmc dev_num=1, port=2, if_type=6
SDIO Port B: 0, SDIO Port C: 1
power init
out reg=c110804c,value=dfffffff
IR init done!
register usb cfg[0][1] = 3ff6fcf4
register usb cfg[2][0] = 3ff72a6c
NAND:  EMMC BOOT: not init nand
do not init nand : cause boot_device_flag without nand
get_boot_device_flag: init_ret -1
get_boot_device_flag EMMC BOOT:
init_part
Emmckey: Access range is illegal!
Emmckey: Access range is illegal!
Unknown partition type on device 'SDIO Port C'
Device 'SDIO Port C' wp size=8388608 port=2
[mmc_init] SDIO Port C:1, if_type=6, initialized OK!
mmc_device_init
mmc_get_partition_table
Start mmc_get_partition_table
Partition table get from SPL is :
        name                        offset              size              flag
===================================================================================
   0: pri_gpt                            0            800000                  0
   1: env                           800000            800000                  0
   2: reserved                     1000000           4000000                  0
   3: logo                         5000000            800000                  1
   4: recovery_bak                 5800000           1000000                  1
   5: recovery                     6800000           1000000                  1
   6: boot                         7800000           1000000                  1
   7: system                       8800000          32000000                  1
   8: data                        3a800000          8c000000                  4
   9: cache                       c6800000          20800000                  2
  10: sec_gpt                     eb800000            800000                  0
mmc read lba=0x8000, blocks=0x1
mmc read lba=0x8001, blocks=0x1
mmc_read_partition_tbl: mmc read partition OK!
eMMC/TSD partition table have been checked OK!
i=0,register --- emmc_key
MMC BOOT, emmc_env_relocate_spec : env_relocate_spec 59
set_storage_device_flag: store 2
[imgread]Secure kernel sz 0x5b36a0
Aml log : M8-R2048 IMG pass!
vpu clk_level in dts: 3
set vpu clk: 182150000Hz, readback: 182150000Hz(0x701)
Net:   Meson_Ethernet
init suspend firmware done. (ret:0)
cvbs trimming.1.v5: 0xa0, 0x0
upgrade_comigo_environment: expect 6 active 6
init_comigo_environment
type:flash,start to read mac...
device init start
aml_keys: version 0 can not be init 3ff72c68
current storer:emmc_key
flash init key ok!!
init flash success
all key names list are(ret=18):
uuid
serialno
mac
4:3:3:0:3:a:4:2:3:3:3:a:3:3:3:9:3:a:3:0:3:2:3:a:3:1:4:6:3:a:3:4:3:9:
mac is: 43:30:3a:42:33:3a:33:39:3a:30:32:3a:31:46:3a:34:39:
read ok!!
read mac success,mac=C0:B3:39:02:1F:49
androidboot.mac is exist in bootargs, mac=C0:B3:39:02:1F:49 androidboot.serialno=A0652602C5706198 androidboot.uuid=4a3842462f47694d364b6f544d3236596b34374e3977
BOARD VERSION=2
reboot_mode=charging
hdcp get form storage medium: auto
don't found keyname,uboot_key_read:1634
prefetch hdcp keys from auto failed
AKSV invalid
hdmi tx power init
mode = 6  vic = 4
set HDMI vic: 4
mode is: 6
viu chan = 1
config HPLL
config HPLL done
reconfig packet setting done
key save in emmc
key size=44
the key name is :
the key data is :4a3842462f47694d364b6f544d3236596b34374e3977
key size=32
the key name is :serialno
the key data is :41303635323630324335373036313938
A0652602C5706198
efuse version is not selected.
Hit ENTER key to stop autoboot:  1 tstc enter

exit abortboot: 1
COMIGO#

С оригинальным U-Boot’ом на этом этапе сделать ничего было нельзя и я решил проверить загрузку с mSD карты. Для этого я припаял разьём и соединил пару контактов конфигурационного регистра с массой, чтобы процессор грузил систему с карты. Новый U-Boot был от экспериментального борта ODROID-C, который базировался на таком же процессоре.

rvsjsvxxncmxhojpuy9gy2erpzq.png

b4mjdjc9hekn_kkmjlq_nrcyqbm.png

Собрав всё это, я вставил SD карту, включил приставку и… увидел такое:

SERIAL:4;STS:0;BOOT:1;INIT:0;READ:0;CHECK:FFFFBF00;USB:3;


И этот текст повторялся каждую секунду вновь и вновь. Процессор вовсе и не собирался грузить мой U-Boot и это меня огорчило.

Поиск причины

Мне стало ясно, что процессор — это первая инстанция, которая проверяет код на подлинность. Параметр CHECK: FFFFBF00 указывал на то, что код, загруженый с mSD карты более чем полностью по каким-то причинам не устраивал процессора. Для сравнения, загрузка оригинала начиналась в свою очередь так:

QA5:A;SVN:B72;POC:17F;STS:0;BOOT:0;INIT:0;READ:0;CHECK:0;PASS:0;


Что же проверял процессор? Этот ответ я надеялся найти в сурсах U-Boot’а для AMLogic. После недолгих поисков, я увидел, что SPL часть от U-Boot’a заканчивается вот такой структурой

   typedef struct {
      unsigned int   nSizeH;         ///4@0
      struct st_secure{
         unsigned int   nORGFileLen;  ///4@4
         unsigned int   nSkippedLen;  ///4@8
         unsigned int   nHASHLength;  ///4@12
         unsigned int   nAESLength;   ///4@16
         unsigned char   szHashKey[32];//32@20
         unsigned char   szTmCreate[24];   //24@52
         unsigned char   szReserved[60];   //60@76
      }secure; //136@136
      unsigned char   szAES_Key_IMG[60];//60@136
      unsigned char   szTmCreate[48];   //48@196
      unsigned int   nSizeT;         ///4@244
      unsigned int   nVer;           ///4@248
      unsigned int   unAMLID;        ///4@252
   }st_aml_chk_blk; //256

Небольшое отступление. Двухступенчатая загрузка системы очень часто применяется на встроеном оборудовании. Т.е. ROM процессора грузит сначала небольшую часть кода (так называемый SPL) и передаёт ему управление. Тот в свою очередь после основных настроек грузит вторую, большую часть (TPL) и передаёт ей управление. Ну, а TPL после окончательных настроек грузит ядро и запускает его.

Забегая немного вперёд, скажу, что SPL состоит из 32КБ, в конце которого находится структура st_aml_chk_blk, которая в расшифрованом виде выглядит вот так:

atmooci0f_ac-d7ayobjcmmgrfc.png
Стало быть этот блок данных использует процессор для проверки SPL.

В поисках решения

Чтобы заставить процессор загрузить мой U-Boot, я испробовал множество вариантов. Но все они не приносили желаемого результата — процессор упорно выдавал CHECK: FFFFBF00. Я постепенно приходил к выводу, что защита сделана на 100% и дыр нет.
После очередной неудачной попытки я оставил приставку и пошёл на кухню (за чаем, конечно), раздумывая подключать JTAG, а когда вернулся, меня ждал вот такой результат:

SERIAL:4;STS:0;BOOT:1;INIT:0;READ:0;CHECK:FFFFBF00;USB:3;SERIAL:4;STS:0;BOOT:1;INIT:0;READ:0;CHECK:FFFFBF00;USB:3;SERIAL:4;STS:0;BOOT:1;INIT:0;READ:0;CHECK:FFFFBF00;USB:3;SERIAL:4;STS:0;BOOT:1;INIT:0;READ:0;CHECK:FFFFBF00;USB:3;SERIAL:4;STS:0;BOOT:1;INIT:0;READ:0;CHECK:FFFFBF00;USB:3;SERIAL:4;STS:0;BOOT:1;INIT:0;READ:0;CHECK:FFFFBF00;USB:3;SERIAL:4;STS:0;BOOT:1;INIT:0;READ:0;CHECK:0;PASS:1;
-----------------------------------------------------------------------
* Welcome to Hardkernel's ODROID-C... (Built at 19:33:00 Dec  8 2014) *
-----------------------------------------------------------------------
CPU : AMLogic S805
MEM : 1024MB (DDR3@792MHz)
BID : 
S/N :
***** Warning!! *****************************************************
* This board have not been autorized or product keys are not valid. *
* Please contact with Hardkernel or your distributor                *
*********************************************************************


О как! После некоторого времени (это было примерно секунд 40) процессор выдавал CHECK:0; PASS:1; и грузил мой U-Boot. Он конечно зависал потом, но это не имело значения, доступ к системе был получен.

Анализ


Первым делом я написал программу, которая сделала дамп ROM’а процессора и фьюза (это такой участок памяти в процессоре, который может быть прошит только один раз. Здесь как раз и хранятся ключи AES, public RSA и некоторые конфигурации процессора). Заполучив таким образом ключ AES я расшифровал оригинальный U-Boot и проанализировал ROM. Принцип загрузки SPL был таков: ROM первым делом считывал конфигурацию из фьюза и сохранял её как переменную. Далее он грузил 32 КБ с флэшки в память и смотрел, что показывала конфигурация на предмет проверки сигнатуры и шифрования. В нашем случае ROM затем вычитывал экспоненту e и модуль N из фьюза, собирал PublicKey (e, N) с которым он расшифровывал st_aml_chk_blk, затем вырешивал sha2 от SPL без st_aml_chk_blk и сравнивал то что получилось с ключом из st_aml_chk_blk. И в случае несовпадения дальнейших действий не предпринимал. Так что же заставило процессор выполнить мой SPL? Сигнатура ведь по-любому не совпадала. Для поддержки RSA в ROM была включена библиотека PolarSSL. ROM управлял свободной памятью исползуя собственный механизм (примерно как malloc и memfree). Перед использованием функций из библиотеки PolarSSL ROM выделял ей определенный кусок памяти, но в нокоторых случаях не всегда освобождал его. Например тогда, когда функция RSA отваливалась из-за невозможности расшифровать инвалидный блок, который я использовал в своём SPL в этой попытке. Что же получалось? ROM выделял память, пробовал расшифровать мою поддельную сигнатуру, забывал освободить память, выдавал ошибку CHECK: FFFFBF00 и цикл повторялся. В один прекрасный момент очередной кусок новой памяти затерал переменную из фьюза, которую ROM сохранял в самом начале. В следующем цикле в этой переменной уже было значение 0, что значило: просто грузи всё что есть и запускай.
Дальше в принципе не было ничего интересного. Я переделал оригинальный U-Boot, убрал проверку TPL, ядра и вернул ему обратно дар речи, чтобы он реагировал на команды. Расшифровав ядро и собрав минималку на BusyBox я запустил линукс из переделанного U-Boot’а, который начинал грузиться через 40 секунд.

© Habrahabr.ru