Skip to content

Commit

Permalink
fw agnostic save mounting
Browse files Browse the repository at this point in the history
  • Loading branch information
bucanero committed May 17, 2024
1 parent ca1af01 commit 0506c87
Show file tree
Hide file tree
Showing 6 changed files with 365 additions and 0 deletions.
15 changes: 15 additions & 0 deletions include/init.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#ifndef INIT_H
#define INIT_H

#include <unistd.h>
#include <sys/stat.h>
#include <orbis/libkernel.h>
#include <libjbc.h>

#include "sd.h"
#include "scall.h"

int init_cred(void);
int init_devices(void);

#endif // INIT_H
9 changes: 9 additions & 0 deletions include/scall.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#ifndef SCALL_H
#define SCALL_H

#include <orbis/libkernel.h>

int sys_open(const char *path, int flags, int mode);
int sys_mknod(const char *path, mode_t mode, dev_t dev);

#endif // SCALL_H
41 changes: 41 additions & 0 deletions include/sd.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#ifndef SD_H
#define SD_H

#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <fcntl.h>
#include <orbis/libkernel.h>
#include <libjbc.h>
#include "scall.h"

#define ENC_SEALEDKEY_LEN 0x60
#define DEC_SEALEDKEY_LEN 0x20
#define MAX_PATH_LEN 256

#define UNUSED(x) (void)(x)

typedef struct {
int blockSize;
uint8_t idk[2];
} CreatePfsSaveDataOpt;

typedef struct {
bool readOnly;
char *budgetid;
} MountSaveDataOpt;

typedef struct {
bool dummy;
} UmountSaveDataOpt;

int loadPrivLibs(void);
int generateSealedKey(uint8_t data[ENC_SEALEDKEY_LEN]);
int decryptSealedKey(uint8_t enc_key[ENC_SEALEDKEY_LEN], uint8_t dec_key[DEC_SEALEDKEY_LEN]);
int decryptSealedKeyAtPath(const char *keyPath, uint8_t decryptedSealedKey[DEC_SEALEDKEY_LEN]);
int mountSave(const char *folder, const char *saveName, const char *mountPath);
int umountSave(const char *mountPath, int handle, bool ignoreErrors);
uint16_t getMaxKeySet(void);

#endif // SD_H
84 changes: 84 additions & 0 deletions source/init.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#include "init.h"

int init_cred(void) {
jbc_cred old_cred;
jbc_cred cred;

memset(&old_cred, 0, sizeof(jbc_cred));
memset(&cred, 0, sizeof(jbc_cred));

if (jbc_get_cred(&old_cred) != 0) {
return -1;
}
old_cred.sonyCred = old_cred.sonyCred | 0x4000000000000000ULL;

if (jbc_get_cred(&cred) != 0) {
return -2;
}
cred.sonyCred = cred.sonyCred | 0x4000000000000000ULL;
cred.sceProcType = 0x3801000000000013ULL;

jbc_set_cred(&cred);
setuid(0);

return 0;
}

int init_devices(void) {
struct stat s;
memset(&s, 0, sizeof(struct stat));

// mount required devices into sandbox
if (jbc_mount_in_sandbox("/dev/", "rootdev") != 0) {
sceKernelDebugOutText(0, "Failed to mount devices\n");
return -1;
}

// create devices
if (stat("/rootdev/pfsctldev", &s) == -1) {
sceKernelDebugOutText(0, "err stat pfsctldev\n");
return -2;
}
else {
if (sys_mknod("/dev/pfsctldev", S_IFCHR | 0777, s.st_dev) == -1) {
sceKernelDebugOutText(0, "err mknod pfsctldev\n");
return -2;
}
}

memset(&s, 0, sizeof(struct stat));

if (stat("/rootdev/lvdctl", &s) == -1) {
sceKernelDebugOutText(0, "err stat lvdctl\n");
return -3;
}
else {
if (sys_mknod("/dev/lvdctl", S_IFCHR | 0777, s.st_dev) == -1) {
sceKernelDebugOutText(0, "err mknod lvdctl\n");
return -3;
}
}

memset(&s, 0, sizeof(struct stat));

if (stat("/rootdev/sbl_srv", &s) == -1) {
sceKernelDebugOutText(0, "err stat sbl_srv\n");
return -4;
}
else {
if (sys_mknod("/dev/sbl_srv", S_IFCHR | 0777, s.st_dev) == -1) {
sceKernelDebugOutText(0, "err mknod sbl_srv\n");
return -4;
}
}

// now unmount devices
if (jbc_unmount_in_sandbox("rootdev") != 0) {
sceKernelDebugOutText(0, "Failed to unmount rootdev\n");
}

// get max keyset that can be decrypted
getMaxKeySet();

return 0;
}
32 changes: 32 additions & 0 deletions source/scall.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#include "scall.h"

