关于 Java 字节码操作:Java 字节码操作 – 如何在方法中间注入?

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

我见过很多框架让你在运行时将字节码注入Java 类。但在所有示例和文档中,它们只是展示了如何注入 BEFORE 和 AFTER 方法。但我需要在方法中间的某个地方注入。我该怎么做?

这是我可能想要注入的示例方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void doSomething() {
    doOneThing();
    doSomeMoreStuff();

    if (someCondition) {
        doEvenMoreThings();
    }

    if (someOtherCondition) {
        doRandomStuff();
    }

    doStuff();
}

我想在这里注入

1
2
3
4
if (someOtherCondition) {
    doRandomStuff();
    // INJECT HERE
}

所以完全转换的方法看起来像这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void doSomething() {
    doOneThing();
    doSomeMoreStuff();

    if (someCondition) {
        doEvenMoreThings();
    }

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

    doStuff();
}

这可能吗?如果有,怎么做?

我见过的每个框架都有文档建议我只能在 doOneThing(); 的正上方或 doStuff(); 的正下方进行注入。

你使用的框架并不重要,任何你喜欢的允许你这样做的对我来说都是一个很好的答案。


如果你使用 ASM 库很容易(其他字节码库也应该有解决方案)。

考虑到 ASM 库,您必须创建自己的 MethodVisitor 并跟踪 doRandomStuff 的方法调用指令(为了简化,假设 doRandomStuff 只有一次调用,否则会更复杂)。

原始字节码序列类似于:

1
2
3
4
5
6
  ...
 aload 0
 invokvirtual owner:doRandomStuff()V
 newLable
 aload 0
 invokevirtual owner:doStuff()V

那么你的 MethodVisitor 类似于:

1
2
3
4
5
6
7
8
9
10
11
12
13
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()
           }
        }
}

然后在某些 ClassVisitor 的 visitMethod() 正文中使用您自己的 methodvisitor


我建议学习 Java 字节码。如果应用程序被严重混淆,则可能很难或不可能以自动化方式进行修改。但是,如果您了解字节码并愿意花时间对其进行逆向工程,则无论它多么模糊,您都可以随时修改它。

一个好的起点是阅读 JVM 规范。然后尝试反汇编各种类,以了解源级构造如何转换为字节码。

我推荐 Krakatau 反汇编器/汇编器,因为它可以处理字节码格式的每个晦涩角落,甚至可以处理混淆代码。不幸的是,不支持 Java 8。 (披露,我写了 Krakatau)

https://github.com/Storyyeller/Krakatau