Add initial support for whole program analysis in transformers

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

@ -13,6 +13,7 @@ import dev.openrs2.asm.MemberDesc;
import dev.openrs2.asm.MemberRef; import dev.openrs2.asm.MemberRef;
import dev.openrs2.util.collect.DisjointSet; import dev.openrs2.util.collect.DisjointSet;
import dev.openrs2.util.collect.ForestDisjointSet; import dev.openrs2.util.collect.ForestDisjointSet;
import org.objectweb.asm.tree.ClassNode;
public final class ClassPath { public final class ClassPath {
private final ClassLoader runtime; private final ClassLoader runtime;
@ -25,6 +26,10 @@ public final class ClassPath {
this.libraries = libraries; this.libraries = libraries;
} }
public List<Library> getLibraries() {
return libraries;
}
public List<ClassMetadata> getLibraryClasses() { public List<ClassMetadata> getLibraryClasses() {
var classes = new ArrayList<ClassMetadata>(); var classes = new ArrayList<ClassMetadata>();
@ -75,6 +80,16 @@ public final class ClassPath {
return metadata; 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() { public DisjointSet<MemberRef> createInheritedFieldSets() {
var disjointSet = new ForestDisjointSet<MemberRef>(); var disjointSet = new ForestDisjointSet<MemberRef>();
var ancestorCache = new HashMap<ClassMetadata, Set<MemberDesc>>(); 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.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.List; import java.util.List;
import java.util.Map;
import dev.openrs2.asm.Library; import dev.openrs2.asm.Library;
import dev.openrs2.asm.Transformer; import dev.openrs2.asm.Transformer;
@ -67,27 +66,6 @@ public final class Deobfuscator {
logger.info("Moving signed classes from loader_gl to runescape_gl"); logger.info("Moving signed classes from loader_gl to runescape_gl");
var glSignedClasses = SignedClassSet.create(glLoader, glClient); 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) */ /* 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"); logger.info("Moving unpack from loader to unpack");
var unpack = new Library(); var unpack = new Library();
@ -113,45 +91,54 @@ public final class Deobfuscator {
ClassNamePrefixer.addPrefix(unpacker, "unpacker_"); ClassNamePrefixer.addPrefix(unpacker, "unpacker_");
ClassNamePrefixer.addPrefix(glUnpacker, "unpacker_"); ClassNamePrefixer.addPrefix(glUnpacker, "unpacker_");
/* remap all class, method and field names */ /* bundle libraries together into a common classpath */
logger.info("Creating remappers");
var runtime = ClassLoader.getPlatformClassLoader(); 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); /* deobfuscate */
var remapper = TypedRemapper.create(new ClassPath(runtime, List.of(), libraries)); 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); logger.info("Transforming client_unsigned");
var glRemapper = TypedRemapper.create(new ClassPath(runtime, List.of(gl), glLibraries)); 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 */ /* transform Class.forName() calls */
logger.info("Transforming Class.forName() calls"); logger.info("Transforming Class.forName() calls");
Transformer transformer = new ClassForNameTransformer(remapper); Transformer transformer = new ClassForNameTransformer(remapper);
for (var library : libraries) { transformer.transform(classPath);
transformer.transform(library);
}
transformer = new ClassForNameTransformer(glRemapper); transformer = new ClassForNameTransformer(glRemapper);
for (var library : glLibraries) { transformer.transform(glClassPath);
transformer.transform(library);
}
transformer = new ClassForNameTransformer(unsignedRemapper); transformer = new ClassForNameTransformer(unsignedRemapper);
transformer.transform(unsignedClient); transformer.transform(unsignedClassPath);
/* add @OriginalName annotations */ /* add @OriginalName annotations */
logger.info("Annotating classes and members with original names"); logger.info("Annotating classes and members with original names");
transformer = new OriginalNameTransformer(); transformer = new OriginalNameTransformer();
for (var library : libraries) { transformer.transform(classPath);
transformer.transform(library); transformer.transform(glClassPath);
} transformer.transform(unsignedClassPath);
for (var library : glLibraries) {
transformer.transform(library);
}
transformer.transform(unsignedClient);
/* write output jars */ /* write output jars */
logger.info("Writing output jars"); logger.info("Writing output jars");

@ -4,6 +4,7 @@ import java.util.HashMap;
import dev.openrs2.asm.Library; import dev.openrs2.asm.Library;
import dev.openrs2.deob.transform.ClassForNameTransformer; import dev.openrs2.deob.transform.ClassForNameTransformer;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.commons.ClassRemapper; import org.objectweb.asm.commons.ClassRemapper;
import org.objectweb.asm.commons.SimpleRemapper; import org.objectweb.asm.commons.SimpleRemapper;
import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.ClassNode;
@ -22,7 +23,13 @@ public final class ClassNamePrefixer {
var remapper = new SimpleRemapper(mapping); var remapper = new SimpleRemapper(mapping);
var transformer = new ClassForNameTransformer(remapper); 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()) { for (var name : mapping.keySet()) {
var in = library.remove(name); var in = library.remove(name);

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

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

@ -2,8 +2,8 @@ package dev.openrs2.deob.transform;
import dev.openrs2.asm.InsnMatcher; import dev.openrs2.asm.InsnMatcher;
import dev.openrs2.asm.InsnNodeUtils; import dev.openrs2.asm.InsnNodeUtils;
import dev.openrs2.asm.Library;
import dev.openrs2.asm.Transformer; import dev.openrs2.asm.Transformer;
import dev.openrs2.asm.classpath.ClassPath;
import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode; import org.objectweb.asm.tree.MethodNode;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -17,7 +17,7 @@ public final class ExceptionTracingTransformer extends Transformer {
private int tracingTryCatches; private int tracingTryCatches;
@Override @Override
public void preTransform(Library library) { public void preTransform(ClassPath classPath) {
tracingTryCatches = 0; tracingTryCatches = 0;
} }
@ -39,7 +39,7 @@ public final class ExceptionTracingTransformer extends Transformer {
} }
@Override @Override
public void postTransform(Library library) { public void postTransform(ClassPath classPath) {
logger.info("Removed {} tracing try/catch blocks", tracingTryCatches); 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.Library;
import dev.openrs2.asm.MemberRef; import dev.openrs2.asm.MemberRef;
import dev.openrs2.asm.Transformer; import dev.openrs2.asm.Transformer;
import dev.openrs2.asm.classpath.ClassPath;
import org.objectweb.asm.Opcodes; import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.ClassNode;
@ -29,15 +30,17 @@ public final class OpaquePredicateTransformer extends Transformer {
private int opaquePredicates, stores; private int opaquePredicates, stores;
@Override @Override
public void preTransform(Library library) { public void preTransform(ClassPath classPath) {
flowObstructors.clear(); flowObstructors.clear();
opaquePredicates = 0; opaquePredicates = 0;
stores = 0; stores = 0;
for (var clazz : library) { for (var library : classPath.getLibraries()) {
for (var method : clazz.methods) { for (var clazz : library) {
if ((method.access & (Opcodes.ACC_NATIVE | Opcodes.ACC_ABSTRACT)) == 0) { for (var method : clazz.methods) {
findFlowObstructors(library, method); if ((method.access & (Opcodes.ACC_NATIVE | Opcodes.ACC_ABSTRACT)) == 0) {
findFlowObstructors(library, method);
}
} }
} }
} }
@ -113,7 +116,7 @@ public final class OpaquePredicateTransformer extends Transformer {
} }
@Override @Override
public void postTransform(Library library) { public void postTransform(ClassPath classPath) {
logger.info("Removed {} opaque predicates and {} redundant stores", opaquePredicates, stores); logger.info("Removed {} opaque predicates and {} redundant stores", opaquePredicates, stores);
} }
} }

Loading…
Cancel
Save