[PS4]Rilasciato il write-up di Flatz per costruire PKG,Self eccTempo di lettura : 38 min

Questo articolo è stato tradotto in italiano ed è solo per scopo didattico e per esperti in programmazione e sviluppatori.Le varie funzioni e le spiegazioni sono state testate da Flatz.Il file completo è disponibile a questo link

Panoramica

Il mio intento originale era quello di implementare una strategia minimalista che potesse portare a fare cose come fselfs e debug pkgs. Non mi piacciono i metodi che fanno un sacco di patch e comportano un sacco di lavoro manuale, quindi preferisco utilizzare la funzionalità integrata quando possibile. Mentre il risultato non è così minimalista, è efficace. Come ho detto più volte, questa roba può essere trasferita su qualsiasi firmware.

Quindi ecco i problemi che abbiamo:

  1. I file eseguibili inclusi SELF s / SPRX s / SEXE s / SDLL sono firmati e crittografati. Mentre le loro versioni fasulle non sono firmate e crittografate, di solito non possiamo usarle su una console retail a causa di restrizioni messe in atto dal gestore di autenticazione che le carica (un modulo sicuro, o SM , che è in esecuzione da SAMU , un sicuro coprocessore che è incorporato nella nostra AMD s’ APU ). Il loader (S) ELF del kernel usa le chiamate SAMU per tutto ciò che riguarda la crittografia.
  2. Le applicazioni, inclusi i giochi (eccetto quelli di sistema) richiedono che tutto sia raggruppato in file pkg. La console usa molto questo sistema, quindi non possiamo semplicemente rilasciare la nostra applicazione nel file system e sperare che tutto funzioni normalmente. Quindi dobbiamo usare i pacchetti anche se non vogliamo darci un mal di testa. Questi pacchetti sono anche crittografati e firmati e non possiamo usare pacchetti di debug su una console di vendita al dettaglio. Anche i pacchetti di debug sono crittografati e firmati a differenza dei file eseguibili. A causa della Sony che utilizza la crittografia asimmetrica, non disponiamo di tutte le chiavi private per decodificarle correttamente.

Ok, quindi, come vogliamo raggiungere il nostro obiettivo? Inizieremo con un metodo per caricare file eseguibili personalizzati. Tieni presente che tutto qui è scritto per gli sviluppatori e non è attualmente utilizzabile da un utente normale. Se non hai molte conoscenze o le competenze su come implementare questo payload, devi solo aspettare che uno sviluppatore lo rilasci come payload pronto per l’uso, o magari integrarlo in un exploit stesso, quindi fare un fork di esso .

Inoltre, fai riferimento alla sezione Definizione, strutture e funzioni di aiuto se vedi definizioni, funzioni, costanti, variabili o strutture sconosciute, perché ne ho escluso alcuni dai frammenti di codice seguenti per la leggibilità.

Autori falsi

Codice del kernel

Poiché le versioni false dei file eseguibili (i cosiddetti FSELF ) non utilizzano alcuna crittografia / firma, potremmo reindirizzare le chiamate SM che caricano i loro segmenti alle nostre funzioni in un carico utile del kernel. È molto banale perché tutto ciò che fanno è chiamare memcpy()in alcuni punti nel tempo. Tutto ciò di cui abbiamo bisogno è trovare i metodi del kernel che descriverò qui sotto e collegarli con il nostro codice (e reindirizzarli alle funzioni originali quando necessario). Tieni presente che questo codice è stato creato per 4.55+ e potrebbe essere necessario aggiustarlo per 4.05, quindi dovrai eseguire il backport in base alle funzionalità della 4.05!

  1. sceSblAuthMgrIsLoadable2
    Questa funzione controlla essenzialmente se un file può essere caricato dalla cartella / partizione specifica e imposta anche le informazioni di autenticazione utilizzate dal sistema per controllare i diritti e le capacità di accesso. Se abbiamo bisogno di alcune funzionalità specifiche, potremmo memorizzarle nel nostro file e inserirle nelle informazioni di autenticazione e per gli altri utilizziamo solo le informazioni di autenticazione predefinite.
static  inline  int  sceSblAuthMgrGetSelfAuthInfoFake ( struct  self_context * ctx, struct  self_auth_info * info) {
	 struct  self_header * hdr;
	struct  self_fake_auth_info * fake_info;

	if (ctx-> format == SELF_FORMAT_SELF) {
		hdr = ( struct  self_header *) ctx-> header;
		fake_info = ( struct  self_fake_auth_info *) (ctx-> header + hdr-> header_size + hdr-> meta_size - 0x100 );
		se (fake_info-> size == sizeof (fake_info-> info)) {
			 memcpy (info, & fake_info-> info, sizeof (* info));
			ritorno  0 ;
		}
		ritorno - 37 ;
	} else {
		 return - 35 ;
	}
}

// ...

static  inline  int  is_fake_self ( struct  self_context * ctx) {
	 struct  self_ex_info * ex_info;
	int ret;

	if (ctx && ctx-> format == SELF_FORMAT_SELF) {
		ret = sceSblAuthMgrGetSelfInfo (ctx, & ex_info);
		se (ret)
			 restituisce  0 ;
		return ex_info-> ptype == SELF_PTYPE_FAKE;
	} else {
		 return  0 ;
	}
}

// ...

static  const  uint8_t s_auth_info_for_exec [] = {
	 0x01 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x31 , 0x00 , 0x00 , 0x00 , 0x00 , 0x80 , 0x03 , 0x00 , 0x20 ,
	 0x00 , 0xFF , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00, 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
	 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x40 , 0x00 , 0x40 , 0x00 , 0x40 ,
	 0x00 , 0x00 ,0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x40 , 0x02 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x80 , 0x00 ,
	 0x00 , 0x40 , 0xFF , 0xFF , 0x00 , 0x00 , 0x00 , 0xF0 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00, 0x00 , 0x00 , 0x00 ,
	 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
	 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
	 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
	 0x00 , 0x00, 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
};

static  const  uint8_t s_auth_info_for_dynlib [] = {
	 0x02 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x31 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
	 0x00 , 0xFF , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00, 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
	 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x40 , 0x00 , 0x30 , 0x00 , 0x30 ,
	 0x00 , 0x00 ,0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x40 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x80 , 0x00 ,
	 0x00 , 0x40 , 0xFF , 0xFF , 0x00 , 0x00 , 0x00 , 0xF0 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00, 0x00 , 0x00 , 0x00 ,
	 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
	 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
	 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
	 0x00 , 0x00, 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
};

static  int  build_self_auth_info_fake ( struct  self_context * ctx, struct  self_auth_info * parent_auth_info, struct  self_auth_info * auth_info) {
	 struct  self_auth_info fake_auth_info;
	struct  self_ex_info * ex_info;
	struct  elf64_ehdr * ehdr = NULL ;
	int ret;

	if (! ctx ||! parent_auth_info ||! auth_info) {
		ret = EINVAL;
		goto error;
	}

	if (! is_fake_self (ctx)) {
		ret = EINVAL;
		goto error;
	}

	ret = sceSblAuthMgrGetSelfInfo (ctx, & ex_info);
	se (ret)
		 goto error;

	ret = sceSblAuthMgrGetElfHeader (ctx, & ehdr);
	se (ret)
		 goto error;
	if (! ehdr) {
		ret = ESRCH;
		goto error;
	}

	ret = sceSblAuthMgrGetSelfAuthInfoFake (ctx, & fake_auth_info);
	if (ret) {
		 switch (ehdr-> type) {
			 case ELF_ET_EXEC:
			 case ELF_ET_SCE_EXEC:
			 case ELF_ET_SCE_EXEC_ASLR:
				 memcpy (& fake_auth_info, s_auth_info_for_exec, sizeof (fake_auth_info));
				ret = 0 ;
				rompere ;

			caso ELF_ET_SCE_DYNAMIC:
				 memcpy (& fake_auth_info, s_auth_info_for_dynlib, sizeof (fake_auth_info));
				ret = 0 ;
				rompere ;

			predefinito :
				ret = ENOTSUP;
				goto error;
		}

		fake_auth_info. paid = ex_info-> paid;

		// TODO: sovrascrive i bit bassi di PAID con il numero ID del titolo
	}

	se (auth_info)
		 memcpy (auth_info, & fake_auth_info, sizeof (* auth_info));

errore:
	return ret;
}

// ...

static  int  sceSblAuthMgrIsLoadable2_hook ( struct  self_context * ctx, struct  self_auth_info * old_auth_info, int path_id, struct  self_auth_info * new_auth_info) {
	 int ret;

	if (ctx-> format == SELF_FORMAT_ELF || is_fake_self (ctx))
		ret = build_self_auth_info_fake (ctx, old_auth_info, new_auth_info);
	else 
		ret = sceSblAuthMgrIsLoadable2 (ctx, old_auth_info, path_id, new_auth_info);

	return ret;
}
  1. sceSblAuthMgrVerifyHeader
    Questa funzione analizza l’intestazione di un file, la decrittografa (per i file finalizzati ) e imposta una struttura di contesto all’interno di SM che verrà utilizzata in seguito per il caricamento. Non possiamo semplicemente omettere un contesto impostato perché è utilizzato da un ulteriore codice di sistema, quindi useremo un piccolo trucco lasciando che il sistema configuri un contesto falso per noi (questo è un po ‘una bugia però, è un contesto reale che SM preparerà, ma non lo useremo come nel kernel). Per fortuna, abbiamo alcuni veri SELF all’interno della memoria del kernel statica (incorporati in un’immagine del disco di memoria che viene caricata all’avvio), quindi non abbiamo nemmeno bisogno di caricarli dal file system esterno. Quindi, abbiamo bisogno di autenticarsi, per esempio,mini-syscore.elfe quindi sostituire una parte della struttura del contesto con le nostre cose.
static  inline  int  auth_self_header ( struct  self_context * ctx) {
	 extern  const  uint8_t * mini_syscore_self_binary; / * TODO: dovresti puntarlo alla sua posizione nella memoria di un kernel (copia il file usando ftp o qualcosa di simile e prova a trovarlo nel dump della memoria del kernel) * /

	struct  self_header * hdr;
	unsigned  int old_total_header_size, new_total_header_size;
	int old_format;
	uint8_t * tmp;
	int is_unsigned;
	int ret;

	is_unsigned = ctx-> format == SELF_FORMAT_ELF || is_fake_self (ctx);

	if (is_unsigned) {
		old_format = ctx-> format;
		old_total_header_size = ctx-> total_header_size;

		/ * prendi un'intestazione da mini-syscore.elf * / 
		hdr = ( struct  self_header *) mini_syscore_self_binary;
		new_total_header_size = hdr-> header_size + hdr-> meta_size;

		tmp = ( uint8_t *) alloc (new_total_header_size);
		if (! tmp) {
			ret = ENOMEM;
			goto error;
		}

		/ * scambia temporaneamente una nostra intestazione con un'intestazione da un vero file SELF * /
		 memcpy (tmp, ctx-> header, new_total_header_size);
		memcpy (ctx-> header, hdr, new_total_header_size);

		/ * ora è SELF, non ELF o qualsiasi altra cosa ... * /
		ctx-> format = SELF_FORMAT_SELF;
		ctx-> total_header_size = new_total_header_size;

		/ * chiama il metodo originale usando un vero file SELF * / *
		ret = sceSblAuthMgrSmVerifyHeader (ctx);

		/ * ripristina tutto ciò che abbiamo fatto prima * /
		 memcpy (ctx-> header, tmp, new_total_header_size);
		ctx-> format = old_format;
		ctx-> total_header_size = old_total_header_size;

		dealloc (tmp);
	} else {
		ret = sceSblAuthMgrSmVerifyHeader (ctx);
	}

errore:
	return ret;
}

// ...

static  int  sceSblAuthMgrVerifyHeader_hook ( struct  self_context * ctx) {
	 sceSblAuthMgrSmStart ();
	restituisce  auth_self_header (ctx);
}
  1. sceSblAuthMgrSmLoadSelfSegment
    I file SELF sono costituiti da segmenti suddivisi in blocchi . Fortunatamente, per i file falsi non ne abbiamo affatto bisogno, quindi un codice per caricarli sarà banale. Nota, abbiamo a che fare con i frame dello stack qui per trovare la struttura di un contesto. Per la 4.05 puoi usare un metodo diverso o forse un offset diverso, basta guardare nel metodo del kernel originale per vedere se è necessario aggiustare qualcosa.
