Ayuda plugin .PRX para el XMB

Moderadores: Kravenbcn, largeroliker, fidelcastro, cerealkiller, pspCaracas, dark_sasuke, m0skit0, LnD, ka69, zacky06

gdljjrod
Enteradillo
Enteradillo
Mensajes: 93
Registrado: 21 Jul 2010, 16:01
Twitter: gdljjrod

Re: Ayuda plugin .PRX para el XMB

Mensaje por gdljjrod »

m0skit0 escribió:
gdljjrod escribió:Pues de hacer algo más sencillo creo que no me gustaría hacer eso y dejar lo que ya he empezado por una traba...

Bueno, la vida está llena de estas cosas. A lo que me refiero es que si acabas de sacarte el carné de conducir, no pretenderás clasificarte para el Gran Premio de F1. Es algo que simplemente no viene a cuento. Hay que ponerse objetivos realistas. Pero eso ya te toca decidir a ti. Yo te respondo a lo que sepa.

Perfecto yo sé que voy empezando con algo y si hubiera hecho un Eboot no me hubiera tocado estas dudas, así que prefiero continuar y aprender, ya que tarde o temprano si pienso seguir aprendiendo a programar tarde que temprano debo obtener más conocimientos...

gdljjrod escribió:ayudarme a entender lo de parchear los Stubs que mencionas

Es que sigues sin preguntarme nada en concreto. A ver, ¿sabes qué quiero decir por parchear? ¿Sabes qué es un stub? ¿Sabes cómo se llaman a las llamadas de sistema en PSP? Es que ponerme yo a intentar adivinar qué sabes me parece tontería. Deberías tú preguntarme exactamente qué no sabes, así avanzamos más rápido y vamos al grano ;)

gdljjrod escribió:Y una duda más al parchear los Stubs esto determinara si funciona para una versión de CFW en especifico???

Supongo que lo que quieres decir es que si parcheas los stubs sólo funcionará para una versión de CFW en concreto. La respuesta es depende de cómo lo programes. Si cargas los offsets de los stubs desde un fichero de configuración por ejemplo puedes hacerlo para cualquier CFW. Si tienes un solo stub hardcoded pues sólo funcionará para esa versión de CFW.


Y bueno pues entonces siendo sinceros un Stub ciertamente no sé que es, pero es para cargar funciones que pueden ser utilizadas por el plugin, d eahi que te pregunte si solo podría ser utilizado solo en algún CFW definido.
Así que empezamos con la teoría, te parece, si me dices que es en si el Stub, llamadas del sistema y parchear los stubs...

Avatar de Usuario
m0skit0
Administrador
Administrador
Mensajes: 5585
Registrado: 03 Sep 2009, 09:35
Ubicación: 0xdeadbeef

Re: Ayuda plugin .PRX para el XMB

Mensaje por m0skit0 »

Ok, pues conceptos básicos entonces.

Llamadas al sistema

La PSP viene más que con un firmware, con un sistema operativo en toda regla. Posiblemente fuera la primera consola en tener uno propiamente dicho. Esto significa que los juegos (y las aplicaciones, como el VSH) no tienen acceso directo al hardware (en todo caso sólo a partes del hardware).

Por tanto el SO debe ofrecer alguna forma para que este software (el llamado código modo usuario) pueda realizar operaciones sobre el hardware a través de unas determinadas funciones (que se ejecutan en el famoso modo kernel). Esto permite al SO controlar qué se puede y qué no se puede hacer desde el modo usuario. Estas funciones son las famosas sceLoQueFuere(), que habrás usado hasta la saciedad. Estas funciones se denominan llamadas al sistema (system calls in inglis).

El código de las llamadas al sistema reside en el SO de la PSP. Esto quiere decir que cuando tú usas por ejemplo sceIoOpen() en tu homebrew (en el software oficial es exactamente igual), el código de sceIoOpen() no estará en tu homebrew, sino que el SO lo enlazará a la hora de ejecutar tu homebrew. ¿Y esto cómo se come? Pues usando importes.

Importes y stubs

Los ejecutables y las librerías de PSP (y PS2, PS3, PSV...) están en formato ELF, que es básicamente un estándar (salvo para Microshit). En concreto en PSP es un ELF especial denominado PRX. Los PRX están divididos en secciones (al igual que todos los ELF) que definen por ejemplo dónde está el código dentro del fichero, así como una serie de parámetros adicionales que permiten al SO cargar el PRX correctamente. A la hora de compilar tu homebrew, el enlazador (linker, y en caso del PSPSDK es concretamente psp-ld) se encarga de generar estas secciones y entre éstas están las que definen los importes. ¿Cómo funciona esto?

