Здравствуйте, меня зовут Александр. Немного о себе. Мне 16 лет, заканчиваю 11 класс, живу в городе, который очень далёк от столицы. Уже 2,5 года занимаюсь программированием по части игровых консолей Sony. Автор многих плагинов и программ, а также, с недавнего времени, прошивок.
Я думаю, многие помнят старушку PSP, которая радовала наш глаз с 2005 года. Многие, будучи детьми, очень завидовали сверстникам с состоятельными родителями, которые могли порадовать своё чадо таким подарком. Но время проходит, технологии совершенствуются, и PSP уже не та. Примерно полгода назад Sony запустила новый проект под названием PSVita, также известная как NGP (Next Generation Portable) и PSP2. Последним термином не брезгует и сама компания, применяя его в качестве кодового названия. Консоль получила хорошие технические характеристики и уникальные возможности. Многие из хакеров поприща PSP кинули свои силы на взлом защиты PSVita.
Имея массивный опыт в области PSP большинство взглядов сцены обратилось именно в сторону эмулятора PSP, устройство которого практически идентично внутреннему устройству самой PSP.
Шаг 1 — Usermode доступ в эмуляторе PSP
Первые и текущие usermode эксплоиты эмулятора PSP на PSVita, позволяющиее запустить неподписанный код были построены на уязвимостях переполнения буфера. Начальный код и «вода», которая переливалась за буфер находились в теле сохранения игры, купленной за кровные деньги в PS Store. Де/криптовка сохранений и поиск уязвимостей на этом попризе стал возможен благодаря совместимости данных сохранений с PSP.
Расскажу подробнее. Что такое уязвимость переполнения буфера знает большинство, от специалистов по компьютерной безопасности до обычных людей, изучавших любой ассемблер. Но я постараюсь объяснить это со стороны своего взгляда.
Игра считывает сохранение, парсит его и записывает данные в структуру. Бывает, что данные копируются функцией strcpy или же выступают в качестве аргумента строки в функции sprintf. Эти функции не являются безопасными, т.к. они не контролируют размер выходного буфера, который выделяется из стека.
Допустим в игре есть код.
// структура данных сохранения
struct {
char username[64];
int other_data[256];
}SaveData;
ReadSavedata(&SaveData); // чтение данных сохранения
// целевой буфер
char username_backup[64];
// уязвимый участок
sprintf(username_backup, "%s", username);
Я не зря оставил участок other_data. Я хотел показать, что в сохранении есть ещё место кроме 64 байт для имени игрока. Если мы удлиним имя игрока до 128 символов, то уязвимая функция, не обращая на размер буфера, запишет данные по его указателю. 128-64=64. То есть буфер будет переполнен на 64 символа. А, так как буфер выделен из стека, то произвольные данные из строки заполнят служебные записи кода.
Существуют 3 разновидности эксплуатации инструкций.
1. Регистр $ra (инструкция jr)
Если в последующем данные из стека будут восстановлены в регистр $ra (регистр возвращения адреса в MIPS), то мы сможем выполнить код с произвольного адреса. Нам сослужит службу инструкция «jr $ra», которая предназначена для возвращения кода на исходный участок из подфункции. Конечно же, мы положим свой код в свободное место в сохранении, осталось ещё 64 байта, которых предостаточно.
2. Аргумент инструкции jalr
Если данные будут восстановлены в регистр, который является аргументом инструкции jalr, то мы также сможем перенаправить код аналогично первому случаю. Отличие лишь в том, что в этом случае вам прийдётся повозиться с исследованием кода, когда же в первом переполнение буфера вы заметите сразу («Exception — Bus error (instr)» в дебаггере).
3. Аргументы sw
Если мы возымеем контроль над аргументами инструкции sw (store word), то мы сможем подставить себе «под ножку» любую инструкцию, в том числе и перенаправление.
Все эти манипуляции проводятся на PSP с помощью дебаггера (psplink), далее сохранение криптуется и подаётся на завтрак PSVita с заведомо купленной уязвимой игрой.
В доказательство предоставлю скриншот Hello World, запущенного с помощью эксплоита в данных сохранения.
При наличии только usermode эксплоита возможно портирование загрузчика usermode софта (Vita Half-Byte Loader). У него куча ограничений и багов, но, тем не менее, это не мешает пользователям наслаждаться лишь эмуляторами первых консолей (Dendy, Atari, Sega, GameBoy Advance).
Шаг 2 — Kernel доступ в эмуляторе PSP
Следующий шаг — kernel доступ. При получении возможности вносить правки в kernel память, мы можем сделать с системой всё что угодно. Это и сделал известный хакер и разработчик Total_Noob, написав и выпустив CEF (Custom Emulator Firmware) на основе утекшего kernel эксплоита от человека, который пожелал остаться неизвестным. После прошивки 1.81 был длительный кризис в плане взлома. Новый эксплоит никто не торопился выпускать, тогда я решил выпустить свой. И вот вы можете использовать CEF на прошивках выше 1.81 вплоть до 2.02. Сейчас я постараюсь объяснить его концепцию. Я думаю, она многим знакома.
1. Read-only kernel эксплоит
Насколько вам известно, имея только user права, записать или даже прочитать kernel память нельзя. Попытки этого ограничиваются эксцепшном «ломящегося» модуля и последующим крешем всей системы. Создаётся впечатление, будто и нет её, этой kernel памяти. Но мы то знаем, что она есть и даже знаем её адрес — 0x88000000.
Но с чего же нам начать? У нас нет модулей прошивки, а прочитать kernel память чтобы получить их мы не можем!
Для этого нам нужно найти read-only kernel эксплоит. Его принцип прост. Мы должны использовать функцию в kernel модуле, которая может записать в указатель или вернуть из функции значение участка кода, адрес которого можно задать аргументом самой функции. Для исследований можно использовать модули прошивки PSP (рекомендую 6.60). Ведь прошивка эмулятора PSP во многих местах просто идентична прошивке PSP, как я уже говорил выше. Соответственно, дыры остались.
Пример «дырявой» функции (пример без мусора и служебных инструкций).
sceKernelReadOnlyKxploit:
move $s0, $a0
lw $v0, 0($s0)
Регистр $a0 является первым аргументом функции, значение которого потом перемещается в регистр $s0. Регистр $s0 в свою очередь есть второй аргумент для инструкции lw, он задаёт адрес. Первым аргументом lw является регистр $v0, который возвращает значение операции return (в Си). Инструкция lw это load word — загрузка четырёхбайтного слова (его адрес это значение второго аргумента) в регистр (первый аргумент).
Осмыслив абзац выше, мы можем сделать заключение, что данная функция вернёт значение четырёхбайтного слова по адресу, заданному в первом аргументе.
Эксплоит для данной функции будет выглядеть так.
u32 *target = (void *)0x08A00000;
u32 i;
for(i = 0; i <= 0x00400000; i += 4)
{
*(target + i) = sceKernelReadOnlyKxploit(0x88000000 + i);
}
В результате по адресу 0x08A00000 в памяти будет записано содержимое kernel памяти размером в 0x00400000 байт. Сохранить его в виде файла не составит трудностей, также как и поиск идентичных уязвимостей.
Вытащив необходимые модули, их можно исследовать. Дизассемблирование производится программой prxtool.
2. Kernel эксплоит с правами записи
С поиском этих зверьков придётся поднатужиться, они очень редки и ценны. Я объясню только основную концепцию.
Посмотрите на функцию sceKernelLibcTime (модуль sysmem.prx, лучше смотерть на код прошивки 6.60 и экспериментировать пока на PSP). Видите jalr на регистр $a1? А теперь подумайте, что будет если затереть «lw $a1, 2244($v1)» несколькими инструкциями выше? Правильно, мы получим контроль над аргументом jalr и сможем перенаправить выполнение кода на любой существующий адрес. А как мы можем это сделать? Только используя kernel функции.
Итак, найти подходящую функцию можно только исследуя kernel функции на наличие контроля второго аргумента store инструкций (sw, sh, sb) с помощью входных аргументов самой функции ($a0-$a3, $t0-$t7) и наличий ошибок в структурах. Будьте внимательны, особенно к структурам.
Ввиду того, что о последнем написано абстрактно, подробнее об этом вы можете почитать здесь, в моей же давней статье.
Хороший справочник по архитектуре MIPS есть на википедии.
Просто отличный учебник по ассемблеру с нуля на примере MIPS (за который отдельная благодарность Bradley Kjell) вы можете найти здесь.
Описание kernel эксплоита на консоли PSP в прошивках 6.20 и 6.60.
Хочу выразить благодарность: SilverSpring, Malloxis, Dark Alex и some1.
В заключение хочу сказать, что я не поддерживаю пиратство и статья написана только в ознакомительных целях. Если у вас возникли вопросы всегда буду рад ответить в твиттере (frostegater). Спасибо за внимание.
Автор: frostegater