Skip to content

Commit

Permalink
Merge pull request java-deobfuscator#812 from Janmm14/cleanup-option-…
Browse files Browse the repository at this point in the history
…and-resilience

Cleanup option and resilience & ByteArrayStringTransformer
  • Loading branch information
ThisTestUser authored Aug 26, 2021
2 parents a4493cf + fec3552 commit 8675b0f
Show file tree
Hide file tree
Showing 6 changed files with 383 additions and 158 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

import com.javadeobfuscator.deobfuscator.exceptions.NoClassInPathException;
import com.javadeobfuscator.deobfuscator.executor.Context;
import com.javadeobfuscator.deobfuscator.executor.providers.Provider;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.ClassNode;
Expand All @@ -33,6 +34,8 @@

public class JavaClass {

private static final WeakHashMap<Provider, Set<String>> notFound = new WeakHashMap<>();

private final String name;
private final Type type;
private final ClassNode classNode;
Expand All @@ -57,7 +60,13 @@ public JavaClass(String name, Context context) {
if (primitive == null) {
this.classNode = context.dictionary.get(elementType.getInternalName());
if (this.classNode == null) {
System.out.println("Could not find classnode " + this.name);
notFound.compute(context.provider, (ctx, set) -> {
if (set == null) set = new HashSet<>();
if (set.add(internalName)) {
System.out.println("Could not find classnode " + this.name);
}
return set;
});
throw new NoClassInPathException(this.name);
}
this.isPrimitive = false;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
package com.javadeobfuscator.deobfuscator.transformers.general;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.LongAdder;

import com.javadeobfuscator.deobfuscator.analyzer.AnalyzerResult;
import com.javadeobfuscator.deobfuscator.analyzer.MethodAnalyzer;
import com.javadeobfuscator.deobfuscator.analyzer.frame.ArrayStoreFrame;
import com.javadeobfuscator.deobfuscator.analyzer.frame.Frame;
import com.javadeobfuscator.deobfuscator.analyzer.frame.LdcFrame;
import com.javadeobfuscator.deobfuscator.analyzer.frame.LocalFrame;
import com.javadeobfuscator.deobfuscator.analyzer.frame.MethodFrame;
import com.javadeobfuscator.deobfuscator.analyzer.frame.NewArrayFrame;
import com.javadeobfuscator.deobfuscator.config.TransformerConfig;
import com.javadeobfuscator.deobfuscator.transformers.Transformer;
import com.javadeobfuscator.deobfuscator.utils.Utils;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;

public class ByteArrayStringTransformer extends Transformer<TransformerConfig> {

@Override
public boolean transform() throws Throwable {
LongAdder counter = new LongAdder();
LongAdder errorCounter = new LongAdder();

classNodes().forEach(classNode -> classNode.methods.stream().filter(Utils::notAbstractOrNative).forEach(methodNode -> {
try {
boolean modify;
do {
modify = false;
AnalyzerResult result = null;

List<AbstractInsnNode> replaceWithPop3Total = new ArrayList<>();

ListIterator<AbstractInsnNode> it = methodNode.instructions.iterator();
while (it.hasNext()) {
AbstractInsnNode ain = it.next();
if (!(ain instanceof MethodInsnNode)) {
continue;
}

MethodInsnNode min = (MethodInsnNode) ain;
if (min.getOpcode() != INVOKESPECIAL) {
continue;
}
if (!min.owner.equals("java/lang/String")) {
continue;
}
if (!min.name.equals("<init>")) {
continue;
}
if (!min.desc.equals("([B)V")) {
continue;
}
if (result == null) {
result = MethodAnalyzer.analyze(classNode, methodNode);
}
Set<String> strResults = new HashSet<>();
Set<AbstractInsnNode> replaceWithPop3 = new HashSet<>();
List<Frame> frames = result.getFrames().get(min);
for (Frame frame0 : frames) {
MethodFrame methodFrame = (MethodFrame) frame0;

if (methodFrame.getArgs().size() != 1) {
continue;
}

Frame f = methodFrame.getArgs().get(0);
if (f instanceof LocalFrame && f.isConstant()) {
f = ((LocalFrame) f).getValue();
}
if (!(f instanceof NewArrayFrame)) {
continue;
}
NewArrayFrame naf = (NewArrayFrame) f;
if (!(naf.getLength() instanceof LdcFrame)) {
continue;
}
LdcFrame length = (LdcFrame) naf.getLength();
if (!naf.isConstant()) {
continue;
}

Map<Frame, AbstractInsnNode> mapping = result.getMapping();

byte[] arr = new byte[((Number) length.getConstant()).intValue()];

ArrayDeque<Frame> children = new ArrayDeque<>(naf.getChildren());
while (!children.isEmpty()) {
Frame child0 = children.pop();
if (child0 == naf) {
continue;
}
if (child0 instanceof LocalFrame && child0.isConstant()) {
children.addAll(child0.getChildren());
continue;
}
if (child0 instanceof MethodFrame && mapping.get(child0) == min) {
continue;
}
if (!(child0 instanceof ArrayStoreFrame)) {
throw new IllegalStateException("Unexpected child frame: " + child0);
}
ArrayStoreFrame arrayStoreFrame = (ArrayStoreFrame) child0;
if (arrayStoreFrame.getOpcode() != BASTORE) {
continue;
}
replaceWithPop3.add(mapping.get(arrayStoreFrame));
Frame arrayPos = arrayStoreFrame.getIndex();
if (!(arrayPos instanceof LdcFrame)) {
throw new IllegalStateException("Unexpected store index frame: " + child0);
}
Frame value = arrayStoreFrame.getObject();
if (!(value instanceof LdcFrame)) {
throw new IllegalStateException("Unexpected store object frame: " + child0);
}
int pos = ((Number) ((LdcFrame) arrayPos).getConstant()).intValue();
arr[pos] = (byte) ((Number) ((LdcFrame) value).getConstant()).intValue();
}
String str = new String(arr);
strResults.add(str);
}
if (strResults.size() != 1) {
continue;
}
String str = strResults.iterator().next();
it.set(new InsnNode(POP));
it.add(new MethodInsnNode(INVOKESPECIAL, "java/lang/String", "<init>", "()V", false));
it.add(new InsnNode(POP));
it.add(new LdcInsnNode(str)); // load const string
counter.increment();
modify = true;
replaceWithPop3Total.addAll(replaceWithPop3);
}
for (AbstractInsnNode insn : replaceWithPop3Total) {
methodNode.instructions.insertBefore(insn, new InsnNode(POP2));
methodNode.instructions.set(insn, new InsnNode(POP));
}
} while (modify);
} catch (Exception ex) {
System.err.println("[ByteArrayStringTransformer] An error occurred while deobfuscating " + classNode.name + " " + methodNode.name + methodNode.desc + ":");
ex.printStackTrace();
errorCounter.increment();
}
}));

System.out.println("[ByteArrayStringTransformer] Successfully transformed " + counter + " byte arrays into strings; " + errorCounter + " errors occurred.");

return counter.sum() > 0;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,7 @@ && getDeobfuscator().isSubclass("java/lang/Throwable", classNode.name)) {
});
}
if (getConfig().isFlowObf()) {
Map<ClassNode, Set<FieldNode>> remove = new HashMap<>();
Map<ClassNode, Set<FieldNode>> remove = new HashMap<>();
//Jumps
for (ClassNode classNode : classNodes()) {
for (MethodNode method : classNode.methods) {
Expand Down Expand Up @@ -1156,12 +1156,12 @@ private void patchMethodString(ClassNode classNode) {
method.instructions.set(ain, new LdcInsnNode(4));
} else if (ain instanceof MethodInsnNode && ((MethodInsnNode) ain).name.equals("getRuntime")
&& ((MethodInsnNode) ain).owner.equals("java/lang/Runtime")) {
if(ain.getNext().getOpcode() == Opcodes.IFNULL)
{
method.instructions.remove(ain.getNext());
method.instructions.remove(ain);
}else
method.instructions.set(ain, new InsnNode(Opcodes.ACONST_NULL));
if (ain.getNext().getOpcode() == Opcodes.IFNULL) {
method.instructions.remove(ain.getNext());
method.instructions.remove(ain);
} else {
method.instructions.set(ain, new InsnNode(Opcodes.ACONST_NULL));
}
} else if (ain.getOpcode() == Opcodes.NEW
&& ((TypeInsnNode) ain).desc.equals("java/util/concurrent/atomic/AtomicInteger")) {
method.instructions.remove(Utils.getNext(ain));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,23 +167,25 @@ private int cleanup() {
remove.add(classNode.name);
}
});
decryptors.forEach(classNode ->
{
boolean resource = false;
for(MethodNode node : classNode.methods)
if(node.desc.equals("(Ljava/io/InputStream;)V")) //Don't delete resource decryptors yet
{
resource = true;
break;
}
if(!resource)
remove.add(classNode.name);
});
remove.forEach(str -> {
total.incrementAndGet();
classes.remove(str);
classpath.remove(str);
});
if (!getConfig().keepDecryptorClasses()) {
decryptors.forEach(classNode ->
{
boolean resource = false;
for (MethodNode node : classNode.methods)
if (node.desc.equals("(Ljava/io/InputStream;)V")) //Don't delete resource decryptors yet
{
resource = true;
break;
}
if (!resource)
remove.add(classNode.name);
});
remove.forEach(str -> {
total.incrementAndGet();
classes.remove(str);
classpath.remove(str);
});
}
return total.get();
}

Expand Down Expand Up @@ -602,6 +604,7 @@ public static class Config extends TransformerConfig
* If you are dealing with multiple layers of stringer, it is best to keep this off.
*/
private boolean removeAllStringerClasses = true;
private boolean keepDecryptorClasses = false;

public Config()
{
Expand All @@ -617,5 +620,13 @@ public void setRemoveAllStringerClasses(boolean removeAllStringerClasses)
{
this.removeAllStringerClasses = removeAllStringerClasses;
}

public boolean keepDecryptorClasses() {
return keepDecryptorClasses;
}

public void setKeepDecryptorClasses(boolean keepDecryptorClasses) {
this.keepDecryptorClasses = keepDecryptorClasses;
}
}
}
Loading

0 comments on commit 8675b0f

Please sign in to comment.