diff --git a/include/init.h b/include/init.h new file mode 100644 index 0000000..8417ab7 --- /dev/null +++ b/include/init.h @@ -0,0 +1,15 @@ +#ifndef INIT_H +#define INIT_H + +#include +#include +#include +#include + +#include "sd.h" +#include "scall.h" + +int init_cred(void); +int init_devices(void); + +#endif // INIT_H \ No newline at end of file diff --git a/include/scall.h b/include/scall.h new file mode 100644 index 0000000..41292cf --- /dev/null +++ b/include/scall.h @@ -0,0 +1,9 @@ +#ifndef SCALL_H +#define SCALL_H + +#include + +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 \ No newline at end of file diff --git a/include/sd.h b/include/sd.h new file mode 100644 index 0000000..b10a61c --- /dev/null +++ b/include/sd.h @@ -0,0 +1,41 @@ +#ifndef SD_H +#define SD_H + +#include +#include +#include +#include +#include +#include +#include +#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 \ No newline at end of file diff --git a/source/init.c b/source/init.c new file mode 100644 index 0000000..2e57850 --- /dev/null +++ b/source/init.c @@ -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; +} \ No newline at end of file diff --git a/source/scall.c b/source/scall.c new file mode 100644 index 0000000..b3d0ea8 --- /dev/null +++ b/source/scall.c @@ -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; +} \ No newline at end of file diff --git a/source/sd.c b/source/sd.c new file mode 100644 index 0000000..cd37cfa --- /dev/null +++ b/source/sd.c @@ -0,0 +1,184 @@ +#include +#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; +} \ No newline at end of file