1

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.

AppleDash
  • 1,544
  • 2
  • 13
  • 27
  • And why do you want this ? – niceman Nov 18 '15 at 20:45
  • @niceman Does it matter? I want to add my own hooks to methods in software I cannot decompile. – AppleDash Nov 18 '15 at 20:48
  • "Does it matter" when you say your goal, you could think to achieve it in a way but then we answer by other ways. – niceman Nov 18 '15 at 20:52
  • In your case, hmmmm Theoretically a software you can't decompile is a software you can't manipulate , but what software do you want to change and why ? – niceman Nov 18 '15 at 20:53
  • I want to modify prerelease versions of the game Minecraft in various ways. The way the code is laid out, a lot of the things I want to hook are buried in 100-line methods inside of an if statement, with no other way or place to hook them. – AppleDash Nov 18 '15 at 20:57
  • hmmm very ambitious I've got to say !!!! Anyway did you check the license of Minecraft and whether you're allowed to manipulate it ? if decompiling gave you strange results then they don't want their code to be manipulated – niceman Nov 18 '15 at 21:05
  • the easy way I think is this : if you don't like something in the game, you can submit to them, otherwise I think you have nothing to do. To answer the question in "General" yes this could be done provided the code isn't obfuscated (protected against decompile) and the code you want to manipulate isn't run by JVM, but this needs good understand of java bytecode and is hard. – niceman Nov 18 '15 at 21:10
  • You could use ASM to modify the data. Although it'll return as a byte[], which you'll have to manually load via a classloader – Vince Nov 19 '15 at 02:13
  • The reason you don’t see other examples in tutorials is that they are too complicated for a tutorial, especially if they are only meant to demonstrate the usage of a library rather than give an exhaustive introduction into Java byte code in general. – Holger Nov 19 '15 at 16:31

2 Answers2

2

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.

shijie xu
  • 1,975
  • 21
  • 52
-1

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

Antimony
  • 37,781
  • 10
  • 100
  • 107