Skip to content

Commit

Permalink
Add decompiled dumps and dump remapping
Browse files Browse the repository at this point in the history
  • Loading branch information
unascribed committed May 20, 2022
1 parent 3068526 commit ccfa9c1
Show file tree
Hide file tree
Showing 3 changed files with 249 additions and 23 deletions.
9 changes: 8 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,20 @@ targetCompatibility = 8

group = "com.unascribed"
archivesBaseName = "NilLoader"
version = "1.1.4"
version = "1.1.5"

tasks.withType(JavaCompile) {
options.release = 8
}

repositories {
mavenCentral()
maven {
url 'https://maven.quiltmc.org/repository/release/'
content {
includeGroup 'org.quiltmc'
}
}
}

configurations {
Expand All @@ -35,6 +41,7 @@ dependencies {

shade 'com.grack:nanojson:1.7'

compileOnly 'org.quiltmc:quiltflower:1.8.1'
compileOnly 'org.slf4j:slf4j-api:1.7.9'
compileOnly 'org.apache.logging.log4j:log4j-api:2.17.1'
compileOnly 'log4j:log4j:1.2.17'
Expand Down
169 changes: 169 additions & 0 deletions src/main/java/nilloader/Decompiler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
package nilloader;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.jar.JarFile;
import java.util.jar.Manifest;

import org.jetbrains.java.decompiler.main.Fernflower;
import org.jetbrains.java.decompiler.main.extern.IBytecodeProvider;
import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;
import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
import org.jetbrains.java.decompiler.main.extern.IResultSaver;

import nilloader.api.NilLogger;

class Decompiler {

private interface QuiltflowerAccess {
String decompile(String name, byte[] clazz);
}

private static final class QuiltflowerAccessImpl implements QuiltflowerAccess {

private final NilLogger log = NilLogger.get("Quiltflower");

@Override
public String decompile(String name, byte[] clazz) {
Map<String, Object> options = new HashMap<>();
options.put(IFernflowerPreferences.INCLUDE_ENTIRE_CLASSPATH, true);
options.put(IFernflowerPreferences.INCLUDE_JAVA_RUNTIME, true);
options.put(IFernflowerPreferences.THREADS, 4);
options.put(IFernflowerPreferences.INDENT_STRING, "\t");
options.put(IFernflowerPreferences.FINALLY_DEINLINE, true);
options.put(IFernflowerPreferences.USE_METHOD_PARAMETERS, true);
options.put(IFernflowerPreferences.USE_DEBUG_VAR_NAMES, true);
options.put(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES, true);
String[] contentArr = new String[] { "// decompilation failed" };
Fernflower ff = new Fernflower(new IBytecodeProvider() {

@Override
public byte[] getBytecode(String externalPath, String internalPath) throws IOException {
if (externalPath != null && externalPath.endsWith(".nil/bad-fernflower-workaround/this-file-does-not-exist.class")) {
return clazz;
}
return new byte[0];
}
}, new IResultSaver() {
@Override public void saveFolder(String path) {}
@Override public void saveDirEntry(String path, String archiveName, String entryName) {}
@Override public void saveClassFile(String path, String qualifiedName, String entryName, String content, int[] mapping) {
contentArr[0] = content;
}
@Override public void saveClassEntry(String path, String archiveName, String qualifiedName, String entryName, String content) {
contentArr[0] = content;
}
@Override public void createArchive(String path, String archiveName, Manifest manifest) {}
@Override public void copyFile(String source, String path, String entryName) {}
@Override public void copyEntry(String source, String path, String archiveName, String entry) {}
@Override public void closeArchive(String path, String archiveName) {}
}, options, new IFernflowerLogger() {

@Override
public void writeMessage(String message, Severity severity, Throwable t) {
if (severity == Severity.WARN) {
log.warn(message, t);
} else if (severity == Severity.ERROR) {
log.error(message, t);
}
}

@Override
public void writeMessage(String message, Severity severity) {
if (severity == Severity.WARN) {
log.warn(message);
} else if (severity == Severity.ERROR) {
log.error(message);
}
}

});
try {
ff.addSource(new File(".nil/bad-fernflower-workaround/this-file-does-not-exist.class"));
try {
ff.decompileContext();
} finally {
ff.clearContext();
}
return contentArr[0];
} catch (Throwable e) {
log.error("Failed to decompile", e);
return "// decompilation failed";
}
}

}

private static boolean failure = false;
private static QuiltflowerAccess access = null;

static void initialize() {
try {
File dotNil = new File(".nil");
File quiltflower = new File(dotNil, "quiltflower.jar");
if (!quiltflower.exists()) {
NilLoaderLog.log.info("Downloading Quiltflower...");
try {
URL u = new URL("https://maven.quiltmc.org/repository/release/org/quiltmc/quiltflower/1.8.1/quiltflower-1.8.1.jar");
String expectedHashB64 = "79hnFEabe9+TEz1KefC7gcugcmuCcz3N1Ok/80kolcs=";
byte[] expectedHash = Base64.getDecoder().decode(expectedHashB64);
int expectedSize = 874457;
dotNil.mkdirs();
File tmp = File.createTempFile("quiltflower", ".jar.part", dotNil);
byte[] buf = new byte[4096];
MessageDigest digest = MessageDigest.getInstance("SHA-256");
int realSize = 0;
try (InputStream in = u.openStream(); OutputStream out = new FileOutputStream(tmp)) {
while (true) {
int read = in.read(buf);
if (read == -1) break;
realSize += read;
if (realSize > expectedSize) throw new IOException("File is longer than expected length "+expectedSize);
out.write(buf, 0, read);
digest.update(buf, 0, read);
}
}
byte[] realHash = digest.digest();
if (!Arrays.equals(expectedHash, realHash)) {
throw new IOException("File hash is incorrect (got "+Base64.getEncoder().encodeToString(realHash)+", but expected "+expectedHashB64+")");
}
tmp.renameTo(quiltflower);
} catch (IOException | NoSuchAlgorithmException e) {
NilLoaderLog.log.error("Failed to download Quiltflower", e);
failure = true;
return;
}
}
NilLoader.injectToSearchPath(new JarFile(quiltflower));
access = new QuiltflowerAccessImpl();
} catch (Throwable t) {
NilLoaderLog.log.error("Failed to load Quiltflower", t);
failure = true;
return;
}
}

static void perform(String name, byte[] before, byte[] after) {
try {
if (failure) return;
String beforeCode = access.decompile(name, before);
String afterCode = access.decompile(name, after);
NilLoader.writeDump(name, beforeCode.getBytes(StandardCharsets.UTF_8), "before", "java");
NilLoader.writeDump(name, afterCode.getBytes(StandardCharsets.UTF_8), "after", "java");
} catch (Throwable t) {
NilLoaderLog.log.error("Failed to perform decompile diff for {}", name, t);
}
}

}
Loading

0 comments on commit ccfa9c1

Please sign in to comment.