int sys_open(const char *path, int flags, int mode) {
int result;
int err;

asm volatile(
".intel_syntax;"
"mov rax, 5;" // System call number for open: 5
"syscall;" // Invoke syscall
: "=a" (result), // Output operand: result
"=@ccc" (err) // Output operand: err (clobbers condition codes)
);

return result;
}

int sys_mknod(const char *path, mode_t mode, dev_t dev) {
int result;
int err;

asm volatile(
".intel_syntax;" // Switches the assembly syntax to Intel syntax
"mov rax, 14;" // Moves the value 14 into the RAX register (which typically holds the system call number)
"mov r10, rcx;" // Moves the value of the RCX register into the R10 register
"syscall;" // Executes a system call using the values in the registers
: "=a"(result), // Output constraint: Tells the compiler that the result of the operation will be stored in the RAX register
"=@ccc"(err) // Output constraint: Indicates that error information will be stored in the specified location
);

return result;
}
184 changes: 184 additions & 0 deletions source/sd.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
#include <unistd.h>
#include "sd.h"

int (*sceFsUfsAllocateSaveData)(int fd, uint64_t imageSize, uint64_t imageFlags, int ext);
int (*sceFsInitCreatePfsSaveDataOpt)(CreatePfsSaveDataOpt *opt);
int (*sceFsCreatePfsSaveDataImage)(CreatePfsSaveDataOpt *opt, const char *volumePath, int idk, uint64_t volumeSize, uint8_t decryptedSealedKey[DEC_SEALEDKEY_LEN]);
int (*sceFsInitMountSaveDataOpt)(MountSaveDataOpt *opt);
int (*sceFsMountSaveData)(MountSaveDataOpt *opt, const char *volumePath, const char *mountPath, uint8_t decryptedSealedKey[DEC_SEALEDKEY_LEN]);
int (*sceFsInitUmountSaveDataOpt)(UmountSaveDataOpt *opt);
int (*sceFsUmountSaveData)(UmountSaveDataOpt *opt, const char *mountPath, int handle, bool ignoreErrors);
void (*statfs)();

// must be loaded upon setup
int loadPrivLibs(void) {
const char privDir[] = "/system/priv/lib";
const char commonDir[] = "/system/common/lib";
int sys;
int kernel_sys;

if (jbc_mount_in_sandbox(privDir, "priv") != 0) {
sceKernelDebugOutText(0, "Failed to mount system/priv/lib directory\n");
return -1;
}

sys = sceKernelLoadStartModule("/priv/libSceFsInternalForVsh.sprx", 0, NULL, 0, NULL, NULL);
if (jbc_unmount_in_sandbox("priv") != 0) {
sceKernelDebugOutText(0, "Failed to unmount priv\n");
}

if (sys >= 0) {
sceKernelDlsym(sys, "sceFsInitCreatePfsSaveDataOpt", (void **)&sceFsInitCreatePfsSaveDataOpt);
sceKernelDlsym(sys, "sceFsCreatePfsSaveDataImage", (void **)&sceFsCreatePfsSaveDataImage);
sceKernelDlsym(sys, "sceFsUfsAllocateSaveData", (void **)&sceFsUfsAllocateSaveData);
sceKernelDlsym(sys, "sceFsInitMountSaveDataOpt", (void **)&sceFsInitMountSaveDataOpt);
sceKernelDlsym(sys, "sceFsMountSaveData", (void **)&sceFsMountSaveData);
sceKernelDlsym(sys, "sceFsInitUmountSaveDataOpt", (void **)&sceFsInitUmountSaveDataOpt);
sceKernelDlsym(sys, "sceFsUmountSaveData", (void **)&sceFsUmountSaveData);
}
else {
sceKernelDebugOutText(0, "Failed to load libSceFsInternalForVsh.sprx\n");
return -2;
}

if (jbc_mount_in_sandbox(commonDir, "common") != 0) {
sceKernelDebugOutText(0, "Failed to mount /system/common/lib directory\n");
return -3;
}
kernel_sys = sceKernelLoadStartModule("/common/libkernel_sys.sprx", 0, NULL, 0, NULL, NULL);
if (jbc_unmount_in_sandbox("common") != 0) {
sceKernelDebugOutText(0, "Failed to unmount common\n");
}

if (kernel_sys >= 0) {
sceKernelDlsym(kernel_sys, "statfs", (void **)&statfs);
}
else {
sceKernelDebugOutText(0, "Failed to load libkernel_sys.sprx\n");
return -4;
}

return 0;
}

