Home Homebrew Installare file PKG su console PlayStation 5 tramite l’API di Sony...

[Scena PS5] Installare file PKG su console PlayStation 5 tramite l’API di Sony – un tutorial passo dopo passo spiega come creare il proprio server per l’invio di file PKG

734
0

Il developer LightingMods non si è limitato a rilasciare ufficialmente gli aggiornamenti per etaHEN e Itemzflow, ma ha pubblicato anche un approfondito articolo che spiega dettagliatamente la procedura di installazione dei file PKG.

Il documento inizia con un’introduzione che fa riferimento al lavoro di Flatz, il quale anni fa aveva pubblicato una guida per l’installazione di pacchetti (PKG) su PlayStation 4 utilizzando il sistema BGFT (Background File Transfer).

 

Tuttavia, questo metodo non è più valido per le console PlayStation 5. Da qui nasce la nuova procedura, che riprende e aggiorna il lavoro di Flatz, offrendo una guida specifica per la console di nuova generazione.

Prerequisiti

Per implementare questa nuova soluzione, sono necessari alcuni requisiti fondamentali. Il primo è un metodo di escalation dei privilegi, ovvero un modo per ottenere autorizzazioni elevate sul sistema PlayStation 5.

Una possibilità è modificare l’Auth ID (identificativo di autenticazione) all’interno della struttura ucred, impostandolo su quello di ShellCore (0x3800000000000010), il componente principale dell’interfaccia di sistema della PlayStation 5.

In alternativa, per i pacchetti FPKG (file modificati per homebrew), si può usare il comando IPC di jailbreak fornito da etaHEN, un payload AIO per PlayStation 5.

Per soluzioni integrate, invece, si può sfruttare la gestione automatica dei privilegi tramite il sistema di plugin di etaHEN o l’SDK ps5-payload-dev.

Inoltre, è necessario caricare e inizializzare il modulo di sistema libSceAppInstUtil.sprx, che si trova in /system/common/lib/.

Questo modulo viene automaticamente gestito se si sviluppano plugin per etaHEN o eseguibili ELF con l’SDK corretto, purché il collegamento alla libreria sia configurato adeguatamente.

Passo 1: Identificare il problema

Il primo passo consiste nel comprendere perché il vecchio metodo BGFT di Flatz non funzioni più. Tutte le chiamate alle funzioni sceBgftServiceIntDownloadRegisterTaskBy* restituiscono l’errore 0x80990006 (SCE_BGFT_ERROR_NOT_SUPPORTED), anche con un’inizializzazione corretta e un Auth ID valido.

Analizzando le chiamate IPC di BGFT all’interno di ShellCore, si scopre che questo errore non è un bug, ma una scelta intenzionale di Sony: la console PlayStation 5 non supporta più il sistema BGFT per l’installazione dei pacchetti, rendendo necessaria la ricerca di un’alternativa.

Viene mostrato un esempio di codice (la funzione FUN_0100ee70 e DownloadRegisterTaskByStorageEx) che evidenzia come il sistema restituisca sempre questo errore, confermando che BGFT è stato abbandonato.

uint FUN_0100ee70(Session *session,int user_id,int entitlement_type,char *id,char *package_type,
                 char *package_sub_type,char *content_url,char *content_name,char *icon_path,
                 char *sku_id,char *playgo_scenario_id,char *release_date,size_t package_size,
                 bgft_task_option_t options,uint slot,void *result) {

  undefined auVar1 [32];
  long lVar2;
  pid_t pid;
  uint uVar3;
  uint uVar4;
  bgft_download_param args;
  
  lVar2 = __stack_chk_guard;
  if (session == NULL) {
    uVar4 = 0x80990050;
  }
  else {
    pid = (*session->_vptr->getClientPid)(session);
    uVar3 = check_authid(pid);
    uVar4 = 0x80990007;
    if (uVar3 == 1) {
      args._60_4_ = 0;
      args.content_url = content_url;
      auVar1._8_8_ = icon_path;
      auVar1._0_8_ = content_name;
      auVar1._16_8_ = sku_id;
      auVar1._24_8_ = 0;
      args._24_32_ = auVar1 << 0x40;
      args.options = options;
      args.playgo_scenario_id = playgo_scenario_id;
      args.release_date = release_date;
      args.package_size = package_size;
      args.user_id = user_id;
      args.entitlement_type = entitlement_type;
      args.id = id;
      args.package_type = package_type;
      args.package_sub_type = package_sub_type;
      uVar4 = DownloadRegisterTaskByStorageEx(&args,result);
    }
  }
  if (__stack_chk_guard == lVar2) {
    return uVar4;
  }

  __stack_chk_fail();
}

