Дайте две или уязвимость защиты многостраничных PIC18

    Эта статья не руководство к действию хакеров, это подсказка, как правильно используя предоставленные MICROCHIP инструменты защитить прошивку внутри чипа.

    Не помню уже сколько лет назад это было, натолкнулся я на статью "Heart of darkness - exploring the uncharted backwaters of hid iСlass security by Milosch Meriac". Суть статьи в проблемах безопасности iCLASS card. В общем-то я по быстренькому стал "пробегать" статью, пока не натолкнулся на: "Copy Protection? You’re kidding me!". И меня "скрючило от восторга"... а чё так можно было!!!?

    Пардон, ничего не понятно, сейчас объясню.

    Не помню какой PIC тогда был под рукой, сейчас есть PIC18F26K20.

    Суть уязвимости.

    Запускаем PICKIT и попутно открываем даташит по программированию PIC18F*K*.

    Смотрим, зеленым биты защиты EEPROM, красным - биты защиты BOOT блока, синим - биты защиты кода, блоки 0-3.

    CPB/WRTB, CP[3:0]/WRT[3:0] - защита от чтения/записи кода, сперва BOOT затем блоки с 0-3.

    CPD/WRTD - Защита от чтения/записи EEP данных.

    А вот самые интересные биты защиты: EBTRB/EBTR[3:0] - защита от чтения секторов из других блоков.

    Смотрим на конфигурацию "защищенного" чипа (защита установлена):

    Смотрим на конфигурацию, - всё защищено!

    Читаем:

    Ноль по вдоль! Всё защищено!

    И вроде проблем нет.

    А они есть, читаем в даташите:

    Написано, - "Биты защиты можно записать только из 1 в 0, наоборот нельзя, чтобы записать в бит защиты 1 нужно стереть все блоки или блок соотв. биту защиты чтобы установить соотв. бит защиты в 1". И опа, оно!

    Стираем BOOT блок, записываем в него свой код, который будет для нас читать прошивку и отдавать её по EUSART.

    Да, но МЫ ПОТЕРЯЛИ ТО ЧТО БЫЛО В BOOT блоке, и тут появляется тема "ДАЙТЕ ДВЕ", берем второй чип с такой же прошивкой и проделываем то же самое только например с блоком 0, причем код помещаем в самый конец блока, всё остальное заполняем NOP'ами, чтобы с определенной вероятностью наш код начался не "с середины". Вот так:

    Затем склеиваем блоки в любом HEX редакторе, и вуаля! Прошивка на руках.

    Код есть, вытаскиваем EEP. Точно так же:

    Выглядит довольно просто. Однако проверим так ли это на самом деле.

    Первая задача, как затереть сектор. Читаем даташит:

    Стереть весь чип: 0x3F8F, BOOT: 0x0084, Block0: 0x0180.

    Только вот вопрос чем тереть то?

    Берем PICKIT3 (а он "из коробки" трет весь чип, нам не подходит):

    Ни для кого не секрет что исходный код этого программатора открыт. Идем на Microchip и качаем исходники прошивки (PICkit3 Programmer Application v3.10).

    Изучаем исходники... понимаем что это провал, внутри программатора интерпретатор команд, ага, значит сами команды в исходном коде приложения PIC KIT Programmer...

    Изучаем исходники (они в том же архиве)... понимаем что и тут интерпретатор!!! Да чтож такое!!! А команды то где?

    А они заботливо сложены в базе данных PK2DeviceFile.dat

    Первая мысль написать небольшой скрипт править базу данных, собственно давным давно я так и сделал, но теперь в поиске используя PK2DeviceFile.dat неожиданно натолкнулся на редактор этой базы (pickit2-editor, только прежде чем получить результат мне пришлось исправить несколько багов ибо при попытке сохранить подправленный файл базы прога "вылетала" неисправимо файл базы запортив).

    Запускаем PicKit2 Editor и находим имя скрипта стирания нашего чипа:

    Переходим в скрипты, находим наш, и правим соотв (0x3F8F -> 0x0084).

    Правим 0x3F на 0x00, а 0x8F на 0x84. Теперь при нажатии на кнопку ERASE, PicKit Programmer будет стирать не весь чип, а только BOOT блок.

    Так, как стереть поняли.

    Теперь код ридера, предлагается использовать стандартный интерфейс EUSART установленный в чипе, будем читать код из памяти и отдавать его по EUASRT, подключим его к любому конвертеру интерфейсов RS232->USB (убедившись что конвертер соотв. напряжению питания чипа). Смотрим опять в даташит:

    Отлично, 18-ый пин соединяем с конвертером интерфейсов (не забываем объединить земли, у меня всё соединено через общий хаб):

    Всё мы готовы. Напишем код ридера:

    data_reader.c
    #include "pic18fregs.h"
    
    /* CONFIG1L */
            #pragma config FOSC     = INTIO67
            #pragma config FCMEN    = OFF
            #pragma config IESO     = OFF
    /* CONFIG2L */
            #pragma config PWRT     = OFF
            #pragma config BOREN    = NOSLP
            #pragma config BORV     = 18
    /* CONFIG2H */
            #pragma config WDTEN      = ON
            #pragma config WDTPS    = 128
    /* CONFIG3H */
              #pragma config CCP2MX   = PORTC
            #pragma config PBADEN   = OFF
            #pragma config LPT1OSC  = OFF
            #pragma config HFOFST   = OFF
            #pragma config MCLRE    = OFF
    /* CONFIG4L */
            #pragma config STVREN   = ON
            #pragma config LVP      = OFF
            #pragma config XINST    = OFF
            #pragma config DEBUG    = OFF
    /* CONFIG5L */
            #pragma config CP0      = ON
            #pragma config CP1      = ON
            #pragma config CP2      = ON
            #pragma config CP3      = ON
    /* CONFIG5H */
            #pragma config CPB      = ON
            #pragma config CPD      = OFF
    /* CONFIG6L */
            #pragma config WRT0     = OFF
            #pragma config WRT1     = OFF
            #pragma config WRT2     = OFF
            #pragma config WRT3     = OFF
    /* CONFIG6H */
            #pragma config WRTD     = OFF
            #pragma config WRTB     = OFF
            #pragma config WRTC     = OFF
    /* CONFIG7L */
            #pragma config EBTR0    = OFF
            #pragma config EBTR1    = OFF
            #pragma config EBTR2    = OFF
            #pragma config EBTR3    = OFF
    /* CONFIG7H */
            #pragma config EBTRB    = OFF
    
    typedef __code unsigned char *CODEPTR;
    
    void main()
    {
        unsigned int uaddr = 0;
        CODEPTR c;
        TRISA = 0;
        TRISB = 0;
        TRISC = 0;
      /* Set Default State of OSC */
      OSCCON = 0b00110000;
      PIR2 = PIE2 = OSCTUNE = 0;
      IPR2 = 0xFF;
    
        /* Disable IRQs */
        INTCONbits.GIE = 0;
    
        /* enable EUSART */
        RCSTAbits.SPEN = 1;
        /* baud rate to 2400 Baud */
        SPBRG = 25;
        /* enable TX + only HI byte divisor */
        TXSTA = 0b00100100;
    
        c = 0x0;
        do
        {
            TXREG = *c++;
            while (!TXSTAbits.TRMT);
            ClrWdt();
        } while (c != (CODEPTR)0x10000);
    
        while (1)
      {
        /* Recharge WDT */
            ClrWdt();
      }
    }
    

    Компилируем и получаем:

    data_reader.hex

    :020000040000FA :10000000926A936A946A300ED36E9B6AA06AA16A60 :10001000FF0EA26EF29EAB8E190EAF6E240EAC6E6A :10002000006A016A026A00C0F6FF01C0F7FF02C061 :10003000F8FF0900F5CFADFF002A02E3014A022ACA :10004000ACA2FED70400005005E1015003E10250CC :0C005000010A01E0E8D70400FED712000E :020000040030CA :03000100081D0FC8 :02000500018177 :0600080000C00FE00F40F4 :00000001FF

    Записываем в чип и видим что байтики поехали:

    Далее если действовать по описанному выше алгоритму получаем всю прошивку целиком.

    В общем то и всё. Время для резюме.

    Применимость уязвимости:

    1. EBTRB/EBTR[3:0] - защита от чтения секторов из других блоков не установлена.

    2. У вас есть два идентичных многостраничных PIC18 чипа с идентичными прошивками.

    Как защититься от уязвимости:

    1. EBTRB/EBTR[3:0] - устанавливать защиту от чтения секторов или хотя бы одного сектора, таким образом получить доступ к "чистой прошивке" будет затруднительно.

    Однако необходимо помнить, - если вы устанавливаете защиту чтения из других блоков, Вам необходимо убедиться что данные для одного блока компилятор не будет собирать в другом! А он так может.

    За сим разрешите откланяться.

    Приятного дня и бодрости духа.

    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 11

      0
      Спасибо за подробное описание и анализ!!!
        0
        Видимо не все ограничения озвучены.
        В примере конфига разрешена запись в Boot блок и в сектора (WRTB, WRT0-WRT1). Скорее всего описанный метод не сработает если эти бит(ы) будут в 0.
        PicKit2 снят с поддержки лет 10 назад. Соответственно эта возможность «распространяется» на старые контроллеры. О новых он просто ничего не знает.
        В новых контроллерах и структура защиты другая, только один общий бит CP, соответственно можно стереть только всё.
          0

          Отчасти вы правы, но можно в программе добавить новый контроллер. В статье использован PicKit3, хотя база данных называется так же как и во второй версии 3-я использует режим совместимости.

            +1
            именно что режим совместимости — база берется от Pickit2, хоть используется PICkit3. Добавлять туда МК можно, но только если используются те же самые или схожие скрипты / алгоритм программирования. Проще тогда свой программатор написать, но это не отменяет того, что в более новых семействах подобный трюк не пройдет из-за отличий в логике CP бита.
            p.s. ну и зная о такой уязвимости можно легко «починить» код от вычитывания. Например, в Boot считать CRC прошивки, при не совпадении — Reset. Тогда потенциально можно вычитать код, но только без Boot части.
              0
              CRC верное решение, однако тут могут возникнуть сложности с обновлениями устройства.
              И да, в серии Q41 например CP бит уже «один на всех»! :)
                0
                CRC в бутлоадерах применяется часто, в микрочиповских примерах — еще и как (один из) триггер на вход в обновление. Каждое включение проверяется CRC. Если CRC не сходится — значит прошивка повреждена (или «что-то пошло не так», или в описываемом случае — стерт один из блоков Flash) — в этом случае ждем прилета обновления приложения. CRC предлагают интегрировать в каждую обновленную прошивку.
          0

          Плюсую!
          Но… на второй фотке — "горизонт USB завален"! ;-)

            0
            не только. PICkit3 поддельный ))
              0
              Ага, сфотал, всё отключил, прибрал, добавил в статью… смотрю завален! ;)
              … и PICKit3 с китайской площадки, древний жесть, ничего от Вас не утаишь! :)
              0
              С PIC16С73A такой фокус не пройдет?
                0
                Неа, там если я правильно помню UV Erasable Devices, One-Time-Programmable…

              Only users with full accounts can post comments. Log in, please.