Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compile and overwrite test #175

Merged
merged 16 commits into from
Oct 15, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 13 additions & 9 deletions runtime-decompiler/src/main/java/org/jrd/backend/data/Cli.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,21 +54,25 @@ public class Cli {
protected static final String HELP = "-help";
protected static final String H = "-h";

protected static final String R = "-r";
protected static final String P = "-p";
protected static final String CP = "-cp";

private final List<String> filteredArgs;
private final VmManager vmManager;
private final PluginManager pluginManager;
private Saving saving;
private boolean isVerbose;

protected static class Saving implements CommonUtils.StatusKeeper {
protected static final String DEFAULT = "default";
protected static final String EXACT = "exact";
protected static final String FQN = "fqn";
protected static final String DIR = "dir";
static class Saving implements CommonUtils.StatusKeeper {
static final String DEFAULT = "default";
static final String EXACT = "exact";
static final String FQN = "fqn";
static final String DIR = "dir";
private final String as;
private final String like;

public Saving(String as, String like) {
Saving(String as, String like) {
this.as = as;
if (like == null) {
this.like = DEFAULT;
Expand Down Expand Up @@ -299,13 +303,13 @@ private final class CompileArguments {
for (int i = 1; i < filteredArgs.size(); i++) {
String arg = filteredArgs.get(i);

if ("-p".equals(arg)) {
if (P.equals(arg)) {
wantedCustomCompiler = filteredArgs.get(i + 1);
i++; // shift
} else if ("-cp".equals(arg)) {
} else if (CP.equals(arg)) {
puc = filteredArgs.get(i + 1);
i++; // shift
} else if ("-r".equals(arg)) {
} else if (R.equals(arg)) {
isRecursive = true;
} else {
File fileToCompile = new File(arg);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public final class Help {
static final String LIST_JVMS_FORMAT = LIST_JVMS;
static final String LIST_PLUGINS_FORMAT = LIST_PLUGINS;
static final String LIST_CLASSES_FORMAT = LIST_CLASSES + " <PUC> [<CLASS REGEX>...]";
static final String COMPILE_FORMAT = COMPILE + " [-p <PLUGIN>] [-cp <PUC>] [-r] <PATH>...";
static final String COMPILE_FORMAT = COMPILE + " [" + P + " <PLUGIN>] [" + CP + " <PUC>] [" + R + "] <PATH>...";
static final String DECOMPILE_FORMAT = DECOMPILE + " <PUC> <PLUGIN> <CLASS REGEX>...";
static final String OVERWRITE_FORMAT = OVERWRITE + " <PUC> <FQN> [<CLASS FILE>]";
static final String INIT_FORMAT = INIT + " <PUC> <FQN>";
Expand All @@ -40,9 +40,9 @@ public final class Help {
private static final String LIST_CLASSES_TEXT = "List all loaded classes of a process, optionally filtering them.\n" +
"Only '" + SAVE_LIKE + " " + Saving.EXACT + "' or '" + SAVE_LIKE + " " + Saving.DEFAULT +
"' are allowed as saving modifiers.";
private static final String COMPILE_TEXT = "Compile local files against runtime classpath, specified by -cp.\n" +
"Use -p to utilize some plugins' (like jasm or jcoder) bundled compilers.\n" +
"Use -r for recursive search if <PATH> is a directory.\n" +
private static final String COMPILE_TEXT = "Compile local files against runtime classpath, specified by " + CP + ".\n" +
"Use " + P + " to utilize some plugins' (like jasm or jcoder) bundled compilers.\n" +
"Use " + R + " for recursive search if <PATH> is a directory.\n" +
"If the argument of '" + SAVE_AS + "' is a valid PID or URL, " +
"the compiled code will be attempted to be injected into that process.\n" +
"If multiple PATHs were specified, but no '" + SAVE_AS + "', the process fails.";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,13 @@

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.List;
Expand Down Expand Up @@ -191,4 +197,27 @@ public String getErr() {
return get(err);
}
}


public static String readBinaryAsString(File f) throws IOException {
try (FileInputStream fis = new FileInputStream(f)) {
return readBinaryAsString(fis, "UTF-8", CodingErrorAction.REPLACE);
}
}

public static String readBinaryAsString(FileInputStream input, String charBase, CodingErrorAction action) throws IOException {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused parameter action.

CharsetDecoder decoder = Charset.forName(charBase).newDecoder();
decoder.onMalformedInput(CodingErrorAction.IGNORE);
InputStreamReader reader = new InputStreamReader(input, decoder);
StringBuilder sb = new StringBuilder();
while (true) {
int i = reader.read();
if (i < 0) {
break;
}
sb.append((char) i);
}
reader.close();
return sb.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -393,9 +393,9 @@ private Stream<Arguments> tooFewArgumentsSource() {
new String[]{DECOMPILE, unimportantPid},
new String[]{DECOMPILE, unimportantPid, "javap"},
new String[]{COMPILE},
new String[]{COMPILE, "-r"},
new String[]{COMPILE, "-r", "-cp", unimportantPid},
new String[]{COMPILE, "-r", "-cp", unimportantPid, "-p", "unimportantPluginName"}
new String[]{COMPILE, R},
new String[]{COMPILE, R, CP, unimportantPid},
new String[]{COMPILE, R, CP, unimportantPid, Cli.P, "unimportantPluginName"}
).map(a -> (Object) a).map(Arguments::of); // cast needed because of varargs factory method .of()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@
import org.junit.jupiter.params.provider.ValueSource;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.Base64;
import java.util.stream.Collectors;

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class CompileUploadCliTest extends AbstractAgentNeedingTest {
Expand Down Expand Up @@ -236,4 +238,198 @@ void testOverwriteWarning() {
Assertions.assertTrue(output.contains("WARNING:"));
}


@Test
void testDecompileCompileCfr() throws Exception {
final String plugin = "Cfr";
File decompiledFile = File.createTempFile("jrd", "test.java");
args = new String[]{
Cli.DECOMPILE,
dummy.getPid(),
plugin,
dummy.getFqn(),
Cli.SAVE_LIKE, Cli.Saving.EXACT,
Cli.SAVE_AS, decompiledFile.getAbsolutePath()
};
cli = new Cli(args, model);
cli.consumeCli();
String sOrig = Files.readAllLines(
decompiledFile.toPath(), StandardCharsets.UTF_8).stream()
.collect(Collectors.joining("\n"));
String sNoCommnets = Files.readAllLines(
decompiledFile.toPath(), StandardCharsets.UTF_8).stream()
.filter(a -> !(a.trim().startsWith("/") || a.trim().startsWith("*")))
.collect(Collectors.joining("\n"));
assertEqualsWithTolerance(sOrig, sNoCommnets, 0.9);
assertEqualsWithTolerance(sNoCommnets, dummy.getDefaultContentWithPackage(), 0.9);

File compiledFile = File.createTempFile("jrd", "test.class");
args = new String[]{
Cli.COMPILE,
Cli.CP, dummy.getPid(),
decompiledFile.getAbsolutePath(),
Cli.SAVE_LIKE, Cli.Saving.EXACT,
Cli.SAVE_AS, compiledFile.getAbsolutePath()
};
cli = new Cli(args, model);
cli.consumeCli();
String compiled = readBinaryAsString(compiledFile);
String original = readBinaryAsString(new File(dummy.getDotClassPath()));
assertEqualsWithTolerance(compiled, original, 0.9);

args = new String[]{
Cli.OVERWRITE,
dummy.getPid(),
dummy.getFqn(),
compiledFile.getAbsolutePath()
};
cli = new Cli(args, model);
cli.consumeCli();

args = new String[]{
Cli.OVERWRITE,
dummy.getPid(),
dummy.getFqn(),
decompiledFile.getAbsolutePath()//nonsense, will not be accepted
};
cli = new Cli(args, model);
Exception ex = null;
try {
cli.consumeCli();
} catch (Exception eex) {
ex = eex;
}
Assertions.assertNotNull(ex);
}


@Test
void testDecompileCompileJasm() throws Exception {
final String plugin = "jasm";
File decompiledFile = File.createTempFile("jrd", "test.java");
args = new String[]{
Cli.DECOMPILE,
dummy.getPid(),
plugin,
dummy.getFqn(),
Cli.SAVE_LIKE, Cli.Saving.EXACT,
Cli.SAVE_AS, decompiledFile.getAbsolutePath()
};
cli = new Cli(args, model);
cli.consumeCli();
String sOrig = Files.readAllLines(decompiledFile.toPath(), StandardCharsets.UTF_8).stream().collect(Collectors.joining("\n"));
String sLine = Files.readAllLines(decompiledFile.toPath(), StandardCharsets.UTF_8).stream().collect(Collectors.joining(" "));
//unluckily there is nothing to compare to, unless we wish to call jasm from here "again"
//so at least some verifiers
Assertions.assertTrue(sOrig.contains("{"));
Assertions.assertTrue(sOrig.contains("}"));
Assertions.assertTrue(sOrig.contains("version"));
Assertions.assertTrue(sOrig.contains("invokevirtual"));
Assertions.assertTrue(sOrig.contains("invokestatic"));
Assertions.assertTrue(sOrig.contains("goto"));
Assertions.assertTrue(sLine.matches(".*package\\s+testing/modifiabledummy;.*"));
Assertions.assertTrue(sLine.matches(".*class\\s+TestingModifiableDummy.*"));
Assertions.assertTrue(sLine.matches(".*public\\s+Method\\s+\"<init>\".*"));
Assertions.assertTrue(sLine.matches(".*new\\s+class\\s+TestingModifiableDummy.*"));
Assertions.assertTrue(sLine.matches(".*private\\s+Method\\s+print.*"));
Assertions.assertTrue(sLine.matches(".*getstatic\\s+Field\\s+java/lang/System.out:\"Ljava/io/PrintStream;\";.*"));
Assertions.assertTrue(sLine.matches(".*ldc\\s+String\\s+\"Hello\";.*"));


File compiledFile = File.createTempFile("jrd", "test.class");
args = new String[]{
Cli.COMPILE,
Cli.CP, dummy.getPid(),
Cli.P, plugin,
decompiledFile.getAbsolutePath(),
Cli.SAVE_LIKE, Cli.Saving.EXACT,
Cli.SAVE_AS, compiledFile.getAbsolutePath()
};
cli = new Cli(args, model);
cli.consumeCli();
String compiled = readBinaryAsString(compiledFile);
String original = readBinaryAsString(new File(dummy.getDotClassPath()));
assertEqualsWithTolerance(compiled, original, 0.4); //yah, jasm performance is not greate

args = new String[]{
Cli.OVERWRITE,
dummy.getPid(),
dummy.getFqn(),
compiledFile.getAbsolutePath()
};
cli = new Cli(args, model);
cli.consumeCli();

args = new String[]{
Cli.OVERWRITE,
dummy.getPid(),
dummy.getFqn(),
decompiledFile.getAbsolutePath() //some nonsense, should fail
};
cli = new Cli(args, model);
Exception ex = null;
try {
cli.consumeCli();
} catch (Exception eex) {
ex = eex;
}
Assertions.assertNotNull(ex);
}

@Test
void testDecompileCompileJcoder() throws Exception {
final String plugin = "jcoder";
File decompiledFile = File.createTempFile("jrd", "test.java");
args = new String[]{
Cli.DECOMPILE,
dummy.getPid(),
plugin,
dummy.getFqn(),
Cli.SAVE_LIKE, Cli.Saving.EXACT,
Cli.SAVE_AS, decompiledFile.getAbsolutePath()
};
cli = new Cli(args, model);
cli.consumeCli();
String sOrig = Files.readAllLines(decompiledFile.toPath(), StandardCharsets.UTF_8).stream().collect(Collectors.joining("\n"));
//unluckily there is nothing to compare to, unless we wish to call jasm from here "again"

File compiledFile = File.createTempFile("jrd", "test.class");
args = new String[]{
Cli.COMPILE,
Cli.CP, dummy.getPid(),
Cli.P, plugin,
decompiledFile.getAbsolutePath(),
Cli.SAVE_LIKE, Cli.Saving.EXACT,
Cli.SAVE_AS, compiledFile.getAbsolutePath()
};
cli = new Cli(args, model);
cli.consumeCli();
String compiled = readBinaryAsString(compiledFile);
String original = readBinaryAsString(new File(dummy.getDotClassPath()));
assertEqualsWithTolerance(compiled, original, 0.4); //yah, jasm performance is not greate

args = new String[]{
Cli.OVERWRITE,
dummy.getPid(),
dummy.getFqn(),
compiledFile.getAbsolutePath()
};
cli = new Cli(args, model);
cli.consumeCli();

args = new String[]{
Cli.OVERWRITE,
dummy.getPid(),
dummy.getFqn(),
decompiledFile.getAbsolutePath() //some nonsense, should fail
};
cli = new Cli(args, model);
Exception ex = null;
try {
cli.consumeCli();
} catch (Exception eex) {
ex = eex;
}
Assertions.assertNotNull(ex);
}
}