diff --git a/app/build.gradle b/app/build.gradle index 9048408..a8a3340 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -47,6 +47,6 @@ dependencies { androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' - implementation 'com.github.xxdark:ssvm:6e795448e4' + implementation 'com.github.xxdark:ssvm:df30743' implementation 'com.android.tools:r8:3.3.28' } diff --git a/app/src/main/java/ma/ssvmandroid/DefaultVMInitializer.kt b/app/src/main/java/ma/ssvmandroid/DefaultVMInitializer.kt new file mode 100644 index 0000000..0058a44 --- /dev/null +++ b/app/src/main/java/ma/ssvmandroid/DefaultVMInitializer.kt @@ -0,0 +1,46 @@ +package ma.ssvmandroid + +import dev.xdark.ssvm.VMInitializer +import dev.xdark.ssvm.VirtualMachine +import ma.ssvmandroid.native.* + +class DefaultVMInitializer : VMInitializer { + + private val nativeInitializers = setOf( + FileChannelImplNatives(), + FileDescriptorNatives(), + RandomAccessFileNatives(), + OldFileSystemNativesEx(), + SunFileDispatcherNatives(), + SunFileKeyNatives(), + SunSharedFileLockTableNatives(), + SunIOUtilNatives(), + SunNativeThreadNatives() + ) + + override fun initBegin(vm: VirtualMachine) { + vm.properties.apply { + setProperty("sun.stderr.encoding", "UTF-8") + setProperty("sun.stdout.encoding", "UTF-8") + setProperty("sun.jnu.encoding", "UTF-8") + setProperty("line.separator", "\n") + setProperty("path.separator", ":") + setProperty("file.separator", "/") + setProperty("user.dir", "/home/mike") + setProperty("user.name", "mike") + setProperty("os.version", "10.0") + setProperty("os.arch", "amd64") + setProperty("os.name", "Linux") + setProperty("file.encoding", "UTF-8") + } + + // Contains android stuffs + vm.getenv().clear() + } + + override fun nativeInit(vm: VirtualMachine) { + nativeInitializers.forEach { + it.init(vm) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ma/ssvmandroid/MainActivity.kt b/app/src/main/java/ma/ssvmandroid/MainActivity.kt index 61e7385..f078213 100644 --- a/app/src/main/java/ma/ssvmandroid/MainActivity.kt +++ b/app/src/main/java/ma/ssvmandroid/MainActivity.kt @@ -73,6 +73,8 @@ class MainActivity : AppCompatActivity() { val time = System.currentTimeMillis() catchVMException { + ssvmTest.addProperty("user.home", getExternalFilesDir("user_home")!!.absolutePath) + ssvmTest.addProperty("java.home", getExternalFilesDir("java_home")!!.absolutePath) // init VM ssvmTest.initVM() diff --git a/app/src/main/java/ma/ssvmandroid/SSVMTest.kt b/app/src/main/java/ma/ssvmandroid/SSVMTest.kt index 2066191..6a0b281 100644 --- a/app/src/main/java/ma/ssvmandroid/SSVMTest.kt +++ b/app/src/main/java/ma/ssvmandroid/SSVMTest.kt @@ -32,61 +32,43 @@ import java.util.zip.ZipFile class SSVMTest( private val rtJar: ZipFile, ) { - private lateinit var vm: VirtualMachine + private val vm: VirtualMachine = object : VirtualMachine(DefaultVMInitializer()) { + override fun createFileDescriptorManager() = HostFileDescriptorManager() - private var initialized: Boolean = false - val isInitialized: Boolean - get() = initialized - - fun initVM() { - vm = object : VirtualMachine() { - override fun createFileDescriptorManager() = HostFileDescriptorManager() - - override fun createManagementInterface() = object : ManagementInterface { - override fun getVersion() = "null" + override fun createManagementInterface() = object : ManagementInterface { + override fun getVersion() = "null" - override fun getStartupTime() = System.currentTimeMillis() + override fun getStartupTime() = System.currentTimeMillis() - override fun getInputArguments() = listOf() - } + override fun getInputArguments() = listOf() + } - override fun createBootClassLoader() = BootClassLoader { - println("[VM] Loading: $it.class") + override fun createBootClassLoader() = BootClassLoader { + println("[VM] Loading: $it.class") - try { - val entry = rtJar.getEntry("$it.class") - ?: return@BootClassLoader null + try { + val entry = rtJar.getEntry("$it.class") + ?: return@BootClassLoader null - rtJar.getInputStream(entry).use { stream -> - val cr = ClassReader(stream) - val node = ClassUtil.readNode(cr) + rtJar.getInputStream(entry).use { stream -> + val cr = ClassReader(stream) + val node = ClassUtil.readNode(cr) - return@BootClassLoader ClassParseResult(cr, node) - } - } catch (e: IOException) { - System.err.println("[VM] Couldn't load class $it: ${Log.getStackTraceString(e)}") + return@BootClassLoader ClassParseResult(cr, node) } - - return@BootClassLoader null + } catch (e: IOException) { + System.err.println("[VM] Couldn't load class $it: ${Log.getStackTraceString(e)}") } - } - vm.properties.apply { - setProperty("sun.stderr.encoding", "UTF-8") - setProperty("sun.stdout.encoding", "UTF-8") - setProperty("sun.jnu.encoding", "UTF-8") - setProperty("line.separator", "\n") - setProperty("path.separator", ":") - setProperty("file.separator", "/") - setProperty("java.home", "/usr/lib/jvm") - setProperty("user.home", "/home/mike") - setProperty("user.dir", "/home/mike") - setProperty("user.name", "mike") - setProperty("os.version", "10.0") - setProperty("os.arch", "amd64") - setProperty("os.name", "Linux") + return@BootClassLoader null } + } + + private var initialized: Boolean = false + val isInitialized: Boolean + get() = initialized + fun initVM() { initialized = try { vm.bootstrap() @@ -120,6 +102,10 @@ class SSVMTest( } } + fun init() { + + } + private fun getVMSystemClassLoader(): Value { val ctx = vm.helper.invokeStatic( vm.symbols.java_lang_ClassLoader(), @@ -190,7 +176,6 @@ class SSVMTest( ) helper.invokeStatic( - klass, method, arrayOf(), arrayOf(helper.emptyArray(symbols.java_lang_String())) @@ -212,13 +197,18 @@ class SSVMTest( } } - private class JitDexClassLoader: JitInstaller.ClassDefiner { + fun addProperty(key: String, value: String) { + vm.properties.setProperty(key, value) + } + + private class JitDexClassLoader : JitInstaller.ClassDefiner { override fun define(jitClass: JitClass): Class<*> { val code = jitClass.code val buffer = transformBytecodeToDex(code) ?: throw IllegalStateException("D8 failed to translate") - val inMemoryDexClassLoader = InMemoryDexClassLoader(buffer, jitClass::class.java.classLoader) + val inMemoryDexClassLoader = + InMemoryDexClassLoader(buffer, jitClass::class.java.classLoader) return inMemoryDexClassLoader.loadClass(jitClass.className) } diff --git a/app/src/main/java/ma/ssvmandroid/native/FileChannelImplNatives.kt b/app/src/main/java/ma/ssvmandroid/native/FileChannelImplNatives.kt new file mode 100644 index 0000000..c5e0492 --- /dev/null +++ b/app/src/main/java/ma/ssvmandroid/native/FileChannelImplNatives.kt @@ -0,0 +1,26 @@ +package ma.ssvmandroid.native + +import dev.xdark.ssvm.VirtualMachine +import dev.xdark.ssvm.execution.Result +import dev.xdark.ssvm.mirror.InstanceJavaClass +import dev.xdark.ssvm.value.LongValue +import org.objectweb.asm.Type + +class FileChannelImplNatives : NativeInitializer { + override fun init(vm: VirtualMachine) { + val fileChannelImpl = vm.findBootstrapClass("sun/nio/ch/FileChannelImpl") as InstanceJavaClass + + vm.`interface`.setInvoker( + fileChannelImpl, + "initIDs", + Type.getMethodDescriptor(Type.LONG_TYPE) + ) { + it.result = LongValue.of(0) + return@setInvoker Result.ABORT + } + } + + operator fun invoke(vm: VirtualMachine) { + init(vm) + } +} \ No newline at end of file diff --git a/app/src/main/java/ma/ssvmandroid/native/FileDescriptorNatives.kt b/app/src/main/java/ma/ssvmandroid/native/FileDescriptorNatives.kt new file mode 100644 index 0000000..a925728 --- /dev/null +++ b/app/src/main/java/ma/ssvmandroid/native/FileDescriptorNatives.kt @@ -0,0 +1,19 @@ +package ma.ssvmandroid.native + +import dev.xdark.ssvm.VirtualMachine +import dev.xdark.ssvm.api.MethodInvoker +import dev.xdark.ssvm.mirror.InstanceJavaClass +import org.objectweb.asm.Type + +class FileDescriptorNatives : NativeInitializer { + override fun init(vm: VirtualMachine) { + val fileDescriptor = vm.findBootstrapClass("java/io/FileDescriptor") as InstanceJavaClass + + vm.`interface`.setInvoker( + fileDescriptor, + "init", + Type.getMethodDescriptor(Type.VOID_TYPE), + MethodInvoker.noop() + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/ma/ssvmandroid/native/NativeInitializer.kt b/app/src/main/java/ma/ssvmandroid/native/NativeInitializer.kt new file mode 100644 index 0000000..7145fb4 --- /dev/null +++ b/app/src/main/java/ma/ssvmandroid/native/NativeInitializer.kt @@ -0,0 +1,8 @@ +package ma.ssvmandroid.native + +import dev.xdark.ssvm.VirtualMachine + +interface NativeInitializer { + + fun init(vm: VirtualMachine) +} \ No newline at end of file diff --git a/app/src/main/java/ma/ssvmandroid/native/OldFileSystemNativesEx.kt b/app/src/main/java/ma/ssvmandroid/native/OldFileSystemNativesEx.kt new file mode 100644 index 0000000..4a24671 --- /dev/null +++ b/app/src/main/java/ma/ssvmandroid/native/OldFileSystemNativesEx.kt @@ -0,0 +1,62 @@ +package ma.ssvmandroid.native + +import dev.xdark.ssvm.VirtualMachine +import dev.xdark.ssvm.execution.Result +import dev.xdark.ssvm.mirror.InstanceJavaClass +import dev.xdark.ssvm.value.InstanceValue +import dev.xdark.ssvm.value.IntValue +import dev.xdark.ssvm.value.Value +import org.objectweb.asm.Type +import java.io.File + +class OldFileSystemNativesEx : NativeInitializer { + override fun init(vm: VirtualMachine) { + val fs: InstanceJavaClass = (vm.findBootstrapClass("java/io/WinNTFileSystem") + ?: vm.findBootstrapClass("java/io/UnixFileSystem")) as InstanceJavaClass + + vm.`interface`.setInvoker( + fs, + "checkAccess", + Type.getMethodDescriptor( + Type.BOOLEAN_TYPE, + Type.getType(File::class.java), + Type.INT_TYPE + ) + ) { + val value = it.locals.load(1) + val path: String = + vm.helper.readUtf8((value as InstanceValue).getValue("path", "Ljava/lang/String;")) + val result = if (File(path).canRead()) 1 else 0 + it.result = IntValue.of(result) + return@setInvoker Result.ABORT + } + + vm.`interface`.setInvoker( + fs, + "delete0", + Type.getMethodDescriptor(Type.BOOLEAN_TYPE, Type.getType(File::class.java)) + ) { + val value = it.locals.load(1) + val path: String = + vm.helper.readUtf8((value as InstanceValue).getValue("path", "Ljava/lang/String;")) + val result = if (File(path).delete()) 1 else 0 + it.result = IntValue.of(result) + return@setInvoker Result.ABORT + } + + vm.`interface`.setInvoker( + fs, "createDirectory", Type.getMethodDescriptor( + Type.BOOLEAN_TYPE, Type.getType(File::class.java) + ) + ) { + val value = it.locals.load(1) + val path: String = + vm.helper.readUtf8((value as InstanceValue).getValue("path", "Ljava/lang/String;")) + val result = if (File(path).mkdir()) 1 else 0 + it.result = IntValue.of(result) + return@setInvoker Result.ABORT + } + } + + +} \ No newline at end of file diff --git a/app/src/main/java/ma/ssvmandroid/native/RandomAccessFileNatives.kt b/app/src/main/java/ma/ssvmandroid/native/RandomAccessFileNatives.kt new file mode 100644 index 0000000..4bb1773 --- /dev/null +++ b/app/src/main/java/ma/ssvmandroid/native/RandomAccessFileNatives.kt @@ -0,0 +1,55 @@ +package ma.ssvmandroid.native + +import dev.xdark.ssvm.VirtualMachine +import dev.xdark.ssvm.api.MethodInvoker +import dev.xdark.ssvm.execution.Result +import dev.xdark.ssvm.mirror.InstanceJavaClass +import dev.xdark.ssvm.value.IntValue +import dev.xdark.ssvm.value.Value +import org.objectweb.asm.Type + +class RandomAccessFileNatives : NativeInitializer { + override fun init(vm: VirtualMachine) { + val randomAccessFile = vm.findBootstrapClass("java/io/RandomAccessFile") as InstanceJavaClass + + vm.`interface`.setInvoker( + randomAccessFile, + "initIDs", + Type.getMethodDescriptor(Type.VOID_TYPE), + MethodInvoker.noop() + ) + + vm.`interface`.setInvoker( + randomAccessFile, + "open", + Type.getMethodDescriptor( + Type.VOID_TYPE, + Type.getType(String::class.java), + Type.INT_TYPE + ) + ) { + val value = it.locals.load(1) + val path: String = vm.helper.readUtf8(value) + val modeValue = it.locals.load(2) + vm.fileDescriptorManager.open(path, modeValue.asInt()) + return@setInvoker Result.ABORT + } + + // no-op, handled in the open() method + vm.`interface`.setInvoker( + randomAccessFile, + "open0", + Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE), + MethodInvoker.noop() + ) + + vm.`interface`.setInvoker( + randomAccessFile, + "close0", + Type.getMethodDescriptor(Type.VOID_TYPE) + ) { + // TODO: read the path field on the random access file and close that + return@setInvoker Result.ABORT + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ma/ssvmandroid/native/SunFileDispatcherNatives.kt b/app/src/main/java/ma/ssvmandroid/native/SunFileDispatcherNatives.kt new file mode 100644 index 0000000..da7f1f6 --- /dev/null +++ b/app/src/main/java/ma/ssvmandroid/native/SunFileDispatcherNatives.kt @@ -0,0 +1,51 @@ +package ma.ssvmandroid.native + +import dev.xdark.ssvm.VirtualMachine +import dev.xdark.ssvm.api.MethodInvoker +import dev.xdark.ssvm.execution.Result +import dev.xdark.ssvm.mirror.InstanceJavaClass +import dev.xdark.ssvm.value.IntValue +import org.objectweb.asm.Type +import java.io.FileDescriptor + +class SunFileDispatcherNatives : NativeInitializer { + override fun init(vm: VirtualMachine) { + val fileDispatcher = vm.findBootstrapClass("sun/nio/ch/FileDispatcherImpl") as InstanceJavaClass + + // no-op, handled in real vm + vm.`interface`.setInvoker( + fileDispatcher, + "init", + Type.getMethodDescriptor(Type.VOID_TYPE), + MethodInvoker.noop() + ) + + vm.`interface`.setInvoker( + fileDispatcher, + "release0", + Type.getMethodDescriptor( + Type.VOID_TYPE, + Type.getType(FileDescriptor::class.java), + Type.LONG_TYPE, + Type.LONG_TYPE) + ) { + // Not sure what to delegate here + return@setInvoker Result.ABORT + } + + vm.`interface`.setInvoker( + fileDispatcher, + "lock0", + Type.getMethodDescriptor( + Type.INT_TYPE, + Type.getType(FileDescriptor::class.java), + Type.BOOLEAN_TYPE, + Type.LONG_TYPE, + Type.LONG_TYPE, + Type.BOOLEAN_TYPE) + ) { + it.result = IntValue.of(0) + return@setInvoker Result.ABORT + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ma/ssvmandroid/native/SunFileKeyNatives.kt b/app/src/main/java/ma/ssvmandroid/native/SunFileKeyNatives.kt new file mode 100644 index 0000000..1805fa1 --- /dev/null +++ b/app/src/main/java/ma/ssvmandroid/native/SunFileKeyNatives.kt @@ -0,0 +1,31 @@ +package ma.ssvmandroid.native + +import dev.xdark.ssvm.VirtualMachine +import dev.xdark.ssvm.api.MethodInvoker +import dev.xdark.ssvm.execution.Result +import dev.xdark.ssvm.mirror.InstanceJavaClass +import org.objectweb.asm.Type +import java.io.FileDescriptor + +class SunFileKeyNatives : NativeInitializer { + override fun init(vm: VirtualMachine) { + val fileKey = vm.findBootstrapClass("sun/nio/ch/FileKey") as InstanceJavaClass + + vm.`interface`.setInvoker( + fileKey, + "initIDs", + Type.getMethodDescriptor(Type.VOID_TYPE), + MethodInvoker.noop() + ) + + vm.`interface`.setInvoker( + fileKey, + "init", + Type.getMethodDescriptor( + Type.VOID_TYPE, + Type.getType(FileDescriptor::class.java) + ), + MethodInvoker.noop() + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/ma/ssvmandroid/native/SunIOUtilNatives.kt b/app/src/main/java/ma/ssvmandroid/native/SunIOUtilNatives.kt new file mode 100644 index 0000000..1466576 --- /dev/null +++ b/app/src/main/java/ma/ssvmandroid/native/SunIOUtilNatives.kt @@ -0,0 +1,32 @@ +package ma.ssvmandroid.native + +import dev.xdark.ssvm.VirtualMachine +import dev.xdark.ssvm.api.MethodInvoker +import dev.xdark.ssvm.execution.Result +import dev.xdark.ssvm.mirror.InstanceJavaClass +import dev.xdark.ssvm.value.IntValue +import org.objectweb.asm.Type + +class SunIOUtilNatives : NativeInitializer { + override fun init(vm: VirtualMachine) { + val ioUtils = vm.findBootstrapClass("sun/nio/ch/IOUtil") as InstanceJavaClass + + // no-op, already handled in the real vm + vm.`interface`.setInvoker( + ioUtils, + "initIDs", + Type.getMethodDescriptor(Type.VOID_TYPE), + MethodInvoker.noop() + ) + + vm.`interface`.setInvoker( + ioUtils, + "iovMax", + Type.getMethodDescriptor(Type.INT_TYPE) + ) { + // Not sure if its correct to return a random value here + it.result = IntValue.of(0) + return@setInvoker Result.ABORT + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ma/ssvmandroid/native/SunNativeThreadNatives.kt b/app/src/main/java/ma/ssvmandroid/native/SunNativeThreadNatives.kt new file mode 100644 index 0000000..9ae101d --- /dev/null +++ b/app/src/main/java/ma/ssvmandroid/native/SunNativeThreadNatives.kt @@ -0,0 +1,31 @@ +package ma.ssvmandroid.native + +import dev.xdark.ssvm.VirtualMachine +import dev.xdark.ssvm.api.MethodInvoker +import dev.xdark.ssvm.execution.Result +import dev.xdark.ssvm.mirror.InstanceJavaClass +import dev.xdark.ssvm.value.LongValue +import org.objectweb.asm.Type + +class SunNativeThreadNatives : NativeInitializer { + override fun init(vm: VirtualMachine) { + val nativeThread = vm.findBootstrapClass("sun/nio/ch/NativeThread") as InstanceJavaClass + + vm.`interface`.setInvoker( + nativeThread, + "init", + Type.getMethodDescriptor(Type.VOID_TYPE), + MethodInvoker.noop() + ) + + vm.`interface`.setInvoker( + nativeThread, + "current", + Type.getMethodDescriptor(Type.LONG_TYPE) + ) { + // TODO: not sure what value to return here + it.result = LongValue.of(0) + return@setInvoker Result.ABORT + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ma/ssvmandroid/native/SunSharedFileLockTableNatives.kt b/app/src/main/java/ma/ssvmandroid/native/SunSharedFileLockTableNatives.kt new file mode 100644 index 0000000..c638667 --- /dev/null +++ b/app/src/main/java/ma/ssvmandroid/native/SunSharedFileLockTableNatives.kt @@ -0,0 +1,20 @@ +package ma.ssvmandroid.native + +import dev.xdark.ssvm.VirtualMachine +import dev.xdark.ssvm.api.MethodInvoker +import dev.xdark.ssvm.execution.Result +import dev.xdark.ssvm.mirror.InstanceJavaClass +import org.objectweb.asm.Type + +class SunSharedFileLockTableNatives : NativeInitializer { + override fun init(vm: VirtualMachine) { + val sharedFileLockIds = vm.findBootstrapClass("sun/nio/ch/SharedFileLockTable") as InstanceJavaClass + + vm.`interface`.setInvoker( + sharedFileLockIds, + "initIDs", + Type.getMethodDescriptor(Type.VOID_TYPE), + MethodInvoker.noop() + ) + } +} \ No newline at end of file