summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Gegy2019-02-19 21:00:43 +0200
committerGravatar GitHub2019-02-19 21:00:43 +0200
commitc0b0388aae76d39d780c2b4ad2531feb9d633e62 (patch)
tree92ed3273d7961c2309c732d39cebd99e687a1532 /src
parentAdded Basic Search (#102) (diff)
downloadenigma-c0b0388aae76d39d780c2b4ad2531feb9d633e62.tar.gz
enigma-c0b0388aae76d39d780c2b4ad2531feb9d633e62.tar.xz
enigma-c0b0388aae76d39d780c2b4ad2531feb9d633e62.zip
Bridge Method Fixes (#111)
* Detect synthetic bridges not marked as bridges, and add back flags to produced bytecode * Remove debug check * Remove more test code * Remove unneeded change to `TranslationClassVisitor`
Diffstat (limited to 'src')
-rw-r--r--src/main/java/cuchaz/enigma/CompiledSourceTypeLoader.java16
-rw-r--r--src/main/java/cuchaz/enigma/Deobfuscator.java15
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/BridgeMethodIndex.java89
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java36
-rw-r--r--src/main/java/cuchaz/enigma/analysis/index/JarIndex.java4
-rw-r--r--src/main/java/cuchaz/enigma/bytecode/translators/SourceFixVisitor.java43
6 files changed, 182 insertions, 21 deletions
diff --git a/src/main/java/cuchaz/enigma/CompiledSourceTypeLoader.java b/src/main/java/cuchaz/enigma/CompiledSourceTypeLoader.java
index b1a8cd58..c746abed 100644
--- a/src/main/java/cuchaz/enigma/CompiledSourceTypeLoader.java
+++ b/src/main/java/cuchaz/enigma/CompiledSourceTypeLoader.java
@@ -15,6 +15,7 @@ import com.google.common.collect.Lists;
15import com.strobel.assembler.metadata.Buffer; 15import com.strobel.assembler.metadata.Buffer;
16import com.strobel.assembler.metadata.ITypeLoader; 16import com.strobel.assembler.metadata.ITypeLoader;
17import cuchaz.enigma.translation.representation.entry.ClassEntry; 17import cuchaz.enigma.translation.representation.entry.ClassEntry;
18import org.objectweb.asm.ClassVisitor;
18import org.objectweb.asm.ClassWriter; 19import org.objectweb.asm.ClassWriter;
19import org.objectweb.asm.Opcodes; 20import org.objectweb.asm.Opcodes;
20import org.objectweb.asm.tree.AbstractInsnNode; 21import org.objectweb.asm.tree.AbstractInsnNode;
@@ -23,18 +24,25 @@ import org.objectweb.asm.tree.MethodInsnNode;
23import org.objectweb.asm.tree.MethodNode; 24import org.objectweb.asm.tree.MethodNode;
24 25
25import java.util.Collection; 26import java.util.Collection;
27import java.util.LinkedList;
26import java.util.List; 28import java.util.List;
29import java.util.function.Function;
27 30
28public class CompiledSourceTypeLoader extends CachingTypeLoader { 31public class CompiledSourceTypeLoader extends CachingTypeLoader {
29 //Store one instance as the classpath shouldn't change during load 32 //Store one instance as the classpath shouldn't change during load
30 private static final ITypeLoader CLASSPATH_TYPE_LOADER = new CachingClasspathTypeLoader(); 33 private static final ITypeLoader CLASSPATH_TYPE_LOADER = new CachingClasspathTypeLoader();
31 34
32 private final CompiledSource compiledSource; 35 private final CompiledSource compiledSource;
36 private final LinkedList<Function<ClassVisitor, ClassVisitor>> visitors = new LinkedList<>();
33 37
34 public CompiledSourceTypeLoader(CompiledSource compiledSource) { 38 public CompiledSourceTypeLoader(CompiledSource compiledSource) {
35 this.compiledSource = compiledSource; 39 this.compiledSource = compiledSource;
36 } 40 }
37 41
42 public void addVisitor(Function<ClassVisitor, ClassVisitor> visitor) {
43 this.visitors.addFirst(visitor);
44 }
45
38 @Override 46 @Override
39 protected byte[] doLoad(String className) { 47 protected byte[] doLoad(String className) {
40 byte[] data = loadType(className); 48 byte[] data = loadType(className);
@@ -66,7 +74,13 @@ public class CompiledSourceTypeLoader extends CachingTypeLoader {
66 removeRedundantClassCalls(node); 74 removeRedundantClassCalls(node);
67 75
68 ClassWriter writer = new ClassWriter(0); 76 ClassWriter writer = new ClassWriter(0);
69 node.accept(writer); 77
78 ClassVisitor visitor = writer;
79 for (Function<ClassVisitor, ClassVisitor> visitorFunction : this.visitors) {
80 visitor = visitorFunction.apply(visitor);
81 }
82
83 node.accept(visitor);
70 84
71 // we have a transformed class! 85 // we have a transformed class!
72 return writer.toByteArray(); 86 return writer.toByteArray();
diff --git a/src/main/java/cuchaz/enigma/Deobfuscator.java b/src/main/java/cuchaz/enigma/Deobfuscator.java
index 843c7614..47cd05ce 100644
--- a/src/main/java/cuchaz/enigma/Deobfuscator.java
+++ b/src/main/java/cuchaz/enigma/Deobfuscator.java
@@ -24,6 +24,7 @@ import cuchaz.enigma.analysis.IndexTreeBuilder;
24import cuchaz.enigma.analysis.ParsedJar; 24import cuchaz.enigma.analysis.ParsedJar;
25import cuchaz.enigma.analysis.index.JarIndex; 25import cuchaz.enigma.analysis.index.JarIndex;
26import cuchaz.enigma.api.EnigmaPlugin; 26import cuchaz.enigma.api.EnigmaPlugin;
27import cuchaz.enigma.bytecode.translators.SourceFixVisitor;
27import cuchaz.enigma.bytecode.translators.TranslationClassVisitor; 28import cuchaz.enigma.bytecode.translators.TranslationClassVisitor;
28import cuchaz.enigma.translation.Translatable; 29import cuchaz.enigma.translation.Translatable;
29import cuchaz.enigma.translation.Translator; 30import cuchaz.enigma.translation.Translator;
@@ -81,7 +82,10 @@ public class Deobfuscator {
81 82
82 listener.accept("Preparing..."); 83 listener.accept("Preparing...");
83 84
84 this.obfSourceProvider = new SourceProvider(SourceProvider.createSettings(), new CompiledSourceTypeLoader(parsedJar)); 85 CompiledSourceTypeLoader typeLoader = new CompiledSourceTypeLoader(parsedJar);
86 typeLoader.addVisitor(visitor -> new SourceFixVisitor(Opcodes.ASM5, visitor, jarIndex));
87
88 this.obfSourceProvider = new SourceProvider(SourceProvider.createSettings(), typeLoader);
85 89
86 // init mappings 90 // init mappings
87 mapper = new EntryRemapper(jarIndex); 91 mapper = new EntryRemapper(jarIndex);
@@ -230,16 +234,19 @@ public class Deobfuscator {
230 } 234 }
231 235
232 //create a common instance outside the loop as mappings shouldn't be changing while this is happening 236 //create a common instance outside the loop as mappings shouldn't be changing while this is happening
237 CompiledSourceTypeLoader typeLoader = new CompiledSourceTypeLoader(translatedClasses::get);
238 typeLoader.addVisitor(visitor -> new SourceFixVisitor(Opcodes.ASM5, visitor, jarIndex));
239
233 //synchronized to make sure the parallelStream doesn't CME with the cache 240 //synchronized to make sure the parallelStream doesn't CME with the cache
234 ITypeLoader typeLoader = new SynchronizedTypeLoader(new CompiledSourceTypeLoader(translatedClasses::get)); 241 ITypeLoader synchronizedTypeLoader = new SynchronizedTypeLoader(typeLoader);
235 242
236 MetadataSystem metadataSystem = new Deobfuscator.NoRetryMetadataSystem(typeLoader); 243 MetadataSystem metadataSystem = new Deobfuscator.NoRetryMetadataSystem(synchronizedTypeLoader);
237 244
238 //ensures methods are loaded on classload and prevents race conditions 245 //ensures methods are loaded on classload and prevents race conditions
239 metadataSystem.setEagerMethodLoadingEnabled(true); 246 metadataSystem.setEagerMethodLoadingEnabled(true);
240 247
241 DecompilerSettings settings = SourceProvider.createSettings(); 248 DecompilerSettings settings = SourceProvider.createSettings();
242 SourceProvider sourceProvider = new SourceProvider(settings, typeLoader, metadataSystem); 249 SourceProvider sourceProvider = new SourceProvider(settings, synchronizedTypeLoader, metadataSystem);
243 250
244 AtomicInteger count = new AtomicInteger(); 251 AtomicInteger count = new AtomicInteger();
245 252
diff --git a/src/main/java/cuchaz/enigma/analysis/index/BridgeMethodIndex.java b/src/main/java/cuchaz/enigma/analysis/index/BridgeMethodIndex.java
index 8f6bd462..de2ba1e5 100644
--- a/src/main/java/cuchaz/enigma/analysis/index/BridgeMethodIndex.java
+++ b/src/main/java/cuchaz/enigma/analysis/index/BridgeMethodIndex.java
@@ -1,22 +1,32 @@
1package cuchaz.enigma.analysis.index; 1package cuchaz.enigma.analysis.index;
2 2
3import com.google.common.collect.Maps; 3import com.google.common.collect.Maps;
4import com.google.common.collect.Sets;
4import cuchaz.enigma.translation.mapping.EntryResolver; 5import cuchaz.enigma.translation.mapping.EntryResolver;
5import cuchaz.enigma.translation.representation.AccessFlags; 6import cuchaz.enigma.translation.representation.AccessFlags;
7import cuchaz.enigma.translation.representation.MethodDescriptor;
8import cuchaz.enigma.translation.representation.TypeDescriptor;
9import cuchaz.enigma.translation.representation.entry.ClassEntry;
10import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
6import cuchaz.enigma.translation.representation.entry.MethodEntry; 11import cuchaz.enigma.translation.representation.entry.MethodEntry;
7 12
8import javax.annotation.Nullable; 13import javax.annotation.Nullable;
9import java.util.Collection; 14import java.util.Collection;
15import java.util.List;
10import java.util.Map; 16import java.util.Map;
17import java.util.Set;
11 18
12public class BridgeMethodIndex implements JarIndexer { 19public class BridgeMethodIndex implements JarIndexer {
13 private final EntryIndex entryIndex; 20 private final EntryIndex entryIndex;
21 private final InheritanceIndex inheritanceIndex;
14 private final ReferenceIndex referenceIndex; 22 private final ReferenceIndex referenceIndex;
15 23
16 private Map<MethodEntry, MethodEntry> accessedToBridge = Maps.newHashMap(); 24 private final Set<MethodEntry> bridgeMethods = Sets.newHashSet();
25 private final Map<MethodEntry, MethodEntry> accessedToBridge = Maps.newHashMap();
17 26
18 public BridgeMethodIndex(EntryIndex entryIndex, ReferenceIndex referenceIndex) { 27 public BridgeMethodIndex(EntryIndex entryIndex, InheritanceIndex inheritanceIndex, ReferenceIndex referenceIndex) {
19 this.entryIndex = entryIndex; 28 this.entryIndex = entryIndex;
29 this.inheritanceIndex = inheritanceIndex;
20 this.referenceIndex = referenceIndex; 30 this.referenceIndex = referenceIndex;
21 } 31 }
22 32
@@ -24,21 +34,26 @@ public class BridgeMethodIndex implements JarIndexer {
24 public void processIndex(EntryResolver resolver) { 34 public void processIndex(EntryResolver resolver) {
25 // look for access and bridged methods 35 // look for access and bridged methods
26 for (MethodEntry methodEntry : entryIndex.getMethods()) { 36 for (MethodEntry methodEntry : entryIndex.getMethods()) {
27 AccessFlags access = entryIndex.getMethodAccess(methodEntry); 37 MethodDefEntry methodDefEntry = (MethodDefEntry) methodEntry;
38
39 AccessFlags access = methodDefEntry.getAccess();
28 if (access == null || !access.isSynthetic()) { 40 if (access == null || !access.isSynthetic()) {
29 continue; 41 continue;
30 } 42 }
31 43
32 indexSyntheticMethod(methodEntry, access); 44 indexSyntheticMethod(methodDefEntry, access);
33 } 45 }
34 } 46 }
35 47
36 private void indexSyntheticMethod(MethodEntry syntheticMethod, AccessFlags access) { 48 private void indexSyntheticMethod(MethodDefEntry syntheticMethod, AccessFlags access) {
37 if (access.isBridge()) { 49 MethodEntry accessedMethod = findAccessMethod(syntheticMethod);
38 MethodEntry accessedMethod = findAccessMethod(syntheticMethod); 50 if (accessedMethod == null) {
39 if (accessedMethod != null) { 51 return;
40 accessedToBridge.put(accessedMethod, syntheticMethod); 52 }
41 } 53
54 if (access.isBridge() || isPotentialBridge(syntheticMethod, accessedMethod)) {
55 bridgeMethods.add(syntheticMethod);
56 accessedToBridge.put(accessedMethod, syntheticMethod);
42 } 57 }
43 } 58 }
44 59
@@ -56,6 +71,60 @@ public class BridgeMethodIndex implements JarIndexer {
56 return referencedMethods.stream().findFirst().orElse(null); 71 return referencedMethods.stream().findFirst().orElse(null);
57 } 72 }
58 73
74 private boolean isPotentialBridge(MethodDefEntry bridgeMethod, MethodEntry accessedMethod) {
75 // Bridge methods only exist for inheritance purposes, if we're private, final, or static, we cannot be inherited
76 AccessFlags bridgeAccess = bridgeMethod.getAccess();
77 if (bridgeAccess.isPrivate() || bridgeAccess.isFinal() || bridgeAccess.isStatic()) {
78 return false;
79 }
80
81 MethodDescriptor bridgeDesc = bridgeMethod.getDesc();
82 MethodDescriptor accessedDesc = accessedMethod.getDesc();
83 List<TypeDescriptor> bridgeArguments = bridgeDesc.getArgumentDescs();
84 List<TypeDescriptor> accessedArguments = accessedDesc.getArgumentDescs();
85
86 // A bridge method will always have the same number of arguments
87 if (bridgeArguments.size() != accessedArguments.size()) {
88 return false;
89 }
90
91 // Check that all argument types are bridge-compatible
92 for (int i = 0; i < bridgeArguments.size(); i++) {
93 if (!areTypesBridgeCompatible(bridgeArguments.get(i), accessedArguments.get(i))) {
94 return false;
95 }
96 }
97
98 // Check that the return type is bridge-compatible
99 return areTypesBridgeCompatible(bridgeDesc.getReturnDesc(), accessedDesc.getReturnDesc());
100 }
101
102 private boolean areTypesBridgeCompatible(TypeDescriptor bridgeDesc, TypeDescriptor accessedDesc) {
103 if (bridgeDesc.equals(accessedDesc)) {
104 return true;
105 }
106
107 // Either the descs will be equal, or they are both types and different through a generic
108 if (bridgeDesc.isType() && accessedDesc.isType()) {
109 ClassEntry bridgeType = bridgeDesc.getTypeEntry();
110 ClassEntry accessedType = accessedDesc.getTypeEntry();
111
112 // If the given types are completely unrelated to each other, this can't be bridge compatible
113 InheritanceIndex.Relation relation = inheritanceIndex.computeClassRelation(accessedType, bridgeType);
114 return relation != InheritanceIndex.Relation.UNRELATED;
115 }
116
117 return false;
118 }
119
120 public boolean isBridgeMethod(MethodEntry entry) {
121 return bridgeMethods.contains(entry);
122 }
123
124 public boolean isAccessedByBridge(MethodEntry entry) {
125 return accessedToBridge.containsKey(entry);
126 }
127
59 @Nullable 128 @Nullable
60 public MethodEntry getBridgeFromAccessed(MethodEntry entry) { 129 public MethodEntry getBridgeFromAccessed(MethodEntry entry) {
61 return accessedToBridge.get(entry); 130 return accessedToBridge.get(entry);
diff --git a/src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java b/src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java
index 17bed54c..1b8d9a8d 100644
--- a/src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java
+++ b/src/main/java/cuchaz/enigma/analysis/index/InheritanceIndex.java
@@ -22,13 +22,23 @@ import java.util.LinkedList;
22import java.util.Set; 22import java.util.Set;
23 23
24public class InheritanceIndex implements JarIndexer { 24public class InheritanceIndex implements JarIndexer {
25 private final EntryIndex entryIndex;
26
25 private Multimap<ClassEntry, ClassEntry> classParents = HashMultimap.create(); 27 private Multimap<ClassEntry, ClassEntry> classParents = HashMultimap.create();
26 private Multimap<ClassEntry, ClassEntry> classChildren = HashMultimap.create(); 28 private Multimap<ClassEntry, ClassEntry> classChildren = HashMultimap.create();
27 29
30 public InheritanceIndex(EntryIndex entryIndex) {
31 this.entryIndex = entryIndex;
32 }
33
28 @Override 34 @Override
29 public void indexClass(ClassDefEntry classEntry) { 35 public void indexClass(ClassDefEntry classEntry) {
36 if (classEntry.isJre()) {
37 return;
38 }
39
30 ClassEntry superClass = classEntry.getSuperClass(); 40 ClassEntry superClass = classEntry.getSuperClass();
31 if (superClass != null) { 41 if (superClass != null && !superClass.getName().equals("java/lang/Object")) {
32 indexParent(classEntry, superClass); 42 indexParent(classEntry, superClass);
33 } 43 }
34 44
@@ -38,9 +48,6 @@ public class InheritanceIndex implements JarIndexer {
38 } 48 }
39 49
40 private void indexParent(ClassEntry childEntry, ClassEntry parentEntry) { 50 private void indexParent(ClassEntry childEntry, ClassEntry parentEntry) {
41 if (childEntry.isJre() || parentEntry.isJre()) {
42 return;
43 }
44 classParents.put(childEntry, parentEntry); 51 classParents.put(childEntry, parentEntry);
45 classChildren.put(parentEntry, childEntry); 52 classChildren.put(parentEntry, childEntry);
46 } 53 }
@@ -70,6 +77,21 @@ public class InheritanceIndex implements JarIndexer {
70 return ancestors; 77 return ancestors;
71 } 78 }
72 79
80 public Relation computeClassRelation(ClassEntry classEntry, ClassEntry potentialAncestor) {
81 if (potentialAncestor.getName().equals("java/lang/Object")) return Relation.RELATED;
82 if (!entryIndex.hasClass(classEntry)) return Relation.UNKNOWN;
83
84 for (ClassEntry ancestor : getAncestors(classEntry)) {
85 if (potentialAncestor.equals(ancestor)) {
86 return Relation.RELATED;
87 } else if (!entryIndex.hasClass(ancestor)) {
88 return Relation.UNKNOWN;
89 }
90 }
91
92 return Relation.UNRELATED;
93 }
94
73 public boolean isParent(ClassEntry classEntry) { 95 public boolean isParent(ClassEntry classEntry) {
74 return classChildren.containsKey(classEntry); 96 return classChildren.containsKey(classEntry);
75 } 97 }
@@ -78,4 +100,10 @@ public class InheritanceIndex implements JarIndexer {
78 Collection<ClassEntry> parents = classParents.get(classEntry); 100 Collection<ClassEntry> parents = classParents.get(classEntry);
79 return parents != null && !parents.isEmpty(); 101 return parents != null && !parents.isEmpty();
80 } 102 }
103
104 public enum Relation {
105 RELATED,
106 UNRELATED,
107 UNKNOWN
108 }
81} 109}
diff --git a/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java b/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java
index 9b21cbae..cb58fced 100644
--- a/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java
+++ b/src/main/java/cuchaz/enigma/analysis/index/JarIndex.java
@@ -46,9 +46,9 @@ public class JarIndex implements JarIndexer {
46 46
47 public static JarIndex empty() { 47 public static JarIndex empty() {
48 EntryIndex entryIndex = new EntryIndex(); 48 EntryIndex entryIndex = new EntryIndex();
49 InheritanceIndex inheritanceIndex = new InheritanceIndex(); 49 InheritanceIndex inheritanceIndex = new InheritanceIndex(entryIndex);
50 ReferenceIndex referenceIndex = new ReferenceIndex(); 50 ReferenceIndex referenceIndex = new ReferenceIndex();
51 BridgeMethodIndex bridgeMethodIndex = new BridgeMethodIndex(entryIndex, referenceIndex); 51 BridgeMethodIndex bridgeMethodIndex = new BridgeMethodIndex(entryIndex, inheritanceIndex, referenceIndex);
52 return new JarIndex(entryIndex, inheritanceIndex, referenceIndex, bridgeMethodIndex); 52 return new JarIndex(entryIndex, inheritanceIndex, referenceIndex, bridgeMethodIndex);
53 } 53 }
54 54
diff --git a/src/main/java/cuchaz/enigma/bytecode/translators/SourceFixVisitor.java b/src/main/java/cuchaz/enigma/bytecode/translators/SourceFixVisitor.java
new file mode 100644
index 00000000..99eef6ae
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/bytecode/translators/SourceFixVisitor.java
@@ -0,0 +1,43 @@
1package cuchaz.enigma.bytecode.translators;
2
3import cuchaz.enigma.analysis.index.BridgeMethodIndex;
4import cuchaz.enigma.analysis.index.JarIndex;
5import cuchaz.enigma.translation.representation.entry.ClassDefEntry;
6import cuchaz.enigma.translation.representation.entry.MethodDefEntry;
7import cuchaz.enigma.translation.representation.entry.MethodEntry;
8import org.objectweb.asm.ClassVisitor;
9import org.objectweb.asm.MethodVisitor;
10import org.objectweb.asm.Opcodes;
11
12public class SourceFixVisitor extends ClassVisitor {
13 private final JarIndex index;
14 private ClassDefEntry ownerEntry;
15
16 public SourceFixVisitor(int api, ClassVisitor visitor, JarIndex index) {
17 super(api, visitor);
18 this.index = index;
19 }
20
21 @Override
22 public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
23 ownerEntry = ClassDefEntry.parse(access, name, signature, superName, interfaces);
24 super.visit(version, access, name, signature, superName, interfaces);
25 }
26
27 @Override
28 public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
29 MethodDefEntry methodEntry = MethodDefEntry.parse(ownerEntry, access, name, descriptor, signature);
30
31 BridgeMethodIndex bridgeIndex = index.getBridgeMethodIndex();
32 if (bridgeIndex.isBridgeMethod(methodEntry)) {
33 access |= Opcodes.ACC_BRIDGE;
34 } else {
35 MethodEntry bridgeMethod = bridgeIndex.getBridgeFromAccessed(methodEntry);
36 if (bridgeMethod != null) {
37 name = bridgeMethod.getName();
38 }
39 }
40
41 return super.visitMethod(access, name, descriptor, signature, exceptions);
42 }
43}