static  int  sceSblAuthMgrSmLoadSelfSegment__sceSblServiceMailbox_hook ( unsigned  long service_id, uint8_t * request, void * response) {
	 / * ottenendo uno stack frame di una funzione genitore * /
	 uint8_t * frame = ( uint8_t *) __builtin_frame_address ( 1 ); / * TODO: potrebbe essere necessario correggere * /

	/ * trovare un puntatore alla struttura di un contesto * /
	 struct  self_context * ctx = * ( struct  self_context **) (frame - 0x100 ); / * TODO: potrebbe essere necessario correggere * /

	int is_unsigned = ctx && is_fake_self (ctx);
	int ret;

	if (is_unsigned) {
		* ( int *) (risposta + 0x04 ) = 0 ; / * imposta il campo errore su zero, quindi non abbiamo errori * / 
		ret = 0 ;
	} else {
		ret = sceSblServiceMailbox (service_id, request, response);
	}

	return ret;
}
  1. sceSblAuthMgrSmLoadSelfBlock
    Qui è dove avviene la decrittazione / copia effettiva. Quindi, come ho detto sopra, abbiamo solo bisogno di copiare i dati del blocco da un buffer all’altro. Tuttavia, c’è un problema, qui abbiamo gli indirizzi della GPU , quindi dobbiamo convertirli in indirizzi CPU per usarli nelle memcpy()chiamate, possiamo farlo esaminando gli elenchi di intervalli di memoria mappati e cercando un indirizzo GPU corrispondente . Notate anche che qui stiamo usando un registro specifico per ottenere la struttura di un contesto. Per la 4.05 si può usare un metodo diverso o forse un registro diverso, basta guardare nel metodo del kernel originale per trovare ciò che deve essere cambiato.
static  int  sceSblAuthMgrSmLoadSelfBlock__sceSblServiceMailbox_hook ( unsigned  long service_id, uint8_t * request, void * response) {
	 register  struct  self_context * ctx __asm__ ( " r14 " ); / * TODO: potrebbe essere necessario correggere * /
	 vm_offset_t segment_data_gpu_va = * ( unsigned  long *) (richiesta + 0x08 );
	vm_offset_t cur_data_gpu_va = * ( unsigned  long *) (richiesta + 0x50 );
	vm_offset_tcur_data2_gpu_va = * ( unsigned  long *) (richiesta + 0x58 );
	unsigned  int data_offset = * ( unsigned  int *) (richiesta + 0x44 );
	unsigned  int data_size = * ( unsigned  int *) (richiesta + 0x48 );
	vm_offset_t segment_data_cpu_va, cur_data_cpu_va, cur_data2_cpu_va;
	int unsigned1  ;
	int is_unsigned = ctx && (ctx-> format == SELF_FORMAT_ELF || is_fake_self (ctx));
	int ret;

	if (is_unsigned) {
		 / * ricerca negli elenchi delle regioni di memoria mappate della GPU * / 
		segment_data_cpu_va = sceSblDriverGpuVaToCpuVa (segment_data_gpu_va, NULL );
		cur_data_cpu_va = sceSblDriverGpuVaToCpuVa (cur_data_gpu_va, NULL );
		cur_data2_cpu_va = cur_data2_gpu_va? sceSblDriverGpuVaToCpuVa (cur_data2_gpu_va, NULL ): 0 ;

		if (segment_data_cpu_va && cur_data_cpu_va) {
			 if (cur_data2_gpu_va && cur_data2_gpu_va! = cur_data_gpu_va && data_offset> 0 ) {
				 / * i dati si estendono su due pagine di memoria consecutiva, quindi dobbiamo copiare due volte * /
				size1 = PAGE_SIZE - data_offset;
				memcpy (( char *) segment_data_cpu_va, ( char *) cur_data_cpu_va + data_offset, size1);
				memcpy (( char *) segment_data_cpu_va + size1, ( char *) cur_data2_cpu_va, data_size - size1);
			} else {
				 memcpy (( char *) segment_data_cpu_va, ( char *) cur_data_cpu_va + data_offset, data_size);
			}
		}

		* ( int *) (richiesta + 0x04 ) = 0 ; / * imposta il campo errore su zero, quindi non abbiamo errori * / 
		ret = 0 ;
	} else {
		ret = sceSblServiceMailbox (service_id, request, response);
	}

	return ret;
}

Fatto, ora li agganciamo tutti (tieni presente che puoi utilizzare qualsiasi metodo di aggancio che desideri). Devi trovare valori di diapositiva per tutte queste funzioni, basta aprire l’elf del kernel (potresti usare quello che è stato trapelato qualche tempo fa o eseguire il dump del tuo), individuare le funzioni corrispondenti e sostituire i loro offset (sono relativi all’indirizzo di base del kernel! ).

void  install_unsigned_loader ( void ) {
	 / * TODO: è necessario modificare una diapositiva dell'istruzione "call sceSblAuthMgrVerifyHeader" * /
	 INSTALL_CALL_HOOK ( 0x61F976 , sceSblAuthMgrVerifyHeader_hook);
	INSTALL_CALL_HOOK ( 0x620599 , sceSblAuthMgrVerifyHeader_hook);

	/ * TODO: è necessario modificare una diapositiva dell'istruzione "call sceSblAuthMgrIsLoadable2" * /
	 INSTALL_CALL_HOOK ( 0x61F24F , sceSblAuthMgrIsLoadable2_hook);

	/ * TODO: è necessario modificare una diapositiva dell'istruzione "call sceSblServiceMailbox" che si trova all'interno di sceSblAuthMgrSmLoadSelfBlock () * /
	 INSTALL_CALL_HOOK ( 0x6244E1 , sceSblAuthMgrSmLoadSelfBlock__sceSblServiceMailbox_hook);

	/ * TODO: è necessario modificare una diapositiva dell'istruzione "call sceSblServiceMailbox" che si trova all'interno di sceSblAuthMgrSmLoadSelfSegment () * /
	 INSTALL_CALL_HOOK ( 0x6238BA , sceSblAuthMgrSmLoadSelfSegment__sceSblServiceMailbox_hook);
}

Analisi

Quando l’ho fatto la prima volta ho pensato “come faccio a testare quel codice?”. Ho deciso di sostituire un’applicazione incorporata che non è racchiusa in un file del pacchetto, ma inizia direttamente dal file SELF . L’applicazione Video Editor ( NPXS20103) soddisfa perfettamente questa esigenza e si trova in /system_ex/app/NPXS20103/eboot.bin. Ho decodificato un eboot originale usando uno dei metodi possibili e quindi avevo bisogno di qualcosa per comprimere un semplice file ELF in FSELF . Tuttavia, non esiste uno strumento di questo tipo, quindi ho creato uno script personalizzato per farlo: fake firmata

C’è un’altra cosa che dovremmo prendere in considerazione: le applicazioni di sistema usano i diritti di accesso e le funzionalità e sono condivise / controllate quando vengono caricate anche le dipendenze, il che avviene usando una self_auth_infostruttura che viene riempita e controllata dal codice di SM . Quindi abbiamo bisogno di scaricare una struttura originale e usarla per aggirare possibili glitch / crash o qualsiasi altra cosa. Potrebbe essere fatto facilmente chiamando semplicemente kern_get_self_auth_info(struct thread* td, const char* self_path, enum uio_seg segflg, struct self_auth_info* auth_info)dal codice del kernel (anche questo è possibile farlo da userland ma questo richiede un po ‘di patching, c’è anche un syscall per questo).

Un esempio di tale struttura che è stata presa dall’originale eboot.binin 4.55 :

	0000h: 11 00 00 00 00 00 00 38 00 00 00 00 00 1C 00 40
	0010h: 00 FF 00 00 00 00 00 A5 00 00 00 00 00 00 00 00
	0020h: 00 00 00 00 00 00 00 00 00 00 00 80 00 40 00 40
	0030h: 00 00 00 00 00 00 00 80 01 00 00 00 00 00 00 04
	0040h: 00 40 FF FF 00 00 00 F0 XX XX XX XX XX XX XX XX
	0050h: XX XX XX XX XX XX XX XX 00 00 00 00 00 00 00 00
	0060h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
	0070h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
	0080h: 00 00 00 00 00 00 00 00

E la stessa discarica ma presa dalle 5.00 :

	0000h: 11 00 00 00 00 00 00 38 00 00 00 00 00 1C 00 40
	0010h: 00 FF 00 00 00 00 00 85 00 00 00 00 00 00 00 00
	0020h: 00 00 00 00 00 00 00 00 00 00 00 80 00 40 00 40
	0030h: 00 00 00 00 00 00 00 80 01 00 00 00 00 00 00 04
	0040h: 00 40 FF FF 00 00 00 F0 XX XX XX XX XX XX XX XX
	0050h: XX XX XX XX XX XX XX XX 00 00 00 00 00 00 00 00
	0060h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
	0070h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
	0080h: 00 00 00 00 00 00 00 00

Come puoi vedere, solo un byte della struttura è diverso. Inoltre, se ricordo bene, i byte sotto XX qui di solito sono gli stessi per tutte le applicazioni di sistema e assumono un ruolo importante per il processo di caricamento (tuttavia ho bisogno di investigare di più), quindi li useremo tutti. Non ho idea se sia un po ‘di chiave o di magia, quindi basta scaricarlo da solo usando il metodo che ho fornito.

Ora puoi usare il mio script per creare un elfo falso firmato di questa applicazione:

$ make_fself.py --paid 0x3800000000000011 --auth-info '<bytes of self_auth_info structure>' eboot.elf eboot.bin

loading elf file: eboot.elf
saving fake signed elf file: eboot.bin
processing segment #00...
processing segment #01...
processing segment #02...
processing segment #08...
done

Sostituiamo ora il file SELF originale con il nostro FSELF . Potremmo farlo da userland rimontando / system_ex , consentendoci così l’accesso in scrittura:

