Skip to content

Commit 8029344

Browse files
committed
Rewrite DashO deobfuscator
1 parent 52396e3 commit 8029344

18 files changed

+855
-178
lines changed

src/main/java/com/javadeobfuscator/deobfuscator/Deobfuscator.java

Lines changed: 37 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,13 @@ public void start() throws Throwable {
322322
public boolean runFromConfig(TransformerConfig config) throws Throwable {
323323
Transformer transformer = config.getImplementation().newInstance();
324324
transformer.init(this, config, classes, classpath, readers);
325-
return transformer.transform();
325+
boolean madeChangesAtLeastOnce = false;
326+
boolean madeChanges;
327+
do {
328+
madeChanges = transformer.transform();
329+
madeChangesAtLeastOnce = madeChangesAtLeastOnce || madeChanges;
330+
} while (madeChanges && getConfig().isSmartRedo());
331+
return madeChangesAtLeastOnce;
326332
}
327333

328334
public ClassNode assureLoaded(String ref) {
@@ -470,47 +476,44 @@ public byte[] toByteArray(ClassNode node) {
470476
}
471477
ClassWriter writer = new CustomClassWriter(ClassWriter.COMPUTE_FRAMES);
472478
try {
473-
try {
479+
node.accept(writer);
480+
} catch (Throwable e) {
481+
if (e instanceof NoClassInPathException) {
482+
NoClassInPathException ex = (NoClassInPathException) e;
483+
System.out.println("Error: " + ex.getClassName() + " could not be found while writing " + node.name + ". Using COMPUTE_MAXS");
484+
writer = new CustomClassWriter(ClassWriter.COMPUTE_MAXS);
474485
node.accept(writer);
475-
} catch (RuntimeException e) {
476-
if (e instanceof NoClassInPathException) {
477-
NoClassInPathException ex = (NoClassInPathException) e;
478-
System.out.println("Error: " + ex.getClassName() + " could not be found while writing " + node.name + ". Using COMPUTE_MAXS");
479-
writer = new CustomClassWriter(ClassWriter.COMPUTE_MAXS);
480-
node.accept(writer);
481-
} else if (e instanceof NegativeArraySizeException || e instanceof ArrayIndexOutOfBoundsException) {
482-
System.out.println("Error: failed to compute frames");
486+
} else if (e instanceof NegativeArraySizeException || e instanceof ArrayIndexOutOfBoundsException) {
487+
System.out.println("Error: failed to compute frames");
488+
writer = new CustomClassWriter(ClassWriter.COMPUTE_MAXS);
489+
node.accept(writer);
490+
} else if (e.getMessage() != null) {
491+
if (e.getMessage().contains("JSR/RET")) {
492+
System.out.println("ClassNode contained JSR/RET so COMPUTE_MAXS instead");
483493
writer = new CustomClassWriter(ClassWriter.COMPUTE_MAXS);
484494
node.accept(writer);
485-
} else if (e.getMessage() != null) {
486-
if (e.getMessage().contains("JSR/RET")) {
487-
System.out.println("ClassNode contained JSR/RET so COMPUTE_MAXS instead");
488-
writer = new CustomClassWriter(ClassWriter.COMPUTE_MAXS);
489-
node.accept(writer);
490-
} else {
491-
throw e;
492-
}
493495
} else {
494-
throw e;
496+
System.out.println("Error while writing " + node.name);
497+
e.printStackTrace(System.out);
495498
}
499+
} else {
500+
System.out.println("Error while writing " + node.name);
501+
e.printStackTrace(System.out);
496502
}
497-
byte[] classBytes = writer.toByteArray();
498-
499-
if (configuration.isVerify()) {
500-
ClassReader cr = new ClassReader(classBytes);
501-
try {
502-
cr.accept(new CheckClassAdapter(new ClassWriter(0)), 0);
503-
} catch (Throwable t) {
504-
System.out.println("Error: " + node.name + " failed verification");
505-
t.printStackTrace(System.out);
506-
}
503+
}
504+
byte[] classBytes = writer.toByteArray();
505+
506+
if (configuration.isVerify()) {
507+
ClassReader cr = new ClassReader(classBytes);
508+
try {
509+
cr.accept(new CheckClassAdapter(new ClassWriter(0)), 0);
510+
} catch (Throwable t) {
511+
System.out.println("Error: " + node.name + " failed verification");
512+
t.printStackTrace(System.out);
507513
}
508-
return classBytes;
509-
} catch (Throwable t) {
510-
System.out.println("Error while writing " + node.name);
511-
t.printStackTrace(System.out);
512514
}
513-
return null;
515+
516+
return classBytes;
514517
}
515518

516519
public Configuration getConfig() {
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/*
2+
* Copyright 2018 Sam Sun <github-contact@samczsun.com>
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.javadeobfuscator.deobfuscator.asm.source;
18+
19+
import org.objectweb.asm.tree.*;
20+
import org.objectweb.asm.tree.analysis.*;
21+
22+
import java.util.*;
23+
24+
public class ConstantPropagatingSourceFinder extends SourceFinderConsumer {
25+
public ConstantPropagatingSourceFinder(SourceFinderConsumer parent) {
26+
super(parent);
27+
}
28+
29+
public ConstantPropagatingSourceFinder() {
30+
super(null);
31+
}
32+
33+
@Override
34+
public SourceResult findSource(MethodNode methodNode, Frame<SourceValue>[] frames, AbstractInsnNode source, SourceValue want, AbstractInsnNode now) {
35+
Frame<SourceValue> sourceFrame = frames[methodNode.instructions.indexOf(source)];
36+
Frame<SourceValue> curFrame = frames[methodNode.instructions.indexOf(now)];
37+
38+
switch (now.getOpcode()) {
39+
case IADD:
40+
case ISUB:
41+
case IMUL:
42+
case IDIV:
43+
case IREM:
44+
case IAND:
45+
case IOR:
46+
case IXOR:
47+
case ISHL:
48+
case ISHR:
49+
case IUSHR: {
50+
SourceResult bres = SourceFinder.findSource(methodNode, frames, this, now, curFrame.getStack(curFrame.getStackSize() - 2));
51+
SourceResult ares = SourceFinder.findSource(methodNode, frames, this, now, curFrame.getStack(curFrame.getStackSize() - 1));
52+
if (bres.isUnknown() || ares.isUnknown()) return SourceResult.unknown();
53+
if (!bres.getExceptions().isEmpty() || !ares.getExceptions().isEmpty())
54+
return SourceResult.unknown(); // todo maybe we should merge this?
55+
if (bres.getValues().size() != ares.getValues().size())
56+
throw new RuntimeException("unexpected different size of values " + bres + " " + ares);
57+
58+
// System.out.println("got " + TransformerHelper.insnToString(now) + " " + bres + " " + ares);
59+
List<Object> values = new ArrayList<>();
60+
List<ExceptionHolder> exceptions = new ArrayList<>();
61+
for (int i = 0; i < ares.getValues().size(); i++) {
62+
int b = (int) bres.getValues().get(i);
63+
int a = (int) ares.getValues().get(i);
64+
// System.out.println("got " + TransformerHelper.insnToString(now) + " " + b + " " + a);
65+
switch (now.getOpcode()) {
66+
case IADD: {
67+
values.add(b + a);
68+
break;
69+
}
70+
case ISUB: {
71+
values.add(b - a);
72+
break;
73+
}
74+
case IMUL: {
75+
values.add(b * a);
76+
break;
77+
}
78+
case IDIV: {
79+
if (a == 0) exceptions.add(new ExceptionHolder("java/lang/ArithmeticException"));
80+
else values.add(b / a);
81+
break;
82+
}
83+
case IREM: {
84+
if (a == 0) exceptions.add(new ExceptionHolder("java/lang/ArithmeticException"));
85+
else values.add(b % a);
86+
break;
87+
}
88+
case IAND: {
89+
values.add(b & a);
90+
break;
91+
}
92+
case IOR: {
93+
values.add(b | a);
94+
break;
95+
}
96+
case IXOR: {
97+
values.add(b ^ a);
98+
break;
99+
}
100+
case ISHL: {
101+
values.add(b << a);
102+
break;
103+
}
104+
case ISHR: {
105+
values.add(b >> a);
106+
break;
107+
}
108+
case IUSHR: {
109+
values.add(b >>> a);
110+
break;
111+
}
112+
default: {
113+
throw new RuntimeException();
114+
}
115+
}
116+
}
117+
return new SourceResult(values, exceptions);
118+
}
119+
case SWAP: {
120+
if (getStackOffset(sourceFrame, want) == sourceFrame.getStackSize() - 1) {
121+
return SourceFinder.findSource(methodNode, frames, this, now, curFrame.getStack(curFrame.getStackSize() - 2));
122+
} else if (getStackOffset(sourceFrame, want) == sourceFrame.getStackSize() - 2) {
123+
return SourceFinder.findSource(methodNode, frames, this, now, curFrame.getStack(curFrame.getStackSize() - 1));
124+
}
125+
throw new RuntimeException(String.valueOf(getStackOffset(sourceFrame, want)));
126+
}
127+
case DUP: {
128+
return SourceFinder.findSource(methodNode, frames, this, now, curFrame.getStack(curFrame.getStackSize() - 1));
129+
}
130+
}
131+
132+
return parent == null ? SourceResult.unknown() : parent.findSource(methodNode, frames, source, want, now);
133+
}
134+
135+
private static int getStackOffset(Frame<SourceValue> frame, SourceValue want) {
136+
for (int i = 0; i < frame.getStackSize(); i++) {
137+
if (frame.getStack(i) == want) {
138+
return i;
139+
}
140+
}
141+
142+
return -1;
143+
}
144+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Copyright 2018 Sam Sun <github-contact@samczsun.com>
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.javadeobfuscator.deobfuscator.asm.source;
18+
19+
import java.util.*;
20+
21+
/**
22+
* Since {@link java.lang.Throwable} doesn't override {@link Object#equals(Object)}, we use an ExceptionHolder to represent
23+
* arbitrary exceptions
24+
*/
25+
public class ExceptionHolder {
26+
private final String exceptionType;
27+
private final Map<String, Object> exceptionProperties;
28+
29+
public ExceptionHolder(String exceptionType) {
30+
this(exceptionType, Collections.emptyMap());
31+
}
32+
33+
public ExceptionHolder(String exceptionType, Map<String, Object> exceptionProperties) {
34+
this.exceptionType = exceptionType;
35+
this.exceptionProperties = new HashMap<>();
36+
this.exceptionProperties.putAll(exceptionProperties);
37+
}
38+
39+
public String getExceptionType() {
40+
return exceptionType;
41+
}
42+
43+
public Map<String, Object> getExceptionProperties() {
44+
return exceptionProperties;
45+
}
46+
47+
@Override
48+
public boolean equals(Object o) {
49+
if (this == o) return true;
50+
if (o == null || getClass() != o.getClass()) return false;
51+
ExceptionHolder that = (ExceptionHolder) o;
52+
return Objects.equals(exceptionType, that.exceptionType) &&
53+
Objects.equals(exceptionProperties, that.exceptionProperties);
54+
}
55+
56+
@Override
57+
public int hashCode() {
58+
59+
return Objects.hash(exceptionType, exceptionProperties);
60+
}
61+
62+
@Override
63+
public String toString() {
64+
return "ExceptionHolder{" +
65+
"exceptionType='" + exceptionType + '\'' +
66+
", exceptionProperties=" + exceptionProperties +
67+
'}';
68+
}
69+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright 2018 Sam Sun <github-contact@samczsun.com>
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.javadeobfuscator.deobfuscator.asm.source;
18+
19+
import org.objectweb.asm.tree.*;
20+
import org.objectweb.asm.tree.analysis.*;
21+
22+
public class SimpleMethodConstantSourceFinder extends SourceFinderConsumer {
23+
public SimpleMethodConstantSourceFinder(SourceFinderConsumer parent) {
24+
super(parent);
25+
}
26+
27+
public SimpleMethodConstantSourceFinder() {
28+
super(null);
29+
}
30+
31+
@Override
32+
public SourceResult findSource(MethodNode methodNode, Frame<SourceValue>[] frames, AbstractInsnNode source, SourceValue want, AbstractInsnNode now) {
33+
34+
35+
return parent == null ? SourceResult.unknown() : parent.findSource(methodNode, frames, source, want, now);
36+
}
37+
}

0 commit comments

Comments
 (0)