diff --git a/deobfuscator-api/src/main/java/dev/xdark/ssvm/classloading/SupplyingClassLoader.java b/deobfuscator-api/src/main/java/dev/xdark/ssvm/classloading/SupplyingClassLoader.java
new file mode 100644
index 0000000..d47c3cb
--- /dev/null
+++ b/deobfuscator-api/src/main/java/dev/xdark/ssvm/classloading/SupplyingClassLoader.java
@@ -0,0 +1,71 @@
+package dev.xdark.ssvm.classloading;
+
+import dev.xdark.ssvm.VirtualMachine;
+import dev.xdark.ssvm.api.MethodInvoker;
+import dev.xdark.ssvm.api.VMInterface;
+import dev.xdark.ssvm.mirror.member.JavaMethod;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLStreamHandler;
+
+/**
+ * A custom classloader to supply additional content to the {@link VirtualMachine}.
+ *
+ * You will want to install this into the VM and use {@link VMInterface#setInvoker(JavaMethod, MethodInvoker)} on the
+ * providing methods to supply classes and resources.
+ *
+ * @see SupplyingClassLoaderInstaller
+ *
+ * @author Matt Coley
+ */
+public class SupplyingClassLoader extends ClassLoader {
+ public native byte[] provideResource(String name);
+
+ public native byte[] provideClass(String name);
+
+ @Override
+ protected Class> findClass(String name) throws ClassNotFoundException {
+ byte[] classBytes = provideClass(name);
+ if (classBytes != null)
+ return defineClass(name, classBytes, 0, classBytes.length);
+ return super.findClass(name);
+ }
+
+ @Override
+ protected URL findResource(String name) {
+ byte[] resourceBytes = provideResource(name);
+ if (resourceBytes != null) {
+ try {
+ return new URL("memory", "", -1, "", new URLStreamHandler() {
+ @Override
+ protected URLConnection openConnection(URL u) {
+ return new URLConnection(u) {
+ private InputStream is;
+
+ @Override
+ public void connect() {
+ // no-op
+ }
+
+ @Override
+ public InputStream getInputStream() {
+ if (is == null) {
+ is = new ByteArrayInputStream(resourceBytes);
+ }
+ return is;
+ }
+ };
+ }
+ });
+ } catch (MalformedURLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ return super.findResource(name);
+ }
+}
diff --git a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/context/Context.java b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/context/Context.java
index f47ba99..641999a 100644
--- a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/context/Context.java
+++ b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/context/Context.java
@@ -5,8 +5,6 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;
-import dev.xdark.ssvm.VirtualMachine;
-import dev.xdark.ssvm.execution.VMException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import uwu.narumi.deobfuscator.api.asm.ClassWrapper;
@@ -24,7 +22,7 @@ public class Context {
private final DeobfuscatorOptions options;
private final LibraryClassLoader libraryLoader;
- private SandBox sandBox = null;
+ private SandBox globalSandBox = null;
public Context(DeobfuscatorOptions options, LibraryClassLoader libraryLoader) {
this.options = options;
@@ -35,19 +33,11 @@ public Context(DeobfuscatorOptions options, LibraryClassLoader libraryLoader) {
* Gets sandbox or creates if it does not exist.
*/
public SandBox getSandBox() {
- if (this.sandBox == null) {
+ if (this.globalSandBox == null) {
// Lazily load sandbox
- VirtualMachine vm = options.virtualMachine() == null ? new VirtualMachine() : options.virtualMachine();
- try {
- this.sandBox = new SandBox(this.libraryLoader, vm);
- } catch (VMException ex) {
- LOGGER.error("SSVM bootstrap failed. Make sure that you run this deobfuscator on java 17");
- SandBox.logVMException(ex, vm);
-
- throw new RuntimeException(ex);
- }
+ this.globalSandBox = new SandBox(this);
}
- return this.sandBox;
+ return this.globalSandBox;
}
public DeobfuscatorOptions getOptions() {
diff --git a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/context/DeobfuscatorOptions.java b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/context/DeobfuscatorOptions.java
index 39400be..2615cb9 100644
--- a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/context/DeobfuscatorOptions.java
+++ b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/context/DeobfuscatorOptions.java
@@ -32,9 +32,7 @@ public record DeobfuscatorOptions(
boolean printStacktraces,
boolean continueOnError,
- boolean verifyBytecode,
-
- VirtualMachine virtualMachine
+ boolean verifyBytecode
) {
public static DeobfuscatorOptions.Builder builder() {
return new DeobfuscatorOptions.Builder();
@@ -70,8 +68,6 @@ public static class Builder {
private boolean continueOnError = false;
private boolean verifyBytecode = false;
- private VirtualMachine virtualMachine = null;
-
private Builder() {
}
@@ -203,12 +199,6 @@ public DeobfuscatorOptions.Builder verifyBytecode() {
return this;
}
- @Contract("_ -> this")
- public DeobfuscatorOptions.Builder virtualMachine(VirtualMachine virtualMachine) {
- this.virtualMachine = virtualMachine;
- return this;
- }
-
/**
* Build immutable {@link DeobfuscatorOptions} with options verification
*/
@@ -240,9 +230,7 @@ public DeobfuscatorOptions build() {
// Other config
printStacktraces,
continueOnError,
- verifyBytecode,
-
- virtualMachine
+ verifyBytecode
);
}
}
diff --git a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/execution/SandBox.java b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/execution/SandBox.java
index 4faa07f..ce88e2f 100644
--- a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/execution/SandBox.java
+++ b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/execution/SandBox.java
@@ -12,84 +12,88 @@
import dev.xdark.ssvm.invoke.InvocationUtil;
import dev.xdark.ssvm.memory.management.MemoryManager;
import dev.xdark.ssvm.mirror.type.InstanceClass;
+import dev.xdark.ssvm.mirror.type.JavaClass;
import dev.xdark.ssvm.operation.VMOperations;
import dev.xdark.ssvm.symbol.Primitives;
import dev.xdark.ssvm.symbol.Symbols;
import dev.xdark.ssvm.thread.ThreadManager;
import dev.xdark.ssvm.util.Reflection;
+
+import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
+import java.util.List;
import dev.xdark.ssvm.value.InstanceValue;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
-import uwu.narumi.deobfuscator.api.library.LibraryClassLoader;
+import uwu.narumi.deobfuscator.api.context.Context;
/**
- * A wrapper for {@link VirtualMachine}
+ * A wrapper for {@link VirtualMachine} with some additional features and patches
*/
public class SandBox {
private static final Logger LOGGER = LogManager.getLogger(SandBox.class);
- private final VirtualMachine virtualMachine;
+ private final VirtualMachine vm;
+ private final Context context;
private final MemoryManager memoryManager;
private final SupplyingClassLoaderInstaller.Helper helper;
private final InvocationUtil invocationUtil;
- public SandBox(LibraryClassLoader loader) {
- this(loader, new VirtualMachine());
+ public SandBox(Context context) {
+ this(context, new VirtualMachine());
}
- public SandBox(LibraryClassLoader loader, VirtualMachine virtualMachine) {
- this.virtualMachine = virtualMachine;
+ public SandBox(Context context, VirtualMachine vm) {
+ LOGGER.info("Initializing SSVM sandbox...");
+ this.context = context;
+ this.vm = vm;
try {
- this.virtualMachine.initialize();
- this.virtualMachine.bootstrap();
- this.memoryManager = virtualMachine.getMemoryManager();
+ this.vm.initialize();
+ this.vm.bootstrap();
+ this.memoryManager = vm.getMemoryManager();
// Install all classes from deobfuscator context
- this.helper = SupplyingClassLoaderInstaller.install(virtualMachine, new ClassLoaderDataSupplier(loader));
- this.invocationUtil = InvocationUtil.create(virtualMachine);
+ this.helper = SupplyingClassLoaderInstaller.install(vm, new ClassLoaderDataSupplier(context.getLibraryLoader()));
+ this.invocationUtil = InvocationUtil.create(vm);
patchVm();
- } catch (Exception e) {
- throw new RuntimeException(e);
+ } catch (VMException ex) {
+ LOGGER.error("SSVM bootstrap failed. Make sure that you run this deobfuscator on java 17");
+ SandBox.logVMException(ex, vm);
+
+ throw new RuntimeException(ex);
+ } catch (IOException ex) {
+ throw new RuntimeException(ex);
}
+ LOGGER.info("Initialized SSVM sandbox");
}
private void patchVm() {
// Some patches to circumvent bugs arising from VM implementation changes in later versions
- if (virtualMachine.getJvmVersion() > 8) {
+ if (vm.getJvmVersion() > 8) {
// Bug in SSVM makes it think there are overlapping sleeps, so until that gets fixed we stub
// out sleeping.
- InstanceClass thread = virtualMachine.getSymbols().java_lang_Thread();
- virtualMachine
- .getInterface()
+ InstanceClass thread = vm.getSymbols().java_lang_Thread();
+ vm.getInterface()
.setInvoker(thread.getMethod("sleep", "(J)V"), MethodInvoker.noop());
// SSVM manages its own memory, and this conflicts with it. Stubbing it out keeps everyone
// happy.
- InstanceClass bits = (InstanceClass) virtualMachine.findBootstrapClass("java/nio/Bits");
+ InstanceClass bits = (InstanceClass) vm.findBootstrapClass("java/nio/Bits");
if (bits != null) {
- virtualMachine
- .getInterface()
+ vm.getInterface()
.setInvoker(bits.getMethod("reserveMemory", "(JJ)V"), MethodInvoker.noop());
}
}
}
- public static String toString(Throwable t) {
- StringWriter stringWriter = new StringWriter();
- PrintWriter printWriter = new PrintWriter(stringWriter);
- t.printStackTrace(printWriter);
- return stringWriter.toString();
- }
-
/**
* @see SandBox#logVMException(VMException, VirtualMachine)
*/
public void logVMException(VMException ex) {
- logVMException(ex, this.virtualMachine);
+ logVMException(ex, this.vm);
}
/**
@@ -106,12 +110,12 @@ public static void logVMException(VMException ex, VirtualMachine vm) {
LOGGER.error(vm.getOperations().toJavaException(oop));
}
- public VirtualMachine getVirtualMachine() {
- return virtualMachine;
+ public VirtualMachine vm() {
+ return vm;
}
public VMInterface getVMInterface() {
- return virtualMachine.getInterface();
+ return vm.getInterface();
}
public MemoryManager getMemoryManager() {
@@ -127,38 +131,47 @@ public InvocationUtil getInvocationUtil() {
}
public Symbols getSymbols() {
- return virtualMachine.getSymbols();
+ return vm.getSymbols();
}
public Primitives getPrimitives() {
- return virtualMachine.getPrimitives();
+ return vm.getPrimitives();
}
public VMOperations getOperations() {
- return virtualMachine.getOperations();
+ return vm.getOperations();
}
public LinkResolver getLinkResolver() {
- return virtualMachine.getLinkResolver();
+ return vm.getLinkResolver();
}
public RuntimeResolver getRuntimeResolver() {
- return virtualMachine.getRuntimeResolver();
+ return vm.getRuntimeResolver();
}
public Reflection getReflection() {
- return virtualMachine.getReflection();
+ return vm.getReflection();
}
public ThreadManager getThreadManager() {
- return virtualMachine.getThreadManager();
+ return vm.getThreadManager();
}
public FileManager getFileManager() {
- return virtualMachine.getFileManager();
+ return vm.getFileManager();
}
public ExecutionEngine getExecutionEngine() {
- return virtualMachine.getExecutionEngine();
+ return vm.getExecutionEngine();
+ }
+
+ /**
+ * Gets all classes from {@link Context} that were used by sandbox
+ */
+ public List getUsedCustomClasses() {
+ return this.vm.getClassStorage().list().stream()
+ .filter(clazz -> this.context.getClasses().containsKey(clazz.getInternalName()))
+ .toList();
}
}
diff --git a/deobfuscator-impl/src/test/java/Bootstrap.java b/deobfuscator-impl/src/test/java/Bootstrap.java
index 8ad2e9b..cd3f1aa 100644
--- a/deobfuscator-impl/src/test/java/Bootstrap.java
+++ b/deobfuscator-impl/src/test/java/Bootstrap.java
@@ -1,4 +1,3 @@
-import dev.xdark.ssvm.VirtualMachine;
import java.nio.file.Path;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
@@ -12,10 +11,6 @@ public static void main(String[] args) {
Deobfuscator.from(
DeobfuscatorOptions.builder()
.inputJar(Path.of("work", "obf-test.jar"))
- .virtualMachine(
- new VirtualMachine() {
- // you can do shit
- })
.transformers(
// Pick your transformers here
() -> new ComposedGeneralFlowTransformer()
diff --git a/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/zkm/ZelixLongEncryptionTransformer.java b/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/zkm/ZelixLongEncryptionTransformer.java
index ba43252..410981d 100644
--- a/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/zkm/ZelixLongEncryptionTransformer.java
+++ b/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/zkm/ZelixLongEncryptionTransformer.java
@@ -18,9 +18,6 @@
import uwu.narumi.deobfuscator.api.helper.AsmHelper;
import uwu.narumi.deobfuscator.api.transformer.Transformer;
-import java.util.ArrayList;
-import java.util.List;
-
/**
* Decrypts {@code long} numbers https://www.zelix.com/klassmaster/featuresLongEncryption.html
*
@@ -53,7 +50,7 @@ public class ZelixLongEncryptionTransformer extends Transformer {
@Override
protected void transform(ClassWrapper scope, Context context) throws Exception {
- List classesToRemove = new ArrayList<>();
+ SandBox sandBox = new SandBox(context);
context.classes(scope).forEach(classWrapper -> classWrapper.findClInit().ifPresent(clinit -> {
MethodContext methodContext = MethodContext.create(classWrapper, clinit);
@@ -77,8 +74,6 @@ protected void transform(ClassWrapper scope, Context context) throws Exception {
ClassWrapper longDecrypterCreatorClass = context.getClasses().get(createDecrypterInsn.owner);
try {
- SandBox sandBox = context.getSandBox();
-
// Create decrypter
InstanceClass clazz = sandBox.getHelper().loadClass(longDecrypterCreatorClass.canonicalName());
ObjectValue longDecrypterInstance = sandBox.getInvocationUtil().invokeReference(
@@ -96,17 +91,6 @@ protected void transform(ClassWrapper scope, Context context) throws Exception {
Argument.int64(decryptKey)
);
- // Add classes to remove
- if (!classesToRemove.contains(longDecrypterCreatorClass.name())) {
- classesToRemove.add(longDecrypterCreatorClass.name());
- }
- if (!classesToRemove.contains(longDecrypterClass.getInternalName())) {
- classesToRemove.add(longDecrypterClass.getInternalName());
- }
- if (!classesToRemove.contains(longDecrypterCreatorClass.getClassNode().interfaces.get(0))) {
- classesToRemove.add(longDecrypterCreatorClass.getClassNode().interfaces.get(0));
- }
-
// Remove all instructions that creates decrypter
decryptContext.removeAll();
@@ -120,6 +104,6 @@ protected void transform(ClassWrapper scope, Context context) throws Exception {
}
}));
- classesToRemove.forEach(className -> context.getClasses().remove(className));
+ sandBox.getUsedCustomClasses().forEach(clazz -> context.getClasses().remove(clazz.getInternalName()));
}
}