Skip to content

Commit

Permalink
more improvements to zelix deobfuscation
Browse files Browse the repository at this point in the history
  • Loading branch information
EpicPlayerA10 committed Sep 11, 2024
1 parent 68b85b8 commit b2891c8
Show file tree
Hide file tree
Showing 11 changed files with 492 additions and 894 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ public InstructionContext of(AbstractInsnNode insn) {
}

public Frame<OriginalSourceValue> frame() {
if (this.methodContext.frames() == null) {
throw new IllegalStateException("Got frameless method context");
}
return this.methodContext.frames().get(this.insn);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,19 @@ public record MethodContext(
@Unmodifiable Map<AbstractInsnNode, Frame<OriginalSourceValue>> frames
) {

public static MethodContext create(ClassWrapper classWrapper, MethodNode methodNode) {
/**
* Creates new {@link MethodContext} and computes its frames
*/
public static MethodContext compute(ClassWrapper classWrapper, MethodNode methodNode) {
Map<AbstractInsnNode, Frame<OriginalSourceValue>> frames = AsmHelper.analyzeSource(classWrapper.getClassNode(), methodNode);
return new MethodContext(classWrapper, methodNode, frames);
}

public InstructionContext createInsnContext(AbstractInsnNode insn) {
public static MethodContext frameless(ClassWrapper classWrapper, MethodNode methodNode) {
return new MethodContext(classWrapper, methodNode, null);
}

public InstructionContext newInsnContext(AbstractInsnNode insn) {
return new InstructionContext(insn, this);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package uwu.narumi.deobfuscator.api.asm;

import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;

/**
* @param owner Class that owns this method
* @param name Method's name
* @param desc Method's descriptor
*/
public record MethodRef(String owner, String name, String desc) {
public static MethodRef of(ClassNode classNode, MethodNode methodNode) {
return new MethodRef(classNode.name, methodNode.name, methodNode.desc);
}

public static MethodRef of(MethodInsnNode methodInsn) {
return new MethodRef(methodInsn.owner, methodInsn.name, methodInsn.desc);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ public MatchContext matchResult(InstructionContext insnContext) {
if (!this.stackMatches.isEmpty()) {
// Match values from stack

if (insnContext.methodContext().frames() == null) {
throw new IllegalStateException("Got frameless method context");
}

if (context.frame() == null) {
// If we expect stack values, then frame can't be null
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,12 @@
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.analysis.Frame;
import org.objectweb.asm.tree.analysis.OriginalSourceValue;
import uwu.narumi.deobfuscator.api.asm.ClassWrapper;
import uwu.narumi.deobfuscator.api.asm.InstructionContext;
import uwu.narumi.deobfuscator.api.asm.MethodContext;
import uwu.narumi.deobfuscator.api.context.Context;

import java.util.Arrays;
import java.util.Map;
import java.util.stream.Stream;

/**
Expand Down Expand Up @@ -60,7 +58,7 @@ protected void transform(ClassWrapper scope, Context context) throws Exception {
if (buildInstructionsStream(Arrays.stream(methodNode.instructions.toArray())).findAny().isEmpty()) return;

// Get frames of the method
MethodContext methodContext = MethodContext.create(classWrapper, methodNode);
MethodContext methodContext = MethodContext.compute(classWrapper, methodNode);

// Iterate over instructions
buildInstructionsStream(Arrays.stream(methodNode.instructions.toArray())).forEach(insn -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

import uwu.narumi.deobfuscator.api.transformer.ComposedTransformer;
import uwu.narumi.deobfuscator.core.other.impl.clean.peephole.JsrInlinerTransformer;
import uwu.narumi.deobfuscator.core.other.impl.pool.InlineStaticFieldTransformer;
import uwu.narumi.deobfuscator.core.other.impl.universal.UniversalNumberTransformer;
import uwu.narumi.deobfuscator.core.other.impl.zkm.ZelixLongEncryptionTransformer;
import uwu.narumi.deobfuscator.core.other.impl.zkm.ZelixUselessTryCatchRemoverTransformer;

/**
* Work in progress
Expand All @@ -11,7 +14,13 @@ public class ComposedZelixTransformer extends ComposedTransformer {
public ComposedZelixTransformer() {
super(
JsrInlinerTransformer::new,
ZelixLongEncryptionTransformer::new

// Fixes flow a bit
ZelixUselessTryCatchRemoverTransformer::new,

ZelixLongEncryptionTransformer::new,
InlineStaticFieldTransformer::new,
UniversalNumberTransformer::new
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@ public class PopUnUsedLocalVariablesTransformer extends Transformer {
@Override
protected void transform(ClassWrapper scope, Context context) throws Exception {
context.classes(scope).forEach(classWrapper -> classWrapper.methods().forEach(methodNode -> {
MethodContext methodContext = MethodContext.create(classWrapper, methodNode);
MethodContext methodContext = MethodContext.compute(classWrapper, methodNode);

List<VarInsnNode> varStoresInUse = new ArrayList<>();

// Find all local variables in use
for (AbstractInsnNode insn : methodNode.instructions.toArray()) {
if ((insn instanceof VarInsnNode && !insn.isVarStore()) || insn instanceof IincInsnNode) {
InstructionContext insnContext = methodContext.createInsnContext(insn);
InstructionContext insnContext = methodContext.newInsnContext(insn);

Frame<OriginalSourceValue> frame = insnContext.frame();
if (frame == null) return;
Expand All @@ -52,7 +52,7 @@ protected void transform(ClassWrapper scope, Context context) throws Exception {
for (AbstractInsnNode insn : methodNode.instructions.toArray()) {
if (insn instanceof VarInsnNode varInsnNode && insn.isVarStore()) {
if (!varStoresInUse.contains(varInsnNode)) {
InstructionContext insnContext = methodContext.createInsnContext(insn);
InstructionContext insnContext = methodContext.newInsnContext(insn);

// Pop the value from the stack
insnContext.pop(1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@
public class UniversalNumberTransformer extends ComposedTransformer {
public UniversalNumberTransformer() {
super(
MethodCallsOnLiteralsTransformer::new,
MathBinaryOperationsTransformer::new,
MathUnaryOperationsTransformer::new,
() -> new ComposedTransformer(true,
MethodCallsOnLiteralsTransformer::new,
MathBinaryOperationsTransformer::new,
MathUnaryOperationsTransformer::new
),

UselessPopCleanTransformer::new
);

this.rerunOnChange = true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@ protected void transform(ClassWrapper scope, Context context) throws Exception {
SandBox sandBox = new SandBox(context);

context.classes(scope).forEach(classWrapper -> classWrapper.findClInit().ifPresent(clinit -> {
MethodContext methodContext = MethodContext.create(classWrapper, clinit);
MethodContext methodContext = MethodContext.compute(classWrapper, clinit);

for (AbstractInsnNode insn : clinit.instructions) {
InstructionContext insnContext = methodContext.createInsnContext(insn);
InstructionContext insnContext = methodContext.newInsnContext(insn);
if (insnContext.frame() == null) return;

MatchContext result = DECRYPT_LONG_MATCHER.matchResult(insnContext);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package uwu.narumi.deobfuscator.core.other.impl.zkm;

import org.objectweb.asm.tree.MethodInsnNode;
import uwu.narumi.deobfuscator.api.asm.ClassWrapper;
import uwu.narumi.deobfuscator.api.asm.InstructionContext;
import uwu.narumi.deobfuscator.api.asm.MethodContext;
import uwu.narumi.deobfuscator.api.asm.MethodRef;
import uwu.narumi.deobfuscator.api.asm.matcher.Match;
import uwu.narumi.deobfuscator.api.asm.matcher.group.SequenceMatch;
import uwu.narumi.deobfuscator.api.asm.matcher.impl.MethodMatch;
import uwu.narumi.deobfuscator.api.asm.matcher.impl.OpcodeMatch;
import uwu.narumi.deobfuscator.api.context.Context;
import uwu.narumi.deobfuscator.api.transformer.Transformer;

import java.util.ArrayList;
import java.util.List;

/**
* Removes useless try catches
* <pre>
* {@code
* try {
* ...
* } catch (PacketEventsLoadFailureException packetEventsLoadFailureException) {
* throw PacketEvents.a(packetEventsLoadFailureException);
* }
*
* // Self return
* private static Exception a(Exception exception) {
* return exception;
* }
* }
* </pre>
*/
public class ZelixUselessTryCatchRemoverTransformer extends Transformer {
private static final Match INSTANT_RETURN_EXCEPTION =
SequenceMatch.of(
OpcodeMatch.of(ALOAD),
OpcodeMatch.of(ARETURN)
);

@Override
protected void transform(ClassWrapper scope, Context context) throws Exception {
context.classes(scope).forEach(classWrapper -> {
List<MethodRef> instantReturnExceptionMethods = new ArrayList<>();

// Find methods that instantly returns an exception
classWrapper.methods().forEach(methodNode -> {
MethodContext framelessContext = MethodContext.frameless(classWrapper, methodNode);

// Check instructions
if (methodNode.instructions.size() == 2 && INSTANT_RETURN_EXCEPTION.matches(framelessContext.newInsnContext(methodNode.instructions.getFirst()))) {
// Add it to list
instantReturnExceptionMethods.add(MethodRef.of(classWrapper.getClassNode(), methodNode));
}
});

Match invokeAndReturnMatch =
SequenceMatch.of(
MethodMatch.invokeStatic().and(Match.predicate(ctx -> {
MethodRef methodRef = MethodRef.of((MethodInsnNode) ctx.insn());
return instantReturnExceptionMethods.contains(methodRef);
})),
OpcodeMatch.of(ATHROW)
);

// Remove try-catches with these instant return exception methods
classWrapper.methods().forEach(methodNode -> {
MethodContext framelessContext = MethodContext.frameless(classWrapper, methodNode);

methodNode.tryCatchBlocks.removeIf(tryBlock -> {
InstructionContext start = framelessContext.newInsnContext(tryBlock.handler.getNext());
if (invokeAndReturnMatch.matches(start)) {
markChange();
return true;
} else {
return false;
}
});
});
});
}
}
Loading

0 comments on commit b2891c8

Please sign in to comment.