Remove dummy local variables

master
Graham 5 years ago
parent 4ad8606b97
commit 7747fe457a
  1. 28
      asm/src/main/java/dev/openrs2/asm/InsnNodeUtils.java
  2. 4
      deob/src/main/java/dev/openrs2/deob/Deobfuscator.java
  3. 83
      deob/src/main/java/dev/openrs2/deob/transform/DummyLocalTransformer.java

@ -1,7 +1,10 @@
package dev.openrs2.asm; package dev.openrs2.asm;
import java.util.ArrayList;
import org.objectweb.asm.Opcodes; import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.InsnNode; import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.IntInsnNode; import org.objectweb.asm.tree.IntInsnNode;
import org.objectweb.asm.tree.LdcInsnNode; import org.objectweb.asm.tree.LdcInsnNode;
@ -305,6 +308,31 @@ public final class InsnNodeUtils {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
public static boolean deleteSimpleExpression(InsnList list, AbstractInsnNode last) {
var deadInsns = new ArrayList<AbstractInsnNode>();
var height = 0;
var insn = last;
do {
deadInsns.add(insn);
var metadata = StackMetadata.get(insn);
if (insn != last) {
height -= metadata.getPushes();
}
height += metadata.getPops();
if (height == 0) {
deadInsns.forEach(list::remove);
return true;
}
insn = insn.getPrevious();
} while (insn != null && insn.getType() != AbstractInsnNode.LABEL && !hasSideEffects(insn));
return false;
}
private InsnNodeUtils() { private InsnNodeUtils() {
/* empty */ /* empty */
} }

@ -18,6 +18,7 @@ import dev.openrs2.deob.transform.BitwiseOpTransformer;
import dev.openrs2.deob.transform.CanvasTransformer; import dev.openrs2.deob.transform.CanvasTransformer;
import dev.openrs2.deob.transform.ClassForNameTransformer; import dev.openrs2.deob.transform.ClassForNameTransformer;
import dev.openrs2.deob.transform.CounterTransformer; import dev.openrs2.deob.transform.CounterTransformer;
import dev.openrs2.deob.transform.DummyLocalTransformer;
import dev.openrs2.deob.transform.DummyTransformer; import dev.openrs2.deob.transform.DummyTransformer;
import dev.openrs2.deob.transform.ExceptionTracingTransformer; import dev.openrs2.deob.transform.ExceptionTracingTransformer;
import dev.openrs2.deob.transform.FieldOrderTransformer; import dev.openrs2.deob.transform.FieldOrderTransformer;
@ -40,7 +41,8 @@ public final class Deobfuscator {
new CanvasTransformer(), new CanvasTransformer(),
new FieldOrderTransformer(), new FieldOrderTransformer(),
new BitwiseOpTransformer(), new BitwiseOpTransformer(),
new DummyTransformer() new DummyTransformer(),
new DummyLocalTransformer()
); );
public static void main(String[] args) throws IOException, AnalyzerException { public static void main(String[] args) throws IOException, AnalyzerException {

@ -0,0 +1,83 @@
package dev.openrs2.deob.transform;
import com.google.common.collect.ImmutableSet;
import dev.openrs2.asm.InsnNodeUtils;
import dev.openrs2.asm.classpath.ClassPath;
import dev.openrs2.asm.transform.Transformer;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.VarInsnNode;
import org.objectweb.asm.tree.analysis.AnalyzerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class DummyLocalTransformer extends Transformer {
private static final Logger logger = LoggerFactory.getLogger(DummyLocalTransformer.class);
private static final ImmutableSet<Integer> LOAD_OPCODES = ImmutableSet.of(
Opcodes.ILOAD,
Opcodes.LLOAD,
Opcodes.FLOAD,
Opcodes.DLOAD,
Opcodes.ALOAD
);
private static final ImmutableSet<Integer> STORE_OPCODES = ImmutableSet.of(
Opcodes.ISTORE,
Opcodes.LSTORE,
Opcodes.FSTORE,
Opcodes.DSTORE,
Opcodes.ASTORE
);
private int localsRemoved;
@Override
protected void preTransform(ClassPath classPath) throws AnalyzerException {
localsRemoved = 0;
}
@Override
protected boolean transformCode(ClassNode clazz, MethodNode method) throws AnalyzerException {
/*
* XXX(gpe): this is primitive (ideally we'd do a proper data flow
* analysis, but we'd need to do it in reverse and ASM only supports
* forward data flow), however, it seems to be good enough to catch
* everything.
*/
var loads = new boolean[method.maxLocals];
for (var it = method.instructions.iterator(); it.hasNext(); ) {
var insn = it.next();
if (LOAD_OPCODES.contains(insn.getOpcode())) {
var load = (VarInsnNode) insn;
loads[load.var] = true;
}
}
for (var it = method.instructions.iterator(); it.hasNext(); ) {
var insn = it.next();
var opcode = insn.getOpcode();
if (!STORE_OPCODES.contains(opcode)) {
continue;
}
var store = (VarInsnNode) insn;
if (loads[store.var]) {
continue;
}
if (InsnNodeUtils.deleteSimpleExpression(method.instructions, insn)) {
localsRemoved++;
}
}
return false;
}
@Override
protected void postTransform(ClassPath classPath) throws AnalyzerException {
logger.info("Removed {} dummy local variables", localsRemoved);
}
}
Loading…
Cancel
Save