Вы используете устаревший браузер. Этот и другие сайты могут отображаться в нём некорректно. Вам необходимо обновить браузер или попробовать использовать другой.
В последнее время (эдак год-полтора) на чит-сцене появилось несколько публичных решений для SMM (System Management Mode). Эти решения как «боевые», так и чисто исследовательские. В основном тема эксплуатации SMM с точки зрения читов обсуждается на англоязычных форумах, но на СНГ сцене как-то по этому поводу тихо.
Я решил, что будет неплохой идеей немного осветить тему, как вообще вся эта шняга работает и как её можно использовать с точки зрения читов, ну или собственных исследований по безопасности платформы. Я в принципе занимаюсь этой темой весьма долго и мне есть что рассказать. Я по большей части буду делать отсылки к AMD, но при необходимости постараюсь впихнуть и Intel. С последним я работал намного меньше, чем с первым, отсюда и больший уклон в красных. Да и примерчики тоже на красных будут.
Также, стоит сказать; Я подразумеваю, что вы уже знакомы с устройством как минимум UEFI, работы процессора и некоторых других вещей. Совсем базовые концепции я детально разбирать не буду.
SMM как режим работы процессора
Для начала стоит просто понять, что из себя подразумевает SMM – это привилегированный режим работы процессора на базе архитектуры x86_64, управляющий аппаратным обеспечением ПК. Более детально, режим работает, например, с управлением энергопотребления платформы, ну или исполняет управляющий OEM-код (ref: AMD SP (AMD PSP), Intel ME), эмулирует какие-нибудь устаревшие вещи, обрабатывает ошибки чипсета или памяти.
Режим работает независимо от любых других режимов, и, по сути, является прозрачным для всех остальных (а также наиболее привилегированным). Часто можно увидеть упоминание «Ring -2» (в топологии колец защит), что и есть SMM.
Код режима SMM защищён от модификаций извне (любой другой режим, кроме RoT (Root-of-Trust: Intel ME, AMD SP (AMD PSP))). Очевидно, что SMM-код должен как-то защищаться, ну и соответственно сперва он должен быть инициализирован.
Инициализация режима и где обитает код для SMM
Очевидно, что SMM код должен инициализироваться где-то в начале работы платформы, когда уже инициализированы и сконфигурированы чипсет и память, была пройдена первичная валидация образов драйверов. Собственно, так оно и есть: платформа инициализирует SMM код ещё на стадии загрузки. DXE Dispatcher получает HOB (Hand-off Block) с SMM драйверами, где в первую очередь находится SMM IPL (SMM Initial Program Loader), который загружает остальные SMM драйвера. Этот же код будет доступен на протяжении всей работы платформы, до подачи сигнала на определённый пин (CPURST#) чипсета. Попроще изображено на иллюстрации ниже:
Отлично, теперь нам стало понятнее, когда загружается код режима SMM. Но тут есть как минимум ещё один вопрос: куда он загружается?
Вообще, ещё на этапе ранней инициализации, UEFI конфигурирует два MSR: SMM_ADDR и SMM_MASK для AMD, а для Intel – SMMR_PHYS_BASE и SMMR_PHYS_MASK. Комбинация двух регистров устанавливает определённый сегмент DRAM, называемый TSEG, в который входит SMRAM – память под код SMM. Этот же сегмент памяти будет защищён от модификаций извне, а также «невидим» для других режимов. Сам сегмент может быть весьма большим, в зависимости от настроек платформы, например, 16 мегабайт и даже больше. Защита сегмента также настраивается отдельными конфигурационными битами. Например, у AMD это SMMLock в HWCR.
Что находится внутри SMRAM?
SMRAM имеет свою определённую «планировку» внутри, в зависимости от количества ядер процессора, мы можем увидеть следующее (на примере AMD):
Теперь, подробнее разберём, что вообще здесь творится:
SMM Base – это базовый адрес SMM для конкретного ядра процессора, на его основе считаются следующие две вещи;
SMM Entrypoint – входная точка для SMM, устанавливается для каждого ядра, на них же переходит управление при срабатывании SMI прерываний. Это весьма забавная цепочка из переходов: Real Mode -> Protected Mode -> Long Mode, а только потом начнёт выполняться код SMI обработчика;
SMM Save State – сохранённое состояние всех регистров процессора перед входом в SMM;
SMM Core – «пограничный» драйвер, обрабатывает прерывания (определённые), регистрирует и предоставляет таблицу сервисов SMM. Об этом драйвере речь зайдёт позже;
SMI Handlers – обработчики SMI прерываний, о прерываниях мы поговорим в следующей главе.
Стоит также сказать, что все SMI обработчики регистрируются SMM драйверами, есть несколько видов обработчиков, но о них поговорим далее.
SMM драйвера также работают изолировано в своём адресном пространстве, но, технически, так как SMRAM может достигать размера 4 гигабайт, то и сами драйвера могут тоже обращаться к адресам до 4 гигабайт, но, вообще, нынче вводятся некоторые изменения в эти особенности. Однако, дальше это может нам понадобиться.
Вход в SMM и выход из него
Мы уже коснулись этой темы, но теперь пройдёмся по ней глубже. Для входа в режим требуется срабатывание SMI (System Management Interrupt) прерывания. Есть несколько способов триггера прерывания, мы разберём все из них. Первым будут прерывания от чипсета или периферии на материнской плате. Это чисто аппаратные прерывания, которые триггерят отдельный пин на самом чипсете. Зачастую он назван весьма прямолинейно:
Такие прерывания можно триггерить через периферию, например, от xHCI. В принципе, варианты есть, надо только найти их.
Вторым типом прерываний будут те, для которых по адресу записи ввода/вывода на который платформой установлена необходимость триггераSMI (иногда можно услышать название IO Trap).
Третьим же будет прерывания, которые срабатывают при записи в определённый порт ввода/вывода, зачастую это 0xB2, но на всякий случай можно проверить поле SmiCmd в Fixed ACPI Description Table таблице ACPI.
Стоит также помнить, что SMI являются одними из самых привилегированных прерываний, что их нельзя «проигнорировать» (cli), и что процессор обработает их в первую очередь (например, если одновременно поступили SMI и NMI прерывания – обработается сначала первое). Выход из режима происходит только по достижению инструкции RSM (Resume From System Management Mode). Инструкция возвращает поток управления в процедуру, которая была прервана SMI, предварительно вернув состояние процессора до триггера SMI.
Разбираемся на примере
Допустим, пусть Core#0 выполняет какой-нибудь код. На пути выполнения пусть будет инструкция out 0xB2, 0x41:
Well, запись в порт 0xB2 сигнализирует чипсету, что поток данных и инструкций переходит в SMM, а значит, время проверить, какие адреса зарезервированы TSEG. Во время перехода, конечно же, сохраняется состояние регистров до триггера SMI (в SMM Save State). Таким образом, поток данных и инструкций переходят в SMM:
По выполнению работы обработчика прерывания, ядро процессора восстановит своё состояние, прочитав из SMM Save State, поток управления достигнет инструкции RSM, и, ядро продолжит своё выполнение после инструкцииout.
Тут может встать вопрос: «А что, если обращение к зарезервированным адресам TSEG произойдёт без SMI?». Ну, вообще на этот вопрос есть весьма простой ответ. Например, у AMD, если приглядеться на одну из картинок выше, в SMM_MASKMSR есть биты TE и AE (ASEG нам не особо интересен, поэтому, я и не упоминаю его). Если эти биты валидны (равны 1), и обращение происходит не в контексте прерывания, то, ядро процессора просто обратится к MMIO:
А значит, мы увидим следующую картину:
Это был пример для AMD, но и для Intel это вроде бы тоже справедливо (если я правильно помню), думаю, что пример равнозначен для двух производителей процессоров.
Надо сказать, что это только про софтварные прерывания (SW SMI), есть немного более сложный алгоритм срабатывания для ACPI SMI (мы их тоже затронем), например. Но, в конечном итоге всё действительно сводится к записи в порт, триггера SMI# пина на чипсете или запись в специальные адреса ввода/вывода. Мы немного познакомились с SMM и его работой, а значит, время для демагогий.
SMM как вектор атаки на игрушки
Чисто технически, SMM это весьма интересный вектор атаки с точки зрения защитных решений – режим, прозрачно работающий для ОС, который никто не может нормально проверить на работу каких-либо приколов в нём (на самом деле может, но об этом позже).
Те же античиты, например, если и интересуются режимом SMM (а в последнее время они реально интересуются, по крайней мере, их разработчики), то делают это «слабо» на текущий момент. Например, можно в тупую просто мониторить счётчик прерываний (что автоматически подразумевает большое количество ложных срабатываний) или же пытаться вычислить «специфичные» участки памяти, используемые, как коммуникация между режимами. Их вычисление тоже не самая простая задача, да и также может генерировать огромное количество ложных срабатываний. На текущий момент остаётся только глядеть маппнутый образ SPI Flash в физической памяти, сопоставлять каждый GUID драйвера (если вы незнакомы с устройством прошивок, то каждый драйвер имеет свой GUID), в надежде, что появится какой-то неизвестный идентификатор. Это, может и действенно, но кто будет держать весь этот список говна? Да и что делать, если вендоры введут какой-то новый драйвер? Собственно, на текущий момент с детектом SMM драйверов всё немного сложно. Но это не значит, что защитные решения стоят на месте.
Единственная проблема, которая может быть – вендор. Intel и AMD уже давно ввели и вводят некоторые штуки, предназначенные для защиты прошивки и целостности платформы от сторонних драйверов. О них речь пойдёт ближе к концу статьи.
Пишем простой SMM драйвер
Наверное, стоит написать что-нибудь простенькое и забавное. Напишем драйвер, регистрирующий SMI обработчик. Пока это будет софтварный (SW SMI) обработчик, но позже мы познакомимся с ещё одним.
(правда, модифицированную под свои нужды, но вам будет достаточно собрать ещё пару библиотек при желании). Собирать мы свой драйвер будем через тулчейн EDKII, так что, я дополнительно покажу ещё и настройку конфигурационных файлов.
Хорошо, что должен сделать наш драйвер? В первую очередь, он должен проверить, находится ли он в SMM (параноидально, но так принято, например, для комбинированных DXE-SMM драйверов), затем, получить таблицу SMM сервисов, а потом зарегистрировать SMI обработчик. Собственно, на картинке ниже это и представлено:
EFI_SMM_SW_DISPATCH2_PROTOCOL. Обработчику будет нужен собственный номер, пусть это будет 0x99. Для регистрации обработчика нужно передать адрес используемой функции в качестве этого обработчика:
Register SW SMI Handler:
EFI_STATUS
EFIAPI
SwSmiHandler(
IN EFI_HANDLE DispatchHandle,
IN CONST VOID *Context OPTIONAL,
IN OUT VOID *CommBuffer OPTIONAL,
IN OUT UINTN *CommBufferSize OPTIONAL
) {
return EFI_SUCCESS;
}
EFI_STATUS
EFIAPI
RegisterSwSmi(
VOID
) {
EFI_SMM_SW_DISPATCH2_PROTOCOL *SwDispatch2 = NULL;
EFI_STATUS Status = gSmst->SmmLocateProtocol(&gEfiSmmSwDispatch2ProtocolGuid, NULL, (VOID **)&SwDispatch2);
if(EFI_ERROR(Status))
return Status;
EFI_SMM_SW_REGISTER_CONTEXT SwSmiRegCtx = { 0 };
SwSmiRegCtx.SwSmiInputValue = 0x99;
EFI_HANDLE Handler = NULL;
return SwDispatch2->Register(SwDispatch2, SwSmiHandler, &SwSmiRegCtx, &Handler);
}
Отлично! У нас есть зарегистрированный обработчик, который будет доступен по записи в порт 0xB2 значения 0x99. Теперь, можно что-нибудь придумать для своего обработчика. Моей жертвой станет ещё один SMM драйвер от AMD – AmdPspP2CmboxV2. Это драйвер для коммуникации между внешним миром и сопроцессором AMD PSP, расположенным на чипсете, вот тут видно:
У меня есть дамп с SPI чипа своего старого ноутбука, мы ещё вернёмся к аппаратной составляющей, так что пока просто держим это в голове.
Драйвер интересен тем, что регистрирует протокол с адресом буфера для коммуникации между PSP и внешним миром. Коммуницировать с PSP можно, конечно, по-разному, но AMD сделало это более удобным способом. Протокол существует исключительно в SMM, поэтому, почему бы не получить его адрес?
Обработчик должен получить адрес протокола PSP_MBOX_SMM_BUFFER_ADDRESS_PROTOCOL и отдать его нам обратно. Как обработчик его вернёт? Ну, для этого мы будем использовать SMM Save State, в который ядро процессора сохраняет своё состояние. Это делается через ещё один
– EFI_SMM_CPU_SAVE_STATE_PROTOCOL. Кешируем последний, а первый получим прям в обработчике. В качестве регистра, в который мы положим результат, будет использоваться r15. Нам также нужно будет извлечь GUID нужного нам протокола.
Кстати, SW SMI обработчикам важно, какое ядро находится в SMM. Обработчики имеют одинаковый набор аргументов, но в случае с SW SMI всё немного интереснее. Если вы взгляните на код, то увидите аргумент CommBuffer. В случае триггера SW SMI, туда будет передана
EFI_SMM_SW_CONTEXT, в которой будет индекс ядра процессора, перешедшего в SMM. Это важно помнить.
Modified Handler:
typedef struct _PSP_MBOX_SMM_BUFFER_ADDRESS_PROTOCOL {
EFI_PHYSICAL_ADDRESS PspMboxSmmBuffer;
UINT64 PspMboxSmmFlagAddr;
} PSP_MBOX_SMM_BUFFER_ADDRESS_PROTOCOL;
EFI_STATUS
EFIAPI
SwSmiHandler(
IN EFI_HANDLE DispatchHandle,
IN CONST VOID *Context OPTIONAL,
IN OUT VOID *CommBuffer OPTIONAL,
IN OUT UINTN *CommBufferSize OPTIONAL
) {
EFI_SMM_SW_CONTEXT *SwCtx = (EFI_SMM_SW_CONTEXT *)CommBuffer;
UINTN CpuIdx = SwCtx->SwSmiCpuIndex;
EFI_GUID PspCmboxProtocolGuid = { 0x579CB2CB, 0x3403, 0x4B26, { 0x84, 0xCD, 0x72, 0x89, 0xFC, 0x91, 0x4D, 0x35 } };
PSP_MBOX_SMM_BUFFER_ADDRESS_PROTOCOL *PspMbox = NULL;
EFI_STATUS Status = gSmst->SmmLocateProtocol(&PspCmboxProtocolGuid, NULL, (VOID **)&PspMbox);
if(EFI_ERROR(Status))
return Status;
// ignore last status, no need to execute another handlers
gSmmCpu->WriteSaveState(gSmmCpu, sizeof(EFI_PHYSICAL_ADDRESS), EFI_MM_SAVE_STATE_REGISTER_R15, CpuIdx, (VOID **)&PspMbox);
return EFI_SUCCESS;
}
Ну, мы написали небольшой SMM драйвер. Теперь нам надо с ним как-то общаться, правда ведь? Напишем теперь DXE драйвер, который триггернёт прерывание и выплюнет нам адрес протокола.
Пишем DXE драйвер
Да собственно тут нечего особо писать. Нам пригодится немножечко ассемблер и прямые руки, но для начала, точка входа:
Теперь мы можем общаться с SMM драйвером. Переходя к этапу сборки, нам нужно будет написать пару конфигов, но можно воспользоваться существующими в EDKII, нащупать самостоятельно что нужно. Останется только отредактируовать конфиг в Conf/target.txt. В ACTIVE_PLATFORM прописать путь до исходника драйверов, в TARGET прописать условный RELEASE, а в TARGET_ARCH прописать X64. После этого можно билдить.
P.S: Вероятнее всего, ещё придётся пошаманить с Conf/tools_def.txt, в частности, вырубить параметр «предупреждения как ошибки» и немного побаловаться с MASM командами.
Модификация содержимого SPI чипа
Далее мы проведём тесты непосредственно на хардвари, так как драйвер весьма простой и ничего нам не сломает. Но я всё равно не несу ответственности за сохранность вашего оборудования. Мы позже поговорим об отладке SMM драйверов и почему это весьма больная тема. А пока, я выбрал своего подопытного – это мой старенький ноутбук, года эдак 2019, если не раньше. В первую очередь нам нужно нащупать SPI чип. На текущий момент на платах все флешки 8-пиновые и бывают двух форм-факторов: SOIC8/SOP8 и WSON8 (различия можно нагуглить). Зная, как они выглядят, можно начинать брут-форсить плату в поиске чипа. Желательно ещё обращать на маркировки чипов, чаще всего это будет какой-нибудь Winbond, Macronix или GigaDevice. С освещением у меня всё плохо, извиняйте:
Это чип от GigaDevice формата SOIC8/SOP8. В качестве программатора у меня xGecu T48, но можно использовать любой другой. В качестве адаптера для чипа я буду «прищепку», она как раз подходит для зажима пинов SOIC8/SOP8:
Здесь нужно не ошибиться с расположением пинов, да и с самим вендором чипа и его вольтажом. Благо, софтина от xGecu умеет в самостоятельное определение по айдишнику. У меня уже есть дамп образа прошивки с чипа, вам же сначала нужно будет его прочитать и сохранить куда-нибудь. Теперь переходим в
(лучше всего версии 0.28.0). Он нам нужен на старом движке, так как в новом нет поддержки модификации образов.
После скачивания тулзы, можно приступить к поиску жертвы на замену. В моём чипе лежал дебажный SMM драйвер для SMBus, поэтому я могу смело заменить его целиком. Вам же, возможно, потребуется дальнейший поиск или модификация существующих SMM драйверов. Всё что нам остаётся сделать – заменить драйвер на наш:
После замены сохраним бинарь с каким-нибудь другим именем, а дальше прошьём наш чип:
Нам останется только найти любую USB флешку, форматнуть в FAT32, закинуть бинарь с EFI Shell (можно собрать или найти самому), закинуть наш DXE драйвер и идти смотреть, что вообще происходит. Как и ожидалось – SMM драйвер выплюнул нам адрес протокола (извиняюсь за классные фотки):
Можно убедиться, что адрес действительно лежит в SMRAM, для этого нам достаточно проверить SMM_ADDR и SMM_MASKMSR, из SMM_MASKмы вычислим размер SMRAM:
Круто, у нас есть свой первый рабочий драйвер! Но ведь это можно развить во что-то большее, ведь так? Да и как отлаживать всё это дело? Об этом мы и поговорим сейчас.
– пакет с драйверами прошивки для QEMU. В случае выбора этого варианта, нужно будет просто закинуть наши драйвера в OvmfPkg, подредактировать конфиги и собрать пакет, затем явно указать QEMU, что он должен грузиться именно с него. Есть множество плюсов такого подхода, например:
Не нужно покупать дополнительное оборудование;
Можно отправлять отладочные сообщения в Serial I/O порты;
QEMU предоставляет GDB Stub;
С пинка можно добиться отладки на уровне сурсов.
По моему опыту, как раз таки QEMU это весьма удобная вещь. По специфике работы, я постоянно пользуюсь WinDbg. В связке GDB Stub + EXDI + WinDbg можно добиться отладки с дебаг-символами, что, собственно, весьма круто и удобно. Я не буду здесь рассказывать о том, как это правильно запустить, оставлю это на совесть читающего.
Вендоры используют такую штуку, как JTAG, аппаратный интерфейс для отладки. Иногда также используются специальные отладочные платы (по рассказам некоторых знакомых – такое бывает). Угарнее становится тогда, когда ты можешь нащупать JTAG-пины на «продуктовых» платах, но чаще всего это обрубки от некогда существовавшего интерфейса:
У Intel есть такая штука, как DCI, по сути, модифицировав прошивку (DCI нужно включать самостоятельно), и сделав самопальный DCI шнур из USB – уже можно пробовать что-то делать. По крайней мере, когда-то можно было, сейчас не знаю. Ещё Intel предоставлял вот такие прекрасные вещи:
Yep, эта беспонтовая херня будет вам стоить дохера, а ещё вам придётся подписывать NDA.
Собственно, самым простым вариантом для самопальных исследований будет QEMU (я опущу процессорный БДСМ с замерами таймингами старта чипсета для определения возможных отладочных интерфейсов).
Развиваем концепцию во что-то большее
Коммуницировать посредством SMM Save State и аутично записывать в порт номер своего обработчика безусловно хорошо, но что, если мы хотим передавать относительно большие массивы данных? Окей, для начала познакомимся с ещё несколькими типами SMI:
Root SMI – это «корневые» прерывания, которые срабатывают при каждом SMI. То есть, если произошёл триггер по записи в порт – Root SMI тоже сработает;
ACPI SMI – из названия не сложно догадаться, для чего они сделаны. Они имеют весьма удобный метод для коммуникации с внешним миром;
Periodic SMI – это прерывания, срабатывающие по определённому тайм-ауту, заданным APIC. Они требуют большего контроля, так как при загрузке системыAPICперенастраивается.
Более подробно мы остановимся на первых двух, начнём, пожалуй, с ACPI SMI.
ACPI SMI как удобный метод коммуникации
Как и любые SMI обработчики – этот также регистрируется в SMM, только для него нужно указывать собственный GUID при регистрации. Собственно, вернёмся к нашему SMM драйверу и перепишем его:
Как же нам коммуницировать с этим обработчиком? Теперь вернёмся к DXE драйверу. Спецификация предоставляет три версии протокола для коммуникации, мы воспользуемся
– EFI_SMM_COMMUNICATION_PROTOCOL. Также, нам нужно будет выделить пул памяти, так как именно в нём будут «конвоироваться» данные из внешнего мира в SMM. Тут возникнет вопрос: «А как ACPI SMI обработчик узнает, что обращаются именно к нему, а не к другому?». Спецификация UEFI предоставляет решение в виде
– это структуры с GUID обработчика, которые помещаются в начало буфера для коммуникации. Это будет проще увидеть на коде:
DXE Driver:
EFI_STATUS
EFIAPI
DxeMain(
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
) {
EFI_SMM_COMMUNICATION_PROTOCOL *SmmCommunication = NULL;
EFI_STATUS Status = SystemTable->BootServices->LocateProtocol(&gEfiSmmCommunicationProtocolGuid, NULL, (VOID **)&SmmCommunication);
if(EFI_ERROR(Status)) {
Print(L"[ ! ] LocateProtocol returned 0x%llX (%r)\n", Status, Status);
return Status;
}
// allocate communication buffer
UINTN Size = 0x40;
VOID *CommPool = NULL;
Status = SystemTable->BootServices->AllocatePool(EfiRuntimeServicesData, Size, &CommPool);
if(EFI_ERROR(Status)) {
Print(L"[ ! ] LocateProtocol returned 0x%llX (%r)\n", Status, Status);
return Status;
}
// craft header
EFI_SMM_COMMUNICATE_HEADER *Hdr = (EFI_SMM_COMMUNICATE_HEADER *)CommPool;
EFI_GUID AcpiSmiHandlerGuid = { 0xA3715FCB, 0x05F9, 0x4FD6, { 0xA3, 0x5B, 0x31, 0x2F, 0x29, 0xD5, 0x08, 0xA3 } };
CopyGuid(&Hdr->HeaderGuid, &AcpiSmiHandlerGuid);
Hdr->MessageLength = Size;
// place here your data
// Hdr->Data = [ your data here ]
// fire SMI
Status = SmmCommunication->Communicate(SmmCommunication, CommPool, &Size);
// process output
return Status;
}
Таким образом мы можем свободно коммуницировать с SMM посредством ACPI SMI обработчика! Теперь разберёмся с тем, как это работает. Помните SMM Core, который присутствует на карте SMRAM выше? Так вооооооот…
Протокол для коммуникации на самом деле отправит нас
, что к чему. То есть, мы видим следующую картину:
Наверное, это всё, что касается ACPI SMI..
Работа с памятью в SMM
SMM «в оригинале» работает в реальном режиме, а значит, для адресации ему доступны только первые 4 гигабайта физической памяти. Это большая проблема для x64 систем, в особенности для адресов расположенных выше 4 гигабайт. У нас есть два решения:
Самостоятельно создавать собственную страничную иерархию выходить в длинный режим. Платформа умеет это делать, но только в случаях выхода из режима S3 и если прилетает капсульный апдейт;
Модифицировать страницы SMRAM и ремаппать каждую «внешнюю» таблицу памяти к себе в SMRAM.
На мой взгляд, из двух вариантов я бы выбрал последний, так как мы можем узнать CR3 ядра процессора, а также уже имеем, допустим, виртуальный адрес, который собираемся «переместить» к себе, нам также понадобится какая-нибудь свободная страница памяти в SMRAM, но её можно спокойно выделить самому. На реализацию можно посмотреть вот
Не самый нужный пункт статьи, но стоит понимать, что вендоры всё же заботятся о целостности прошивки, в особенности об SMM. Например, у Intel и AMD есть «загрузочные» решения - Intel Boot Guard и AMD Platform Secure Boot. Они срабатывают ещё до передачи управления SEC Core (ещё одна стадия, которая в разговоре не упоминалась), то есть, код для проверки подлинности находится уже в микрокоде процессора. На примере Intel будет схожая цепочка:
Не вдаваясь глубоко в суть вопроса (это действительно отдельная большая тема) – первый сперва находит в SPI чипе отдельный модуль, который верифицирует стартовую часть прошивки, где находится основной модуль, который проверифицирует основную часть прошивки. Вот такая цепь доверенной загрузки.
AMD PSB работает схожим образом, но немного интереснее. Если IBG в случае «правильной» конфигурации, записанной в FPF, просто не загрузит платформу, то PSB, судя по всему, не загрузит модифицированный (или заменённый) драйвер (судя по моим тестам).
Также, уже на новых чипсетах Intel есть SMM Isolation, который ломает приколы с адресацией в SMM, вот
Ну, вот и краткое описание того, что представляет из себя SMM, как под него писать, куда можно ориентироваться при разработке смешных штук. Возможно, это кому-то пригодится или хотя бы заинтересует.
В идеале, если нацеливаться на игоры – смотреть на SMI# пин чипсета, как он триггерится и при каких событиях, я упоминал тот же xHCI, с чего, в принципе, можно и начать. Меньше оставляешь следов – живёшь дольше.
виртуальный ты передаешь сам снаружи до входа SMM там всего 2 варианта если я не ошибаюсь(в статье был пример через SW SMI регистры) а другой вариант он через Commbuffer
Ты самостоятельно передаёшь виртуальный адрес и значение CR3 регистра своему обработчику SMI прерывания. Ты можешь это делать через буфер для коммуникации, через манипуляции с SMM Save State, да хоть через NVRAM переменные - не важно.
Как только ты положил свой виртуальный адрес и CR3 процесса, в котором ты хочешь что-либо прочитать, ты триггеришь срабатывание своего обработчика по порту 0xB2 (или любому другому, что указан в поле SmiCmd ACPI таблицы FADT), вот после этого ты попадаешь в SMM, к своему обработчику, где и проводишь операцию трансляции адреса и его последующий ремаппинг. И только потом ты можешь модифицировать или читать память.
Ты самостоятельно передаёшь виртуальный адрес и значение CR3 регистра своему обработчику SMI прерывания. Ты можешь это делать через буфер для коммуникации, через манипуляции с SMM Save State, да хоть через NVRAM переменные - не важно.
Как только ты положил свой виртуальный адрес и CR3 процесса, в котором ты хочешь что-либо прочитать, ты триггеришь срабатывание своего обработчика по порту 0xB2 (или любому другому, что указан в поле SmiCmd ACPI таблицы FADT), вот после этого ты попадаешь в SMM, к своему обработчику, где и проводишь операцию трансляции адреса и его последующий ремаппинг. И только потом ты можешь модифицировать или читать память.
Ты самостоятельно передаёшь виртуальный адрес и значение CR3 регистра своему обработчику SMI прерывания. Ты можешь это делать через буфер для коммуникации, через манипуляции с SMM Save State, да хоть через NVRAM переменные - не важно.
Как только ты положил свой виртуальный адрес и CR3 процесса, в котором ты хочешь что-либо прочитать, ты триггеришь срабатывание своего обработчика по порту 0xB2 (или любому другому, что указан в поле SmiCmd ACPI таблицы FADT), вот после этого ты попадаешь в SMM, к своему обработчику, где и проводишь операцию трансляции адреса и его последующий ремаппинг. И только потом ты можешь модифицировать или читать память.