Skip to content

Commit cf1a423

Browse files
committed
automatically calculate how many stack values to pop
1 parent a7dad4d commit cf1a423

File tree

9 files changed

+84
-26
lines changed

9 files changed

+84
-26
lines changed

deobfuscator-api/src/main/java/org/objectweb/asm/tree/AbstractInsnNode.java

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737

3838
import org.jetbrains.annotations.Nullable;
3939
import org.objectweb.asm.MethodVisitor;
40+
import org.objectweb.asm.Opcodes;
4041
import uwu.narumi.deobfuscator.api.asm.NamedOpcodes;
4142
import org.objectweb.asm.Type;
4243

@@ -648,6 +649,66 @@ public String namedOpcode() {
648649
return NamedOpcodes.map(this.getOpcode());
649650
}
650651

652+
/**
653+
* Returns the number of stack values required by this instruction.
654+
*/
655+
public int getRequiredStackValuesCount() {
656+
return switch (this.opcode) {
657+
// Unary operations (one value)
658+
case Opcodes.ISTORE, Opcodes.LSTORE, Opcodes.FSTORE, Opcodes.DSTORE, Opcodes.ASTORE, Opcodes.POP, Opcodes.POP2,
659+
Opcodes.DUP, Opcodes.DUP_X1, Opcodes.DUP_X2, Opcodes.DUP2, Opcodes.DUP2_X1, Opcodes.DUP2_X2, Opcodes.SWAP,
660+
Opcodes.INEG, Opcodes.LNEG, Opcodes.FNEG, Opcodes.DNEG, Opcodes.I2L, Opcodes.I2F, Opcodes.I2D, Opcodes.L2I,
661+
Opcodes.L2F, Opcodes.L2D, Opcodes.F2I, Opcodes.F2L, Opcodes.F2D, Opcodes.D2I, Opcodes.D2L, Opcodes.D2F,
662+
Opcodes.I2B, Opcodes.I2C, Opcodes.I2S, Opcodes.IFEQ, Opcodes.IFNE, Opcodes.IFLT, Opcodes.IFGE, Opcodes.IFGT,
663+
Opcodes.IFLE, Opcodes.TABLESWITCH, Opcodes.LOOKUPSWITCH, Opcodes.IRETURN, Opcodes.LRETURN, Opcodes.FRETURN,
664+
Opcodes.DRETURN, Opcodes.ARETURN, Opcodes.PUTSTATIC, Opcodes.GETFIELD, Opcodes.NEWARRAY, Opcodes.ANEWARRAY,
665+
Opcodes.ARRAYLENGTH, Opcodes.ATHROW, Opcodes.CHECKCAST, Opcodes.INSTANCEOF, Opcodes.MONITORENTER,
666+
Opcodes.MONITOREXIT, Opcodes.IFNULL, Opcodes.IFNONNULL -> 1;
667+
// Binary operations (two values)
668+
case Opcodes.IALOAD, Opcodes.LALOAD, Opcodes.FALOAD, Opcodes.DALOAD, Opcodes.AALOAD, Opcodes.BALOAD,
669+
Opcodes.CALOAD, Opcodes.SALOAD, Opcodes.IADD, Opcodes.LADD, Opcodes.FADD, Opcodes.DADD, Opcodes.ISUB,
670+
Opcodes.LSUB, Opcodes.FSUB, Opcodes.DSUB, Opcodes.IMUL, Opcodes.LMUL, Opcodes.FMUL, Opcodes.DMUL,
671+
Opcodes.IDIV, Opcodes.LDIV, Opcodes.FDIV, Opcodes.DDIV, Opcodes.IREM, Opcodes.LREM, Opcodes.FREM,
672+
Opcodes.DREM, Opcodes.ISHL, Opcodes.LSHL, Opcodes.ISHR, Opcodes.LSHR, Opcodes.IUSHR, Opcodes.LUSHR,
673+
Opcodes.IAND, Opcodes.LAND, Opcodes.IOR, Opcodes.LOR, Opcodes.IXOR, Opcodes.LXOR, Opcodes.LCMP,
674+
Opcodes.FCMPL, Opcodes.FCMPG, Opcodes.DCMPL, Opcodes.DCMPG, Opcodes.IF_ICMPEQ, Opcodes.IF_ICMPNE,
675+
Opcodes.IF_ICMPLT, Opcodes.IF_ICMPGE, Opcodes.IF_ICMPGT, Opcodes.IF_ICMPLE, Opcodes.IF_ACMPEQ,
676+
Opcodes.IF_ACMPNE, Opcodes.PUTFIELD -> 2;
677+
// Ternary operations (three values)
678+
case Opcodes.IASTORE, Opcodes.LASTORE, Opcodes.FASTORE, Opcodes.DASTORE, Opcodes.AASTORE, Opcodes.BASTORE,
679+
Opcodes.CASTORE, Opcodes.SASTORE -> 3;
680+
681+
// Method invocation
682+
case Opcodes.INVOKEVIRTUAL, Opcodes.INVOKESPECIAL, Opcodes.INVOKESTATIC, Opcodes.INVOKEINTERFACE,
683+
Opcodes.INVOKEDYNAMIC -> getRequiredStackValuesCountForMethodInvocation();
684+
// Multi-dimensional array creation
685+
case Opcodes.MULTIANEWARRAY -> ((MultiANewArrayInsnNode) this).dims;
686+
687+
default -> throw new IllegalStateException("Unknown opcode: " + this);
688+
};
689+
}
690+
691+
/**
692+
* Calculates the number of stack values required for a method invocation by descriptor.
693+
*/
694+
private int getRequiredStackValuesCountForMethodInvocation() {
695+
String desc;
696+
if (this instanceof MethodInsnNode methodInsn) {
697+
desc = methodInsn.desc;
698+
} else if (this instanceof InvokeDynamicInsnNode invokeDynamicInsn) {
699+
desc = invokeDynamicInsn.desc;
700+
} else {
701+
throw new IllegalStateException("Not a method instruction");
702+
}
703+
704+
int count = Type.getArgumentCount(desc); // Arguments count = Stack values count
705+
if (this.getOpcode() != Opcodes.INVOKESTATIC && this.getOpcode() != Opcodes.INVOKEDYNAMIC) {
706+
count++; // "this" reference
707+
}
708+
709+
return count;
710+
}
711+
651712
@Override
652713
public String toString() {
653714
return "(" + namedOpcode() + ") " + super.toString();

deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/InsnContext.java

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package uwu.narumi.deobfuscator.api.asm;
22

3-
import org.objectweb.asm.Opcodes;
43
import org.objectweb.asm.tree.AbstractInsnNode;
5-
import org.objectweb.asm.tree.InsnNode;
64
import org.objectweb.asm.tree.MethodNode;
75
import org.objectweb.asm.tree.analysis.Frame;
86
import org.objectweb.asm.tree.analysis.OriginalSourceValue;
@@ -50,12 +48,11 @@ public MethodContext methodContext() {
5048
}
5149

5250
/**
53-
* Places POP or POP2 instruction before current instruction to remove the value from the stack
54-
*
55-
* @param count Stack values count to pop
51+
* Places POPs instructions before current instruction to remove source values from the stack.
52+
* This method automatically calculates how many stack values to pop.
5653
*/
57-
public void pop(int count) {
58-
for (int i = 0; i < count; i++) {
54+
public void placePops() {
55+
for (int i = 0; i < this.insn.getRequiredStackValuesCount(); i++) {
5956
int stackValueIdx = frame().getStackSize() - (i + 1);
6057
OriginalSourceValue sourceValue = frame().getStack(stackValueIdx);
6158

deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/helper/AsmMathHelper.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ public final class AsmMathHelper {
130130
AbstractInsnNode originalInsn = originalSourceValue.getProducer();
131131
if (!originalInsn.isString()) return false;
132132

133-
context.pop(1);
133+
context.placePops();
134134
context.methodNode().instructions.set(
135135
context.insn(),
136136
AsmHelper.numberInsn(originalInsn.asString().length())
@@ -152,7 +152,7 @@ public final class AsmMathHelper {
152152
AbstractInsnNode originalInsn = originalSourceValue.getProducer();
153153
if (!originalInsn.isString()) return false;
154154

155-
context.pop(1);
155+
context.placePops();
156156
context.methodNode().instructions.set(
157157
context.insn(),
158158
AsmHelper.numberInsn(originalInsn.asString().hashCode())
@@ -175,7 +175,7 @@ public final class AsmMathHelper {
175175
// Integer#parseInt(String)
176176
if (!originalInsn.isString()) return false;
177177

178-
context.pop(1);
178+
context.placePops();
179179
context.methodNode().instructions.set(
180180
context.insn(),
181181
AsmHelper.numberInsn(Integer.parseInt(originalInsn.asString()))
@@ -202,7 +202,7 @@ public final class AsmMathHelper {
202202
// Integer#parseInt(String, int)
203203
if (!originalFirstInsn.isString() || !originalSecondInsn.isInteger()) return false;
204204

205-
context.pop(2);
205+
context.placePops();
206206
context.methodNode().instructions.set(
207207
context.insn(),
208208
AsmHelper.numberInsn(
@@ -227,7 +227,7 @@ public final class AsmMathHelper {
227227
// Integer#reverse(int)
228228
if (!originalInsn.isInteger()) return false;
229229

230-
context.pop(1);
230+
context.placePops();
231231
context.methodNode().instructions.set(
232232
context.insn(),
233233
AsmHelper.numberInsn(Integer.reverse(originalInsn.asInteger()))
@@ -250,7 +250,7 @@ public final class AsmMathHelper {
250250
// Long#reverse(long)
251251
if (!originalInsn.isLong()) return false;
252252

253-
context.pop(1);
253+
context.placePops();
254254
context.methodNode().instructions.set(
255255
context.insn(),
256256
AsmHelper.numberInsn(Long.reverse(originalInsn.asLong()))
@@ -273,7 +273,7 @@ public final class AsmMathHelper {
273273
// Float#floatToIntBits(float)
274274
if (!originalInsn.isFloat()) return false;
275275

276-
context.pop(1);
276+
context.placePops();
277277
context.methodNode().instructions.set(
278278
context.insn(),
279279
AsmHelper.numberInsn(Float.floatToIntBits(originalInsn.asFloat()))
@@ -296,7 +296,7 @@ public final class AsmMathHelper {
296296
// Float#intBitsToFloat(int)
297297
if (!originalInsn.isInteger()) return false;
298298

299-
context.pop(1);
299+
context.placePops();
300300
context.methodNode().instructions.set(
301301
context.insn(),
302302
AsmHelper.numberInsn(Float.intBitsToFloat(originalInsn.asInteger()))
@@ -319,7 +319,7 @@ public final class AsmMathHelper {
319319
// Double#doubleToLongBits(double)
320320
if (!originalInsn.isDouble()) return false;
321321

322-
context.pop(1);
322+
context.placePops();
323323
context.methodNode().instructions.set(
324324
context.insn(),
325325
AsmHelper.numberInsn(Double.doubleToLongBits(originalInsn.asDouble()))
@@ -342,7 +342,7 @@ public final class AsmMathHelper {
342342
// Double#longBitsToDouble(long)
343343
if (!originalInsn.isLong()) return false;
344344

345-
context.pop(1);
345+
context.placePops();
346346
context.methodNode().instructions.set(
347347
context.insn(),
348348
AsmHelper.numberInsn(Double.longBitsToDouble(originalInsn.asLong()))

deobfuscator-impl/src/main/java/uwu/narumi/deobfuscator/Deobfuscator.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ private void loadInput() {
9494
LOGGER.error("Could not load external file: {}", externalFile.pathInJar(), e);
9595
}
9696
}
97+
98+
LOGGER.info("Loaded {} classes", this.context.getClassesMap().size());
9799
}
98100

99101
private void loadClassOrFile(String pathInJar, byte[] bytes) {

deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/clean/peephole/PopUnUsedLocalVariablesTransformer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ protected void transform() throws Exception {
5353
InsnContext insnContext = methodContext.newInsnContext(insn);
5454

5555
// Pop the value from the stack
56-
insnContext.pop(1);
56+
insnContext.placePops();
5757

5858
methodNode.instructions.remove(insn);
5959

deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/universal/flow/CleanRedundantJumpsTransformer.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,8 @@ protected void transform() throws Exception {
2020

2121
boolean ifResult = optIfResult.get();
2222

23-
if (AsmMathHelper.isOneValueCondition(jumpInsn.getOpcode())) {
24-
insnContext.pop(1);
25-
} else if (AsmMathHelper.isTwoValuesCondition(jumpInsn.getOpcode())) {
26-
insnContext.pop(2);
23+
if (AsmMathHelper.isOneValueCondition(jumpInsn.getOpcode()) || AsmMathHelper.isTwoValuesCondition(jumpInsn.getOpcode())) {
24+
insnContext.placePops();
2725
}
2826

2927
// Replace if with corresponding GOTO or remove it

deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/universal/flow/CleanRedundantSwitchesTransformer.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ protected void transform() throws Exception {
2626

2727
LabelNode predictedJump = optPredictedJump.get();
2828
// Remove value from stack
29-
insnContext.pop(1);
29+
insnContext.placePops();
3030
// Replace lookup switch with predicted jump
3131
insnContext.methodNode().instructions.set(lookupSwitchInsn, new JumpInsnNode(GOTO, predictedJump));
3232

@@ -39,7 +39,7 @@ protected void transform() throws Exception {
3939

4040
LabelNode predictedJump = optPredictedJump.get();
4141
// Remove value from stack
42-
insnContext.pop(1);
42+
insnContext.placePops();
4343
// Replace lookup switch with predicted jump
4444
insnContext.methodNode().instructions.set(tableSwitchInsn, new JumpInsnNode(GOTO, predictedJump));
4545

deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/universal/number/MathBinaryOperationsTransformer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ protected void transform() throws Exception {
3939
return;
4040
}
4141

42-
insnContext.pop(2);
42+
insnContext.placePops();
4343
insnContext.methodNode().instructions.set(insnContext.insn(), AsmHelper.numberInsn(result));
4444

4545
markChange();

deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/universal/number/MathUnaryOperationsTransformer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ protected void transform() throws Exception {
2626
if (valueInsn.isNumber()) {
2727
Number castedNumber = AsmMathHelper.mathUnaryOperation(valueInsn.asNumber(), insnContext.insn().getOpcode());
2828

29-
insnContext.pop(1);
29+
insnContext.placePops();
3030
insnContext.methodNode().instructions.set(insnContext.insn(), AsmHelper.numberInsn(castedNumber));
3131

3232
markChange();

0 commit comments

Comments
 (0)