summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Thiakil2018-07-11 15:58:19 +0800
committerGravatar Thiakil2018-07-11 15:58:19 +0800
commitfb3e7bbf5b450138ab0b8493ff725f36407ee5bc (patch)
tree65895bbc6cf1766f4ca01e1257619ab1993e71dc
parentmake CachingClasspathTypeLoader public (diff)
downloadenigma-fb3e7bbf5b450138ab0b8493ff725f36407ee5bc.tar.gz
enigma-fb3e7bbf5b450138ab0b8493ff725f36407ee5bc.tar.xz
enigma-fb3e7bbf5b450138ab0b8493ff725f36407ee5bc.zip
support enum switches with obfuscated SwitchMaps
-rw-r--r--src/main/java/cuchaz/enigma/Deobfuscator.java12
-rw-r--r--src/main/java/oml/ast/transformers/ObfuscatedEnumSwitchRewriterTransform.java414
2 files changed, 426 insertions, 0 deletions
diff --git a/src/main/java/cuchaz/enigma/Deobfuscator.java b/src/main/java/cuchaz/enigma/Deobfuscator.java
index b2cecfe2..6ea1c40b 100644
--- a/src/main/java/cuchaz/enigma/Deobfuscator.java
+++ b/src/main/java/cuchaz/enigma/Deobfuscator.java
@@ -25,6 +25,7 @@ import com.strobel.decompiler.languages.java.JavaOutputVisitor;
25import com.strobel.decompiler.languages.java.ast.AstBuilder; 25import com.strobel.decompiler.languages.java.ast.AstBuilder;
26import com.strobel.decompiler.languages.java.ast.CompilationUnit; 26import com.strobel.decompiler.languages.java.ast.CompilationUnit;
27import com.strobel.decompiler.languages.java.ast.InsertParenthesesVisitor; 27import com.strobel.decompiler.languages.java.ast.InsertParenthesesVisitor;
28import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform;
28import cuchaz.enigma.analysis.*; 29import cuchaz.enigma.analysis.*;
29import cuchaz.enigma.bytecode.ClassProtectifier; 30import cuchaz.enigma.bytecode.ClassProtectifier;
30import cuchaz.enigma.bytecode.ClassPublifier; 31import cuchaz.enigma.bytecode.ClassPublifier;
@@ -32,6 +33,7 @@ import cuchaz.enigma.mapping.*;
32import cuchaz.enigma.mapping.entry.*; 33import cuchaz.enigma.mapping.entry.*;
33import cuchaz.enigma.throwables.IllegalNameException; 34import cuchaz.enigma.throwables.IllegalNameException;
34import cuchaz.enigma.utils.Utils; 35import cuchaz.enigma.utils.Utils;
36import oml.ast.transformers.ObfuscatedEnumSwitchRewriterTransform;
35import org.objectweb.asm.ClassWriter; 37import org.objectweb.asm.ClassWriter;
36import org.objectweb.asm.Opcodes; 38import org.objectweb.asm.Opcodes;
37import org.objectweb.asm.tree.ClassNode; 39import org.objectweb.asm.tree.ClassNode;
@@ -200,6 +202,7 @@ public class Deobfuscator {
200 AstBuilder builder = new AstBuilder(context); 202 AstBuilder builder = new AstBuilder(context);
201 builder.addType(resolvedType); 203 builder.addType(resolvedType);
202 builder.runTransformations(null); 204 builder.runTransformations(null);
205 runCustomTransforms(builder, context);
203 return builder.getCompilationUnit(); 206 return builder.getCompilationUnit();
204 } 207 }
205 208
@@ -656,6 +659,15 @@ public class Deobfuscator {
656 throw new Error("Unknown entry desc: " + obfEntry); 659 throw new Error("Unknown entry desc: " + obfEntry);
657 } 660 }
658 661
662 public static void runCustomTransforms(AstBuilder builder, DecompilerContext context){
663 List<IAstTransform> transformers = Arrays.asList(
664 new ObfuscatedEnumSwitchRewriterTransform(context)
665 );
666 for (IAstTransform transform : transformers){
667 transform.run(builder.getCompilationUnit());
668 }
669 }
670
659 public interface ProgressListener { 671 public interface ProgressListener {
660 void init(int totalWork, String title); 672 void init(int totalWork, String title);
661 673
diff --git a/src/main/java/oml/ast/transformers/ObfuscatedEnumSwitchRewriterTransform.java b/src/main/java/oml/ast/transformers/ObfuscatedEnumSwitchRewriterTransform.java
new file mode 100644
index 00000000..6005b7f7
--- /dev/null
+++ b/src/main/java/oml/ast/transformers/ObfuscatedEnumSwitchRewriterTransform.java
@@ -0,0 +1,414 @@
1/*
2 * Originally:
3 * EnumSwitchRewriterTransform.java
4 *
5 * Copyright (c) 2013 Mike Strobel
6 *
7 * This source code is based on Mono.Cecil from Jb Evain, Copyright (c) Jb Evain;
8 * and ILSpy/ICSharpCode from SharpDevelop, Copyright (c) AlphaSierraPapa.
9 *
10 * This source code is subject to terms and conditions of the Apache License, Version 2.0.
11 * A copy of the license can be found in the License.html file at the root of this distribution.
12 * By using this source code in any fashion, you are agreeing to be bound by the terms of the
13 * Apache License, Version 2.0.
14 *
15 * You must not remove this notice, or any other, from this software.
16 */
17
18package oml.ast.transformers;
19
20import com.strobel.assembler.metadata.BuiltinTypes;
21import com.strobel.assembler.metadata.FieldDefinition;
22import com.strobel.assembler.metadata.MethodDefinition;
23import com.strobel.assembler.metadata.TypeDefinition;
24import com.strobel.assembler.metadata.TypeReference;
25import com.strobel.core.SafeCloseable;
26import com.strobel.core.VerifyArgument;
27import com.strobel.decompiler.DecompilerContext;
28import com.strobel.decompiler.languages.java.ast.AssignmentExpression;
29import com.strobel.decompiler.languages.java.ast.AstBuilder;
30import com.strobel.decompiler.languages.java.ast.AstNode;
31import com.strobel.decompiler.languages.java.ast.CaseLabel;
32import com.strobel.decompiler.languages.java.ast.ContextTrackingVisitor;
33import com.strobel.decompiler.languages.java.ast.Expression;
34import com.strobel.decompiler.languages.java.ast.IdentifierExpression;
35import com.strobel.decompiler.languages.java.ast.IndexerExpression;
36import com.strobel.decompiler.languages.java.ast.InvocationExpression;
37import com.strobel.decompiler.languages.java.ast.Keys;
38import com.strobel.decompiler.languages.java.ast.MemberReferenceExpression;
39import com.strobel.decompiler.languages.java.ast.PrimitiveExpression;
40import com.strobel.decompiler.languages.java.ast.SwitchSection;
41import com.strobel.decompiler.languages.java.ast.SwitchStatement;
42import com.strobel.decompiler.languages.java.ast.TypeDeclaration;
43import com.strobel.decompiler.languages.java.ast.TypeReferenceExpression;
44import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform;
45
46import java.util.ArrayList;
47import java.util.IdentityHashMap;
48import java.util.LinkedHashMap;
49import java.util.List;
50import java.util.Map;
51
52/**
53 * Copy of {@link com.strobel.decompiler.languages.java.ast.transforms.EnumSwitchRewriterTransform} modified to:
54 * - Not rely on a field containing "$SwitchMap$" (Proguard strips it)
55 * - Ignore classes *with* SwitchMap$ names (so the original can handle it)
56 * - Ignores inner synthetics that are not package private
57 */
58@SuppressWarnings("Duplicates")
59public class ObfuscatedEnumSwitchRewriterTransform implements IAstTransform {
60 private final DecompilerContext _context;
61
62 public ObfuscatedEnumSwitchRewriterTransform(final DecompilerContext context) {
63 _context = VerifyArgument.notNull(context, "context");
64 }
65
66 @Override
67 public void run(final AstNode compilationUnit) {
68 compilationUnit.acceptVisitor(new Visitor(_context), null);
69 }
70
71 private final static class Visitor extends ContextTrackingVisitor<Void> {
72 private final static class SwitchMapInfo {
73 final String enclosingType;
74 final Map<String, List<SwitchStatement>> switches = new LinkedHashMap<>();
75 final Map<String, Map<Integer, Expression>> mappings = new LinkedHashMap<>();
76
77 TypeDeclaration enclosingTypeDeclaration;
78
79 SwitchMapInfo(final String enclosingType) {
80 this.enclosingType = enclosingType;
81 }
82 }
83
84 private final Map<String, SwitchMapInfo> _switchMaps = new LinkedHashMap<>();
85 private boolean _isSwitchMapWrapper;
86
87 protected Visitor(final DecompilerContext context) {
88 super(context);
89 }
90
91 @Override
92 public Void visitTypeDeclaration(final TypeDeclaration typeDeclaration, final Void p) {
93 final boolean oldIsSwitchMapWrapper = _isSwitchMapWrapper;
94 final TypeDefinition typeDefinition = typeDeclaration.getUserData(Keys.TYPE_DEFINITION);
95 final boolean isSwitchMapWrapper = isSwitchMapWrapper(typeDefinition);
96
97 if (isSwitchMapWrapper) {
98 final String internalName = typeDefinition.getInternalName();
99
100 SwitchMapInfo info = _switchMaps.get(internalName);
101
102 if (info == null) {
103 _switchMaps.put(internalName, info = new SwitchMapInfo(internalName));
104 }
105
106 info.enclosingTypeDeclaration = typeDeclaration;
107 }
108
109 _isSwitchMapWrapper = isSwitchMapWrapper;
110
111 try {
112 super.visitTypeDeclaration(typeDeclaration, p);
113 }
114 finally {
115 _isSwitchMapWrapper = oldIsSwitchMapWrapper;
116 }
117
118 rewrite();
119
120 return null;
121 }
122
123 @Override
124 public Void visitSwitchStatement(final SwitchStatement node, final Void data) {
125 final Expression test = node.getExpression();
126
127 if (test instanceof IndexerExpression) {
128 final IndexerExpression indexer = (IndexerExpression) test;
129 final Expression array = indexer.getTarget();
130 final Expression argument = indexer.getArgument();
131
132 if (!(array instanceof MemberReferenceExpression)) {
133 return super.visitSwitchStatement(node, data);
134 }
135
136 final MemberReferenceExpression arrayAccess = (MemberReferenceExpression) array;
137 final Expression arrayOwner = arrayAccess.getTarget();
138 final String mapName = arrayAccess.getMemberName();
139
140 if (mapName == null || mapName.startsWith("$SwitchMap$") || !(arrayOwner instanceof TypeReferenceExpression)) {
141 return super.visitSwitchStatement(node, data);
142 }
143
144 final TypeReferenceExpression enclosingTypeExpression = (TypeReferenceExpression) arrayOwner;
145 final TypeReference enclosingType = enclosingTypeExpression.getType().getUserData(Keys.TYPE_REFERENCE);
146
147 if (!isSwitchMapWrapper(enclosingType) || !(argument instanceof InvocationExpression)) {
148 return super.visitSwitchStatement(node, data);
149 }
150
151 final InvocationExpression invocation = (InvocationExpression) argument;
152 final Expression invocationTarget = invocation.getTarget();
153
154 if (!(invocationTarget instanceof MemberReferenceExpression)) {
155 return super.visitSwitchStatement(node, data);
156 }
157
158 final MemberReferenceExpression memberReference = (MemberReferenceExpression) invocationTarget;
159
160 if (!"ordinal".equals(memberReference.getMemberName())) {
161 return super.visitSwitchStatement(node, data);
162 }
163
164 final String enclosingTypeName = enclosingType.getInternalName();
165
166 SwitchMapInfo info = _switchMaps.get(enclosingTypeName);
167
168 if (info == null) {
169 _switchMaps.put(enclosingTypeName, info = new SwitchMapInfo(enclosingTypeName));
170
171 final TypeDefinition resolvedType = enclosingType.resolve();
172
173 if (resolvedType != null) {
174 AstBuilder astBuilder = context.getUserData(Keys.AST_BUILDER);
175
176 if (astBuilder == null) {
177 astBuilder = new AstBuilder(context);
178 }
179
180 try (final SafeCloseable importSuppression = astBuilder.suppressImports()) {
181 final TypeDeclaration declaration = astBuilder.createType(resolvedType);
182
183 declaration.acceptVisitor(this, data);
184 }
185 }
186 }
187
188 List<SwitchStatement> switches = info.switches.get(mapName);
189
190 if (switches == null) {
191 info.switches.put(mapName, switches = new ArrayList<>());
192 }
193
194 switches.add(node);
195 }
196
197 return super.visitSwitchStatement(node, data);
198 }
199
200 @Override
201 public Void visitAssignmentExpression(final AssignmentExpression node, final Void data) {
202 final TypeDefinition currentType = context.getCurrentType();
203 final MethodDefinition currentMethod = context.getCurrentMethod();
204
205 if (_isSwitchMapWrapper &&
206 currentType != null &&
207 currentMethod != null &&
208 currentMethod.isTypeInitializer()) {
209
210 final Expression left = node.getLeft();
211 final Expression right = node.getRight();
212
213 if (left instanceof IndexerExpression &&
214 right instanceof PrimitiveExpression) {
215
216 String mapName = null;
217
218 final Expression array = ((IndexerExpression) left).getTarget();
219 final Expression argument = ((IndexerExpression) left).getArgument();
220
221 if (array instanceof MemberReferenceExpression) {
222 mapName = ((MemberReferenceExpression) array).getMemberName();
223 }
224 else if (array instanceof IdentifierExpression) {
225 mapName = ((IdentifierExpression) array).getIdentifier();
226 }
227
228 if (mapName == null || mapName.startsWith("$SwitchMap$")) {
229 return super.visitAssignmentExpression(node, data);
230 }
231
232 if (!(argument instanceof InvocationExpression)) {
233 return super.visitAssignmentExpression(node, data);
234 }
235
236 final InvocationExpression invocation = (InvocationExpression) argument;
237 final Expression invocationTarget = invocation.getTarget();
238
239 if (!(invocationTarget instanceof MemberReferenceExpression)) {
240 return super.visitAssignmentExpression(node, data);
241 }
242
243 final MemberReferenceExpression memberReference = (MemberReferenceExpression) invocationTarget;
244 final Expression memberTarget = memberReference.getTarget();
245
246 if (!(memberTarget instanceof MemberReferenceExpression) || !"ordinal".equals(memberReference.getMemberName())) {
247 return super.visitAssignmentExpression(node, data);
248 }
249
250 final MemberReferenceExpression outerMemberReference = (MemberReferenceExpression) memberTarget;
251 final Expression outerMemberTarget = outerMemberReference.getTarget();
252
253 if (!(outerMemberTarget instanceof TypeReferenceExpression)) {
254 return super.visitAssignmentExpression(node, data);
255 }
256
257 final String enclosingType = currentType.getInternalName();
258
259 SwitchMapInfo info = _switchMaps.get(enclosingType);
260
261 if (info == null) {
262 _switchMaps.put(enclosingType, info = new SwitchMapInfo(enclosingType));
263
264 AstBuilder astBuilder = context.getUserData(Keys.AST_BUILDER);
265
266 if (astBuilder == null) {
267 astBuilder = new AstBuilder(context);
268 }
269
270 info.enclosingTypeDeclaration = astBuilder.createType(currentType);
271 }
272
273 final PrimitiveExpression value = (PrimitiveExpression) right;
274
275 assert value.getValue() instanceof Integer;
276
277 Map<Integer, Expression> mapping = info.mappings.get(mapName);
278
279 if (mapping == null) {
280 info.mappings.put(mapName, mapping = new LinkedHashMap<>());
281 }
282
283 final IdentifierExpression enumValue = new IdentifierExpression( Expression.MYSTERY_OFFSET, outerMemberReference.getMemberName());
284
285 enumValue.putUserData(Keys.MEMBER_REFERENCE, outerMemberReference.getUserData(Keys.MEMBER_REFERENCE));
286
287 mapping.put(((Number) value.getValue()).intValue(), enumValue);
288 }
289 }
290
291 return super.visitAssignmentExpression(node, data);
292 }
293
294 private void rewrite() {
295 if (_switchMaps.isEmpty()) {
296 return;
297 }
298
299 for (final SwitchMapInfo info : _switchMaps.values()) {
300 rewrite(info);
301 }
302
303 //
304 // Remove switch map type wrappers that are no longer referenced.
305 //
306
307 outer:
308 for (final SwitchMapInfo info : _switchMaps.values()) {
309 for (final String mapName : info.switches.keySet()) {
310 final List<SwitchStatement> switches = info.switches.get(mapName);
311
312 if (switches != null && !switches.isEmpty()) {
313 continue outer;
314 }
315 }
316
317 final TypeDeclaration enclosingTypeDeclaration = info.enclosingTypeDeclaration;
318
319 if (enclosingTypeDeclaration != null) {
320 enclosingTypeDeclaration.remove();
321 }
322 }
323 }
324
325 private void rewrite(final SwitchMapInfo info) {
326 if (info.switches.isEmpty()) {
327 return;
328 }
329
330 for (final String mapName : info.switches.keySet()) {
331 final List<SwitchStatement> switches = info.switches.get(mapName);
332 final Map<Integer, Expression> mappings = info.mappings.get(mapName);
333
334 if (switches != null && mappings != null) {
335 for (int i = 0; i < switches.size(); i++) {
336 if (rewriteSwitch(switches.get(i), mappings)) {
337 switches.remove(i--);
338 }
339 }
340 }
341 }
342 }
343
344 private boolean rewriteSwitch(final SwitchStatement s, final Map<Integer, Expression> mappings) {
345 final Map<Expression, Expression> replacements = new IdentityHashMap<>();
346
347 for (final SwitchSection section : s.getSwitchSections()) {
348 for (final CaseLabel caseLabel : section.getCaseLabels()) {
349 final Expression expression = caseLabel.getExpression();
350
351 if (expression.isNull()) {
352 continue;
353 }
354
355 if (expression instanceof PrimitiveExpression) {
356 final Object value = ((PrimitiveExpression) expression).getValue();
357
358 if (value instanceof Integer) {
359 final Expression replacement = mappings.get(value);
360
361 if (replacement != null) {
362 replacements.put(expression, replacement);
363 continue;
364 }
365 }
366 }
367
368 //
369 // If we can't rewrite all cases, we abort.
370 //
371
372 return false;
373 }
374 }
375
376 final IndexerExpression indexer = (IndexerExpression) s.getExpression();
377 final InvocationExpression argument = (InvocationExpression) indexer.getArgument();
378 final MemberReferenceExpression memberReference = (MemberReferenceExpression) argument.getTarget();
379 final Expression newTest = memberReference.getTarget();
380
381 newTest.remove();
382 indexer.replaceWith(newTest);
383
384 for (final Map.Entry<Expression, Expression> entry : replacements.entrySet()) {
385 entry.getKey().replaceWith(entry.getValue().clone());
386 }
387
388 return true;
389 }
390
391 private static boolean isSwitchMapWrapper(final TypeReference type) {
392 if (type == null) {
393 return false;
394 }
395
396 final TypeDefinition definition = type instanceof TypeDefinition ? (TypeDefinition) type
397 : type.resolve();
398
399 if (definition == null || !definition.isSynthetic() || !definition.isInnerClass() || !definition.isPackagePrivate()) {
400 return false;
401 }
402
403 for (final FieldDefinition field : definition.getDeclaredFields()) {
404 if (!field.getName().startsWith("$SwitchMap$") &&
405 BuiltinTypes.Integer.makeArrayType().equals(field.getFieldType())) {
406
407 return true;
408 }
409 }
410
411 return false;
412 }
413 }
414} \ No newline at end of file