diff --git a/deobfuscator-api/src/main/java/org/objectweb/asm/tree/AbstractInsnNode.java b/deobfuscator-api/src/main/java/org/objectweb/asm/tree/AbstractInsnNode.java index 0efddd64..e6351504 100644 --- a/deobfuscator-api/src/main/java/org/objectweb/asm/tree/AbstractInsnNode.java +++ b/deobfuscator-api/src/main/java/org/objectweb/asm/tree/AbstractInsnNode.java @@ -37,6 +37,7 @@ import org.jetbrains.annotations.Nullable; import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; import uwu.narumi.deobfuscator.api.asm.NamedOpcodes; import org.objectweb.asm.Type; @@ -648,6 +649,66 @@ public String namedOpcode() { return NamedOpcodes.map(this.getOpcode()); } + /** + * Returns the number of stack values required by this instruction. + */ + public int getRequiredStackValuesCount() { + return switch (this.opcode) { + // Unary operations (one value) + case Opcodes.ISTORE, Opcodes.LSTORE, Opcodes.FSTORE, Opcodes.DSTORE, Opcodes.ASTORE, Opcodes.POP, Opcodes.POP2, + Opcodes.DUP, Opcodes.DUP_X1, Opcodes.DUP_X2, Opcodes.DUP2, Opcodes.DUP2_X1, Opcodes.DUP2_X2, Opcodes.SWAP, + Opcodes.INEG, Opcodes.LNEG, Opcodes.FNEG, Opcodes.DNEG, Opcodes.I2L, Opcodes.I2F, Opcodes.I2D, Opcodes.L2I, + Opcodes.L2F, Opcodes.L2D, Opcodes.F2I, Opcodes.F2L, Opcodes.F2D, Opcodes.D2I, Opcodes.D2L, Opcodes.D2F, + Opcodes.I2B, Opcodes.I2C, Opcodes.I2S, Opcodes.IFEQ, Opcodes.IFNE, Opcodes.IFLT, Opcodes.IFGE, Opcodes.IFGT, + Opcodes.IFLE, Opcodes.TABLESWITCH, Opcodes.LOOKUPSWITCH, Opcodes.IRETURN, Opcodes.LRETURN, Opcodes.FRETURN, + Opcodes.DRETURN, Opcodes.ARETURN, Opcodes.PUTSTATIC, Opcodes.GETFIELD, Opcodes.NEWARRAY, Opcodes.ANEWARRAY, + Opcodes.ARRAYLENGTH, Opcodes.ATHROW, Opcodes.CHECKCAST, Opcodes.INSTANCEOF, Opcodes.MONITORENTER, + Opcodes.MONITOREXIT, Opcodes.IFNULL, Opcodes.IFNONNULL -> 1; + // Binary operations (two values) + case Opcodes.IALOAD, Opcodes.LALOAD, Opcodes.FALOAD, Opcodes.DALOAD, Opcodes.AALOAD, Opcodes.BALOAD, + Opcodes.CALOAD, Opcodes.SALOAD, Opcodes.IADD, Opcodes.LADD, Opcodes.FADD, Opcodes.DADD, Opcodes.ISUB, + Opcodes.LSUB, Opcodes.FSUB, Opcodes.DSUB, Opcodes.IMUL, Opcodes.LMUL, Opcodes.FMUL, Opcodes.DMUL, + Opcodes.IDIV, Opcodes.LDIV, Opcodes.FDIV, Opcodes.DDIV, Opcodes.IREM, Opcodes.LREM, Opcodes.FREM, + Opcodes.DREM, Opcodes.ISHL, Opcodes.LSHL, Opcodes.ISHR, Opcodes.LSHR, Opcodes.IUSHR, Opcodes.LUSHR, + Opcodes.IAND, Opcodes.LAND, Opcodes.IOR, Opcodes.LOR, Opcodes.IXOR, Opcodes.LXOR, Opcodes.LCMP, + Opcodes.FCMPL, Opcodes.FCMPG, Opcodes.DCMPL, Opcodes.DCMPG, Opcodes.IF_ICMPEQ, Opcodes.IF_ICMPNE, + Opcodes.IF_ICMPLT, Opcodes.IF_ICMPGE, Opcodes.IF_ICMPGT, Opcodes.IF_ICMPLE, Opcodes.IF_ACMPEQ, + Opcodes.IF_ACMPNE, Opcodes.PUTFIELD -> 2; + // Ternary operations (three values) + case Opcodes.IASTORE, Opcodes.LASTORE, Opcodes.FASTORE, Opcodes.DASTORE, Opcodes.AASTORE, Opcodes.BASTORE, + Opcodes.CASTORE, Opcodes.SASTORE -> 3; + + // Method invocation + case Opcodes.INVOKEVIRTUAL, Opcodes.INVOKESPECIAL, Opcodes.INVOKESTATIC, Opcodes.INVOKEINTERFACE, + Opcodes.INVOKEDYNAMIC -> getRequiredStackValuesCountForMethodInvocation(); + // Multi-dimensional array creation + case Opcodes.MULTIANEWARRAY -> ((MultiANewArrayInsnNode) this).dims; + + default -> throw new IllegalStateException("Unknown opcode: " + this); + }; + } + + /** + * Calculates the number of stack values required for a method invocation by descriptor. + */ + private int getRequiredStackValuesCountForMethodInvocation() { + String desc; + if (this instanceof MethodInsnNode methodInsn) { + desc = methodInsn.desc; + } else if (this instanceof InvokeDynamicInsnNode invokeDynamicInsn) { + desc = invokeDynamicInsn.desc; + } else { + throw new IllegalStateException("Not a method instruction"); + } + + int count = Type.getArgumentCount(desc); // Arguments count = Stack values count + if (this.getOpcode() != Opcodes.INVOKESTATIC && this.getOpcode() != Opcodes.INVOKEDYNAMIC) { + count++; // "this" reference + } + + return count; + } + @Override public String toString() { return "(" + namedOpcode() + ") " + super.toString(); diff --git a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/InsnContext.java b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/InsnContext.java index 0e7aa57e..cd303168 100644 --- a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/InsnContext.java +++ b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/InsnContext.java @@ -1,8 +1,6 @@ package uwu.narumi.deobfuscator.api.asm; -import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.AbstractInsnNode; -import org.objectweb.asm.tree.InsnNode; import org.objectweb.asm.tree.MethodNode; import org.objectweb.asm.tree.analysis.Frame; import org.objectweb.asm.tree.analysis.OriginalSourceValue; @@ -50,12 +48,11 @@ public MethodContext methodContext() { } /** - * Places POP or POP2 instruction before current instruction to remove the value from the stack - * - * @param count Stack values count to pop + * Places POPs instructions before current instruction to remove source values from the stack. + * This method automatically calculates how many stack values to pop. */ - public void pop(int count) { - for (int i = 0; i < count; i++) { + public void placePops() { + for (int i = 0; i < this.insn.getRequiredStackValuesCount(); i++) { int stackValueIdx = frame().getStackSize() - (i + 1); OriginalSourceValue sourceValue = frame().getStack(stackValueIdx); diff --git a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/helper/AsmMathHelper.java b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/helper/AsmMathHelper.java index 30202c0f..0fd48ba5 100644 --- a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/helper/AsmMathHelper.java +++ b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/helper/AsmMathHelper.java @@ -130,7 +130,7 @@ public final class AsmMathHelper { AbstractInsnNode originalInsn = originalSourceValue.getProducer(); if (!originalInsn.isString()) return false; - context.pop(1); + context.placePops(); context.methodNode().instructions.set( context.insn(), AsmHelper.numberInsn(originalInsn.asString().length()) @@ -152,7 +152,7 @@ public final class AsmMathHelper { AbstractInsnNode originalInsn = originalSourceValue.getProducer(); if (!originalInsn.isString()) return false; - context.pop(1); + context.placePops(); context.methodNode().instructions.set( context.insn(), AsmHelper.numberInsn(originalInsn.asString().hashCode()) @@ -175,7 +175,7 @@ public final class AsmMathHelper { // Integer#parseInt(String) if (!originalInsn.isString()) return false; - context.pop(1); + context.placePops(); context.methodNode().instructions.set( context.insn(), AsmHelper.numberInsn(Integer.parseInt(originalInsn.asString())) @@ -202,7 +202,7 @@ public final class AsmMathHelper { // Integer#parseInt(String, int) if (!originalFirstInsn.isString() || !originalSecondInsn.isInteger()) return false; - context.pop(2); + context.placePops(); context.methodNode().instructions.set( context.insn(), AsmHelper.numberInsn( @@ -227,7 +227,7 @@ public final class AsmMathHelper { // Integer#reverse(int) if (!originalInsn.isInteger()) return false; - context.pop(1); + context.placePops(); context.methodNode().instructions.set( context.insn(), AsmHelper.numberInsn(Integer.reverse(originalInsn.asInteger())) @@ -250,7 +250,7 @@ public final class AsmMathHelper { // Long#reverse(long) if (!originalInsn.isLong()) return false; - context.pop(1); + context.placePops(); context.methodNode().instructions.set( context.insn(), AsmHelper.numberInsn(Long.reverse(originalInsn.asLong())) @@ -273,7 +273,7 @@ public final class AsmMathHelper { // Float#floatToIntBits(float) if (!originalInsn.isFloat()) return false; - context.pop(1); + context.placePops(); context.methodNode().instructions.set( context.insn(), AsmHelper.numberInsn(Float.floatToIntBits(originalInsn.asFloat())) @@ -296,7 +296,7 @@ public final class AsmMathHelper { // Float#intBitsToFloat(int) if (!originalInsn.isInteger()) return false; - context.pop(1); + context.placePops(); context.methodNode().instructions.set( context.insn(), AsmHelper.numberInsn(Float.intBitsToFloat(originalInsn.asInteger())) @@ -319,7 +319,7 @@ public final class AsmMathHelper { // Double#doubleToLongBits(double) if (!originalInsn.isDouble()) return false; - context.pop(1); + context.placePops(); context.methodNode().instructions.set( context.insn(), AsmHelper.numberInsn(Double.doubleToLongBits(originalInsn.asDouble())) @@ -342,7 +342,7 @@ public final class AsmMathHelper { // Double#longBitsToDouble(long) if (!originalInsn.isLong()) return false; - context.pop(1); + context.placePops(); context.methodNode().instructions.set( context.insn(), AsmHelper.numberInsn(Double.longBitsToDouble(originalInsn.asLong())) diff --git a/deobfuscator-impl/src/main/java/uwu/narumi/deobfuscator/Deobfuscator.java b/deobfuscator-impl/src/main/java/uwu/narumi/deobfuscator/Deobfuscator.java index cf7b207d..3f24eb69 100644 --- a/deobfuscator-impl/src/main/java/uwu/narumi/deobfuscator/Deobfuscator.java +++ b/deobfuscator-impl/src/main/java/uwu/narumi/deobfuscator/Deobfuscator.java @@ -94,6 +94,8 @@ private void loadInput() { LOGGER.error("Could not load external file: {}", externalFile.pathInJar(), e); } } + + LOGGER.info("Loaded {} classes", this.context.getClassesMap().size()); } private void loadClassOrFile(String pathInJar, byte[] bytes) { diff --git a/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/clean/peephole/PopUnUsedLocalVariablesTransformer.java b/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/clean/peephole/PopUnUsedLocalVariablesTransformer.java index 891919e5..86bbf4c3 100644 --- a/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/clean/peephole/PopUnUsedLocalVariablesTransformer.java +++ b/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/clean/peephole/PopUnUsedLocalVariablesTransformer.java @@ -53,7 +53,7 @@ protected void transform() throws Exception { InsnContext insnContext = methodContext.newInsnContext(insn); // Pop the value from the stack - insnContext.pop(1); + insnContext.placePops(); methodNode.instructions.remove(insn); diff --git a/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/universal/flow/CleanRedundantJumpsTransformer.java b/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/universal/flow/CleanRedundantJumpsTransformer.java index 118c966e..0484b61f 100644 --- a/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/universal/flow/CleanRedundantJumpsTransformer.java +++ b/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/universal/flow/CleanRedundantJumpsTransformer.java @@ -20,10 +20,8 @@ protected void transform() throws Exception { boolean ifResult = optIfResult.get(); - if (AsmMathHelper.isOneValueCondition(jumpInsn.getOpcode())) { - insnContext.pop(1); - } else if (AsmMathHelper.isTwoValuesCondition(jumpInsn.getOpcode())) { - insnContext.pop(2); + if (AsmMathHelper.isOneValueCondition(jumpInsn.getOpcode()) || AsmMathHelper.isTwoValuesCondition(jumpInsn.getOpcode())) { + insnContext.placePops(); } // Replace if with corresponding GOTO or remove it diff --git a/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/universal/flow/CleanRedundantSwitchesTransformer.java b/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/universal/flow/CleanRedundantSwitchesTransformer.java index 98af7a4a..368709d7 100644 --- a/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/universal/flow/CleanRedundantSwitchesTransformer.java +++ b/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/universal/flow/CleanRedundantSwitchesTransformer.java @@ -26,7 +26,7 @@ protected void transform() throws Exception { LabelNode predictedJump = optPredictedJump.get(); // Remove value from stack - insnContext.pop(1); + insnContext.placePops(); // Replace lookup switch with predicted jump insnContext.methodNode().instructions.set(lookupSwitchInsn, new JumpInsnNode(GOTO, predictedJump)); @@ -39,7 +39,7 @@ protected void transform() throws Exception { LabelNode predictedJump = optPredictedJump.get(); // Remove value from stack - insnContext.pop(1); + insnContext.placePops(); // Replace lookup switch with predicted jump insnContext.methodNode().instructions.set(tableSwitchInsn, new JumpInsnNode(GOTO, predictedJump)); diff --git a/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/universal/number/MathBinaryOperationsTransformer.java b/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/universal/number/MathBinaryOperationsTransformer.java index dadb41c6..79a820c4 100644 --- a/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/universal/number/MathBinaryOperationsTransformer.java +++ b/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/universal/number/MathBinaryOperationsTransformer.java @@ -39,7 +39,7 @@ protected void transform() throws Exception { return; } - insnContext.pop(2); + insnContext.placePops(); insnContext.methodNode().instructions.set(insnContext.insn(), AsmHelper.numberInsn(result)); markChange(); diff --git a/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/universal/number/MathUnaryOperationsTransformer.java b/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/universal/number/MathUnaryOperationsTransformer.java index fc18dab3..1d673e66 100644 --- a/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/universal/number/MathUnaryOperationsTransformer.java +++ b/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/universal/number/MathUnaryOperationsTransformer.java @@ -26,7 +26,7 @@ protected void transform() throws Exception { if (valueInsn.isNumber()) { Number castedNumber = AsmMathHelper.mathUnaryOperation(valueInsn.asNumber(), insnContext.insn().getOpcode()); - insnContext.pop(1); + insnContext.placePops(); insnContext.methodNode().instructions.set(insnContext.insn(), AsmHelper.numberInsn(castedNumber)); markChange();