Skip to content

Commit

Permalink
Implement remote JDK usage for SSVM boot classpath
Browse files Browse the repository at this point in the history
  • Loading branch information
Col-E committed Feb 3, 2024
1 parent 0dd20eb commit 382ff62
Show file tree
Hide file tree
Showing 7 changed files with 56 additions and 14 deletions.
3 changes: 2 additions & 1 deletion dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ def asmVersion = '9.6'
def cafeDudeVersion = '2.0.1'
def junitVersion = '5.10.0'
def jasmVersion = '518b763f37'
def ssvmVersion = 'vm-copying-scopes-SNAPSHOT'
def ssvmVersion = 'aba637d196'

project.ext {
asm = "org.ow2.asm:asm:$asmVersion"
Expand Down Expand Up @@ -72,6 +72,7 @@ project.ext {
ssvm_core = "com.github.xxDark.SSVM:ssvm-core:$ssvmVersion"
ssvm_invoke = "com.github.xxDark.SSVM:ssvm-invoke:$ssvmVersion"
ssvm_io = "com.github.xxDark.SSVM:ssvm-io:$ssvmVersion"
ssvm_server = 'software.coley:class-server:1.0.0'

treemapfx = 'software.coley:treemap-fx:1.1.0'

Expand Down
1 change: 1 addition & 0 deletions recaf-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ dependencies {
api ssvm_core
api ssvm_invoke
api ssvm_io
api ssvm_server
api vineflower
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ public final class ConfigGroups {
* Group for specific decompiler components.
*/
public static final String SERVICE_DECOMPILE_IMPL = SERVICE_DECOMPILE + PACKAGE_SPLIT + "impl";
/**
* Group for deobfuscation components.
*/
public static final String SERVICE_DEOBFUSCATION = SERVICE + PACKAGE_SPLIT + "deobfuscation";
/**
* Group for IO components.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,6 @@ public static BuilderStage0 withinVM(@Nonnull VirtualMachine vm) {
return new BuilderStage0(vm);
}


/**
* Initial stage for configuring classloader options within the VM.
*/
Expand Down Expand Up @@ -354,6 +353,9 @@ public BuilderStage1 withSameClassloaderAs(@Nonnull JavaClass classWithinLoader)
}
}

/**
* Second stage for configuring initialization behavior and which method is to be invoked.
*/
public static class BuilderStage1 {
private final VirtualMachine vm;
private final JavaClass classWithinLoader;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,29 @@
import dev.xdark.ssvm.api.MethodInvoker;
import dev.xdark.ssvm.api.VMInterface;
import dev.xdark.ssvm.classloading.BootClassFinder;
import dev.xdark.ssvm.classloading.ParsedClassData;
import dev.xdark.ssvm.classloading.SupplyingClassLoader;
import dev.xdark.ssvm.execution.Interpreter;
import dev.xdark.ssvm.execution.Result;
import dev.xdark.ssvm.filesystem.*;
import dev.xdark.ssvm.filesystem.DelegatingFileManager;
import dev.xdark.ssvm.filesystem.FileManager;
import dev.xdark.ssvm.filesystem.HostFileManager;
import dev.xdark.ssvm.filesystem.SimpleFileManager;
import dev.xdark.ssvm.invoke.InvocationUtil;
import dev.xdark.ssvm.mirror.type.InstanceClass;
import dev.xdark.ssvm.operation.VMOperations;
import dev.xdark.ssvm.timezone.SimpleTimeManager;
import dev.xdark.ssvm.timezone.TimeManager;
import dev.xdark.ssvm.util.ClassUtil;
import dev.xdark.ssvm.value.InstanceValue;
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.tree.ClassNode;
import software.coley.classserver.JdkProperties;
import software.coley.classserver.JdkResourcesServer;
import software.coley.recaf.analytics.logging.DebuggingLogger;
import software.coley.recaf.analytics.logging.Logging;
import software.coley.recaf.path.ClassPathNode;
Expand All @@ -30,7 +39,10 @@

import java.io.IOException;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
Expand Down Expand Up @@ -200,7 +212,7 @@ public boolean refreshVm() {
} catch (Throwable t) {
cvm = null;
cvmInitFail = t;
logger.warn("Failed to initialize VM. Current ");
logger.warn("Failed to initialize VM. VM based abilities will be unavailable.");

Check warning on line 215 in recaf-core/src/main/java/software/coley/recaf/services/vm/CommonVirtualService.java

View check run for this annotation

Codecov / codecov/patch

recaf-core/src/main/java/software/coley/recaf/services/vm/CommonVirtualService.java#L212-L215

Added lines #L212 - L215 were not covered by tests
}
vm = cvm;
vmInitFailure = cvmInitFail;
Expand Down Expand Up @@ -340,14 +352,36 @@ public InvocationUtil getInvocationUtil() {

@Override
protected BootClassFinder createBootClassFinder() {
if (config.useHostFileManager().getValue()) {
String jdkPath = config.getAlternateJdkPath().getValue();
// TODO: If path exists, create a remote boot-class finder
// - Should spin up a JVM of that version, and then we contact it over a socket
// - This is done in 3x with SSVM 1.X and needs to be ported over
// - Can be handled similar to how our instrumentation server works (self-extracting stub)
logger.warn("Alternative boot path not yet supported");
if (config.useAlternateJdkBootPath().getValue()) {
Path jdkPath = Paths.get(config.getAlternateJdkPath().getValue());

Check warning on line 356 in recaf-core/src/main/java/software/coley/recaf/services/vm/CommonVirtualService.java

View check run for this annotation

Codecov / codecov/patch

recaf-core/src/main/java/software/coley/recaf/services/vm/CommonVirtualService.java#L356

Added line #L356 was not covered by tests
try {
// SSVM uses the VM properties to detect what version it is targeting.
// We must fetch the properties and update our values to match.
JdkProperties remoteProperties = JdkProperties.getProperties(jdkPath);
var env = getenv();
var properties = getProperties();
env.clear();
env.putAll(remoteProperties.getEnvironment());
properties.clear();
properties.putAll(remoteProperties.getSystemProperties());

Check warning on line 366 in recaf-core/src/main/java/software/coley/recaf/services/vm/CommonVirtualService.java

View check run for this annotation

Codecov / codecov/patch

recaf-core/src/main/java/software/coley/recaf/services/vm/CommonVirtualService.java#L360-L366

Added lines #L360 - L366 were not covered by tests

// Initialize the resource server and create a class finder with it.
JdkResourcesServer server = JdkResourcesServer.start(jdkPath);
return name -> {

Check warning on line 370 in recaf-core/src/main/java/software/coley/recaf/services/vm/CommonVirtualService.java

View check run for this annotation

Codecov / codecov/patch

recaf-core/src/main/java/software/coley/recaf/services/vm/CommonVirtualService.java#L369-L370

Added lines #L369 - L370 were not covered by tests
try {
byte[] bytes = server.requestResource(name + ".class");
ClassReader cr = new ClassReader(bytes);
ClassNode node = ClassUtil.readNode(cr);
return new ParsedClassData(cr, node);
} catch (Throwable t) {
return null;

Check warning on line 377 in recaf-core/src/main/java/software/coley/recaf/services/vm/CommonVirtualService.java

View check run for this annotation

Codecov / codecov/patch

recaf-core/src/main/java/software/coley/recaf/services/vm/CommonVirtualService.java#L372-L377

Added lines #L372 - L377 were not covered by tests
}
};
} catch (IOException ex) {
logger.error("Failed to create remote server for JDK classes using '{}'", jdkPath, ex);

Check warning on line 381 in recaf-core/src/main/java/software/coley/recaf/services/vm/CommonVirtualService.java

View check run for this annotation

Codecov / codecov/patch

recaf-core/src/main/java/software/coley/recaf/services/vm/CommonVirtualService.java#L380-L381

Added lines #L380 - L381 were not covered by tests
}
}

return super.createBootClassFinder();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import software.coley.recaf.info.member.MethodMember;
import software.coley.recaf.test.TestBase;
import software.coley.recaf.test.dummy.ArrayStuff;
import software.coley.recaf.test.dummy.unoptimized.CountTo100;
import software.coley.recaf.test.dummy.CountTo100;
import software.coley.recaf.workspace.WorkspaceManager;
import software.coley.recaf.workspace.model.Workspace;

Expand Down Expand Up @@ -107,7 +107,7 @@ void testBasicServiceWithArgs() throws VmUnavailableException {
Argument[] arguments = ArgumentBuilder.withinVM(commonVirtualService.getSharedVm())
.withClassloader(helper.getClassLoaderInstance())
.forMethod(declaringClass, methodTarget)
.add(new String[] { "0 1 2 3 4 5 6" })
.add(new String[]{"0 1 2 3 4 5 6"})
.build();

try {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package software.coley.recaf.test.dummy.unoptimized;
package software.coley.recaf.test.dummy;

@SuppressWarnings("all")
public class CountTo100 {
Expand Down

0 comments on commit 382ff62

Please sign in to comment.