uint DownloadRegisterTaskByStorageEx(bgft_download_param *param_1,void *param_2) {
  printf("[BGFT] ERROR: [%d] ",0x18);
  puts("NOT SUPPORTED API");
  return 0x80990006;
}

Passo 2: Trovare la nuova funzione per installare i PKG

Con il metodo BGFT fuori gioco, LightningMods decide di concentrarsi sull’interfaccia utente di sistema della PlayStation 5, chiamata ShellUI, che gestisce funzionalità come il menu delle Impostazioni di Debug, incluso il servizio di installazione dei pacchetti.

Attraverso il reverse engineering di ShellUI, utilizzando strumenti come DnSpy per analizzare i moduli mono (un framework utilizzato da Sony per l’interfaccia), si scopre che la PS5 utilizza un’implementazione chiamata UI3 Settings, evoluzione delle versioni UI (PS4 iniziale) e UI2 (PS4 successiva).

All’interno di questa struttura, vengono identificate le funzioni chiave per l’installazione dei PKG: ExecuteInstall e OnUpdate, insieme alle relative strutture dati (mTargetList per la coda dei pacchetti e mInstallIndex per identificare il pacchetto target).

Tuttavia, queste funzioni non possono essere chiamate direttamente tramite metodi mono standard, richiedendo un approccio alternativo. Il codice C# fornito mostra come ExecuteInstall avvia l’installazione e OnUpdate monitora lo stato, usando il wrapper AppInstUtilWrapper.

private Task<int> ExecuteInstall()
{
	return Task.Run<int>(delegate()
	{
		this.Lock();
		AppInstUtilWrapper.SceAppInstallPkgInfo sceAppInstallPkgInfo = default(AppInstUtilWrapper.SceAppInstallPkgInfo);
		string[] array = new string[30];
		string[] array2 = new string[64];
		string[] array3 = new string[64];
		for (int i = 0; i < array.Length; i++)
		{
			array[i] = "";
		}
		for (int j = 0; j < array2.Length; j++)
		{
			array2[j] = "";
		}
		for (int k = 0; k < array3.Length; k++)
		{
			array3[k] = "";
		}
		int num = AppInstUtilWrapper.AppInstUtilInstallByPackage(this.mTargetList[this.mInstallIndex], "", "", "", "", "", 0U, false, ref sceAppInstallPkgInfo, array, array2, array3);
		if (num == 0)
		{
			this.mTimer = new UITimer(0.1f, true);
			UITimer uitimer = this.mTimer;
			uitimer.Executed = (UITimer.ExecutedHandler)Delegate.Combine(uitimer.Executed, new UITimer.ExecutedHandler(this.OnUpdate));
			this.mTimer.Start();
		}
		else
		{
			this.Unlock();
		}
		return num;
	});
}

private bool OnUpdate()
{
	ShellCoreUtilWrapper.sceShellCoreUtilResetAutoPowerDownTimer();
	AppInstUtilWrapper.SceAppInstallStatusInstalled sceAppInstallStatusInstalled = default(AppInstUtilWrapper.SceAppInstallStatusInstalled);
	int num = AppInstUtilWrapper.AppInstUtilGetInstallStatus(this.mContentId, ref sceAppInstallStatusInstalled);
	if (num == 0)
	{
		if (sceAppInstallStatusInstalled.total_size != 0UL)
		{
			this.mProgressBar.Progress = sceAppInstallStatusInstalled.downloaded_size / sceAppInstallStatusInstalled.total_size;
		}
		if (sceAppInstallStatusInstalled.status == "playable" || sceAppInstallStatusInstalled.status == "error" || sceAppInstallStatusInstalled.status == "none")
		{
			this.Unlock();
			this.mTimer.Stop();
			if (sceAppInstallStatusInstalled.status == "error" || sceAppInstallStatusInstalled.status == "none")
			{
				int error_code = sceAppInstallStatusInstalled.error_info.error_code;
				this.ShowError(error_code);
				return false;
			}
			this.Next();
		}
		return false;
	}
	this.Unlock();
	this.mTimer.Stop();
	this.ShowErrorDialog(num);
	return false;
}

Passo 3: Usare sceAppInstUtilInstallByPackage in C/C++

Per passare dal codice mono al codice nativo C/C++, è necessario un ulteriore lavoro di reverse engineering, poiché replicare direttamente le definizioni mono porta all’errore SCE_APP_INSTALLER_ERROR_PARAM.

Questo errore deriva da una mancata corrispondenza dei parametri, dato che i wrapper mono gestiscono trasformazioni non evidenti nel codice ad alto livello.