static  int  mount_large_fs ( const  char * dispositivo, const  char * mountpoint, const  char * fstype, const  char * mode, unsigned  int flags) {
	 struct  iovec * iov = NULL ;
	int iovlen = 0 ;
	int ret;

	build_iovec (& iov, & iovlen, " fstype " , fstype, - 1 );
	build_iovec (& iov, & iovlen, " fspath " , mountpoint, - 1 );
	build_iovec (& iov, & iovlen, " from " , device, - 1 );
	build_iovec (& iov, & iovlen, " large " , " yes " , - 1 );
	build_iovec (& iov, & iovlen, " fuso orario " , "1 );
	build_iovec (& iov, & iovlen, " async " , " " , - 1 );
	build_iovec (& iov, & iovlen, " ignoreacl " , " " , - 1 );
	if (mode) {
		 build_iovec (& iov, & iovlen, " dirmask " , mode, - 1 );
		build_iovec (& iov, & iovlen, " mask " , mode, - 1 );
	}

	printf ( "   [I] Montaggio% s (% s) in% s ... \ n " , dispositivo, fstype, punto di montaggio);
	ret = nmount (iov, iovlen, flags);
	if (ret < 0 ) {
		 printf ( "   [E] non riuscito:% d (errno:% d). " , ret, errno);
		goto error;
	} else {
		 printf ( "   [I] Success. " );
	}

errore:
	return ret;
}

// ...

static  int  remount_partitions ( void ) {
	 int ret;

	// ...

	ret = mount_large_fs ( " /dev/da0x5.crypt " , " / system_ex " , " exfatfs " , " 511 " , MNT_UPDATE);
	se (ret)
		 goto error;

	// ...

errore:
	return ret;
}

Dopo aver chiamato, remount_partitions()è possibile utilizzare qualsiasi metodo per trasferire un nuovo file nel file system della console (non dimenticare di eseguire un backup del file originale).

Ora registriamo un video e proviamo a tagliarlo. Ciò consentirà a PS4 di avviare l’applicazione di editor video e, se tutto è stato fatto correttamente, dovresti vedere che l’applicazione funziona normalmente (altrimenti potresti vedere un crash).

Usando questo metodo è possibile modificare quasi tutti i binari di sistema, e se qualcuno potrebbe implementare un metodo per riavviare la console (se è anche possibile), potrebbe reindirizzare le partizioni di sistema alle proprie posizioni con i binari di sistema con firma falsi personalizzati , ottenendo così qualche tipo di CFW con modifiche del firmware.

PKG falsi

toolchain

Come ho detto sopra, tutti i pacchetti sono crittografati e firmati e utilizzano algoritmi di crittografia asimmetrica come RSA . Questo include un algoritmo per generare anche le chiavi del pacchetto. Gli sviluppatori potevano specificare un passcode pseudo casuale che viene utilizzato come chiave radice per generare altre chiavi, che potrebbero essere utilizzate in seguito per generare più chiavi e così via …

Una di queste chiavi derivate è EKPFS , chiave di crittografia per PFS . Questa chiave viene utilizzata in seguito per generare chiavi finali per crittografare e firmare il file system interno di un file di pacchetto (chiamato PFS , un file system di Playstation ). Sfortunatamente per noi, è crittografato usando algoritmi RSA + AES , e sebbene abbiamo la chiave AES, non abbiamo la chiave RSA che è più importante. Crittografato (o messo più appropriatamente, depositato ) EKPFS si chiama EEKPFS , SAMU lo decifra internamente e non ci darà il risultato, rimane all’interno di SMmemoria e non lo lascia mai (credo anche che ci sia un ulteriore livello AES nel mezzo ma non è importante per quello che stiamo facendo). EEKPFS è crittografato e le chiavi finali generate dall’EKPFS decrittografato vengono impostate direttamente negli slot chiave del criptocenter ( CCP ) e utilizzate in seguito nelle richieste CCP per eseguire la decrittografia e la verifica. Questo è il problema principale che rende questo metodo un po ‘complicato, perché è necessarioper conoscere questa chiave ed è impossibile al momento, quindi abbiamo bisogno di creare la nostra chiave e crittografare qualsiasi contenuto che utilizzi, quindi dobbiamo modificare la toolchain e impostare una chiave personalizzata lì, quindi modificare il kernel per usare anche questa chiave ( bypassando le chiamate SM ) per i nostri contenuti.

Per raggiungere il nostro obiettivo, abbiamo bisogno di modificare gli strumenti di pubblicazione Orbis , fa parte dell’SDKtrapelato . Consiste di diversi strumenti ma abbiamo bisogno solo di due file eseguibili (beh, forse tre se hai intenzione di utilizzare lo strumento GUI): orbis-pub-cmd.exeext/sc.exe(e facoltativamente orbis-pub-prx.dll). Il primo è il nostro strumento principale e il secondo è utilizzato per le operazioni di crittografia (detiene le chiavi, ecc.). Inizieremo con ext/sc.exe.

All’interno è presente un file XML crittografato sc.exeche contiene tutte le chiavi necessarie, i dizionari, i codici di errore e altre cose. Abbiamo bisogno di decodificarlo, inserire la nostra chiave e ricodificarlo. Possiamo scrivere un editor esadecimale, ad esempio l’ editor 010 con il modello PE , e guardare l’inizio della sezione .data dove è memorizzato il nostro blob, copiare questi byte fino a trovare 16 byte zero. Gli ultimi 0x20 byte di dati sono la nostra IV / chiave ( all’interno di quell’ordine! ) Per AES 128 , quindi tagliala dai dati e decrittografali con la modalità CBC , dovresti ottenere l’ XMLdocumento, non dimenticare di tagliare i byte zero finali (sono usati come padding per ottenere il blocco AES corretto). Se si calcola l’ hash SHA256 sul file XML risultante , si noterà che è lo stesso di IV / chiave, tenerne conto.

Posizione del file .xml crittografato all'interno di sc.exe, 4.50 SDK

È inoltre possibile utilizzare questo script per decrittografare XML (inserire il blob crittografato con iv / key in xml.enc.orig): sc xml decrypter

Ho già calcolato una coppia di chiavi RSA 2048 che suggerisco di utilizzare per mantenere la compatibilità tra diversi payload e strumenti:

Chiave pubblica per pacchetti personalizzati 
Chiave privata per pacchetti personalizzati
L’esponente della chiave pubblica è 65537.

Si potrebbe usare OpenSSL per scaricare tutti i componenti: openssl rsa -in ypkg_private.pem -text -nooutmoduluscome s_ypkg_npublicExponentcome s_ypkg_eprivateExponentcome s_ypkg_dprime1come s_ypkg_pprime2come s_ypkg_qexponent1come s_ypkg_dmp1exponent2come s_ypkg_dmq1, e coefficientcome s_ypkg_iqmpSi noti che nel codice l’ordine dei byte è invertito ). Quindi sostituiamo una chiave all’interno <mount-image>...</mount-image>del file XMLcon la nostra modulus(dovresti rimuovere il primo zero byte):

  < immagine di montaggio >
    0xc6 0xcf 0x71 0xe7 0xe5 0x9a 0xf0 0xd1 0x2a 0x2c 0x45 0x8b 0xf9 0x2a 0x0e 0xc1
    0x43 0x05 0x8b 0xc3 0x71 0x17 0x80 0x1d 0xcd 0x49 0x7d 0xde 0x35 0x9d 0x25 0x9b
    0xa0 0xd7 0xa0 0xf2 0x7d 0x6c 0x08 0x7e 0xaa 0x55 0x02 0x68 0x2b 0x23 0xc6 0x44
    0xb8 0x44 0x18 0xeb 0x56 0xcf 0x16 0xa2 0x48 0x03 0xc9 0xe7 0x4f 0x87 0xeb 0x3d
    0x30 0xc3 0x15 0x88 0xbf 0x20 0xe7 0x9d 0xff 0x77 0x0c 0xde 0x1d 0x24 0x1e 0x63
    0xa9 0x4f 0x8a 0xbf 0x5b 0xbe 0x60 0x19 0x68 0x33 0x3b 0xfc 0xed 0x9f 0x47 0x4e
    0x5f 0xf8 0xea 0xcb 0x3d 0x00 0xbd 0x67 0x01 0xf9 0x2c 0x6d 0xc6 0xac 0x13 0x64
    0xe7 0x67 0x14 0xf3 0xdc 0x52 0x69 0x6a 0xb9 0x83 0x2c 0x42 0x30 0x13 0x1b 0xb2
    0xd8 0xa5 0x02 0x0d 0x79 0xed 0x96 0xb1 0x0d 0xf8 0xcc 0x0c 0xdf 0x81 0x95 0x4f
    0x03 0x58 0x09 0x57 0x0e 0x80 0x69 0x2e 0xfe 0xff 0x52 0x77 0xea 0x75 0x28 0xa8
    0xfb 0xc9 0xbe 0xbf 0x9f 0xbb 0xb7 0x79 0x8e 0x18 0x05 0xe1 0x80 0xbd 0x50 0x34
    0x94 0x81 0xd3 0x53 0xc2 0x69 0xa2 0xd2 0x4c 0xcf 0x6c 0xf4 0x57 0x2c 0x10 0x4a
    0x3f 0xfb 0x22 0xfd 0x8b 0x97 0xe2 0xc9 0x5b 0xa6 0x2b 0xcd 0xd6 0x1b 0x6b 0xdb
    0x68 0x7f 0x4b 0xc2 0xa0 0x50 0x34 0xc0 0x05 0xe5 0x8d 0xef 0x24 0x67 0xff 0x93
    0x40 0xcf 0x2d 0x62 0xa2 0xa0 0x50 0xb1 0xf1 0x3a 0xa8 0x3d 0xfd 0x80 0xd1 0xf9
    0xb8 0x05 0x22 0xaf 0xc8 0x35 0x45 0x90 0x58 0x8e 0xe3 0x3a 0x7c 0xbd 0x3e 0x27
  </ mount-image >

Assicurati di avere le stesse dimensioni del file del file originale. Ora dobbiamo calcolare un hash SHA256 su un nuovo file, aggiungere zero byte finali per il riempimento e crittografare i dati usando IV / chiave in base all’hash che abbiamo appena calcolato. Qui aggiungeremo una nuova chiave IV / ai dati crittografati e inseriremo un nuovo blob in sc.exe.

È anche possibile utilizzare questo script per crittografare XML (posizionare il file modificato senza riempimento in xml.dec.new): sc xml encrypter

Sarebbe anche bello avere il supporto per la generazione di pacchetti che contengono FSELF (creati con uno strumento sopra, per esempio), invece di trattare con ELF grezzi . Purtroppo, gli strumenti di pubblicazione (e orbis-pub-cmd.exein particolare) non permetteranno di comporre elfs firmati (anche con firma falsa ), ma c’è un’eccezione: sce_sys/about/right.sprxè un file SELF al dettaglio incorporato nello strumento stesso ed è incluso automaticamente in un file di pacchetto, quindi lo strumento sta facendo un’eccezione per questo file. Quindi possiamo riutilizzare questa funzione per i nostri FSELF reindirizzando un percorso di codice che stampa questo errore su un percorso di codice che è usato per gestire right.sprx. Per fare questo dobbiamo patch sc,exee, facoltativamente,orbis-pub-prx.dll. Senza queste patch toolchain stamperà questo errore:

[Error]	Format of the elf file is not valid. (eboot.bin, already converted from elf file to self file)

Quindi, apriamo il file eseguibile in IDA , facciamo clic su Stringhe e already converted from elf file to selfcerchiamo questa stringa:, ricorda questa posizione, torneremo qui più tardi. Nella stessa funzione dobbiamo trovare un riferimento a sce_sys/about/right.sprx, dove è usato all’interno della strncmp()funzione, e dove il nome del file in entrata è una corrispondenza. Salterà in qualche luogo, questa posizione è il nostro obiettivo (nel mio caso, è loc_454B16).

push 100h
mov esi, eax
push offset aSce_sysAboutRi ; "sce_sys/about/right.sprx"
push esi
call ebp ; __imp_strncmp
add esp, 0Ch
test eax, eax
jz loc_454B16

Ora torniamo indietro (alle pushistruzioni per le stringhe), dobbiamo usare la funzionalità di assembler di IDA per correggere un binario usando un frammento di codice jmp loc_454B16:

Originale ( per il mio caso! ):

push offset aAlreadyConvert ; "already converted from elf file to self"...
jmp loc_45502E

Patched uno ( per il mio caso! ):

jmp unk_5E63FC-1918E6h
jmp loc_45502E

Non importa le strane jmpistruzioni mutate , è un problema con il disassemblatore di IDA e non influenzerà la funzionalità. Ora possiamo creare file di pacchetto con successo anche con FSELF s all’interno.

Ma non chiudere ancora il file binario. Abbiamo bisogno di estrarre una chiave di debug / falso RIF da esso, in quanto verrà utilizzato in seguito nel codice del kernel. Si dovrebbe usare la ricerca binaria in IDA per trovare un RIF ‘s valore magico, 52 49 46 00:

mov [esp+44Ch+Dst], 464952h
mov [esp+44Ch+var_3D6], cx
call ds:__imp_strncpy

Salta alcune righe di codice da questa posizione finché non trovi un posto dove viene chiamata una funzione e ci sono un sacco di valori di byte immediati prima di questa chiamata, questo è dove viene copiata la chiave RIF di debug / falso , salvala in qualche posto ( assicurati di avere un ordine byte appropriato).

Posizione della chiave RIF debug / falso all'interno di orbis-pub-cmd.exe, 4.50 SDK

Modding di Shellcore

Le patch Shellcore sono necessarie per permetterci di installare pacchetti di debug e falsi . Tieni presente che i pacchetti falsinon sono pacchetti di debug perché abbiamo cambiato la chiave e non corrisponde alla chiave di debug che non conosciamo come ho già detto sopra. Quindi, su qualsiasi console di sviluppo, non funzioneranno senza le nostre patch. Analogamente, i pacchetti di debug non funzioneranno su una console di vendita al dettaglio per lo stesso motivo. Queste patch sono anche necessarie per posizionare un RIF di debug all’interno dei nostri file di pacchetto falso in una posizione comune corretta sul disco rigido, altrimenti non saremo in grado di eseguire homebrew.

Per fare queste patch si può usare un payload  del kernel per applicare patch al SceShellCoreprocesso (o solo ptrace()cose). Dovrebbe trovare il SceShellCoreprocesso e fare le patch lì usando, ad esempio, la proc_rwmem()funzione. Non entrerò nei dettagli di questo in questo articolo perché è banale.

