AppleDash
AppleDash

Reputation: 1714

Java Bytecode Manipulation - How to inject in middle of method?

I have seen many frameworks around that let you inject bytecode into Java classes at runtime. But in all of the examples and documentation, they just show how to inject BEFORE and AFTER methods. But I need to inject somewhere in the MIDDLE of a method. How do I do this?

Here is an example method I might want to inject into:

public void doSomething() {
    doOneThing();
    doSomeMoreStuff();

    if (someCondition) {
        doEvenMoreThings();
    }

    if (someOtherCondition) {
        doRandomStuff();
    }

    doStuff();
}

I want to inject here

if (someOtherCondition) {
    doRandomStuff();
    // INJECT HERE
}

so that the fully transformed method looks something like this:

public void doSomething() {
    doOneThing();
    doSomeMoreStuff();

    if (someCondition) {
        doEvenMoreThings();
    }

    if (someOtherCondition) {
        doRandomStuff();
        callMyInjectedMethodHere(); // This call has been injected
    }

    doStuff();
}

Is this possible? If so, how?

Every framework I've ever seen has docs that would suggest I can only inject directly above doOneThing(); or directly below doStuff();.

The framework you use doesn't really matter, any one you like that allows you to do this is a good answer for me.

Upvotes: 1

Views: 2220

Answers (2)

shijie xu
shijie xu

Reputation: 2097

It is easy if you use ASM library (Other bytecode libraries should also have solution for it ).

Considering the ASM library, you have to create your own MethodVisitor and track the method invocation instruction for doRandomStuff(Assume there is ONLY ONE invocation for doRandomStuff for simplification, otherwise, it would be more complicate).

The original bytecode sequence is something like:

  ...
 aload 0
 invokvirtual owner:doRandomStuff()V
 newLable 
 aload 0
 invokevirtual owner:doStuff()V

Then your MethodVisitor is something like:

class YourMethodVisitor extends MethodVisitor{
      @Override
      public void visitMethodInsn(int opcode, String owner, String name,
                String desc, boolean itf) {
          String target = MethodType.methodType(void.class).toMethodDescriptorString();
           super.visitMethodInsn(opcode, owner, name, desc, itf); //visit doRandomStuff() 

           if(opcode == Opcodes.INVOKEVIRTUAL && owner == "yourOwner" && name.equals("doRandomStuff") && desc.equals(target)){
               mv.visitVarInsn(Opcodes.ALOAD, 0);    //Load this
               super.visitMethodInsn(opcode, owner, "callMyInjectedMethodHere", target, itf);  //visit callMyInjectedMethodHere()
           }
        }
}

Then using your own methodvisitor in the body of visitMethod() of some ClassVisitor.

Upvotes: 2

Antimony
Antimony

Reputation: 39451

I'd recommend learning Java bytecode. If the application is heavily obfuscated, it can be difficult or impossible to modify in an automated fashion. However, if you are knowledgeable about bytecode and willing to take the time to reverse engineer it, you can always modify it, no matter how obfuscated it is.

A good place to start is reading the the JVM specification. Then try disassembling various classes to get a feeling for how source level constructs get translated into bytecode.

I'd recommend the Krakatau disassembler/assembler since it handles every obscure corner of the bytecode format and works even on obfuscated code. Unfortunately, Java 8 isn't supported. (Disclosure, I wrote Krakatau)

https://github.com/Storyyeller/Krakatau

Upvotes: -1

Related Questions