Большое спасибо Davee и some1 за всё.
Сегодня я хочу вам рассказать про поиск kernel эксплоитов. Для начала разберемся для чего нам это нужно. PSP имеет много разделов памяти (вы можете посмотреть их ниже в спойлере), но речь пойдет только про 2: user и kernel. Различается память в уровне доступа. Код выполненный с kernel правами (запущенный из модуля с kernel правами) имеет права системы и имеет доступ ко всему, в свою очередь user имеет доступ только к: Scratchpad, VRAM, P5 и Extended Memory (в общем ко всему, кроме kernel).
И так, для чего же нам нужны kernel права? Для того чтобы сделать изменения в прошивке. Проще говоря, создание полноценного HEN или CFW невозможно без наложения патчей на модули системы. Для этого мы и будем получать kernel права. Хочу огорчить, вам необходимы знания языка Си и ассемблера под архитектуру MIPS (чтобы изучить прочти всё про MIPS в разделе "Интересные ссылки" (см. в конец)).
В прошивке PSP модули подразделяются на 2 категории: user и kernel. Права модулей различаются соответственно. User модули грузятся в user память, kernel модули грузятся в kernel память. Ниже приведен список kernel модулей.
Для того чтобы получить kernel права мы должны сделать jump (jr, jalr) из kernel модуля на свой код, который может быть в любом участке памяти. Не будем изобретать велосипед и будем использовать уже известный метод. Это использование функции sceKernelLibcTime.
Посмотрим в код модуля sysmem.prx, который и содержит функцию sceKernelLibcTime, найдите его, задав поиск по тексту. Я напишу его в двух вариациях, смотрите на второй. Первый понадобится позже.
sceKernelLibcTime(arg0, arg1) // arg1 это второй аргумент функции...
{
v1 = 0;
arg1 = mem[v1 + 2244]; // ...но здесь он перекрывается, то есть
// arg1 уже задается не из аргумента и мы
// не можем его контроллировать.
// Назовем это место "ЦЕЛЬ", запомните его и
// его адрес 0x0000F798 в sysmem.prx.
if(arg1 == 0) return 0; // Проверка второго аргумента, можно не обращать
// внимания, он всё равно не будет равен 0
if((0x80000000 & arg0) >= 0) // нам нужно пройти эту проверку,
{ // то есть коньюнкция v1 и arg0 должна
// быть положительной или равной 0, поэтому
// первый аргумент (arg0) мы сделаем 0.
// То есть: 0x80000000 & 0 = 0
jalr $arg1 // делаем jump по адресу, записаному в arg1
}
return 0;
}
Прочитав псевдокод функции sceKernelLibcTime вы видите, что мы можем сделать jump на любой адрес, но нам мешает операция "ЦЕЛЬ" по адресу 0x0000F798. Нужно её уничтожить. А уничтожать можно любой инструкцией, которая не взаимодействует с $a1. В идеале это nop (0x00000000). Проще говоря мы должны забить нулями 4 байта по адресу 0x0000F798 в модуле sysmem.prx. Но не тут то было. Мы не можем этого сделать из программы с user правами, ведь вы помните: к kernel памяти из user нет доступа.
Кстати, забивать нулями мы должны 4 байта в памяти, то есть адрес_модуля+адрес_инструкции_в_модуле. sysmem.prx всегда находится по адресу 0x88000000, поэтому реальный адрес будет 0x88000000 + 0x0000F798 = 0x8800F798.
Теперь предопределим цель следующего пункта. Мы должны зануллить 4 байта памяти по адресу 0x8800F798.
И вот опять незадача. Адрес 0x8800F798 это kernel память к которой мы не имеем доступа из user памяти.
Использовать kernel память мы можем только из kernel модулей. Для этого мы будем использовать функции из kernel модулей. Использовать можно лишь те функции, которые экспортированы в user память.
Поставим подцель. Мы должны иметь полный контроль над sw, sh или sb инструкцией в kernel функции.
sw $a0, C($a1) - эта инструкция позволяет заполнить 4 байта ($a0) в памяти по адресу $a1 + C
Пример использования:
Код:
li $a0, 0x12345678 ; значение
li $a1, 0x88000000 ; адрес
sw $a0, 0($a1)
sh $a0, C($a1) - эта инструкция позволяет заполнить 2 байта ($a0) в памяти по адресу $a1 + C
Пример использования:
Код:
li $a0, 0x1234 ; значение
li $a1, 0x88000000 ; адрес
sh $a0, 0($a1)
sb $s0, C($a1) - эта инструкция позволяет заполнить 1 байт ($a0) в памяти по адресу $a1 + C
Пример использования:
Код:
li $a0, 0x12 ; значение
li $a1, 0x88000000 ; адрес
sb $a0, 0($a1)
Допустим у нас есть функция:
Код:
sceLolFunc1:
sw $zr, 0($a0)
Мы можем напрямик записать 0x00000000 в любой адрес памяти, но такие функции это мечта и таких, увы, не бывает (по крайней мере импортированных в user). Sony хорошо защитила код от посягательств на kernel память и сейчас мы рассмотрим как.
Есть такой регистр: $k1. Изначально он равен 0x00010000.
Рассмотрим пример защищённой функции:
Код:
sceLolFunc2:
sll $k1, $k1, 11 ; k1 << 11 = 0x80000000
and $v1, $k1, $a0 ; Если один из аргументов будет = 0
; то v1 будет = 0.
bgezl $v1, loc1 ; Переход к подпрограмме loc1 если v1 >= 0.
ret:
jr $ra ; выход
loc1:
sw $zr, 0($a0) ; делаем заветное sw
j ret ; перенаправление на выход (ret)
Чтобы перейти к подпрограмме с sw нам нужно выполнить условие: v1 >= 0. Если вы подумали, что 0x8800F798 это положительное число, то это неправда. Просто число записано в типе u32. Чтобы узнать реальное число (signed) отнимите u32 число от 0xFFFF. signed=0xFFFF-u32.
Но не суть. Мы можем только сделать 1 аргумент нулем чтобы условие оказалось верно. И это будет k1, т.к. a0(аргумент функции) не может быть равен 0, так как он должен быть 0x8800F798.
Теперь сделаем k1 нулем. Это просто, но не просто найти подходящую функцию.
Допустим есть ещё 1 функция sceLolFunc3:
Код:
sceLolFunc3:
sll $k1, $k1, 11 ; k1 = 0x80000000
move $a0, $a0 ; просто чтобы было видно что аргумент
; текущей функции управляет аргументом функции sceLolFunc2
jal sceLolFunc2
nop
sceLolFunc2:
sll $k1, $k1, 11 ; k1 = 0x40000000000, но k1 вмещает
; только 32 бита, то есть k1 будет равен 0x00000000
and $v1, $k1, $a0 ; v1 будет = 0
bgezl $v1, loc1 ; и мы спокойно пройдем ЭТУ проверку
ret:
jr $ra
loc1:
sw $zr, 0($a0) ; и зарисуем нолик в нужный адрес
j ret
Теперь напишем эксплоит для наших выдуманных функций:
int kernel_permission_call()
{
// код с kernel правами
return 0;
}
void do_exploit()
{
sceLolFunc3(0x8800F798); // перетираем нулем 4 байта (u32) значение по адресу 0x8800F798
// тем самым убиваем заглушку второго аргумента в функции sceKernelLibcTime
int kernel_permission_call()
{
recovery_sysmem();
clear_caches();
// код с kernel правами
return 0;
}
void do_exploit()
{
sceLolFunc3(0x8800F798); // перетираем нулем 4 байта (u32) значение по адресу 0x8800F798
// тем самым убиваем заглушку второго аргумента в функции sceKernelLibcTime
Добавил: "Стандартный шаблон kernel эксплоита" и исправил недочёт в разрисовке устройства памяти PSP.
ErikPshat
16.06.2012 18:41
frostegater, о, спасибо за тутор. Не знал, что память делится на множество областей с различными правами доступа, ну помимо 2-ух областей: kernel и user.
А если показать области памяти в порядке возрастания, т.е. в виде физического порядка расположения.
Думаю более наглядно будет.
Код:
Ф и з и ч е с к о е _ р а с п о л о ж е н и е _ п а м я т и
User: находится по адресу 0x08800000 и занимает размер равный 2097152 байт (20 Мбайт).
Эмм как бы 2097152 байт совсем не 20 Мб.
По поводу VRAM согласен, но это всего лишь 2 Мб ;)
frostegater
16.06.2012 19:04
Scratchpad - высокоскоростная встроеная память. Используется для временного хранения данных. Доступна из всех режимов.
VRAM(Video RAM) - память, хранящая видеобуфер, создаваемый видеоадаптером. Может редактироваться. Получение буфера осуществляется функцией sceDisplayGetFrameBuf, установка же sceDisplaySetFrameBuf.
KERNEL память - используется системой (ядром), не может быть доступна из других областей памяти (ну мы то знаем как может ;)).
USER память - противоположна KERNEL памяти. В неё загружаются обычные пользовательские процессы.
P5 память - проще будет привести мой диалог с wololo:
Цитата:
frostegater: hi wololo.. you know what is P5 memory? Just very interesting. If I dont bother you.
wololo: sure
wololo: it is additional user memory, but very unstable
wololo: any process can use it whenever they want, I believe
wololo: it is called "volatile" memory
wololo: not sure if allocs, etc... work well in it
wololo: maybe it is reserved for savegame operations, etc...
frostegater: more thanks
Extended memory(XMS) - переводится как "дополнительная память", может использоваться для хранения данных, но не для процессорного кода.
Проще говоря, при помощи этой функции сделайте так чтобы по адресу 0x08800000 было 0x00000000
frostegater
06.11.2012 08:31
Я немного неверно изложил суть kxploit'а в шапке.
1-ое что мы должны иметь это возможность поставить любую дрянь в памяти ядра (получить контроль над инструкцией sw (хотя бы вторым аргументом) внутри функции с k-правами).
2-ое это поставить её вот вместо этого (lui $a1, 0x8801 - это взято из функции sceKernelPowerLock):
Код:
lui $a1, 0x8801 # вот это ОБЯЗАТЕЛЬНО перетереть какойнибудь хренью, но не этой
lw $v1, 16632($a1) # тут мы загружаем адрес нашего кода, поэтому можно заметить что от 2-го аргумента
# функции sceKernelPowerLock отнимаем 16632 (0x40F8) ну или стоящее там значение.
addiu $sp, $sp, -16
sw $ra, 0($sp)
bnez $v1, 0x8800CBDC # переходим в место с меткой (!!!)
move $v0, $zr
lw $ra, 0($sp)
jr $ra
addiu $sp, $sp, 16
lw $v0, 16($v1) # !!! тут грузим загруженый адрес нашего кода (вот почему отнимаем 16 (0x10) - смотрим любой kxploit)
jalr $v0 # ну и прыгаем на шеллкод
nop
Блин, ребята. Давайте хоть какую-то движуху, а то мне одному ломать скучно. Кому я это всё говорю?! Самое сложное тут - это найти функцию, которая будет иметь контроль над sw, а это не так сложно. Это можно сказать самое главное. Yoti, давай хоть ты подсуетись. Если чё не понятно, вы спрашуйте!
ErikPshat
06.11.2012 08:37
Цитата:
Сообщение от frostegater
(Сообщение 1053856)
lui $a1, 0x8801 # вот это ОБЯЗАТЕЛЬНО перетереть какойнибудь хренью, но не этой
В смысле, это наверное нужно совсем занулить.
Ну я могу подключиться, просто тоже своих проектов навалом. Перепрыгиваешь на новый проект, а старые незаконченные забываются, считай навсегда )))
frostegater
06.11.2012 09:49
ErikPshat, ну лишь бы не взаимодействовало с $a1, т.к. тут a1 задается аргумент инструкцией lui которая равносильна (a1 = 0x8801), а так мы можем задать a1 (второй аргумент функции) извне.
Temik007
14.11.2012 20:33
frostegater, запустил пример на псп, стоит 6.60 ме, но при влючении держал home и попал якобы в "офф", так вот в таком режиме exploit failed
попробую официальную поставлю
Temik007 добавил 14.11.2012 в 20:33
на официальной та же песня, может пример всего-лишь пример?)
frostegater
15.11.2012 00:19
Цитата:
Сообщение от Temik007
(Сообщение 1054965)
frostegater, запустил пример на псп, стоит 6.60 ме, но при влючении держал home и попал якобы в "офф", так вот в таком режиме exploit failed
попробую официальную поставлю
Temik007 добавил 14.11.2012 в 20:33
на официальной та же песня, может пример всего-лишь пример?)
Это шаблон
frostegater
15.11.2012 13:05
Шапка полностью переработана. Теперь это не "принцип действия kernel эксплоита", а ман по их поиску.