Reputation: 1714
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
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
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