Skip to content

3.4. Other goodies

EpicPlayerA10 edited this page Sep 7, 2025 · 2 revisions

The deobfuscator provides many utility functions and extensions that make working with bytecode easier. These "goodies" are small but powerful helpers that can significantly simplify transformer development.

πŸ”§ InsnContext Extensions

placePops()

Places POP instructions before the current instruction to remove the stack values that this instruction would consume.

public void placePops()

This method is used when you want to remove an instruction but need to clean up the stack values that would have been consumed by that instruction. It calculates how many stack values the current instruction consumes and inserts appropriate POP instructions.

Key concept: This is for removing instructions, not cleaning arbitrary stack values.

Example usage:

Original bytecode:

aload_0        # Stack: [this]
ldc 42         # Stack: [this, int] 
ldc "hello"    # Stack: [this, int, String]
ldc2_w 3.14    # Stack: [this, int, String, double]
invokevirtual SomeClass.someMethod (ILjava/lang/String;D)V  # Would consume all 4 values

Java code to remove the method call:

MethodInsnNode methodCall = ...; // The invokevirtual instruction
InsnContext insnContext = methodContext.at(methodCall);

insnContext.placePops();  // Automatically inserts appropriate POPs
methodNode.instructions.remove(methodCall);

Result after placePops():

aload_0        # Stack: [this]
ldc 42         # Stack: [this, int] 
ldc "hello"    # Stack: [this, int, String]
ldc2_w 3.14    # Stack: [this, int, String, double]
pop2           # Stack: [this, int, String] - removes double
pop            # Stack: [this, int] - removes String  
pop            # Stack: [this] - removes int
pop            # Stack: [] - removes this
# Method call removed, stack is clean

getConsumedStackValuesCount()

Returns how many stack values the instruction consumes from the stack.

public int getConsumedStackValuesCount()

Example values:

  • INVOKEVIRTUAL println(String)V β†’ 2 (PrintStream object + String parameter)
  • IADD β†’ 2 (two integers)
  • POP β†’ 1 (one value)
  • POP2 β†’ 2 (one long/double OR two single-word values)

πŸ—οΈ AbstractInsnNode Extensions

The deobfuscator extends ASM's AbstractInsnNode with many convenient methods:

Type Conversion Methods

asString()

Extracts string value from LDC instructions.

public String asString()

Example:

AbstractInsnNode insn = ...;
String value = insn.asString(); // "Hello World"

Bytecode:

ldc "Hello World"

asInteger(), asLong(), asFloat(), asDouble()

Extract numeric values from various numeric instructions.

public int asInteger()      // From ICONST_*, BIPUSH, SIPUSH, LDC
public long asLong()        // From LCONST_*, LDC  
public float asFloat()      // From FCONST_*, LDC
public double asDouble()    // From DCONST_*, LDC

Examples:

// Different ways integers can be encoded:
AbstractInsnNode insn1 = ...;
int value1 = insn1.asInteger(); // 5

AbstractInsnNode insn2 = ...;
int value2 = insn2.asInteger(); // 100

AbstractInsnNode insn3 = ...;
int value3 = insn3.asInteger(); // 50000

// Long values:
AbstractInsnNode longInsn = ...;
long longValue = longInsn.asLong(); // 123456789L

Bytecode examples:

iconst_5        # Pushes constant 5
bipush 100      # Pushes byte constant 100
ldc 50000       # Loads constant 50000
ldc 123456789L  # Loads long constant

asNumber()

Generic method that returns any numeric value as a Number.

public Number asNumber()

Usage:

AbstractInsnNode insn = ...;
if (insn.isNumber()) {
    Number value = insn.asNumber();
    // Handle any numeric type uniformly
}

asConstant()

Returns the constant value for instructions that define constant value. It kind of generalizes asString(), asInteger(), etc. and returns Object.

public Object asConstant()

Example:

AbstractInsnNode insn = ...;
Object constantValue = insn.asConstant(); // "Hello", 42, or 3.14

Bytecode examples:

ldc "Hello"    # String constant
ldc 42         # Integer constant  
ldc 3.14       # Double constant

Type Casting Methods

asJump(), asMethodInsn(), asFieldInsn()

Safe casting methods that return the instruction as specific types.

public JumpInsnNode asJump()
public MethodInsnNode asMethodInsn()  
public FieldInsnNode asFieldInsn()
public InvokeDynamicInsnNode asInvokeDynamicInsn()

Usage:

// Instead of manual casting:
if (insn instanceof MethodInsnNode methodInsn) {
    // use methodInsn
}

// Use the extension:
MethodInsnNode methodInsn = insn.asMethodInsn();
// Automatically handles the cast

Type Checking Methods

isNumber(), isString(), isType()

Quick type checking methods.

public boolean isNumber()    // Checks if instruction pushes a number
public boolean isString()    // Checks if instruction pushes a string  
public boolean isType()      // Checks if instruction pushes a Type

Example usage:

Arrays.stream(methodNode.instructions.toArray())
    .filter(AbstractInsnNode::isNumber)  // Only numeric instructions
    .forEach(insn -> {
        Number value = insn.asNumber();
        System.out.println("Found number: " + value);
    });

πŸ”¨ AsmHelper Utilities

Number Instruction Generation

Automatically creates the most efficient bytecode instruction for numbers.

numberInsn(int), numberInsn(long), etc.

public static AbstractInsnNode numberInsn(int number)
public static AbstractInsnNode numberInsn(long number)  
public static AbstractInsnNode numberInsn(float number)
public static AbstractInsnNode numberInsn(double number)

Examples:

AsmHelper.numberInsn(5)        // β†’ iconst_5
AsmHelper.numberInsn(100)      // β†’ bipush 100
AsmHelper.numberInsn(1000)     // β†’ sipush 1000  
AsmHelper.numberInsn(50000)    // β†’ ldc 50000

AsmHelper.numberInsn(0L)       // β†’ lconst_0
AsmHelper.numberInsn(1L)       // β†’ lconst_1
AsmHelper.numberInsn(123L)     // β†’ ldc 123L

Stack Management

toPop(OriginalSourceValue)

Creates appropriate POP instruction for a stack value.

public static AbstractInsnNode toPop(OriginalSourceValue value)

Logic:

  • Category 1 values (int, float, object) β†’ POP
  • Category 2 values (long, double) β†’ POP2

These utilities make bytecode manipulation much more readable and less error-prone. They handle edge cases and provide a more intuitive API for common operations.

Clone this wiki locally