static  int  do_shellcore_patches ( void ) {
	 struct  proc * p = NULL ;
	struct  proc_vm_map_entry * entries = NULL ;
	uint8_t * text_seg_base = NULL ;
	size_t num_entries;
	size_t i, n;
	int ret = 0 ;

	/ * XXX: tutti gli offset seguenti appartengono alle funzioni che analizzano i file .pkg * /
	 statico  uint32_t call_ofs_for__xor__eax__3nop [] = {
		 0x11A0DB , // chiama sceKernelIsGenuineCEX (0x1486BB per 4,55) 
		0x66EA3B , // chiama sceKernelIsGenuineCEX (0x6E523B per 4,55) 
		0x7F554B , / / chiama sceKernelIsGenuineCEX (0x852C6B per 4.55)

		0x11A107 , // chiama nidf_libSceDipsw_0xD21CE9E2F639A83C (0x1486E7 per 4,55) 
		0x66EA67 , // chiama nidf_libSceDipsw_0xD21CE9E2F639A83C (0x6E5267 per 4,55) 
		0x7F5577 , // chiama nidf_libSceDipsw_0xD21CE9E2F639A83C (0x852C97 per 4,55)
	};

	p = proc_find_by_name ( " SceShellCore " );
	if (! p) {
		 // printf ("Impossibile trovare il processo shellcore. \ n");
		ret = ENOENT;
		goto error;
	}

	ret = proc_get_vm_map (p, & entries, & num_entries);
	if (ret) {
		 // printf ("proc_get_vm_map (% p) fallito. \ n", p); 
		goto error;
	}

	for (i = 0 ; i <num_entries; ++ i) {
		 if (entries [i]. prot == (PROT_READ | PROT_EXEC)) {
			text_seg_base = ( uint8_t *) voci [i]. inizio ;
			rompere ;
		}
	}
	if (! text_seg_base) {
		 // printf ("Impossibile trovare il segmento di testo base di shellcore. \ n");
		ret = ESRCH;
		goto error;
	}

	// 
	// Abilita l'installazione dei pacchetti di debug. 
	//

	for (i = 0 ; i < COUNT_OF (call_ofs_for__xor__eax_eax__3nop); ++ i) {
		ret = proc_write_mem (p, text_seg_base + call_ofs_for__xor__eax_eax__3nop [i], 5 , " \ x31 \ xC0 \ x90 \ x90 \ x90 " , & n);
		if (ret) {
			 // printf ("proc_write_mem (% p) fallito. \ n", p); 
			goto error;
		}
	}

	/ * XXX: questo offset corrisponde alla stringa "falso \ 0" nella memoria di Shellcore * / 
	ret = proc_write_mem (p, text_seg_base + 0xC980EE  / * 0x40F28 per 4.55 * / , strlen ( " libero " ) + 1 , " libero " , & n);
	if (ret) {
		 // printf ("proc_write_mem (% p) fallito. \ n", p); 
		goto error;
	}

	// printf ("Il processo Shellcore è stato riparato. \ n");

errore:
	se (voci)
		 dealloc (voci);

	return ret;
}

Si prega di notare che qui ho trovato offset per 4.05 già, così come per 4.55 (guarda i commenti)!

Ora potremmo dover testare se funzionano. Ho trovato alcuni file di testo all’interno di una partizione di sistema che contiene collegamenti ai pacchetti debug / retail corrispondenti di una nota applicazione, il client OMSK ( NPXS29005). Quindi potremmo usare un pacchetto di debug per testare le patch che abbiamo appena applicato. Se il pacchetto viene installato con successo (non saremo ancora in grado di eseguirlo), allora tutto è a posto. Ecco questi link:

File .pkg OMSK

File .rif OMSK

Codice del kernel

L’idea è semplice: prima proviamo a decodificare il pacchetto normalmente e se questo passaggio fallisce e abbiamo un pacchetto falso , proviamo a decrittarlo manualmente usando la chiave personalizzata. Quindi stiamo facendo le stesse cose come Orbis Publishing Toolsper la generazione di chiavi, e con queste chiavi possiamo decodificare l’ immagine PFSall’interno del file del pacchetto. Ma poiché il sistema utilizza il meccanismo delle chiavi di slot per gestire queste operazioni di crittografia, dobbiamo ometterlo e dire al sistema che abbiamo chiavi semplici invece di chiavi di slot, alimentiamo queste chiavi in ​​richieste crittografiche e quindi tutto viene fatto dal sistema. Dovrei anche notare che falso rif non è veramente usato qui perché è un file fittizio ma dobbiamo gestirlo anche noi.

# define  MAX_FAKE_KEYS  32

struct  fake_key_desc {
	 chiave uint8_t [ 0x20 ];
	int occupato;
};

/ * manteniamo un elenco di chiavi false conosciute per poterle trovare e apportare alcune modifiche nelle richieste di crittografia * /
 static  struct  fake_key_desc s_fake_keys [MAX_FAKE_KEYS];
static  struct  sx s_fake_keys_lock;

/ * contrassegniamo la nostra chiave usando un pattern che possiamo controllare in seguito * /
 static  const  uint8_t s_fake_key_seed [ 0x10 ] = {
	 0x46 , 0x41 , 0x4B , 0x45 , 0x46 , 0x41 , 0x4B , 0x45 , 0x46 , 0x41 , 0x4B , 0x45 , 0x46 , 0x41 , 0x4B , 0x45 ,
};

static  struct  fake_key_desc * get_free_fake_key_slot ( void ) {
	 struct  fake_key_desc * slot = NULL ;
	size_t i;

	sx_xlock (& s_fake_keys_lock);
	{
		per (i = 0 ; i < COUNT_OF (s_fake_keys); ++ i) {
			 se (!. s_fake_keys [i] occupato ) {
				s_fake_keys [i]. occupato = 1 ;
				slot = s_fake_keys + i;
				rompere ;
			}
		}
	}
	sx_xunlock (& s_fake_keys_lock);

	slot di ritorno ;
}

static  struct  fake_key_desc * is_fake_pfs_key ( uint8_t * key) {
	 struct  fake_key_desc * slot = NULL ;
	size_t i;

	sx_xlock (& s_fake_keys_lock);
	{
		per (i = 0 ; i < COUNT_OF (s_fake_keys); ++ i) {
			 se (s_fake_keys [i]!. occupata )
				 continua ;

			if ( memcmp (s_fake_keys [i]. chiave , chiave, sizeof (s_fake_keys [i]. chiave )) == 0 ) {
				slot = s_fake_keys + i;
				rompere ;
			}
		}
	}
	sx_xunlock (& s_fake_keys_lock);

	slot di ritorno ;
}

static  void  debug_pfs_cleanup ( void * arg) {
	 sx_destroy (& s_fake_keys_lock);
}

// ...

/ * questi componenti appartengono alla chiave che ho generato per il nostro contenuto * /

static  const  uint8_t s_ypkg_n [ 0x100 ] = {
	 / * TODO: incolla qui una chiave corrispondente come descritto in un write-up, non dimenticare di rimuovere un byte zero iniziale e l'ordine dei byte corretto * /
};

statico  const  unsigned  int s_ypkg_e = UINT32_C ( 0x10001 );

static  const  uint8_t s_ypkg_d [ 0x100 ] = {
	 / * TODO: incolla qui una chiave corrispondente come descritto in un write-up, fai attenzione ad usare l'ordine dei byte corretto * /
};

static  const  uint8_t s_ypkg_p [ 0x80 ] = {
	 / * TODO: incolla qui una chiave corrispondente come descritto in un write-up, fai attenzione a usare l'ordine dei byte corretto * /
};

static  const  uint8_t s_ypkg_q [ 0x80 ] = {
	 / * TODO: incolla qui una chiave corrispondente come descritto in un write-up, fai attenzione a usare l'ordine dei byte corretto * /
};

static  const  uint8_t s_ypkg_dmp1 [ 0x80 ] = {
	 / * TODO: incolla qui una chiave corrispondente come descritto in un write-up, fai attenzione a usare l'ordine dei byte corretto * /
};

static  const  uint8_t s_ypkg_dmq1 [ 0x80 ] = {
	 / * TODO: incolla qui una chiave corrispondente come descritto in un write-up, fai attenzione ad usare l'ordine dei byte corretto * /
};

static  const  uint8_t s_ypkg_iqmp [ 0x80 ] = {
	 / * TODO: incolla qui una chiave corrispondente come descritto in un write-up, fai attenzione a usare l'ordine dei byte corretto * /
};

/ * Una funzione comune per generare una chiave finale per PFS * /
 static  linea  vuoto  pfs_gen_crypto_key ( uint8_t * ekpfs, uint8_t seme [PFS_SEED_SIZE], unsigned  int index, uint8_t tasto [PFS_FINAL_KEY_SIZE]) {
	 struct  filo * td = curthread ();
	uint8_t d [ 4 + PFS_SEED_SIZE];

	memset (d, 0 , sizeof (d));

	/ * un indice indica quale chiave dovremmo generare * / 
	* ( uint32_t *) d = LE32 ( indice );
	memcpy (d + sizeof ( uint32_t ), seed, PFS_SEED_SIZE);

	fpu_kern_enter (td, fpu_kern_ctx, 0 );
	{
		Sha256Hmac (chiave, d, sizeof (d), ekpfs, EKPFS_SIZE);
	}
	fpu_kern_leave (td, fpu_kern_ctx);
}

/ * un generatore di chiavi di crittografia basato su seme di intestazione EKPFS e PFS * /
 statico  inline  void  pfs_generate_enc_key ( uint8_t * ekpfs, uint8_t seed [PFS_SEED_SIZE], uint8_t chiave [PFS_FINAL_KEY_SIZE]) {
	 pfs_gen_crypto_key (ekpfs, seed, 1 , key);
}

/ *   generatore di chiavi assegnate in base a seme di intestazione EKPFS e PFS * /
 statico  inline  void  pfs_generate_sign_key ( uint8_t * ekpfs, uint8_t seed [PFS_SEED_SIZE], uint8_t chiave [PFS_FINAL_KEY_SIZE]) {
	 pfs_gen_crypto_key (ekpfs, seed, 2 , key);
}

static  int  sceSblPfsKeymgrIoctl__sceSblPfsKeymgrGenEKpfsForGDGPAC__hook ( pfs_key_blob_t * blob) {
	 struct  thread * td = curthread ();
	struct  rsa_buffer in_data;
	struct  rsa_buffer out_data;
	struct  chiave rsa_key ;
	uint8_t dec_data [EEKPFS_SIZE];
	struct  fake_key_desc * fake_key_slot;
	int ret;

	/ * provare a decrittografare EEKPFS normalmente * / 
	ret = sceSblPfsKeymgrGenEKpfsForGDGPAC (blob);

	if (ret) {
		 / * se questa chiave è per il contenuto debug / falso, potremmo provare a decodificarlo manualmente * /
		 if (! blob-> finalizzato) {
			 memset (& in_data, 0 , sizeof (in_data));
			{
				in_data. ptr = blob-> eekpfs;
				in_data. size = sizeof (blob-> eekpfs);
			}

			memset (& out_data, 0 , sizeof (out_data));
			{
				out_data. ptr = dec_data;
				out_data. size = sizeof (dec_data);
			}

			memset (& tasto, 0 , sizeof (chiave));
			{
				/ * qui forniamo una chiave personalizzata all'algoritmo * / 
				chiave. p = ( uint8_t *) s_ypkg_p;
				chiave. q = ( uint8_t *) s_ypkg_q;
				chiave. dmp1 = ( uint8_t *) s_ypkg_dmp1;
				chiave. dmq1 = ( uint8_t *) s_ypkg_dmq1;
				chiave. iqmp = ( uint8_t *) s_ypkg_iqmp;
			}

			fpu_kern_enter (td, fpu_kern_ctx, 0 );
			{
				/ * RSA PKCS1v15 * / 
				ret = RsaesPkcs1v15Dec2048CRT (& out_data, & in_data, & key);
			}
			fpu_kern_leave (td, fpu_kern_ctx);

			if (ret == 0 ) { / * ha la chiave EKPFS? * /
				 memcpy (blob-> ekpfs, dec_data, sizeof (blob-> ekpfs));

				/ * aggiungilo alla nostra lista chiavi * / 
				fake_key_slot = get_free_fake_key_slot ();
				if (fake_key_slot)
					 memcpy (fake_key_slot-> key, blob-> ekpfs, sizeof (fake_key_slot-> key));
			}
		}
	}

	return ret;
}

static  int  pfs_sbl_init__sceSblPfsSetKey__hook ( unsigned  int * ekh, unsigned  int * skh , uint8_t * key, uint8_t * iv, int mode, int inutilizzato, uint8_t disc_flag) {
	 struct  sbl_key_rbtree_entry * key_entry;
	int is_fake_key;
	int ret;

	ret = sceSblPfsSetKey (ekh, skh, chiave, iv, mode, non usato, disc_flag);

	/ * controlla se è una chiave che abbiamo decrittografato manualmente * / 
	is_fake_key = is_fake_pfs_key (chiave)! = NULL ;

	key_entry = sceSblKeymgrGetKey (* ekh); / * trova una voce chiave corrispondente * /
	 if (key_entry) {
		 if (is_fake_key) {
			 / * genera una chiave di crittografia * /
			 pfs_generate_enc_key (chiave, iv, key_entry-> desc. pfs . key );
			memcpy (key_entry-> desc. pfs . seed , s_fake_key_seed, sizeof (s_fake_key_seed));
		}
	}
	key_entry = sceSblKeymgrGetKey (* skh ); / * trova una voce chiave corrispondente * /
	 if (key_entry) {
		 if (is_fake_key) {
			 / * genera una chiave di firma * /
			 pfs_generate_sign_key (chiave, iv, key_entry-> desc. pfs . key );
			memcpy (key_entry-> desc. pfs . seed , s_fake_key_seed, sizeof (s_fake_key_seed));
		}
	}

	return ret;
}

