Als Beweiß werde ich euch zeigen wie ihr die CFW/HEN Patches deaktiviert.
Das ganze wird anhand der Firmware 6.20 erklärt, aber es ist leicht für die anderen Firmwares portierbar.
Also erstmal, wenn auf der PSP z.b. ein loadexec Befehl ausgeführt wird, wird der PSP-Kernel neugestartet.
Zu diesem Zweck wird sceReboot (die reboot.bin, welche fast identisch zu iplpayload, also iplstage 3 ist) auf Adresse 0x88600000 geladen, und es wird auf diese Adresse gesprungen.
Das ganze wird von sceLoadExec durchgeführt. SceReboot ist eine Art loader, sie lädt sceSystemMemoryManager und sceLoaderCore, welche dann alle Module von der pspbtcnf.bin bis zur init.prx laden werden.
Die CFW/der HEN muss SceReboot patchen, sodass unsignierte Module (systemctrl in CFWs/TN-HEN, systemex in ChickHEN) geladen werden können. Zu diesem Zweck wird die Laderoutine und der Aufruf von SceReboot
umgelenkt. SceReboot wird von der sub_00000000 Routine von loadexec, meistens DecompresReboot benannt, dekomprimiert und auf Adresse 0x88600000 geladen.
Das ganze Geheimniss liegt in der loc_00002C98 von sceLoadExec:
loc_00002C98: ; Refs: 0x00002C7C
0x00002C98: 0x0C0002EF '....' - jal sub_00000BBC
0x00002C9C: 0x02A02821 '!(..' - move $a1, $s5
0x00002CA0: 0x24160420 ' ..$' - li $s6, 1056
0x00002CA4: 0x3C048840 '@..<' - lui $a0, 0x8840
0x00002CA8: 0x3C050040 '@..<' - lui $a1, 0x40
0x00002CAC: 0x0C000D0F '....' - jal SysMemForKernel_31DFE03F
0x00002CB0: 0x2406000C '...$' - li $a2, 12
0x00002CB4: 0x1276001E '..v.' - beq $s3, $s6, loc_00002D30
0x00002CB8: 0x3C048860 '`..<' - lui $a0, 0x8860
0x00002CBC: 0x00002821 '!(..' - move $a1, $zr
0x00002CC0: 0x0C000D15 '....' - jal SysMemForKernel_74F3DC82
0x00002CC4: 0x3C060020 ' ..<' - lui $a2, 0x20
0x00002CC8: 0x3C048860 '`..<' - lui $a0, 0x8860
0x00002CCC: 0x3C050020 ' ..<' - lui $a1, 0x20
0x00002CD0: 0x3C060000 '...<' - lui $a2, 0x0
0x00002CD4: 0x24C60004 '...$' - addiu $a2, $a2, 4
0x00002CD8: 0x0C000000 '....' - jal sub_00000000
0x00002CDC: 0x00003821 '!8..' - move $a3, $zr
0x00002CE0: 0x04400020 ' .@.' - bltz $v0, loc_00002D64
0x00002CE4: 0x0040A021 '!.@.' - move $s4, $v0
0x00002CE8: 0x0C000D49 'I...' - jal UtilsForKernel_79D1C3FA
0x00002CEC: 0x00000000 '....' - nop
0x00002CF0: 0x0C000D4B 'K...' - jal UtilsForKernel_920F104A
0x00002CF4: 0x00000000 '....' - nop
0x00002CF8: 0x0C000D47 'G...' - jal UtilsForKernel_39FFB756
0x00002CFC: 0x00002021 '! ..' - move $a0, $zr
0x00002D00: 0x3C040000 '...<' - lui $a0, 0x0
0x00002D04: 0x0C000CF3 '....' - jal KDebugForKernel_84F370BC
0x00002D08: 0x24843ACC '.:.$' - addiu $a0, $a0, 15052
0x00002D0C: 0x3C040000 '...<' - lui $a0, 0x0
0x00002D10: 0x0C000CF3 '....' - jal KDebugForKernel_84F370BC
0x00002D14: 0x24843AE8 '.:.$' - addiu $a0, $a0, 15080
0x00002D18: 0x02402021 '! @.' - move $a0, $s2
0x00002D1C: 0x02A02821 '!(..' - move $a1, $s5
0x00002D20: 0x02603021 '!0`.' - move $a2, $s3
0x00002D24: 0x3C018860 '`..<' - lui $at, 0x8860
0x00002D28: 0x0020F809 '.. .' - jalr $at
0x00002D2C: 0x03C03821 '!8..' - move $a3, $fp
Das ganze in pseudo-C Form (damit man das ganze genauer sieht):
loc_00002C98:
{
sub_00000BBC(s2, s5);
SysMemForKernel_31DFE03F(0x88400000, 0x40, 12);
if(s3 == s6)
return s4;
SysMemForKernel_74F3DC82(0x88600000, 0, 0x20);
/* hier wird SceReboot eingesetzt */
int ret = sub_00000000((void *)0x88600000, 0x20, 4, 0);
if(ret < 0)
goto loc_00002D64;
sceKernelDcacheWritebackAll();
sceKernelIcacheInvalidateAll();
UtilsForKernel_39FFB756(0);
Kprintf("***** reboot start *****");
/* bei 0x3AE8 steht gleich am Anfang des Strings ein \0-Zeichen */
Kprintf("");
/* lui $at, 0x8860 */
void (* SceReboot) (int a0, int a1, int a2, int a3) = 0x88600000;
/* jalr $at */
SceReboot(s2, s5, s4, fp);
};
In dieser Routine wird also SceReboot eingesetzt und geladen. Jetzt kommt die Idee... wieso nicht einfach als erstes einen SceReboot Ersatz einsetzten, und Adresse des SceReboot-sprungs patchen?
Genauso wirds gemacht...
0x00002CD8: 0x0C000000 '....' - jal sub_00000000
Hier wird SceReboot eingesetzt. Wir können mit einem Kernel Modul diese Instruction so patchen, dass sie auf eine eigene Funktion springt, wir müssern allerdings auch die originaladresse der Funktion speichern, weil SceReboot ja auch eingesetzt werden muss:
#define JAL_OPCODE 0x0C000000
#define MAKE_JAL(a, f) _sw(((f >> 2) & 0x03ffffff) | JAL_OPCODE, a)
/* Zeiger auf die originale sub_00000000 von loadexec */
int (* DecompressReboot) (u32 addr, int unk1, int unk2, int unk3) = NULL;
/* Die Funkion, die anstatt sub_00000000 von loadexec aufgerufen werden soll */
int DecompressRebootPatch(u32 addr, int unk1, int unk2, int unk3)
{
/* Jetzt wird SceReboot eingesetzt */
return DecompressReboot;
};
int module_start(int args, void *argp)
{
/* Eine SceModule2 Struktur von sceLoadExec bekommen, damit wir die Text Adresse auslesen koennen */
SceModule2 *mod = sceKernelFindModuleByName("sceLoadExec");
/* Pruefen ob das Modul erfolgreich gefunden wurde */
if(mod)
{
/*
* Die Originaladresse von DecompresReboot speichern, diese ist
* gleich der Text Adresse von loadexec, weil DecompressReboot sub_00000000
* von loadexec ist.
*/
DecompressReboot = (void *)mod->text_addr;
/*
* Den Sprung auf die SceReboot-Einsetzroutine auf unsere Funktion umlenken
* Aus
* 0x00002CD8: 0x0C000000 '....' - jal sub_00000000
* wird damit
* 0x00002CD8: 0x0C000000 '....' - jal DecompressRebootPatch
*/
MAKE_JAL(mod->text_addr + 0x00002CD8, (int)DecompressRebootPatch);
};
/* Die Caches lehren, dies sollte man nach dem Patchen immer machen */
sceKernelIcacheInvalidateAll();
sceKernelDcacheWritebackInvalidateAll();
/* Den Kernel neustarten */
sceKernelExitVSHVSH(NULL);
};
Der Code ist natürlich noch nutzlos, da unser DecompresReboot Patch direkt auf die originale DecompressReboot Funktion springt.
Aber so würde es doch dann schon viel besser aussehen:
/* Die Funkion, die anstatt sub_00000000 von loadexec aufgerufen werden soll */
int DecompressRebootPatch(u32 addr, int unk1, int unk2, int unk3)
{
/* Einen SceReboot "Ersatz" auf Adresse 0x88fb0000 einsetzten */
memcpy((void *)0x88fb0000, rebootex, size_rebootex);
/* Jetzt wird SceReboot eingesetzt */
return DecompressReboot;
};
Naja, nicht wirklich SceReboot-Ersatz, dieser Code soll nur VOR SceReboot ausgeführt werden. Rebootex steht für Reboot Extension, also Reboot Erweiterung.
Jetzt müssen wir allerdings noch die PSP dazu bringen, auf die rebootex, anstatt auf SceReboot zu springen.
Da springt uns doch folgendes ins Gesicht:
0x00002D24: 0x3C018860 '`..<' - lui $at, 0x8860
0x00002D28: 0x0020F809 '.. .' - jalr $at
Hier wird die Adresse 0x88600000, also die Adresse von SceReboot, in $at geschrieben, und danach wird auf diesen Register gesprungen.
Wir müssen also nur die Instruction zum laden der Adresse (lui $at, 0x8860) patchen, um stattdessen die Adresse der Rebootex in $at zu laden, also müssen wir sie zu "lui $at, 0x88fb" ändern.
Lui hat den Opcode 0x3C000000. Die 8 Bits nach dem Opcode geben die Nummer des Zielregisters an ($at ist Register 1). Die letzten 16 Bits geben an, was geladen werden soll.
/* Pruefen ob das Modul erfolgreich gefunden wurde */
if(mod)
{
/*
* Die Originaladresse von DecompresReboot speichern, diese ist
* gleich der Text Adresse von loadexec, weil DecompressReboot sub_00000000
* von loadexec ist.
*/
DecompressReboot_Real = (void *)mod->text_addr;
/*
* Den Sprung auf die SceReboot-Einsetzroutine auf unsere Funktion umlenken
* Aus
* 0x00002CD8: 0x0C000000 '....' - jal sub_00000000
* wird damit
* 0x00002CD8: 0x0C000000 '....' - jal DecompressRebootPatch
*/
MAKE_CALL(mod->text_addr + 0x00002CD8, (int)DecompressRebootPatch);
/*
* Wir aendern dass "lui $at, 0x8860" auf Adresse 0x2D24 im Textsegment von loadexec zu "lui $at, 0x88fb"
* Dadurch wird das "jalr $at" nicht mehr auf 0x88600000 springen, sondern auf 0x88fb0000, was dazu f¸hrt
* dass die Rebootex anstatt SceReboot geladen wird
*/
_sw(0x3c0188fb, mod->text_addr + 0x00002D24);
};
Damit wird auch nichtmehr die HEN-Rebootex geladen, sondern unsere. Im Normalfall würden die Adressen 0x88fb0000 und 0x88fb0004 benutzt werden, um Informationen vom HEN an die Rebootex weiterzugeben,
aber hier ist jetzt unsere Rebootex. Die TN-HEN Rebootex wäre normalerweiße auf Adresse 0x88fc0000, aber dank unserem DecompressReboot Patch wird diese ja nicht eingesetzt.
Und... jetzt müssen wir noch eine Rebootex schreiben
Es muss eine pure Binary sein, da sie ja nicht mit irgendwelchen Modulemanager Funktionen geladen wird, sondern einfach auf sie gesprungen wird.
Wir machen jetzt eine sehr einfache rebootex, die nur auf SceReboot springt:
/* SceReboot liegt auf Adresse 0x88600000, wo wir dann auch hinspringen werden */
void (* SceReboot) (int a0, int a1, int a2, int a3) = 0x88600000;
/* Diese Funktion liegt direkt am Anfang der Rebootex, also auf Adresse 0x88fb0000 */
void rebootex_start(int a0, int a1, int a2, int a3)__attribute__((section(".text.start")));
void rebootex_start(int a0, int a1, int a2, int a3)
{
/* SceReboot laden */
SceReboot(a0, a1, a2, a3);
return;
};
Für pure Binarys brauchen wir auch eine Linkfile, eine Datei in der diverse Adressen festgelegt werden, zum Beispiel die Adresse der .text.start.
Hier die linkfile.l:
OUTPUT_FORMAT("elf32-littlemips")
OUTPUT_ARCH(mips)
ENTRY(Reboot_Entry)
SECTIONS
{
. = 0x88fb0000;
.text.start : {
*(.text.start)
}
.text : {
*(.text)
}
.rodata : {
*(.rodata)
}
.data : {
*(.data)
}
.bss : {
*(.bss)
}
}
Und die Makefile:
PSPDEV=C:/pspsdk/bin
INCLUDES=-I C:/pspsdk/psp/sdk/include
all: a.bin
a.bin:
$(PSPDEV)/psp-gcc $(INCLUDES) -W -Wall -G0 -fno-pic -mno-abicalls -w -S main.c -o rebootex.s
$(PSPDEV)/psp-as rebootex.s -o rebootex.o
$(PSPDEV)/psp-ld -T linkfile.l rebootex.o -o rebootex.elf
$(PSPDEV)/psp-strip -s rebootex.elf
$(PSPDEV)/psp-objcopy -O binary rebootex.elf rebootex.bin
bin2c rebootex.bin rebootex.h rebootex
clean:
rm -rf *~ *.o *.elf *.bin *.bin
bin2c wird eine Header datei erstellen (rebootex.h), die ein C-Array mit allen Bytes der Rebootex enthält, und einer Variable in der die Größe der Rebootex angegeben ist.
Diese Datei inkludiert ihr in euerem Modul, und fertig ist das ganze