Skip to content

Commit

Permalink
make sandbox global and for per instance usage. This fixes ZelixLongE…
Browse files Browse the repository at this point in the history
…ncryptionTransformer not removing decrypter classes correctly
  • Loading branch information
EpicPlayerA10 committed Sep 11, 2024
1 parent 88c44a4 commit 6af601c
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 92 deletions.
Original file line number Diff line number Diff line change
@@ -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}.
* <br>
* 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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -70,8 +68,6 @@ public static class Builder {
private boolean continueOnError = false;
private boolean verifyBytecode = false;

private VirtualMachine virtualMachine = null;

private Builder() {
}

Expand Down Expand Up @@ -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
*/
Expand Down Expand Up @@ -240,9 +230,7 @@ public DeobfuscatorOptions build() {
// Other config
printStacktraces,
continueOnError,
verifyBytecode,

virtualMachine
verifyBytecode
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/**
Expand All @@ -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() {
Expand All @@ -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<JavaClass> getUsedCustomClasses() {
return this.vm.getClassStorage().list().stream()
.filter(clazz -> this.context.getClasses().containsKey(clazz.getInternalName()))
.toList();
}
}
5 changes: 0 additions & 5 deletions deobfuscator-impl/src/test/java/Bootstrap.java
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import dev.xdark.ssvm.VirtualMachine;
import java.nio.file.Path;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
Expand All @@ -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()
Expand Down
Loading

0 comments on commit 6af601c

Please sign in to comment.