Decompilando l’implementazione nativa sottostante, LightningMods scopre la vera struttura dei parametri richiesti da sceAppInstUtilInstallByPackage, che differisce significativamente dalle versioni gestite, soprattutto per la gestione della memoria e il passaggio dei parametri.

Viene mostrata una funzione decompilata (UndefinedFunction_001b8aa0) che rivela come la funzione nativa accetti tre strutture: MetaInfo (con informazioni come URI e nome del contenuto), SceAppInstallPkgInfo (dettagli sul pacchetto) e PlayGoInfo (dati aggiuntivi come lingue e ID).

Si scopre anche che il parametro uri supporta sia percorsi locali (es. /data/test.pkg) che URL remoti (es. http://127.0.0.1/test.pkg), offrendo flessibilità simile alla gestione degli aggiornamenti su PS4.

Passo 4: Usare sceAppInstUtilGetInstallStatus in C/C++

La funzione sceAppInstUtilGetInstallStatus viene poi spiegata come uno strumento per monitorare lo stato dell’installazione.

Questa funzione è simile alla sua controparte C# e richiede una struttura nativa SceAppInstallStatusInstalled, che include dettagli come la percentuale di download, lo stato (es. “playable” o “error”) e informazioni sugli errori.

LightningMods decide allora di convertire le strutture gestite in equivalenti C/C++ con un allineamento corretto, permettendo di tracciare il progresso dell’installazione usando l’ID del contenuto (content_id) del pacchetto.

Questo passaggio è essenziale per creare interfacce personalizzate con barre di avanzamento e gestione degli errori.

Passo 5: Combinare le funzioni per sostituire BGFT

Infine, l’autore descrive come integrare sceAppInstUtilInstallByPackage e sceAppInstUtilGetInstallStatus per replicare la funzionalità di BGFT.

Il processo inizia con l’inizializzazione del modulo libSceAppInstUtil.sprx tramite sceAppInstUtilInitialize, un passaggio obbligatorio per evitare crash.

Poi, si avvia l’installazione con sceAppInstUtilInstallByPackage, che opera in modo asincrono, e si monitora il progresso con sceAppInstUtilGetInstallStatus in un ciclo. Questo permette di seguire l’avanzamento (percentuale completata), rilevare errori e sapere quando l’installazione viene terminata con successo.

Viene fornito un esempio di codice C che mostra l’intero flusso, dall’inizializzazione all’installazione di un file PKG, con log per tracciare i risultati.

int ret = sceAppInstUtilInitialize();
if(ret){
   printf("sceAppInstUtilInitialize failed: 0x%08X\n", ret);
   return -1;
}

PlayGoInfo arg3;
SceAppInstallPkgInfo pkg_info;
(void)memset(&arg3, 0, sizeof(arg3));

for (size_t i = 0; i < NUM_LANGUAGES; i++) {
    strncpy(arg3.languages[i], "", sizeof(arg3.languages[i]) - 1);
}

for (size_t i = 0; i < NUM_IDS; i++) {
     strncpy(arg3.playgo_scenario_ids[i], "",
                sizeof(playgo_scenario_id_t) - 1);
     strncpy(*arg3.content_ids, "", sizeof(content_id_t) - 1);
}

MetaInfo in = {
    .uri = "/path/to/pkg.pkg",
    .ex_uri = "",
    .playgo_scenario_id = "",
    .content_id = "",
    .content_name = "PKG TITLE",
    .icon_url = ""
};

int num = sceAppInstUtilInstallByPackage(&in, &pkg_info, &arg3);
if (num == 0) {
    puts("Download and Install console Task initiated");
} else {
    printf("DPI: Install failed with error code %d\n", num);
}
float prog = 0;
SceAppInstallStatusInstalled status;

while (strcmp(status.status, "playable") != 0) {
    sceAppInstUtilGetInstallStatus(pkg_info.content_id, &status);
    
    if (status.total_size != 0) {
        prog = ((float)status.downloaded_size / status.total_size) * 100.0f;
    }

    printf("DPI: Status: %s | error: %d | progress %.2f%% (%llu/%llu)\n", 
               status.status, status.error_info.error_code, 
               prog, status.downloaded_size, status.total_size);
}

Extra

Il writeup include anche una lista di codici di errore (AppInstErrorCodes) che possono verificarsi durante l’installazione, come mancanza di spazio (SCE_APP_INSTALLER_ERROR_NOSPACE) o parametri errati (SCE_APP_INSTALLER_ERROR_PARAM).

Fonte: x.com

LASCIA UN COMMENTO

Per favore inserisci il tuo commento!
Per favore inserisci il tuo nome qui

Questo sito utilizza Akismet per ridurre lo spam. Scopri come vengono elaborati i dati derivati dai commenti.