Манипуляции с байт-кодом Java - как внедрить в середине метода?

Я видел много фреймворков, которые позволяют внедрять байт-код в классы Java во время выполнения. Но во всех примерах и документации они просто показывают, как внедрить методы BEFORE и AFTER. Но мне нужно ввести где-то в СРЕДНЕМ методе. Как мне это сделать?

Вот пример метода, в который я мог бы ввести:

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

    if (someCondition) {
        doEvenMoreThings();
    }

    if (someOtherCondition) {
        doRandomStuff();
    }

    doStuff();
}

Я хочу залить сюда

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

чтобы полностью преобразованный метод выглядел примерно так:

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

    if (someCondition) {
        doEvenMoreThings();
    }

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

    doStuff();
}

Это возможно? Если да, то как?

Каждый фреймворк, который я когда-либо видел, имеет документацию, которая предполагает, что я могу внедрять только непосредственно над doOneThing(); или непосредственно под doStuff();.

Фреймворк, который вы используете, на самом деле не имеет значения, любой, который вам нравится, позволяет вам это сделать, это хороший ответ для меня.


person AppleDash    schedule 18.11.2015    source источник
comment
А зачем тебе это?   -  person niceman    schedule 18.11.2015
comment
@niceman Это имеет значение? Я хочу добавить свои собственные хуки к методам в программном обеспечении, которое я не могу декомпилировать.   -  person AppleDash    schedule 18.11.2015
comment
Имеет ли значение, когда вы говорите о своей цели, вы могли бы думать, как достичь ее каким-то образом, но тогда мы отвечаем другими способами.   -  person niceman    schedule 18.11.2015
comment
В вашем случае, хммм. Теоретически программное обеспечение, которое вы не можете декомпилировать, — это программное обеспечение, которым вы не можете манипулировать, но какое программное обеспечение вы хотите изменить и почему?   -  person niceman    schedule 18.11.2015
comment
Я хочу модифицировать пререлизные версии игры Майнкрафт различными способами. Из-за того, как устроен код, многие вещи, которые я хочу перехватить, скрыты в 100-строчных методах внутри оператора if, и нет другого способа или места для их перехвата.   -  person AppleDash    schedule 18.11.2015
comment
хмм очень амбициозный я должен сказать !!!! В любом случае, вы проверили лицензию Minecraft и разрешено ли вам манипулировать ею? если декомпиляция дала вам странные результаты, то они не хотят, чтобы их код манипулировали   -  person niceman    schedule 19.11.2015
comment
Самый простой способ, я думаю, таков: если вам что-то не нравится в игре, вы можете подчиняться им, в противном случае, я думаю, вам нечего делать. Чтобы ответить на вопрос в целом, да, это можно сделать при условии, что код не запутан (защищен от декомпиляции), а код, которым вы хотите манипулировать, не запускается JVM, но это требует хорошего понимания байт-кода Java и сложно.   -  person niceman    schedule 19.11.2015
comment
Вы можете использовать ASM для изменения данных. Хотя он вернется как байт [], который вам придется вручную загрузить через загрузчик классов.   -  person Dioxin    schedule 19.11.2015
comment
Причина, по которой вы не видите других примеров в руководствах, заключается в том, что они слишком сложны для руководства, особенно если они предназначены только для демонстрации использования библиотеки, а не для исчерпывающего введения в байт-код Java в целом.   -  person Holger    schedule 19.11.2015


Ответы (2)


Это легко, если вы используете библиотеку ASM (другие библиотеки байт-кода также должны иметь решение для этого).

Принимая во внимание библиотеку ASM, вы должны создать свой собственный MethodVisitor и отслеживать инструкцию вызова метода для doRandomStuff (для упрощения предположим, что есть ТОЛЬКО ОДИН вызов для doRandomStuff, иначе это будет более сложно).

Исходная последовательность байт-кода выглядит примерно так:

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

Тогда ваш MethodVisitor выглядит примерно так:

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()
           }
        }
}

Затем используя свой methodvisitor в теле visitMethod() какого-нибудь ClassVisitor.

person shijie xu    schedule 04.03.2016

Я бы порекомендовал изучить байт-код Java. Если приложение сильно запутано, его может быть трудно или невозможно изменить автоматически. Однако, если вы хорошо разбираетесь в байт-коде и готовы потратить время на его реинжиниринг, вы всегда можете изменить его, независимо от того, насколько он запутан.

Хорошее место для начала — чтение спецификации JVM. Затем попробуйте разобрать различные классы, чтобы понять, как конструкции исходного кода транслируются в байт-код.

Я бы порекомендовал дизассемблер/ассемблер Krakatau, так как он обрабатывает каждый неясный угол формата байт-кода и работает даже с запутанным кодом. К сожалению, Java 8 не поддерживается. (Раскрытие, я написал Кракатау)

https://github.com/Storyyeller/Krakatau

person Antimony    schedule 19.11.2015