Por ejemplo, supongamos que usas sceIoOpen() en tu homebrew. A la hora de enlazarlo, en vez de hacer una llamada "real" a sceIoOpen() donde tú la haces en tu código, lo que hace el enlazador es algo diferente:

  1. Pone sceIoOpen() en una entrada de los stubs (una sección del ELF)
  2. Rellena la información referente a sceIoOpen() para que el SO pueda resolverla
  3. Hace que todas las llamadas a sceIoOpen() en tu PRX apunten a este stub en concreto

En ensamblador, tu PRX quedaría así al compilarlo:

Código: Seleccionar todo

[...]
jal sceIoOpen #Llamada a sceIoOpen
nop
[...]
jal sceIoOpen #Llamada a sceIoOpen
nop
[...]
jal sceIoOpen #Llamada a sceIoOpen
nop
[...]

sceIoOpen_stub:
jr $ra
nop

Pero como puedes ver, el stub de sceIoOpen no hace absolutamente nada salvo un return. Esto es porque, como he comentado antes, el código de sceIoOpen está en el SO, por tanto es el SO quien tiene que enlazarlo a la hora de cargar el PRX. Para ello, las llamadas al sistema se codifican como librería + NID. Un NID es simplemente un identificador de 32 bits para la llamada al sistema. En caso de sceIoOpen estaría referenciada como IoFileMgrForUser y 0x109F50BC.

Pues bien (y ya estamos acabando...) a la hora de cargar el PRX en RAM para ser ejecutado (o usado como librería), el SO tiene una tabla que tiene qué número de llamada al sistema corresponde con cada importe librería + NID. Supongamos que sceIoOpen corresponde con 0x2000. Entonces lo que hace el SO es reemplazar el stub de sceIoOpen con dicha llamada al sistema, que quedaría tal que:

Código: Seleccionar todo

sceIoOpen_stub:
jr $ra
syscall 0x2000

Al ejecutar la instrucción syscall, la ejecución pasa al manejador de excepciones del kernel (ya que syscall genera una excepción MIPS), que tiene una tabla que relaciona este número de llamadas al sistema con una dirección de memoria modo kernel, que es donde se encuentra cargada la función sceIoOpen, a la que, finalmente, hace un salto para que se ejecute.

Parchear stubs

Entonces con parchear los stubs nos referimos a modificar los stubs de un determinado PRX cargado en memoria para desviar las llamadas al sistema de este PRX a otro código. En tu caso, serían los stubs del VSH. Cuando se esté ejecutando tu plugin, deberías anular los stubs de las llamadas al sistema de recogida de pulsaciones. Por ejemplo, el stub de sceCtrlPeekBufferPositive (sceCtrl + 0x3A622550) estaría por ejemplo como:

Código: Seleccionar todo

jr $ra
syscall 0x2802

y tendrías que modificarlo para dejarlo como

Código: Seleccionar todo

jr $ra
nop

mientras tu menú está activo. Obviamente tendrías que volver a dejarlo como estaba cuando salgas de tu menú, o el VSH no reconocería las pulsaciones de teclas.

Cualquier duda, me preguntas ;)
Imagen

xerpi
Desarrollador
Desarrollador
Mensajes: 65
Registrado: 07 Ago 2010, 21:55
PSN ID: xerpi
Ubicación: Catalunya

Re: Ayuda plugin .PRX para el XMB

Mensaje por xerpi »

Jefe crees que algo así estaría bien?

Código: Seleccionar todo

void nop_stub(unsigned int *address){
    *(address + 4) = (unsigned int)0x0;
}

unsigned int get_stub(unsigned int *address){
    return *(address + 4);
}

void patch_stub(unsigned int *address, unsigned int value){
    *(address + 4) = value;
}

//Imagina que el stub de sceCtrlPeekBufferPositive está en 0xDEADBEEF xD
unsigned int *sceCtrlPeekBufferPositive_stub_addr = 0xDEADBEEF;
unsigned int stub_original = get_stub(sceCtrlPeekBufferPositive_stub_addr); //guardo el valor original
nop_stub(sceCtrlPeekBufferPositive_stub_addr); //parcheo el syscall con un NOP
/*aqui tu código*/
patch_stub(sceCtrlPeekBufferPositive_stub_addr, stub_original); //reseteo el stub (vuelvo a activar los controles)
Imagen

