KINGx - Das inoffizielle PlayStation Forum & News Portal

Normale Version: [TUT] Funktionsweise eines HENs
Sie sehen gerade eine vereinfachte Darstellung unserer Inhalte. Normale Ansicht mit richtiger Formatierung.
Hi, ich hab mich mal enschieden, euch zu erklären wie ein HEN bzw. eine CFW funktioniert.
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:

Code:
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):

Code:
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...

Code:
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:

Code:
#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:

Code:
/* 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:

Code:
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.

Code:
    /* 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 Wink
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:

Code:
/* 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:

Code:
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:

Code:
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 Smile

Sehr schön Smile
Gutes Tutorial, mach weiter so Wink
Alles gut erklärt und ein sehr schönes Tutorial. Ich werde es mal anpinnen und morgen in Ruhe mal genau durchlesen um es besser nachvollziehen zu können Wink
Ich verstehe nichts -.-
Wozu ist das ganze?

Harakiri :
Ich verstehe nichts -.-
Wozu ist das ganze?


Vielleicht will er es anderen Entwicklern leicher machen, die Funktionsweiße zu verstehen?
Damit wären diese nicht mehr auf irgendwelche halb-fertigen Source Codes angewießen, sondern hätten ein gut erklärtes Tutorial.

Und wenn du nichts davon verstehst, wieso postest du dann?

Gut erklärt. Die Funktion die du 'DecompressReboot' nennst ist übrigens die selbe, die die PlayStation Resource Files (.PRF oder auch .RCO) dekompressiert (ist das richtiges Deutsch? Bin nur noch Englisch gewohnt xD), der richtige Name wäre also eher 'DecodeRLZ' (es gibt übrigens auch eine OpenSource Library von BenHur zur RLZ-Dekompression).

Immer schön zu sehen wenn sich andere mit den genauen technischen Details auseinandersetzen thumb

Harakiri :
Ich verstehe nichts -.-
Wozu ist das ganze?


Wie HacKmaN sagte.

btw. ist das nicht so (ja das geht so und dann so und dann passiert das) n00bish erklärt..
Auserdem warum treibst du dich hier überhaupt rum.. wenn du nichts verstehst.? -.-'

Ich würd das sehr gern verstehen Wink
Zum viertel tu ich das auch aber ich kann bisher nur ein bisschen python,c,c++ noch weniger und html

OT.:kann mir jemand sagen wie man bei c zB. ein ü schreibt ? Ich gegoogelt wie man Hexadezimal Code 'übersetzt' hab aber nichts gefunden Sad

EDIT.:Was müsste man jetzt in die rebootex schreiben um seinen eigenen code zu laden ? hmm zB. ein Linux für die PSP ( xD finde ich eine richtig gute idee!) oder ist das ein schlechtes Bsp.?

onlinesoccer :
OT.:kann mir jemand sagen wie man bei c zB. ein ü schreibt ? Ich gegoogelt wie man Hexadezimal Code 'übersetzt' hab aber nichts gefunden Sad


Die deutschen Sonderzeichen sind in der ASCII Tabelle vertreten und deswegen im Werte Bereich eines char. Nur die Schriftart sollte die Sonderzeichen unterstützen, dann hast du kein Problem.

pspbricker :

onlinesoccer :
OT.:kann mir jemand sagen wie man bei c zB. ein ü schreibt ? Ich gegoogelt wie man Hexadezimal Code 'übersetzt' hab aber nichts gefunden Sad


Die deutschen Sonderzeichen sind in der ASCII Tabelle vertreten und deswegen im Werte Bereich eines char. Nur die Schriftart sollte die Sonderzeichen unterstützen, dann hast du kein Problem.


wenn ich ein c programm kompiliere und ausführe stehen ind der shell komische zeichen aber ich habe einen PC mit deutschem win7.

@dxem danke für deine mühe das ist eine ziemlich harte nuss Wink aber gut genug! erklärt.

onlinesoccer :
EDIT.:Was müsste man jetzt in die rebootex schreiben um seinen eigenen code zu laden ? hmm zB. ein Linux für die PSP ( xD finde ich eine richtig gute idee!) oder ist das ein schlechtes Bsp.?


dazu müste man die reboot.bin reversen um den code besser zu verstehen zu können und die checkes ausschalten und ein module/plugin miteinschleusen!

Gutes TuT thumb
Alles bis auf's kleinste Detail erklärt,saubere Arbeit Big Grin

mfg

Dr. Soup :
Gut erklärt. Die Funktion die du 'DecompressReboot' nennst ist übrigens die selbe, die die PlayStation Resource Files (.PRF oder auch .RCO) dekompressiert (ist das richtiges Deutsch? Bin nur noch Englisch gewohnt xD), der richtige Name wäre also eher 'DecodeRLZ' (es gibt übrigens auch eine OpenSource Library von BenHur zur RLZ-Dekompression).

Immer schön zu sehen wenn sich andere mit den genauen technischen Details auseinandersetzen thumb


Bist du sicher das es RLZ ist?
Auf der Projekt Seite finde ich nur eine Library zum Entpacken von LZR Datein xD

Chaosduckman :
Bist du sicher das es RLZ ist?
Auf der Projekt Seite finde ich nur eine Library zum Entpacken von LZR Datein xD

Ja, die Funktion in loadexec, die sceReboot dekompressiert ist diesselbe wie die Funktion in paf, die .RCOs dekompressiert.

Dr. Soup :
Gut erklärt. Die Funktion die du 'DecompressReboot' nennst ist übrigens die selbe, die die PlayStation Resource Files (.PRF oder auch .RCO) dekompressiert (ist das richtiges Deutsch? Bin nur noch Englisch gewohnt xD), der richtige Name wäre also eher 'DecodeRLZ' (es gibt übrigens auch eine OpenSource Library von BenHur zur RLZ-Dekompression).

Immer schön zu sehen wenn sich andere mit den genauen technischen Details auseinandersetzen thumb


Interessant... das hab ich nicht gewusst Smile

Seiten: 1 2
Referenz-URLs