/* Copyright (C) 2025 John Törnblom This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, see . */ #include #include #include #include #include #include #include "zip.h" #define IOVEC_SIZE(x) (sizeof(x) / sizeof(struct iovec)) #define IOVEC_ENTRY(x) {x ? x : 0, x ? strlen(x)+1 : 0} #define JAR_PATH "/system_ex/app/NPXS40140/cdc/bdjstack.jar" #define JAR_BAK_PATH "/system_ex/app/NPXS40140/cdc/bdjstack.jar.bak" #define ENTRY_NAME "org/bdj/sandbox/ExploitInternal.class" typedef struct notify_request { char useless1[45]; char message[3075]; } notify_request_t; int sceKernelSendNotificationRequest(int, notify_request_t*, size_t, int); static void notify(const char *fmt, ...) { notify_request_t req; va_list args; bzero(&req, sizeof req); va_start(args, fmt); vsnprintf(req.message, sizeof req.message, fmt, args); va_end(args); sceKernelSendNotificationRequest(0, &req, sizeof req, 0); } static struct iovec iov_sysex[] = { IOVEC_ENTRY("from"), IOVEC_ENTRY("/dev/ssd0.system_ex"), IOVEC_ENTRY("fspath"), IOVEC_ENTRY("/system_ex"), IOVEC_ENTRY("fstype"), IOVEC_ENTRY("exfatfs"), IOVEC_ENTRY("large"), IOVEC_ENTRY("yes"), IOVEC_ENTRY("timezone"), IOVEC_ENTRY("static"), IOVEC_ENTRY("async"), IOVEC_ENTRY(NULL), IOVEC_ENTRY("ignoreacl"), IOVEC_ENTRY(NULL), }; static const unsigned char exploit_class[943] = { 0xCA, 0xFE, 0xBA, 0xBE, 0x00, 0x00, 0x00, 0x30, 0x00, 0x32, 0x07, 0x00, 0x19, 0x0A, 0x00, 0x01, 0x00, 0x1A, 0x0A, 0x00, 0x1B, 0x00, 0x1C, 0x07, 0x00, 0x1D, 0x0A, 0x00, 0x04, 0x00, 0x1E, 0x07, 0x00, 0x1F, 0x0A, 0x00, 0x0B, 0x00, 0x1A, 0x0A, 0x00, 0x20, 0x00, 0x21, 0x0A, 0x00, 0x20, 0x00, 0x22, 0x0A, 0x00, 0x04, 0x00, 0x23, 0x07, 0x00, 0x24, 0x07, 0x00, 0x25, 0x01, 0x00, 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6C, 0x65, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x4D, 0x61, 0x6E, 0x61, 0x67, 0x65, 0x72, 0x01, 0x00, 0x03, 0x28, 0x29, 0x5A, 0x01, 0x00, 0x04, 0x43, 0x6F, 0x64, 0x65, 0x01, 0x00, 0x0F, 0x4C, 0x69, 0x6E, 0x65, 0x4E, 0x75, 0x6D, 0x62, 0x65, 0x72, 0x54, 0x61, 0x62, 0x6C, 0x65, 0x01, 0x00, 0x0A, 0x45, 0x78, 0x63, 0x65, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x73, 0x07, 0x00, 0x26, 0x01, 0x00, 0x06, 0x3C, 0x69, 0x6E, 0x69, 0x74, 0x3E, 0x01, 0x00, 0x03, 0x28, 0x29, 0x56, 0x01, 0x00, 0x03, 0x72, 0x75, 0x6E, 0x01, 0x00, 0x14, 0x28, 0x29, 0x4C, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x6C, 0x61, 0x6E, 0x67, 0x2F, 0x4F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x3B, 0x01, 0x00, 0x0A, 0x53, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6C, 0x65, 0x01, 0x00, 0x14, 0x45, 0x78, 0x70, 0x6C, 0x6F, 0x69, 0x74, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x61, 0x6C, 0x2E, 0x6A, 0x61, 0x76, 0x61, 0x01, 0x00, 0x1F, 0x6F, 0x72, 0x67, 0x2F, 0x62, 0x64, 0x6A, 0x2F, 0x73, 0x61, 0x6E, 0x64, 0x62, 0x6F, 0x78, 0x2F, 0x45, 0x78, 0x70, 0x6C, 0x6F, 0x69, 0x74, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x61, 0x6C, 0x0C, 0x00, 0x13, 0x00, 0x14, 0x07, 0x00, 0x27, 0x0C, 0x00, 0x28, 0x00, 0x29, 0x01, 0x00, 0x11, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x6C, 0x61, 0x6E, 0x67, 0x2F, 0x42, 0x6F, 0x6F, 0x6C, 0x65, 0x61, 0x6E, 0x0C, 0x00, 0x2A, 0x00, 0x0E, 0x01, 0x00, 0x13, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x6C, 0x61, 0x6E, 0x67, 0x2F, 0x45, 0x78, 0x63, 0x65, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x07, 0x00, 0x2B, 0x0C, 0x00, 0x2C, 0x00, 0x2D, 0x0C, 0x00, 0x2E, 0x00, 0x2F, 0x0C, 0x00, 0x30, 0x00, 0x31, 0x01, 0x00, 0x10, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x6C, 0x61, 0x6E, 0x67, 0x2F, 0x4F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x01, 0x00, 0x27, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x2F, 0x50, 0x72, 0x69, 0x76, 0x69, 0x6C, 0x65, 0x67, 0x65, 0x64, 0x45, 0x78, 0x63, 0x65, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x41, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x01, 0x00, 0x27, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x2F, 0x50, 0x72, 0x69, 0x76, 0x69, 0x6C, 0x65, 0x67, 0x65, 0x64, 0x41, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x45, 0x78, 0x63, 0x65, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x01, 0x00, 0x1E, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x2F, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x43, 0x6F, 0x6E, 0x74, 0x72, 0x6F, 0x6C, 0x6C, 0x65, 0x72, 0x01, 0x00, 0x0C, 0x64, 0x6F, 0x50, 0x72, 0x69, 0x76, 0x69, 0x6C, 0x65, 0x67, 0x65, 0x64, 0x01, 0x00, 0x3D, 0x28, 0x4C, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x2F, 0x50, 0x72, 0x69, 0x76, 0x69, 0x6C, 0x65, 0x67, 0x65, 0x64, 0x45, 0x78, 0x63, 0x65, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x41, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x3B, 0x29, 0x4C, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x6C, 0x61, 0x6E, 0x67, 0x2F, 0x4F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x3B, 0x01, 0x00, 0x0C, 0x62, 0x6F, 0x6F, 0x6C, 0x65, 0x61, 0x6E, 0x56, 0x61, 0x6C, 0x75, 0x65, 0x01, 0x00, 0x10, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x6C, 0x61, 0x6E, 0x67, 0x2F, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x01, 0x00, 0x12, 0x73, 0x65, 0x74, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x4D, 0x61, 0x6E, 0x61, 0x67, 0x65, 0x72, 0x01, 0x00, 0x1E, 0x28, 0x4C, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x6C, 0x61, 0x6E, 0x67, 0x2F, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x4D, 0x61, 0x6E, 0x61, 0x67, 0x65, 0x72, 0x3B, 0x29, 0x56, 0x01, 0x00, 0x12, 0x67, 0x65, 0x74, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x4D, 0x61, 0x6E, 0x61, 0x67, 0x65, 0x72, 0x01, 0x00, 0x1D, 0x28, 0x29, 0x4C, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x6C, 0x61, 0x6E, 0x67, 0x2F, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x4D, 0x61, 0x6E, 0x61, 0x67, 0x65, 0x72, 0x3B, 0x01, 0x00, 0x07, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x4F, 0x66, 0x01, 0x00, 0x16, 0x28, 0x5A, 0x29, 0x4C, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x6C, 0x61, 0x6E, 0x67, 0x2F, 0x42, 0x6F, 0x6F, 0x6C, 0x65, 0x61, 0x6E, 0x3B, 0x00, 0x21, 0x00, 0x01, 0x00, 0x0B, 0x00, 0x01, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x09, 0x00, 0x0D, 0x00, 0x0E, 0x00, 0x02, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x42, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x16, 0xBB, 0x00, 0x01, 0x59, 0xB7, 0x00, 0x02, 0xB8, 0x00, 0x03, 0xC0, 0x00, 0x04, 0x4B, 0x2A, 0xB6, 0x00, 0x05, 0xAC, 0x4B, 0x03, 0xAC, 0x00, 0x01, 0x00, 0x00, 0x00, 0x12, 0x00, 0x13, 0x00, 0x06, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, 0x00, 0x12, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x0E, 0x00, 0x0B, 0x00, 0x13, 0x00, 0x0C, 0x00, 0x14, 0x00, 0x0D, 0x00, 0x11, 0x00, 0x00, 0x00, 0x04, 0x00, 0x01, 0x00, 0x12, 0x00, 0x02, 0x00, 0x13, 0x00, 0x14, 0x00, 0x01, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x2A, 0xB7, 0x00, 0x07, 0xB1, 0x00, 0x00, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x01, 0x00, 0x15, 0x00, 0x16, 0x00, 0x02, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x13, 0x01, 0xB8, 0x00, 0x08, 0xB8, 0x00, 0x09, 0xC7, 0x00, 0x07, 0x04, 0xA7, 0x00, 0x04, 0x03, 0xB8, 0x00, 0x0A, 0xB0, 0x00, 0x00, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x04, 0x00, 0x15, 0x00, 0x11, 0x00, 0x00, 0x00, 0x04, 0x00, 0x01, 0x00, 0x06, 0x00, 0x01, 0x00, 0x17, 0x00, 0x00, 0x00, 0x02, 0x00, 0x18 }; static int file_copy(const char *src, const char *dst) { char buf[4096]; size_t n; FILE *in = fopen(src, "rb"); if (!in) return -1; FILE *out = fopen(dst, "wb"); if (!out) { fclose(in); return -1; } while ((n = fread(buf, 1, sizeof(buf), in)) > 0) { fwrite(buf, 1, n, out); } fclose(in); fclose(out); return 0; } int main(void) { struct zip_t *zip = zip_open(JAR_PATH, 0, 'r'); if (!zip) { notify("zip_open (read) failed: %s", strerror(errno)); return 1; } int exists = (zip_entry_open(zip, ENTRY_NAME) == 0); if (exists) zip_entry_close(zip); zip_close(zip); if (exists) { notify("bdjstack.jar already patched, skipping"); return 0; } // Only remount rw if we actually need to patch if (nmount(iov_sysex, IOVEC_SIZE(iov_sysex), MNT_UPDATE)) { notify("system_ex remount rw failed: %s", strerror(errno)); return 1; } if (file_copy(JAR_PATH, JAR_BAK_PATH)) { notify("file_copy (backup) failed: %s", strerror(errno)); nmount(iov_sysex, IOVEC_SIZE(iov_sysex), MNT_UPDATE | MNT_RDONLY); return 1; } zip = zip_open(JAR_PATH, ZIP_DEFAULT_COMPRESSION_LEVEL, 'a'); if (!zip) { notify("zip_open (append) failed: %s", strerror(errno)); nmount(iov_sysex, IOVEC_SIZE(iov_sysex), MNT_UPDATE | MNT_RDONLY); return 1; } zip_entry_open(zip, ENTRY_NAME); zip_entry_write(zip, exploit_class, sizeof(exploit_class)); zip_entry_close(zip); zip_close(zip); notify("Patched bdjstack.jar"); // Lock partition back to ro if (nmount(iov_sysex, IOVEC_SIZE(iov_sysex), MNT_UPDATE | MNT_RDONLY)) { notify("system_ex remount ro failed: %s", strerror(errno)); return 1; } return 0; }