// ...

static  int  npdrm_decrypt_debug_rif ( unsigned  int tipo, uint8_t * dati) {
	 static  const  uint8_t rif_debug_key [ 0x10 ] = { / * TODO: posto qui un debug / falso RIF chiave * / };
	struct  thread * td = curthread ();
	int ret;

	fpu_kern_enter (td, fpu_kern_ctx, 0 );
	{
		/ * decrypt fake rif manualmente usando una chiave da strumenti di pubblicazione * / 
		ret = AesCbcCfb128Decrypt (dati + RIF_DIGEST_SIZE, dati + RIF_DIGEST_SIZE, RIF_DATA_SIZE, rif_debug_key, sizeof (rif_debug_key) * 8 , dati);
		se (ret)
			ret = SCE_SBL_ERROR_NPDRM_ENOTSUP;
	}
	fpu_kern_leave (td, fpu_kern_ctx);

	return ret;
}

static  int  npdrm_decrypt_isolated_rif__sceSblKeymgrSmCallfunc__hook ( unione keymgr_payload * payload) {
	 / * è richiesta SM, quindi qui abbiamo l'indirizzo GPU, quindi dobbiamo convertirlo all'indirizzo della CPU * /
	 union keymgr_request * request = ( union keymgr_request *) sceSblDriverGpuVaToCpuVa (payload- > dati, NULL );
	int ret;

	/ * prova a decrypt rif normalmente * / 
	ret = sceSblKeymgrSmCallfunc (payload);

	/ * e se fallisce, controlliamo se è falso e proviamo a decifrarlo da soli * /
	 if ((ret! = 0 || payload-> status! = 0 ) && request) {
		 if (request-> decrypt_rif. scrivi == 0x200 ) { / * fake? * / 
			ret = npdrm_decrypt_debug_rif (request-> decrypt_rif. type , request-> decrypt_rif. data );
			payload-> status = ret;
			ret = 0 ;
		}
	}

	return ret;
}

// ...

static  int  ccp_msg_populate_key ( unsigned  int key_handle, uint8_t * key, int reverse) {
	 struct  sbl_key_rbtree_entry * key_entry;
	uint8_t * in_key;
	int i;
	stato int = 0 ;

	/ * ricerca di una voce chiave * / 
	key_entry = sceSblKeymgrGetKey (key_handle);

	se (key_entry) {
		 / * ne abbiamo trovato uno, ora controllando se è la nostra chiave * /
		 if ( memcmp (key_entry-> desc. pfs . seed , s_fake_key_seed, sizeof (key_entry-> desc. pfs . seed )) == 0 ) {
			 / * al momento abbiamo una richiesta crittografica che utilizza uno slot chiave che dovrebbe essere già in CCP, ma poiché abbiamo 
			   fatto tutto manualmente, non abbiamo questo slot chiave, quindi dobbiamo rimuovere l'uso dello slot chiave e posizionare 
			   un chiave semplice qui * / 
			in_key = key_entry-> desc. pfs . chiave ;
			if (reverse) { / * reverse byte di una chiave se è necessario * /
				 for (i = 0 ; i < 0x20 ; ++ i)
					tasto [ 0x20 - i - 1 ] = in_key [i];
			} else { / * copia una chiave così com'è * /
				 memcpy (chiave, in_key, 0x20 );
			}
			stato = 1 ;
		}
	}

	stato di ritorno ;
}

static  int  ccp_msg_populate_key_if_needed ( struct  ccp_msg * msg) {
	 unsigned  int cmd = msg-> op. comune . cmd ;
	unsigned  int type = CCP_OP (cmd);
	uint8_t * buf;
	stato int = 0 ;

	/ * Salta messaggi che utilizzano chiavi semplici e slot chiave * /
	 se ((cmd e CCP_USE_KEY_HANDLE)!)
		 Goto saltare;

	buf = ( uint8_t *) & msg-> op;

	/ * abbiamo solo bisogno di gestire xts / hmac operazioni crypto * /
	 switch (type) {
		 case CCP_OP_XTS:
			status = ccp_msg_populate_key (* ( uint32_t *) (buf + 0x28 ), buf + 0x28 , 1 ); / * La chiave xts ha un ordine di byte invertito * /
			 break ;

		caso CCP_OP_HMAC:
			status = ccp_msg_populate_key (* ( uint32_t *) (buf + 0x40 ), buf + 0x40 , 0 ); / * La chiave hmac ha un normale byte order * /
			 break ;

		impostazione predefinita :
			 goto skip;
	}

	/ * se la chiave è stata popolata correttamente, quindi rimuovere il flag che indica a CCP di utilizzare uno slot chiave * /
	 if (stato)
		msg> op. comune . cmd & = ~ CCP_USE_KEY_HANDLE;

Salta:
	stato di ritorno ;
}

static  int  pfs_crypto__sceSblServiceCryptAsync__hook ( struct  ccp_req * request) {
	 struct  ccp_msg * msg;
	int ret;

	TAILQ_FOREACH (msg, & request-> msgs, next) {
		 / * gestisce ogni messaggio in crypto request * /
		 ccp_msg_populate_key_if_needed (msg);
	}

	/ * esegue normalmente una funzione di crittografia * / 
	ret = sceSblServiceCryptAsync (richiesta);

	return ret;
}

// ...

# define  pfs_generate_icv_sub__sceSblServiceCryptAsync__hook pfs_crypto__sceSblServiceCryptAsync__hook
# define  pfs_generate_icv_async_sub__sceSblServiceCryptAsync__hook pfs_crypto__sceSblServiceCryptAsync__hook
# define  pfs_dec_sub__sceSblServiceCryptAsync__hook pfs_crypto__sceSblServiceCryptAsync__hook
# define  pfs_dec_icv_sub__sceSblServiceCryptAsync__hook pfs_crypto__sceSblServiceCryptAsync__hook
# define  pfs_dec_icv_async_sub__sceSblServiceCryptAsync__hook pfs_crypto__sceSblServiceCryptAsync__hook
# define  pfs_enc_sub__sceSblServiceCryptAsync__hook pfs_crypto__sceSblServiceCryptAsync__hook
# define  pfs_icv_enc_sub__sceSblServiceCryptAsync__hook pfs_crypto__sceSblServiceCryptAsync__hook

static  void  do_debug_pfs_patches ( void ) {
	 memset (s_fake_keys, 0 , sizeof (s_fake_keys));

	sx_init (& s_fake_keys_lock, " fake_keys_lock " );

	EVENTHANDLER_REGISTER (shutdown_pre_sync, & debug_pfs_cleanup, NULL , 0 );

	/ * TODO: è necessario modificare una diapositiva dell'istruzione "call sceSblKeymgrSmCallfunc" all'interno di npdrm_decrypt_isolated_rif () * /
	 INSTALL_CALL_HOOK ( 0x62DF00 , npdrm_decrypt_isolated_rif__sceSblKeymgrSmCallfunc__hook);

	/ * TODO: è necessario modificare le diapositive delle istruzioni "call sceSblPfsKeymgrGenEKpfsForGDGPAC" all'interno di sceSblPfsKeymgrIoctl () * /
	 INSTALL_CALL_HOOK ( 0x607045 , sceSblPfsKeymgrIoctl__sceSblPfsKeymgrGenEKpfsForGDGPAC__hook);
	INSTALL_CALL_HOOK ( 0x6070E1 , sceSblPfsKeymgrIoctl__sceSblPfsKeymgrGenEKpfsForGDGPAC__hook);

	/ * TODO: è necessario cambiare le diapositive delle istruzioni "call sceSblPfsSetKey" all'interno di pfs_sbl_init_sub () e pfs_sbl_init () * /
	 INSTALL_CALL_HOOK ( 0x69DB4A , pfs_sbl_init__sceSblPfsSetKey__hook);
	INSTALL_CALL_HOOK ( 0x69DBD8 , pfs_sbl_init__sceSblPfsSetKey__hook);

	/ * TODO: è necessario modificare le diapositive delle istruzioni "call sceSblServiceCryptAsync" all'interno delle corrispondenti funzioni pfs _ * _ sub () (facili da trovare osservando i riferimenti stringa che contengono nomi di funzioni) * /
	 INSTALL_CALL_HOOK ( 0x69DDE4 , pfs_generate_icv_sub__sceSblServiceCryptAsync__hook); // hmac 
	INSTALL_CALL_HOOK ( 0x69E28C , pfs_generate_icv_async_sub__sceSblServiceCryptAsync__hook); // hmac 
	INSTALL_CALL_HOOK ( 0x69E4E8 , pfs_dec_sub__sceSblServiceCryptAsync__hook); // xts 
	INSTALL_CALL_HOOK ( 0x69E85D, pfs_dec_icv_sub__sceSblServiceCryptAsync__hook); // hmac, xts 
	INSTALL_CALL_HOOK ( 0x69EC7E , pfs_dec_icv_async_sub__sceSblServiceCryptAsync__hook); // hmac, xts 
	INSTALL_CALL_HOOK ( 0x69EF0D , pfs_enc_sub__sceSblServiceCryptAsync__hook); // xts 
	INSTALL_CALL_HOOK ( 0x69F252 , pfs_icv_enc_sub__sceSblServiceCryptAsync__hook); // hmac, xts 
}

Potresti anche aver bisogno di queste patch del kernel:

	// Disabilita il controllo della firma RSA per PFS. 
	kernel_text_base + 0x69F4E0 : 55  48  89  E5 -> 31 C0 C3 90

	// Abilita i RIF di debug. 
	kernel_text_base + 0x62D30D : E8 0E 04  00  00 EB 38 3D -> B8 01  00  00  00 EB 38 3D

E ancora, dovrai trovare i valori delle diapositive per tutto quanto sopra perché è per 4.55.

Definizione, strutture e funzioni di aiuto

Alcune definizioni / strutture e funzioni sono appena prese dal codice sorgente di FreeBSD9 , quindi si prega di fare riferimento anche lì. Questa sezione descrive solo quelli che sono collegati a PS4 direttamente.

Uso alcune macro speciali (basate su questo bel frammento di codice: Sparse, strutture C / C ++ basate su offset ) per creare una struttura parzialmente nota con campi a offset specifici, quindi non è necessario inserire membri sconosciuti tra campi noti e aggiungi un’imbottitura adeguata poiché è tutta gestita per te.

# define  JOIN_HELPER ( x, y ) x ## y
# define  JOIN ( x, y ) JOIN_HELPER (x, y)

// ...

# define  TYPE_PAD ( size ) char  JOIN (_pad_, __COUNTER __) [size]
# define  TYPE_VARIADIC_BEGIN ( name ) name { union {
# define  TYPE_BEGIN ( name, size ) name { union { TYPE_PAD (size)
# define  TYPE_END (...)}; } __VA_ARGS__
# define  TYPE_FIELD ( field, offset ) struct {TYPE_PAD (offset); campo; }

