Внедрение метода Java _до_ вызова другого метода

Я использую ASM и хочу переписать что-то вроде:

someMethod().targetMethod(args...)

to:

someMethod().injectedMethod(arg).targetMethod(args...)

Проблема в том, что я не знаю, какой метод был раньше, я знаю только целевой метод (поэтому найти someMethod() и внедрить после этого не вариант).

У меня также есть много версий целевого метода с разными наборами параметров, с которыми я хочу, чтобы он работал.

Используя ASM, я могу легко найти вызов целевого метода, но, к сожалению, стек операндов в этот момент таков:

[ argN, ..., arg1, instance, ... ]

И хотя я могу вычислить, как далеко будет находиться экземпляр, я не могу внедрить байт-код, который будет его читать. Я знаю, что вы можете сделать это для 4 параметров, используя трюки с командами дублирования, но мне нужно общее решение.

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

Я думаю, что сработало бы, если бы я мог отслеживать, какие инструкции отвечают за размещение указателя экземпляра в стеке, а затем я мог бы внедрить вызов своего метода туда, а не при вызове целевого метода. Однако мне не повезло найти что-нибудь, чтобы помочь мне сделать это.

Я знаю, что такие вещи, как AspectJ, допускают это, но должны делать это для многих классов, поскольку они загружаются, а AspectJ слишком медленный.

Может ли кто-нибудь указать мне на инструменты анализа, построенные поверх ASM, которые могли бы позволить мне сделать это, или кто-нибудь может придумать лучший подход для внедрения одного вызова метода перед другим?


person David    schedule 18.12.2012    source источник
comment
Я не понимаю, вам действительно нужно использовать ASM? потому что кажется, что вы пытаетесь взломать существующий Jar. Для этого было бы лучше декомпилировать его ......... Если нет, и вы пишете java-приложение, можете ли вы использовать для этого агрегацию?   -  person fredcrs    schedule 18.12.2012
comment
Я не могу декомпилировать классы, потому что я могу подключить это только как javaagent во время загрузки класса. Я не «взламываю существующий jar» и не «пишу Java-приложение»; Я ввожу байт-код в классы по мере их загрузки.   -  person David    schedule 18.12.2012
comment
Я работал с Сут в школе и делал точно такие же вещи (хорошо, может быть, немного менее сложный). Посмотри. К сожалению, я не знаю, построен ли он поверх ASM. Единственный другой вариант, который приходит на ум, — это AspectJ.   -  person arin    schedule 19.12.2012


Ответы (3)


Если я правильно понимаю ваш вопрос, я добился того же, что и вы, но по-другому.

Используя модификацию байт-кода, управляемую событиями ASM, я прежде всего переименовал someMethod(arg, arg, arg) в copyOf_someMethod(arg, arg, arg). Затем я создал новый метод с именем someMethod(arg, arg, arg), который выполнил некоторую обработку, а затем вызвал copyOf_someMethod(arg, arg, arg).

Я переименовал метод в методе visitMethod(..) класса ClassVisitor, который я реализовал:

MethodVisitor methodVisitor =
    super.visitMethod(
        methodAccess, "copyOf_" + methodName, methodDesc,
            methodSignature, methodExceptions );

return methodVisitor;

В методе visitMethod(..) я также сохранил все детали подписи метода в переменных класса, готовых для использования в методе visitEnd().

На самом деле я сохранил детали метода в объекте MethodDetail и поместил его в очередь:

private Queue<MethodDetail> methodDetails = new LinkedList<MethodDetail>();

Я создал новую реализацию someMethod( arg, arg, arg ), используя метод visitEnd() класса ClassVisitor, который я реализовал. Я использовал ASMFier для генерации кода для добавления метода visitEnd(). Реализация использовала данные, которые я ранее сохранил в методе visitMethod(..). Новая реализация выполнила некоторую обработку, а затем вызвала метод copyOf_someMethod(). В методе visitEnd() я извлек все MethodDetail из очереди и для каждого MethodDetail вызвал код ASM, который я ранее сгенерировал ASMFier.

Используя этот дизайн, я создал прокси для метода, который выполнял некоторую обработку, а затем вызывал исходный метод. Обратите внимание, что исходный метод был переименован в copyOf_someMethod(..). Обратите также внимание, что я предоставил новую реализацию исходного метода, который выступал в качестве прокси.

Для поддержки нескольких аргументов я использовал ASMFier для генерации другого кода для 1 arg, 2 arg, 3 arg и т. д. Я поддерживал до 7 аргументов и выдавал неподдерживаемое исключение, если проксируемый метод имел более 7 аргументов. В методе visitEnd(..) я вызывал другой код (сгенерированный ASMFier) в зависимости от того, сколько аргументов метода было у исходного метода.

Я использовал javaagent для перехвата загрузки класса и изменения байтов.

Поскольку я новичок в ASM, возможно, я неправильно понял ваш вопрос, однако, если ваш вопрос касался создания прокси-сервера, который выполняет некоторую обработку, а затем вызывает исходный метод, тогда мое решение работает. Вроде не медленно. Время загрузки класса для класса с проксируемыми методами было ненамного медленнее, чем без модификации байт-кода. Скорость выполнения введенного кода не была низкой, код ASM, сгенерированный ASMFier, оказался очень быстрым.

Ваше здоровье

person John Dickerson    schedule 19.12.2012

В общем случае вы можете выгружать значения из стека во временные локальные переменные. Адаптер LocalVariableSorter из общего пакета ASM делает это очень легко. Но на самом деле методы с более чем 4 аргументами - редкость. В любом случае, это намного проще и надежнее, чем полномасштабный анализ потока данных во время выполнения.

org.objectweb.asm ASM. tree.analysis предоставляет средства для анализа потока данных, например вы можете использовать SourceInterpreter для отследить, какие инструкции произвели значения для каждой переменной и слотов стека. Дополнительные сведения см. в Руководстве пользователя ASM.

person Eugene Kuleshov    schedule 19.12.2012
comment
что вы подразумеваете под полномасштабным анализом данных во время выполнения? и как бы вы это сделали? - person vijay; 19.12.2012
comment
Я добавил некоторые разъяснения и прямые ссылки на документацию. - person Eugene Kuleshov; 19.12.2012
comment
Анализ дерева ASM предлагает статический анализ кода (из документации). Есть ли способ провести анализ во время выполнения? - person Marin; 11.06.2019
comment
Это стоило бы отдельного вопроса. - person Eugene Kuleshov; 12.06.2019

Загляните в org.objectweb.asm.tree.analysis. SourceIterpreter должен дать вам инструкции, которые помещают значение в стек.

person ruediste    schedule 19.12.2012