int generateSealedKey(uint8_t data[ENC_SEALEDKEY_LEN]) {
uint8_t dummy[0x30];
uint8_t sealedKey[ENC_SEALEDKEY_LEN];
int fd;

UNUSED(dummy);

memset(sealedKey, 0, sizeof(sealedKey));

if ((fd = open("/dev/sbl_srv", O_RDWR)) == -1) {
sceKernelDebugOutText(0, "sbl_srv open fail!\n");
return -1;
}

if (ioctl(fd, 0x40845303, sealedKey) == -1) {
close(fd);
return -2;
}

memcpy(data, sealedKey, sizeof(sealedKey));
close(fd);

return 0;
}

int decryptSealedKey(uint8_t enc_key[ENC_SEALEDKEY_LEN], uint8_t dec_key[DEC_SEALEDKEY_LEN]) {
uint8_t dummy[0x10];
uint8_t data[ENC_SEALEDKEY_LEN + DEC_SEALEDKEY_LEN];
int fd;

memset(data, 0, sizeof(data));

UNUSED(dummy);

if ((fd = open("/dev/sbl_srv", O_RDWR)) == -1) {
sceKernelDebugOutText(0, "sbl_srv open fail!\n");
return -1;
}

memcpy(data, enc_key, ENC_SEALEDKEY_LEN);

if (ioctl(fd, 0xc0845302, data) == -1) {
close(fd);
return -2;
}

memcpy(dec_key, &data[ENC_SEALEDKEY_LEN], DEC_SEALEDKEY_LEN);

close(fd);
return 0;
}

int decryptSealedKeyAtPath(const char *keyPath, uint8_t decryptedSealedKey[DEC_SEALEDKEY_LEN]) {
uint8_t sealedKey[ENC_SEALEDKEY_LEN];
// ssize_t bytesRead;
int fd;

if ((fd = sys_open(keyPath, O_RDONLY, 0)) == -1) {
return -1;
}

if (read(fd, sealedKey, ENC_SEALEDKEY_LEN) != ENC_SEALEDKEY_LEN) {
return -2;
close(fd);
}
close(fd);

if (decryptSealedKey(sealedKey, decryptedSealedKey) == -1) {
return -3;
}

return 0;
}

int mountSave(const char *folder, const char *saveName, const char *mountPath) {
char volumeKeyPath[MAX_PATH_LEN];
char volumePath[MAX_PATH_LEN];
char bid[] = "system";
int ret;
uint8_t decryptedSealedKey[DEC_SEALEDKEY_LEN];
MountSaveDataOpt opt;

memset(&opt, 0, sizeof(MountSaveDataOpt));

sprintf(volumeKeyPath, "%s/%s.bin", folder, saveName);
sprintf(volumePath, "%s/%s", folder, saveName);

if ((ret = decryptSealedKeyAtPath(volumeKeyPath, decryptedSealedKey)) < 0) {
return ret;
}

sceFsInitMountSaveDataOpt(&opt);
opt.budgetid = bid;

if ((ret = sceFsMountSaveData(&opt, volumePath, mountPath, decryptedSealedKey)) < 0) {
return ret;
}

return 0;
}

int umountSave(const char *mountPath, int handle, bool ignoreErrors) {
UmountSaveDataOpt opt;
sceFsInitUmountSaveDataOpt(&opt);
return sceFsUmountSaveData(&opt, mountPath, handle, ignoreErrors);
}

uint16_t maxKeyset = 0;
uint16_t getMaxKeySet(void) {
if (maxKeyset > 0) {
return maxKeyset;
}

uint8_t sampleSealedKey[ENC_SEALEDKEY_LEN];
if (generateSealedKey(sampleSealedKey) != 0) {
return 0;
}

maxKeyset = (sampleSealedKey[9] << 8) + sampleSealedKey[8];
return maxKeyset;
}

0 comments on commit 0506c87

Please sign in to comment.