# define  TYPE_CHECK_SIZE ( name, size ) \
	 _Static_assert ( sizeof (name) == (size), "Size of" #name " ! = " #size)

# define  TYPE_CHECK_FIELD_OFFSET ( nome, membro, offset ) \
	 _Static_assert ( offsetof (nome, membro) == (offset), " Offset di " #name " . " #member " ! = " #offset)

# define  TYPE_CHECK_FIELD_SIZE ( nome, membro, dimensione ) \
	 _Static_assert ( sizeof (((nome *) 0 ) -> membro) == (dimensione), " Dimensione di " #name " . " #member " ! = " #size)

Okay, riassumiamo tutte le strutture necessarie (se ne ho dimenticato alcune, basta fare un ping):

# define  PAGE_SIZE  0x4000

// ...

struct  fpu_kern_ctx ;

/ * TODO: dovresti puntarlo alla sua posizione nella memoria di un kernel (prendi questa variabile da qualche funzione che fa operazioni di criptazione, per esempio, sceSblSsGenerateSealedKey ()) * /
 struct  fpu_kern_ctx * fpu_kern_ctx;

static  struct inline  thread * curthread ( void ) {
	 struct thread * td;  

	__asm__  __volatile__ (
		 " mov %% gs: 0,% 0 " 
		: " = r " (td)
	);

	ritorno td;
}

// ...

# define  SCE_SBL_ERROR_NPDRM_ENOTSUP  0x800F0A25

// ...

# define  ELF_IDENT_SIZE  0x10 
# define  ELF_EHDR_EXT_SIZE  0x1000

# define  ELF_IDENT_MAG0   0 
# define  ELF_IDENT_MAG1   1 
# define  ELF_IDENT_MAG2   2 
# define  ELF_IDENT_MAG3   3 
# define  ELF_IDENT_CLASS  4 
# define  ELF_IDENT_DATA   5

# define  ELF_CLASS_64  2 
# define  ELF_DATA_LSB  1

# define  ELF_TYPE_NONE  0 
# define  ELF_TYPE_EXEC  2

# define  ELF_MACHINE_X86_64  0x3E

# define  ELF_PHDR_TYPE_NULL            0x0 
# define  ELF_PHDR_TYPE_LOAD            0x1 
# define  ELF_PHDR_TYPE_SCE_DYNLIBDATA  0x61000000 
# define  ELF_PHDR_TYPE_SCE_RELRO       0x61000010 
# define  ELF_PHDR_TYPE_SCE_COMMENT     0x6FFFFF00 
# define  ELF_PHDR_TYPE_SCE_VERSION     0x6FFFFF01

# define  ELF_PHDR_FLAG_X  0x1 
# define  ELF_PHDR_FLAG_W  0x2 
# define  ELF_PHDR_FLAG_R  0x4

# define  ELF_ET_EXEC           0x2 
# define  ELF_ET_SCE_EXEC       0xFE00 
# define  ELF_ET_SCE_EXEC_ASLR  0xFE10 
# define  ELF_ET_SCE_DYNAMIC    0xFE18

typedef  uint16_t  elf64_half_t ;
typedef  uint32_t  elf64_word_t ;
typedef  uint64_t  elf64_xword_t ;
typedef  uint64_t  elf64_off_t ;
typedef  uint64_t  elf64_addr_t ;

struct  elf64_ehdr {
	 uint8_t ident [ELF_IDENT_SIZE];
	elf64_half_t type;
	macchina elf64_half_t ;
	versione elf64_word_t ;
	voce elf64_addr_t ;
	elf64_off_t phoff;
	elf64_off_t shoff;
	elf64_word_t flags;
	elf64_half_t ehsize;
	elf64_half_t phentsize;
	elf64_half_t phnum;
	elf64_half_t shentsize;
	elf64_half_t shnum;
	elf64_half_t shstrndx;
};

struct  elf64_phdr {
	 elf64_word_t type;
	elf64_word_t flags;
	elf64_off_t offset;
	elf64_addr_t vaddr;
	elf64_addr_t paddr;
	elf64_xword_t filesz;
	elf64_xword_t memsz;
	elf64_xword_t align;
};

struct  elf64_shdr {
	 nome elf64_word_t ;
	elf64_word_t type;
	elf64_xword_t flags;
	elf64_addr_t addr;
	elf64_off_t offset;
	dimensione elf64_xword_t ;
	collegamento elf64_word_t ;
	elf64_word_t informazioni;
	elf64_xword_t addralign;
	elf64_xword_t entsize;
};

// ...

# define  SELF_DIGEST_SIZE  0x20 
# define  SELF_CONTENT_ID_SIZE  0x13 
# define  SELF_RANDOM_PAD_SIZE  0x0D 
# define  SELF_MAX_HEADER_SIZE  0x4000

enum self_format {
	SELF_FORMAT_NONE,
	SELF_FORMAT_ELF,
	SELF_FORMAT_SELF,
};

# define  SIZEOF_SELF_PAGER  0x100  // XXX: random, non usare direttamente senza ripararlo

TYPE_BEGIN ( struct  self_pager , SIZEOF_SELF_PAGER);
	TYPE_FIELD ( struct  mtx lock, 0x00 );
TYPE_END ();

# define  SIZEOF_SELF_INFO  0x100  // XXX: random, non usare direttamente senza ripararlo

TYPE_BEGIN ( struct  self_info , SIZEOF_SELF_INFO);
	TYPE_FIELD ( struct  vnode * vp, 0x20 );
	TYPE_FIELD ( struct  self_pager * pager, 0x28 );
	TYPE_FIELD ( intestazione uint8_t *, 0x38 );
	TYPE_FIELD ( int ctx_id, 0x40 );
TYPE_END ();

# define  SIZEOF_SELF_CONTEXT  0x60  // sceSblAuthMgrAuthHeader: bzero (sbl_authmgr_context, 0x60)

TYPE_BEGIN ( struct  self_context , SIZEOF_SELF_CONTEXT);
	TYPE_FIELD ( formato self_format enum , 0x00 );
	TYPE_FIELD ( int elf_auth_type, 0x04 ); / * auth id è basato su quello * /
	 TYPE_FIELD ( unsigned  int total_header_size, 0x08 );
	TYPE_FIELD ( int ctx_id, 0x1C );
	TYPE_FIELD ( uint64_t svc_id, 0x20 );
	TYPE_FIELD ( intbuf_id, 0x30 );
	TYPE_FIELD ( intestazione uint8_t *, 0x38 );
	TYPE_FIELD ( struct  mtx lock, 0x40 );
TYPE_END ();

# define  SIZEOF_SELF_HEADER  0x20

TYPE_BEGIN ( struct  self_header , SIZEOF_SELF_HEADER);
	TYPE_FIELD ( uint32_t magic, 0x00 );
# Definire  SELF_MAGIC  0x1D3D154F 
# definiscono  ELF_MAGIC   0x464C457F 
	TYPE_FIELD ( uint8_t versione, 0x04 );
	TYPE_FIELD ( uint8_t modalità, 0x05 );
	TYPE_FIELD ( uint8_t endian, 0x06 );
	TYPE_FIELD ( uint8_t attr, 0x07 );
	TYPE_FIELD ( uint32_t key_type, 0x08 );
	TYPE_FIELD ( uint16_t header_size, 0x0C );
	TYPE_FIELD ( uint16_t meta_size, 0x0E );
	TYPE_FIELD ( uint64_t file_size, 0x10 );
	TYPE_FIELD ( uint16_t num_entries, 0x18 );
	TYPE_FIELD ( flag uint16_t , 0x1A );
TYPE_END ();

# define  SIZEOF_SELF_ENTRY  0x20

TYPE_BEGIN ( struct  self_entry , SIZEOF_SELF_ENTRY);
	TYPE_FIELD ( puntelli uint64_t , 0x00 );
	TYPE_FIELD ( offset uint64_t , 0x08 );
	TYPE_FIELD ( uint64_t file_size, 0x10 );
	TYPE_FIELD ( uint64_t memory_size, 0x18 );
TYPE_END ();

# define  SIZEOF_SELF_EX_INFO  0x40

TYPE_BEGIN ( struct  self_ex_info , SIZEOF_SELF_EX_INFO);
	TYPE_FIELD ( uint64_t paid, 0x00 );
	TYPE_FIELD ( uint64_t ptype, 0x08 );
# define  SELF_PTYPE_FAKE  0x1 
	TYPE_FIELD ( uint64_t app_version, 0x10 );
	TYPE_FIELD ( uint64_t fw_version, 0x18 );
	TYPE_FIELD ( uint8_t digest [SELF_DIGEST_SIZE], 0x20 );
TYPE_END ();

# define  SIZEOF_SELF_AUTH_INFO  0x88  // sceSblAuthMgrIsLoadable2: bzero (auth_info, 0x88)

TYPE_BEGIN ( struct  self_auth_info , SIZEOF_SELF_AUTH_INFO);
	TYPE_FIELD ( uint64_t paid, 0x00 );
	TYPE_FIELD ( uint64_t caps [ 4 ], 0x08 );
	TYPE_FIELD ( uint64_t attrs [ 4 ], 0x28 );
	TYPE_FIELD ( uint8_t unk [ 0x40 ], 0x48 );
TYPE_END ();

# define  SIZEOF_SELF_FAKE_AUTH_INFO ( sizeof ( uint64_t ) + SIZEOF_SELF_AUTH_INFO)

TYPE_BEGIN ( struct  self_fake_auth_info , SIZEOF_SELF_FAKE_AUTH_INFO);
	TYPE_FIELD ( dimensione uint64_t , 0x00 );
	TYPE_FIELD ( struct  self_auth_info info, 0x08 );
TYPE_END ();

int  sceSblAuthMgrGetSelfInfo ( struct  self_context * ctx, struct  self_ex_info ** info);

int  sceSblAuthMgrIsLoadable2 ( struct  self_context * ctx, struct  self_auth_info * old_auth_info, int path_id, struct  self_auth_info * new_auth_info);

void  sceSblAuthMgrSmStart ( void );
int  sceSblAuthMgrSmVerifyHeader ( struct  self_context * ctx);

static  inline  int  sceSblAuthMgrGetElfHeader ( struct  self_context * ctx, struct  elf64_ehdr ** ehdr) {
	 struct  self_header * self_hdr;
	struct  elf64_ehdr * elf_hdr;
	size_t pdata_size;

	if (ctx-> format == SELF_FORMAT_ELF) {
		elf_hdr = ( struct  elf64_ehdr *) ctx-> header;
		se (ehdr)
			* ehdr = elf_hdr;
		ritorno  0 ;
	} else  if (ctx-> format == SELF_FORMAT_SELF) {
		self_hdr = ( struct  self_header *) ctx-> header;

		pdata_size = self_hdr-> header_size - sizeof ( struct  self_entry ) * self_hdr-> num_entries - sizeof ( struct  self_header );
		if (pdata_size> = sizeof ( struct  elf64_ehdr ) && (pdata_size & 0xF ) == 0 ) {
			elf_hdr = ( struct  elf64_ehdr *) (( uint8_t *) self_hdr + sizeof ( struct  self_header ) + sizeof ( struct  self_entry ) * self_hdr-> num_entries);
			se (ehdr)
				* ehdr = elf_hdr;
			ritorno  0 ;
		}

		ritorno - 37 ;
	}

	ritorno - 35 ;
}

int  kern_get_self_auth_info ( struct  thread * td, const  char * percorso, enum uio_seg pathseg, struct  self_auth_info * info);

// ...

# define  CONTENT_KEY_SEED_SIZE  0x10 
# define  SELF_KEY_SEED_SIZE  0x10 
# define  EEKC_SIZE  0x20

struct  ekc {
	 uint8_t content_key_seed [CONTENT_KEY_SEED_SIZE];
	uint8_t self_key_seed [SELF_KEY_SEED_SIZE];
};

# define  SIZEOF_SBL_KEY_DESC  0x7C  // sceSblKeymgrSetKey

unione sbl_key_desc {
	 struct {
		 uint16_t cmd;
		uint16_t pad;
		chiave uint8_t [ 0x20 ];
		uint8_t seed [ 0x10 ];
	} pfs;

	// ...

	uint8_t raw [SIZEOF_SBL_KEY_DESC];
};
TYPE_CHECK_SIZE ( unione sbl_key_desc, SIZEOF_SBL_KEY_DESC);

# define  SIZEOF_SBL_KEY_RBTREE_ENTRY  0xA8  // sceSblKeymgrSetKey

# define  TYPE_SBL_KEY_RBTREE_ENTRY_DESC_OFFSET  0x04 
# define  TYPE_SBL_KEY_RBTREE_ENTRY_LOCKED_OFFSET  0x80

TYPE_BEGIN ( struct  sbl_key_rbtree_entry , SIZEOF_SBL_KEY_RBTREE_ENTRY);
	TYPE_FIELD ( uint32_t handle, 0x00 );
	TYPE_FIELD ( unione sbl_key_desc desc, TYPE_SBL_KEY_RBTREE_ENTRY_DESC_OFFSET);
	TYPE_FIELD ( uint32_t locked, TYPE_SBL_KEY_RBTREE_ENTRY_LOCKED_OFFSET);
	TYPE_FIELD ( struct  sbl_key_rbtree_entry * left, 0x88 );
	TYPE_FIELD ( struct  sbl_key_rbtree_entry * right, 0x90 );
	TYPE_FIELD ( struct sbl_key_rbtree_entry * parent, 0x98 );
	TYPE_FIELD ( uint32_t set, 0xA0 );
TYPE_END ();

# define  RIF_DIGEST_SIZE  0x10 
# define  RIF_DATA_SIZE  0x90 
# define  RIF_KEY_TABLE_SIZE  0x230 
# define  RIF_MAX_KEY_SIZE  0x20 
# define  RIF_PAYLOAD_SIZE (RIF_DIGEST_SIZE + RIF_DATA_SIZE)

# define  SIZEOF_ACTDAT  0x200

TYPE_BEGIN ( struct  actdat , SIZEOF_ACTDAT);
	TYPE_FIELD ( uint32_t magic, 0x00 );
	TYPE_FIELD ( uint16_t version_major, 0x04 );
	TYPE_FIELD ( uint16_t version_minor, 0x06 );
	TYPE_FIELD ( uint64_t account_id, 0x08 );
	TYPE_FIELD ( uint64_t start_time, 0x10 );
	TYPE_FIELD ( uint64_t end_time, 0x18 );
	TYPE_FIELD (uint64_t flags, 0x20 );
	TYPE_FIELD ( uint32_t unk3, 0x28 );
	TYPE_FIELD ( uint32_t unk4, 0x2C );
	TYPE_FIELD ( uint8_t open_psid_hash [ 0x20 ], 0x60 );
	TYPE_FIELD ( uint8_t static_per_console_data_1 [ 0x20 ], 0x80 );
	TYPE_FIELD ( uint8_t digest [ 0x10 ], 0xA0 );
	TYPE_FIELD ( uint8_t key_table [ 0x20], 0xB0 );
	TYPE_FIELD ( uint8_t static_per_console_data_2 [ 0x10 ], 0xD0 );
	TYPE_FIELD ( uint8_t static_per_console_data_3 [ 0x20 ], 0xE0 );
	TYPE_FIELD ( firma uint8_t [ 0x100 ], 0x100 );
TYPE_END ();

# define  SIZEOF_RIF  0x400

TYPE_BEGIN ( struct  rif , SIZEOF_RIF);
	TYPE_FIELD ( uint32_t magic, 0x00 );
	TYPE_FIELD ( uint16_t version_major, 0x04 );
	TYPE_FIELD ( uint16_t version_minor, 0x06 );
	TYPE_FIELD ( uint64_t account_id, 0x08 );
	TYPE_FIELD ( uint64_t start_time, 0x10 );
	TYPE_FIELD ( uint64_t end_time, 0x18 );
	TYPE_FIELD (char content_id [ 0x30 ], 0x20 );
	TYPE_FIELD ( formato uint16_t , 0x50 );
	TYPE_FIELD ( uint16_t drm_type, 0x52 );
	TYPE_FIELD ( uint16_t content_type, 0x54 );
	TYPE_FIELD ( uint16_t sku_flag, 0x56 );
	TYPE_FIELD ( uint64_t content_flags, 0x58 );
	TYPE_FIELD ( uint32_t iro_tag, 0x60 );
	TYPE_FIELD (uint32_t ekc_version, 0x64 );
	TYPE_FIELD ( uint16_t unk3, 0x6A );
	TYPE_FIELD ( uint16_t unk4, 0x6C );
	TYPE_FIELD ( uint8_t digest [ 0x10 ], 0x260 );
	TYPE_FIELD ( uint8_t data [RIF_DATA_SIZE], 0x270 );
	TYPE_FIELD ( firma uint8_t [ 0x100 ], 0x300 );
TYPE_END ();

unione keymgr_payload {
	 struct {
		 uint32_t cmd;
		stato uint32_t ;
		uint64_t dati;
	};

	uint8_t buf [ 0x80 ];
};

union keymgr_request {
	 struct {
		 uint32_t type;
		chiave uint8_t [RIF_MAX_KEY_SIZE];
		uint8_t dati [RIF_DIGEST_SIZE + RIF_DATA_SIZE];
	} decrypt_rif;
};

union keymgr_response {
	 struct {
		 uint32_t type;
		chiave uint8_t [RIF_MAX_KEY_SIZE];
		uint8_t dati [RIF_DIGEST_SIZE + RIF_DATA_SIZE];
	} decrypt_rif;
};

/ * TODO: dovresti puntarlo alla sua posizione nella memoria del kernel (vedi un codice di sceSblKeymgrSetKey (), appena prima di un ciclo c'è qualcosa di simile: 
       mov rdx, cs: xxx 
   vai lì, è il tuo target * /
 struct  sbl_key_rbtree_entry ** sbl_keymgr_key_rbtree;

 struct inline  statica sbl_key_rbtree_entry * sceSblKeymgrGetKey ( handle int unsigned ) {
	 struct sbl_key_rbtree_entry * entry = * sbl_keymgr_key_rbtree;   

	while (entry) {
		 if (entry-> handle <handle)
			entry = entry-> right;
		altrimenti  se (entry-> handle> handle)
			voce = entrata-> sinistra;
		altrimenti  if (entry-> handle == handle)
			 return entry;
	}

	restituisce  NULL ;
}

int  sceSblKeymgrSmCallfunc ( unione keymgr_payload * payload);

// ...

# define  EKPFS_SIZE  0x20 
# define  EEKPFS_SIZE  0x100 
# define  PFS_SEED_SIZE  0x10 
# define  PFS_FINAL_KEY_SIZE  0x20

# define  SIZEOF_PFS_KEY_BLOB  0x158

struct  pfs_key_blob {
	 uint8_t ekpfs [EKPFS_SIZE];
	uint8_t eekpfs [EEKPFS_SIZE];
	struct  ekc eekc;
	uint32_t key_ver;
	uint32_t pubkey_ver;
	uint32_t type;
	uint32_t finalizzato;
	uint32_t is_disc;
	uint32_t pad;
};

typedef  struct  pfs_key_blob  pfs_key_blob_t ;

TYPE_CHECK_SIZE ( pfs_key_blob_t , SIZEOF_PFS_KEY_BLOB);

int  sceSblPfsKeymgrGenEKpfsForGDGPAC ( struct  pfs_key_blob * key_blob);

int  sceSblPfsSetKey ( uint32_t * ekh, uint32_t * skh , uint8_t * chiave, uint8_t * iv, int type, int inutilizzato, uint8_t is_disc);
int  sceSblPfsClearKey ( uint32_t ekh, uint32_t skh );

// ...

# define  CCP_MAX_PAYLOAD_SIZE  0x88

# define  CCP_OP ( cmd ) (cmd >> 24 )

# define  CCP_OP_XTS  2 
# define  CCP_OP_HMAC  9

# define  CCP_USE_KEY_HANDLE ( 1 << 20 )

struct  ccp_link {
	 void * p;
};

struct  ccp_msg {
	 union ccp_op op;

	indice uint32_t ;
	risultato uint32_t ;

	TAILQ_ENTRY (ccp_msg) successivo;

	uint64_t message_id;
	Link LIST_ENTRY (ccp_link);
};

struct  ccp_req {
	 TAILQ_HEAD (, ccp_msg) msgs;

	void (* cb) ( void * arg, int result);
	void * arg;

	uint64_t message_id;
	Link LIST_ENTRY (ccp_link);
};

unione ccp_op {
	 struct {
		 uint32_t cmd;
		stato uint32_t ;
	} Comune;

	// ...

	uint8_t buf [CCP_MAX_PAYLOAD_SIZE];
};

// ...

# definisce  SBL_MSG_SERVICE_MAILBOX_MAX_SIZE  0x80

int  sceSblServiceMailbox ( unsigned  lungo service_id, uint8_t richiesta [SBL_MSG_SERVICE_MAILBOX_MAX_SIZE], uint8_t risposta);

int  sceSblServiceCrypt ( struct  ccp_req * request);
int  sceSblServiceCryptAsync ( struct  ccp_req * request);

// ...

struct  sbl_mapped_page_group ;

# define  SIZEOF_SBL_MAP_LIST_ENTRY  0x50  // sceSblDriverMapPages

TYPE_BEGIN ( struct  sbl_map_list_entry , SIZEOF_SBL_MAP_LIST_ENTRY);
	TYPE_FIELD ( struct  sbl_map_list_entry * next, 0x00 );
	TYPE_FIELD ( struct  sbl_map_list_entry * prev, 0x08 );
	TYPE_FIELD ( unsigned  long cpu_va, 0x10 );
	TYPE_FIELD ( unsigned  int num_page_groups, 0x18 );
	TYPE_FIELD ( gpu_va unsigned  long , 0x20 );
	TYPE_FIELD (struct  sbl_mapped_page_group * page_groups, 0x28 );
	TYPE_FIELD ( int num_pages senza  segno, 0x30 );
	TYPE_FIELD ( flag lunghi non firmati , 0x38 );
	TYPE_FIELD ( struct proc * proc, 0x40 );
	TYPE_FIELD ( void * vm_page, 0x48 );
TYPE_END ();  

 struct statica in linea  sbl_map_list_entry * sceSblDriverFindMappedPageListByGpuVa ( vm_offset_t gpu_va) {
	 struct sbl_map_list_entry * entry;  

	se (! gpu_va)
		 restituisce  NULL ;

	entry = * sbl_driver_mapped_pages;
	while (entry) {
		 if (entry-> gpu_va == gpu_va)
			 return entry;
		entry = entry-> next;
	}

	restituisce  NULL ;
}

/ * TODO: dovresti puntarlo alla sua posizione nella memoria del kernel (vedi un codice di sceSblDriverGvmInitialize (), appena prima di una chiamata a mtx_init () c'è qualcosa del genere: 
       mov cs: xxx, 0 
   vai lì, è il tuo obiettivo * /
 struct  sbl_map_list_entry ** sbl_driver_mapped_pages;

 struct statica in linea  sbl_map_list_entry * sceSblDriverFindMappedPageListByCpuVa ( vm_offset_t cpu_va) {
	 struct sbl_map_list_entry * entry;  

	se (! cpu_va)
		 restituisce  NULL ;

	entry = * sbl_driver_mapped_pages;
	while (entry) {
		 if (entry-> cpu_va == cpu_va)
			 return entry;
		entry = entry-> next;
	}

	restituisce  NULL ;
}

static  inline  vm_offset_t  sceSblDriverGpuVaToCpuVa ( vm_offset_t gpu_va, size_t * num_page_groups) {
	 struct  sbl_map_list_entry * entry = sceSblDriverFindMappedPageListByGpuVa (gpu_va);
	se (! entry)
		 restituisce  0 ;
	if (num_page_groups)
		* num_page_groups = entry-> num_page_groups;
	return entry-> cpu_va;
}

// ...

struct  rsa_buffer {
	 uint8_t * ptr;
	taglia_t ;
};

# define  SIZEOF_RSA_KEY  0x48

TYPE_BEGIN ( struct  rsa_key , SIZEOF_RSA_KEY);
	TYPE_FIELD ( uint8_t * p, 0x20 );
	TYPE_FIELD ( uint8_t * q, 0x28 );
	TYPE_FIELD ( uint8_t * dmp1, 0x30 );
	TYPE_FIELD ( uint8_t * dmq1, 0x38 );
	TYPE_FIELD ( uint8_t * iqmp, 0x40 );
TYPE_END ();

int  AesCbcCfb128Encrypt ( uint8_t * out, const  uint8_t * in, size_t data_size, const  uint8_t * chiave, int key_size, uint8_t * iv);
int  AesCbcCfb128Decrypt ( uint8_t * out, const  uint8_t * in, size_t data_size, const  uint8_t * chiave, int key_size, uint8_t * iv);

void  Sha256Hash ( uint8_t hash [ 0x20 ], const  uint8_t * data, size_t data_size);
void  Sha256Hmac ( uint8_t hash [ 0x20 ], const  uint8_t * dati, size_t data_size, const  uint8_t * chiave, int key_size);

int  RsaesPkcs1v15Enc2048 ( struct  rsa_buffer * out, struct  rsa_buffer * in, struct  rsa_key * key);
int  RsaesPkcs1v15Dec2048CRT ( struct  rsa_buffer * out, struct  rsa_buffer * in, struct  rsa_key * key);

// ...

void  build_iovec ( struct  iovec ** iov, int * iovlen, const  char * nome, const  void * val, size_t len) {
	 int i;

	se (* iovlen < 0 )
		 restituisce ;

	i = * iovlen;
	* iov = realloc (* iov, sizeof ** iov * (i + 2 ));
	if (* iov == NULL ) {
		* iovlen = - 1 ;
		ritorno ;
	}

	(* IOV) [i]. iov_base = strdup (nome);
	(* IOV) [i]. iov_len = strlen (nome) + 1 ;
	++ i;

	(* IOV) [i]. iov_base = ( void *) val;
	if (len == ( size_t ) - 1 ) {
		 if (val! = NULL )
			len = strlen (val) + 1 ;
		altrimenti 
			len = 0 ;
	}
	(* IOV) [i]. iov_len = ( int ) len;

	* iovlen = ++ i;
}

// ...

struct  proc * proc_find_by_name ( const  char * name) {
	 struct  proc * p;

	se (! nome)
		 restituisce  NULL ;

	sx_slock (allproc_lock);

	FOREACH_PROC_IN_SYSTEM (p) {
		 PROC_LOCK (p);

		if ( strncmp (p-> p_comm, name, sizeof (p-> p_comm)) == 0 ) {
			 PROC_UNLOCK (p);
			goto fatto;
		}

		PROC_UNLOCK (p);
	}

	p = NULL ;

fatto:
	sx_sunlock (allproc_lock);

	ritorno p;
}

int  proc_get_vm_map ( struct  proc * p, struct  proc_vm_map_entry ** entries, size_t * num_entries) {
	 struct  vmspace * vm;
	struct  proc_vm_map_entry * info = NULL ;
	vm_map_t map;
	voce vm_map_entry_t ;
	size_t n, i;
	int ret;

	se (! p) {
		ret = EINVAL;
		goto error;
	}
	se (! voci) {
		ret = EINVAL;
		goto error;
	}
	if (! num_entries) {
		ret = EINVAL;
		goto error;
	}

	PROC_LOCK (p);
	if (p-> p_flag & P_WEXIT) {
		 PROC_UNLOCK (p);
		ret = ESRCH;
		goto error;
	}
	_PHOLD (p);
	PROC_UNLOCK (p);

	vm = vmspace_acquire_ref (p);
	if (! vm) {
		 PRELE (p);
		ret = ESRCH;
		goto error;
	}
	map = & vm-> vm_map;

	vm_map_lock_read (mappa);
	per (entry = map-> header. next , n = 0 ; entry! = & map-> header; entry = entry-> next) {
		 if (entry-> eflags & MAP_ENTRY_IS_SUB_MAP)
			 continua ;
		++ n;
	}
	se (n == 0 )
		 goto fatto;
	info = ( struct  proc_vm_map_entry *) alloc (n * sizeof (* info));
	if (! info) {
		 vm_map_unlock_read (mappa);
		vmspace_free (vm);

		PRELE (p);

		ret = ENOMEM;
		goto error;
	}
	memset (informazioni, 0 , n * sizeof (* informazioni));
	per (entry = map-> header. next , i = 0 ; entry! = & map-> header; entry = entry-> next) {
		 if (entry-> eflags & MAP_ENTRY_IS_SUB_MAP)
			 continua ;

		info [i]. start = entry-> start;
		info [i]. end = entry-> end;
		info [i]. offset = entry-> offset;

		info [i]. prot = 0 ;
		if (entry-> protection & VM_PROT_READ)
			info [i]. prot | = PROT_READ;
		if (entry-> protection & VM_PROT_WRITE)
			info [i]. prot | = PROT_WRITE;
		if (entry-> protection & VM_PROT_EXECUTE)
			info [i]. prot | = PROT_EXEC;

		++ i;
	}

fatto:
	vm_map_unlock_read (mappa);
	vmspace_free (vm);

	PRELE (p);

	* num_entries = n;
	* entries = informazioni;

	info = NULL ;
	ret = 0 ;

errore:
	if (info)
		 dealloc (informazioni);

	return ret;
}

int  proc_rw_mem ( struct  proc * p, void * ptr, size_t size, void * data, size_t * n, int write) {
	 struct  thread * td = curthread ();
	struct  iovec iov;
	struct  uio uio;
	int ret;

	se (! p) {
		ret = EINVAL;
		goto error;
	}

	if (size == 0 ) {
		 if (n)
			* n = 0 ;
		ret = 0 ;
		goto error;
	}

	memset (& iov, 0 , sizeof (iov));
	IOV. iov_base = ( caddr_t ) dati;
	IOV. iov_len = dimensione;

	memset (& uio, 0 , sizeof (uio));
	UIO. uio_iov = & iov;
	UIO. uio_iovcnt = 1 ;
	UIO. uio_offset = ( off_t ) ptr;
	UIO. uio_resid = ( ssize_t ) dimensione;
	UIO. uio_segflg = UIO_SYSSPACE;
	UIO. uio_rw = scrivi ? UIO_WRITE: UIO_READ;
	UIO. uio_td = td;

	ret = proc_rwmem (p, & uio);
	se (n)
		* n = ( size_t ) (( ssize_t ) size - uio. uio_resid );

errore:
	return ret;
}

// ...

static  inline  int  proc_write_mem ( struct  proc * p, void * ptr, size_t size, void * data, size_t * n) {
	 return  proc_rw_mem (p, ptr, size, data, n, 1 );
}

Problemi di reimballo dei pacchetti

Sony ha introdotto il sistema PlayGo in PS4 , che consente ai giochi di utilizzare il chunk read. Ciò consente funzionalità utili per l’utente. Ad esempio, se vuoi giocare all’aspetto multigiocatore di un gioco, non devi aspettare che l’aspetto single-player sia scaricato / installato, o non devi perdere tempo guardando lo schermo mentre il gioco cerca di caricare la lingua cinese se non stai vivendo in Cina.

Gli sviluppatori possono specificare quali file appartengono a quali chunk e quali blocchi devono essere caricati da uno o più scenari. Queste informazioni vengono utilizzate dagli strumenti di pubblicazione Orbis per creare un pacchetto in cui i file sono archiviati in un ordine specifico. E il gioco potrebbe utilizzare un’API per controllare il sistema PlayGo (ad esempio, quale scenario dovrebbe essere caricato ad un evento specifico, ecc.). Alcune informazioni sugli scenari usati in un gioco sono memorizzate all’interno di un file di pacchetto, inoltre ci sono alcune informazioni su quali lingue sono usate in cose specifiche (tuttavia, vi è una perdita di alcune informazioni su di loro durante un processo di compilazione).

Quindi, questo è un problema per quelli di noi che vogliono reimballare un pacchetto originale. Noi DOBBIAMOsappiamo queste informazioni o noi DOBBIAMO in grado di recuperare queste informazioni in qualche modo se vogliamo ricostruire un pacchetto che funziona. Altrimenti potremmo vedere glitch, crash o qualche altro bug strano, ma alcuni giochi potrebbero funzionare normalmente se non si basano su questo. Credo che questo sia il motivo principale per cui molti giochi non funzionano (o lavorano con alcuni problemi) utilizzando il precedente metodo basato su PlayRoom , perché PlayRoom utilizza un semplice layout di file.Quindi, per ovviare al problema, dobbiamo fare delle ipotesi basate sulla struttura del file del pacchetto e funziona, si potrebbe anche farlo in modo semi-automatico (questo è ciò che fa il mio strumento). Ma per farlo correttamente è necessario ottenere l’ immagine PFS interna (chiamata pfs_image.dat). Questo è fondamentalmente per i giochi che utilizzano scenari complessi e non usano solo un blocco e uno scenario predefinito. Ho visto molti giochi che usano solo un chunk / uno scenario e funzioneranno come sono senza il bisogno di fare qualcos’altro, quindi basta reimballarli e funzioneranno bene (uno di questi è DriveClub ). Ma voglio discutere altri giochi che portano questi problemi di cui sto parlando.

Qui dovrei notare che Orbis OS gestisce la decrittografia e la decompressione dei pacchetti (sì, i pacchetti possono essere compressi anche se ce n’è bisogno, gli sviluppatori possono specificare anche questo) in modo trasparente, e il SO monterà i contenuti del gioco in una directory specifica all’interno del file system e anche «memorizza» un’immagine PFS interna in un’altra directory. Ma come ho già detto, è un file immagine decrittografato e decompresso . Tuttavia, se si desidera ottenere informazioni sui posizionamenti di blocchi all’interno di un file di pacchetto, è necessario conoscere i loro reali offset, tali offset che vengono utilizzati internamente da Orbis OS quando esegue la decrittazione/decompressione del file del pacchetto per individuare i dati del file. Successivamente, utilizzando questi offset è possibile recuperare le informazioni sui blocchi e creare un layout corretto del file del pacchetto. Ma non è possibile ottenere questi offset reali se si sta operando su unfile PFS interno che era già stato decompresso per noi dal sistema. Così, la decrittazione va bene , ma la decompressione è male , quindi è necessario un modo per ottenere un decriptato grezzo (compressa )pfs_image.datfile. Potrebbe essere possibile con codice del kernel personalizzato che utilizza alcunefunzioniPFS buil-tindal kernel, ma è anche necessario scrivere codice personalizzato che salterà la decompressione. Penso che potrebbe anche essere fattibile registrando gli offset dei file nel file .pkg originale quando si tenta di caricare tutti i file da esso, quindi è possibile utilizzare queste informazioni per organizzare correttamente i blocchi. Entrambi i metodi dovrebbero essere realizzati come applicazioni PS4 e molto probabilmente richiederanno alcuni carichi utili del kernel.

Non ho intenzione di implementare quello strumento perché, naturalmente, è uno scaricatore di giochi che fa un po ‘più di una semplice copia pfs_image.datmentre alcuni giochi sono in esecuzione, quindi non voglio problemi legali, ma non è una scienza missilistica, quindi qualcuno con le conoscenze e le abilità potrebbe implementarlo. Tuttavia, potrei aiutare rilasciando una parte del codice dal mio strumento che esegue l’ analisi dei file SFO , il generatore di blocchi di PlayGo e infine la generazione del progetto GP4 . Qualcuno potrebbe usarlo per implementare un’applicazione simile.

Analisi dei file SFO

System file object ( SFO ) viene utilizzato per memorizzare alcuni parametri relativi all’applicazione / gioco utilizzati dal sistema, quindi questo codice viene utilizzato per analizzarlo.

sfo.h 
sfo.c

Builder di chunk di PlayGo

Estrazione e recupero di blocchi (e di altri) informazioni sui file memorizzati all’interno del file del pacchetto.

playgo.h 
playgo.c

Generazione del progetto GP4

Questo codice viene utilizzato per generare il file di progetto .gp4 dal file del pacchetto facendo alcune ipotesi e utilizzando il suo formato di struttura.

gp4.h 
gp4.c

Alcune cose su PKG / PFS

Qui ho caricato alcune strutture, definizioni e funzioni relative al formato di file PKG , potresti vedere alcuni riferimenti al codice GP4 .

pkg.h 
pkg.c

Conclusione

Troppo tempo è passato dal rilascio del primo exploit pubblico per il firmware 1.76 e ancora non esisteva alcun metodo utilizzabile per caricare il contenuto personalizzato. Non ho idea del perché sia ​​successo, perché non è una scienza missilistica da fare (sì, anche con tutti questi frammenti di codice sopra, non è difficile implementare, o meglio dire, portarlo una volta su ogni firmware sfruttabile, avendo così modo abituale di eseguire qualsiasi roba). Certo, ci vorrà più tempo per implementare tutte le funzionalità che la comunità vuole, ma hey, è fattibile e almeno alcune persone lo hanno già fatto.

Spero solo che le persone intelligenti inizieranno a contribuire alla scena ripresa. Ci sono molte cose che devono ancora essere investigate e che sono per lo più legate alla sicurezza, incluso SAMU , questo è ciò che vogliamo ottenere alla fine. Alcune persone, incluso me, che partecipano all’hacking della console non sono interessate alla pirateria perché ci sono compiti più interessanti da fare, quindi non prenderò molto impegno pubblicamente. Tuttavia, ho invertito quasi tutte le cose relative alla roba PKG / PFS / NPDRM e un mucchio di altre PS4funzionalità, quindi potrei dare suggerimenti se ne hai bisogno. In ogni caso vedremo quanto lontano andrà. E forse gli exploit sui firmware recenti vedranno la luce anche nel futuro, portando così modi più eleganti ed eleganti per fare cose che abbiamo discusso prima. (:

In bocca al lupo.

Ti è piaciuta la notizia? Supporta theheroGAC su Patreon!

theheroGAC

Nato negli anni 80 con la passione dei videogiochi e delle console.Il mio primo home computer è l'Amiga 600 regalato a 10 anni.Amo aiutare le persone in difficoltà e scrivere notizie sulle console.Studio all'università e il sito Games And Consoles è la mia passione.Per gli amici mi potete chiamare Ciccio

Ti potrebbe anche interessare

Rispondi

Attenzione:Il sito Gamesandconsole è contro la pirateria.I dati, i documenti, i files e le informazioni contenute in questo sito web sono forniti a solo scopo informativo