Avatar de Usuario
dark_sasuke
Moderador Global
Moderador Global
Mensajes: 3379
Registrado: 03 Sep 2009, 02:13
Twitter: D4rkyt0

Re: Ayuda plugin .PRX para el XMB

Mensaje por dark_sasuke »

xerpi escribió:Jefe


Nononononono, que el es vice :P
Imagen

Avatar de Usuario
m0skit0
Administrador
Administrador
Mensajes: 5585
Registrado: 03 Sep 2009, 09:35
Ubicación: 0xdeadbeef

Re: Ayuda plugin .PRX para el XMB

Mensaje por m0skit0 »

xerpi escribió:Jefe crees que algo así estaría bien?

Para llamadas al sistema con instrucción syscall está OK, para las de jump no. No lo he comentado antes, pero sólo las llamadas al sistema que saltan al kernel (que son el 90%) se usa lo comentado anteriormente. Para las que son a módulos en modo usuario (como pueden ser varias librerías) se usa jump (instrucción j). En este caso se resuelve el stub tal que:

Código: Seleccionar todo

j dirección
nop

El HBL tiene mil ejemplos de esto, ya que el HBL debe extraer el ELF del EBOOT y cargarlo manualmente, ya que no se puede usar nada del kernel al ser OFW. Os dejo algunas funciones de ejemplo:

Resuelve stubs syscall/jump
Spoiler:

Código: Seleccionar todo

//
// Code
//
// MIPS opcodes
#define JR_RA_OPCODE 0x03E00008
#define NOP_OPCODE 0x00000000

// To distinguish between SYSCALL and JUMP stubs
#define SYSCALL_MASK_IMPORT 0x01000000
#define SYSCALL_MASK_RESOLVE 0xFFF00000

[...]

// Subsitutes the right instruction
void resolve_call(u32 *call_to_resolve, u32 call_resolved)
{
        // SYSCALL
        if(!(call_resolved & SYSCALL_MASK_RESOLVE))
        {
                *call_to_resolve = JR_RA_OPCODE;
                *(++call_to_resolve) = call_resolved;
        }
       
        // JUMP
        else
        {
                *call_to_resolve = call_resolved;
                *(++call_to_resolve) = NOP_OPCODE;
        }
}


Parsea y resuelve todos los importes de un ELF cargado en RAM
Spoiler:

Código: Seleccionar todo

// Resolves imports in ELF's program section already loaded in memory
// Returns number of resolves
unsigned int resolve_imports(tStubEntry* pstub_entry, unsigned int stubs_size)
{
        unsigned int i,j,nid_index;
        u32* cur_nid;
        u32* cur_call;
        u32 real_call;
        unsigned int resolving_count = 0;

#ifdef HOOK_CHDIR_AND_FRIENDS
    tGlobals * g = get_globals();
    g->chdir_ok = test_sceIoChdir();
#endif

        LOGSTR1("RESOLVING IMPORTS. Stubs size: %d\n", stubs_size);
        /* Browse ELF stub headers */
        for(i=0; i<stubs_size; i+=sizeof(tStubEntry))
        {
                LOGSTR1("Pointer to stub entry: 0x%08lX\n", (u32)pstub_entry); 

                cur_nid = pstub_entry->nid_pointer;
                cur_call = pstub_entry->jump_pointer;

                LOGSTR1("Current library: %s\n", (u32)pstub_entry->library_name);
               
                // Load utility if necessary
                int mod_id = is_utility((char*)pstub_entry->library_name);
                if (mod_id > 0)
                        load_utility_module(mod_id, (char*)pstub_entry->library_name);

                /* For each stub header, browse all stubs */
                for(j=0; j<pstub_entry->stub_size; j++)
                {

                        LOGSTR1("Current nid: 0x%08lX\n", *cur_nid);
                        NID_LOGSTR1("Current call: 0x%08lX\n", (u32)cur_call);

                        // Get syscall/jump instruction for current NID
                        nid_index = get_call_nidtable(*cur_nid, &real_call);

                        NID_LOGSTR1("Index for NID on table: %d\n", nid_index);
           
                        u32 hook_call = setup_hook(*cur_nid, real_call);

                        if (hook_call != 0)
                                real_call = hook_call;

                        NID_LOGSTR1("Real call before estimation: 0x%08lX\n", real_call);
           
                        /* If NID not found in game imports */
                        /* generic error/ok if syscall estimation is not available */
                        /* OR Syscall estimation if syscall estimation is ON (default)  and library available */
                        if (real_call == 0)
                        {
#ifdef DEACTIVATE_SYSCALL_ESTIMATION
                real_call = setup_default_nid(*cur_nid);
#else                           
                                real_call = estimate_syscall((char *)pstub_entry->library_name, *cur_nid, g->syscalls_known ? FROM_LOWEST : FROM_CLOSEST);
#endif                         
                        }
           
                        NID_LOGSTR1("Real call after estimation: 0x%08lX\n", real_call);

                        /* If it's an instruction, resolve it */
                        /* 0xC -> syscall 0 */
                        /* Jumps are always > 0xC */           
                        if(real_call > 0xC)
                        {       
                                /* Write it in ELF stubs memory */
                                resolve_call(cur_call, real_call);
                                resolving_count++;
                        }

                        LOGSTR3("Resolved stub 0x%08lX: 0x%08lX 0x%08lX\n", (u32)cur_call, *cur_call, *(cur_call+1));

                        CLEAR_CACHE;

                        cur_nid++;
                        cur_call += 2;
                }
               
                pstub_entry++;
        }
       
    LOGSTR0("RESOLVING IMPORTS: Done.");
        return resolving_count;
}
Imagen

