Add initial support for whole program analysis in transformers

pull/48/head
Graham 5 years ago
parent 15ccc70c37
commit 0c8d0f9cc3
  1. 31
      asm/src/main/java/dev/openrs2/asm/Transformer.java
  2. 15
      asm/src/main/java/dev/openrs2/asm/classpath/ClassPath.java
  3. 77
      deob/src/main/java/dev/openrs2/deob/Deobfuscator.java
  4. 9
      deob/src/main/java/dev/openrs2/deob/remap/ClassNamePrefixer.java
  5. 6
      deob/src/main/java/dev/openrs2/deob/transform/BitShiftTransformer.java
  6. 22
      deob/src/main/java/dev/openrs2/deob/transform/CounterTransformer.java
  7. 6
      deob/src/main/java/dev/openrs2/deob/transform/ExceptionTracingTransformer.java
  8. 15
      deob/src/main/java/dev/openrs2/deob/transform/OpaquePredicateTransformer.java

@ -1,5 +1,6 @@
package dev.openrs2.asm;
import dev.openrs2.asm.classpath.ClassPath;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldNode;
@ -7,29 +8,31 @@ import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.analysis.AnalyzerException;
public abstract class Transformer {
public void transform(Library library) throws AnalyzerException {
preTransform(library);
public void transform(ClassPath classPath) throws AnalyzerException {
preTransform(classPath);
for (var clazz : library) {
transformClass(clazz);
for (var library : classPath.getLibraries()) {
for (var clazz : library) {
transformClass(clazz);
for (var field : clazz.fields) {
transformField(clazz, field);
}
for (var field : clazz.fields) {
transformField(clazz, field);
}
for (var method : clazz.methods) {
transformMethod(clazz, method);
for (var method : clazz.methods) {
transformMethod(clazz, method);
if ((method.access & (Opcodes.ACC_NATIVE | Opcodes.ACC_ABSTRACT)) == 0) {
transformCode(clazz, method);
if ((method.access & (Opcodes.ACC_NATIVE | Opcodes.ACC_ABSTRACT)) == 0) {
transformCode(clazz, method);
}
}
}
}
postTransform(library);
postTransform(classPath);
}
public void preTransform(Library library) throws AnalyzerException {
public void preTransform(ClassPath classPath) throws AnalyzerException {
/* empty */
}
@ -49,7 +52,7 @@ public abstract class Transformer {
/* empty */
}
public void postTransform(Library library) throws AnalyzerException {
public void postTransform(ClassPath classPath) throws AnalyzerException {
/* empty */
}
}

@ -13,6 +13,7 @@ import dev.openrs2.asm.MemberDesc;
import dev.openrs2.asm.MemberRef;
import dev.openrs2.util.collect.DisjointSet;
import dev.openrs2.util.collect.ForestDisjointSet;
import org.objectweb.asm.tree.ClassNode;
public final class ClassPath {
private final ClassLoader runtime;
@ -25,6 +26,10 @@ public final class ClassPath {
this.libraries = libraries;
}
public List<Library> getLibraries() {
return libraries;
}
public List<ClassMetadata> getLibraryClasses() {
var classes = new ArrayList<ClassMetadata>();
@ -75,6 +80,16 @@ public final class ClassPath {
return metadata;
}
public ClassNode getNode(String name) {
for (var library : libraries) {
var clazz = library.get(name);
if (clazz != null) {
return clazz;
}
}
return null;
}
public DisjointSet<MemberRef> createInheritedFieldSets() {
var disjointSet = new ForestDisjointSet<MemberRef>();
var ancestorCache = new HashMap<ClassMetadata, Set<MemberDesc>>();

@ -5,7 +5,6 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Map;
import dev.openrs2.asm.Library;
import dev.openrs2.asm.Transformer;
@ -67,27 +66,6 @@ public final class Deobfuscator {
logger.info("Moving signed classes from loader_gl to runescape_gl");
var glSignedClasses = SignedClassSet.create(glLoader, glClient);
/* deobfuscate */
var allLibraries = Map.of(
"unpacker", unpacker,
"unpacker_gl", glUnpacker,
"loader", loader,
"loader_gl", glLoader,
"jaggl", gl,
"runescape", client,
"runescape_gl", glClient,
"runescape_unsigned", unsignedClient
);
for (var entry : allLibraries.entrySet()) {
logger.info("Transforming library {}", entry.getKey());
for (var transformer : TRANSFORMERS) {
logger.info("Running transformer {}", transformer.getClass().getSimpleName());
transformer.transform(entry.getValue());
}
}
/* move unpack class out of the loader (so the unpacker and loader can both depend on it) */
logger.info("Moving unpack from loader to unpack");
var unpack = new Library();
@ -113,45 +91,54 @@ public final class Deobfuscator {
ClassNamePrefixer.addPrefix(unpacker, "unpacker_");
ClassNamePrefixer.addPrefix(glUnpacker, "unpacker_");
/* remap all class, method and field names */
logger.info("Creating remappers");
/* bundle libraries together into a common classpath */
var runtime = ClassLoader.getPlatformClassLoader();
var classPath = new ClassPath(runtime, List.of(), List.of(client, loader, signLink, unpack, unpacker));
var glClassPath = new ClassPath(runtime, List.of(gl), List.of(glClient, glLoader, glSignLink, glUnpack, glUnpacker));
var unsignedClassPath = new ClassPath(runtime, List.of(), List.of(unsignedClient));
var libraries = List.of(client, loader, signLink, unpack, unpacker);
var remapper = TypedRemapper.create(new ClassPath(runtime, List.of(), libraries));
/* deobfuscate */
logger.info("Transforming client");
for (var transformer : TRANSFORMERS) {
logger.info("Running transformer {}", transformer.getClass().getSimpleName());
transformer.transform(classPath);
}
logger.info("Transforming client_gl");
for (var transformer : TRANSFORMERS) {
logger.info("Running transformer {}", transformer.getClass().getSimpleName());
transformer.transform(glClassPath);
}
var glLibraries = List.of(glClient, glLoader, glSignLink, glUnpack, glUnpacker);
var glRemapper = TypedRemapper.create(new ClassPath(runtime, List.of(gl), glLibraries));
logger.info("Transforming client_unsigned");
for (var transformer : TRANSFORMERS) {
logger.info("Running transformer {}", transformer.getClass().getSimpleName());
transformer.transform(unsignedClassPath);
}
var unsignedRemapper = TypedRemapper.create(new ClassPath(runtime, List.of(), List.of(unsignedClient)));
/* remap all class, method and field names */
logger.info("Creating remappers");
var remapper = TypedRemapper.create(classPath);
var glRemapper = TypedRemapper.create(glClassPath);
var unsignedRemapper = TypedRemapper.create(unsignedClassPath);
/* transform Class.forName() calls */
logger.info("Transforming Class.forName() calls");
Transformer transformer = new ClassForNameTransformer(remapper);
for (var library : libraries) {
transformer.transform(library);
}
transformer.transform(classPath);
transformer = new ClassForNameTransformer(glRemapper);
for (var library : glLibraries) {
transformer.transform(library);
}
transformer.transform(glClassPath);
transformer = new ClassForNameTransformer(unsignedRemapper);
transformer.transform(unsignedClient);
transformer.transform(unsignedClassPath);
/* add @OriginalName annotations */
logger.info("Annotating classes and members with original names");
transformer = new OriginalNameTransformer();
for (var library : libraries) {
transformer.transform(library);
}
for (var library : glLibraries) {
transformer.transform(library);
}
transformer.transform(unsignedClient);
transformer.transform(classPath);
transformer.transform(glClassPath);
transformer.transform(unsignedClassPath);
/* write output jars */
logger.info("Writing output jars");

@ -4,6 +4,7 @@ import java.util.HashMap;
import dev.openrs2.asm.Library;
import dev.openrs2.deob.transform.ClassForNameTransformer;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.commons.ClassRemapper;
import org.objectweb.asm.commons.SimpleRemapper;
import org.objectweb.asm.tree.ClassNode;
@ -22,7 +23,13 @@ public final class ClassNamePrefixer {
var remapper = new SimpleRemapper(mapping);
var transformer = new ClassForNameTransformer(remapper);
transformer.transform(library);
for (var clazz : library) {
for (var method : clazz.methods) {
if ((method.access & (Opcodes.ACC_NATIVE | Opcodes.ACC_ABSTRACT)) == 0) {
transformer.transformCode(clazz, method);
}
}
}
for (var name : mapping.keySet()) {
var in = library.remove(name);

@ -4,8 +4,8 @@ import java.util.Set;
import dev.openrs2.asm.InsnMatcher;
import dev.openrs2.asm.InsnNodeUtils;
import dev.openrs2.asm.Library;
import dev.openrs2.asm.Transformer;
import dev.openrs2.asm.classpath.ClassPath;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
@ -21,7 +21,7 @@ public final class BitShiftTransformer extends Transformer {
private int simplified;
@Override
public void preTransform(Library library) {
public void preTransform(ClassPath classPath) {
simplified = 0;
}
@ -42,7 +42,7 @@ public final class BitShiftTransformer extends Transformer {
}
@Override
public void postTransform(Library library) {
public void postTransform(ClassPath classPath) {
logger.info("Simplified {} bit shifts", simplified);
}
}

@ -6,9 +6,9 @@ import java.util.Map;
import java.util.Set;
import dev.openrs2.asm.InsnMatcher;
import dev.openrs2.asm.Library;
import dev.openrs2.asm.MemberRef;
import dev.openrs2.asm.Transformer;
import dev.openrs2.asm.classpath.ClassPath;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
@ -26,22 +26,24 @@ public final class CounterTransformer extends Transformer {
private final Set<MemberRef> counters = new HashSet<>();
@Override
public void preTransform(Library library) {
public void preTransform(ClassPath classPath) {
counters.clear();
var references = new HashMap<MemberRef, Integer>();
var resets = new HashMap<MemberRef, Integer>();
var increments = new HashMap<MemberRef, Integer>();
for (var clazz : library) {
for (var method : clazz.methods) {
if ((method.access & (Opcodes.ACC_NATIVE | Opcodes.ACC_ABSTRACT)) == 0) {
findCounters(method, references, resets, increments);
for (var library : classPath.getLibraries()) {
for (var clazz : library) {
for (var method : clazz.methods) {
if ((method.access & (Opcodes.ACC_NATIVE | Opcodes.ACC_ABSTRACT)) == 0) {
findCounters(method, references, resets, increments);
}
}
}
}
deleteCounters(library, references, resets, increments);
deleteCounters(classPath, references, resets, increments);
}
private void findCounters(MethodNode method, Map<MemberRef, Integer> references, Map<MemberRef, Integer> resets, Map<MemberRef, Integer> increments) {
@ -68,7 +70,7 @@ public final class CounterTransformer extends Transformer {
});
}
private void deleteCounters(Library library, Map<MemberRef, Integer> references, Map<MemberRef, Integer> resets, Map<MemberRef, Integer> increments) {
private void deleteCounters(ClassPath classPath, Map<MemberRef, Integer> references, Map<MemberRef, Integer> resets, Map<MemberRef, Integer> increments) {
for (Map.Entry<MemberRef, Integer> entry : references.entrySet()) {
var counter = entry.getKey();
@ -84,7 +86,7 @@ public final class CounterTransformer extends Transformer {
continue;
}
ClassNode owner = library.get(counter.getOwner());
ClassNode owner = classPath.getNode(counter.getOwner());
owner.fields.removeIf(f -> f.name.equals(counter.getName()) && f.desc.equals(counter.getDesc()));
counters.add(counter);
@ -112,7 +114,7 @@ public final class CounterTransformer extends Transformer {
}
@Override
public void postTransform(Library library) {
public void postTransform(ClassPath classPath) {
logger.info("Removed {} counters", counters.size());
}
}

@ -2,8 +2,8 @@ package dev.openrs2.deob.transform;
import dev.openrs2.asm.InsnMatcher;
import dev.openrs2.asm.InsnNodeUtils;
import dev.openrs2.asm.Library;
import dev.openrs2.asm.Transformer;
import dev.openrs2.asm.classpath.ClassPath;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
import org.slf4j.Logger;
@ -17,7 +17,7 @@ public final class ExceptionTracingTransformer extends Transformer {
private int tracingTryCatches;
@Override
public void preTransform(Library library) {
public void preTransform(ClassPath classPath) {
tracingTryCatches = 0;
}
@ -39,7 +39,7 @@ public final class ExceptionTracingTransformer extends Transformer {
}
@Override
public void postTransform(Library library) {
public void postTransform(ClassPath classPath) {
logger.info("Removed {} tracing try/catch blocks", tracingTryCatches);
}
}

@ -8,6 +8,7 @@ import dev.openrs2.asm.InsnMatcher;
import dev.openrs2.asm.Library;
import dev.openrs2.asm.MemberRef;
import dev.openrs2.asm.Transformer;
import dev.openrs2.asm.classpath.ClassPath;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
@ -29,15 +30,17 @@ public final class OpaquePredicateTransformer extends Transformer {
private int opaquePredicates, stores;
@Override
public void preTransform(Library library) {
public void preTransform(ClassPath classPath) {
flowObstructors.clear();
opaquePredicates = 0;
stores = 0;
for (var clazz : library) {
for (var method : clazz.methods) {
if ((method.access & (Opcodes.ACC_NATIVE | Opcodes.ACC_ABSTRACT)) == 0) {
findFlowObstructors(library, method);
for (var library : classPath.getLibraries()) {
for (var clazz : library) {
for (var method : clazz.methods) {
if ((method.access & (Opcodes.ACC_NATIVE | Opcodes.ACC_ABSTRACT)) == 0) {
findFlowObstructors(library, method);
}
}
}
}
@ -113,7 +116,7 @@ public final class OpaquePredicateTransformer extends Transformer {
}
@Override
public void postTransform(Library library) {
public void postTransform(ClassPath classPath) {
logger.info("Removed {} opaque predicates and {} redundant stores", opaquePredicates, stores);
}
}

Loading…
Cancel
Save