xerpi
Desarrollador
Desarrollador
Mensajes: 65
Registrado: 07 Ago 2010, 21:55
PSN ID: xerpi
Ubicación: Catalunya

Re: Ayuda plugin .PRX para el XMB

Mensaje por xerpi »

Crees que estos pasos son correctos? (quiero intentar razonarlo sin mirar sources xD)

Código: Seleccionar todo

1.- Backupear la función a hookear (malloc y luego un memcpy).
2.- Hookear función:
   2.a.- $ra -> stack
   2.b.- j nueva_funcion
   2.c.- nop
   2.d.- stack -> $ra
   2.e.- jr $ra
   2.f.- nop
3.- Modificar nueva_funcion como se quiera (se tiene la función original backupeada así que se puede llamar dentro de la función hookeada).
Imagen

Avatar de Usuario
m0skit0
Administrador
Administrador
Mensajes: 5585
Registrado: 03 Sep 2009, 09:35
Ubicación: 0xdeadbeef

Re: Ayuda plugin .PRX para el XMB

Mensaje por m0skit0 »

Hmmm no hace falta hacer backup de la función. Simplemente apuntas el stub a la tuya. La original seguirá en su sitio en el kernel y siempre la puedes llamar si la incluyes en los importes de tu PRX.
Imagen

xerpi
Desarrollador
Desarrollador
Mensajes: 65
Registrado: 07 Ago 2010, 21:55
PSN ID: xerpi
Ubicación: Catalunya

Re: Ayuda plugin .PRX para el XMB

Mensaje por xerpi »

¿Cuál es la mejor forma de encontrar los stubs de un PRX? He probado con el PRXDecrypter pero el problema es que no tiene las llaves del FW 6.60 (he mirado en el source del PRXDecrypter ya que me da error al intentar desencriptar) y por lo tanto no puede desencriptarlo.
Imagen

Avatar de Usuario
m0skit0
Administrador
Administrador
Mensajes: 5585
Registrado: 03 Sep 2009, 09:35
Ubicación: 0xdeadbeef

Re: Ayuda plugin .PRX para el XMB

Mensaje por m0skit0 »

Como hemos comentado por GTalk, lo puedes volcar de la RAM una vez cargado. Ahí tienes prácticamente todo el PRX.
Imagen

gdljjrod
Enteradillo
Enteradillo
Mensajes: 93
Registrado: 21 Jul 2010, 16:01
Twitter: gdljjrod

Re: Ayuda plugin .PRX para el XMB

Mensaje por gdljjrod »

Bueno gracias a todos y al final he optado por usar la librería de Plum:http://t.co/YI0W7Ykr muy buena por cierto, así para poder continuar con el proyecto y al finalizarlo me queda claro que debo darme tiempo para obtener más conocimientos sobre este tema.
Gracias :D

Responder