In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-02-24 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
This article mainly introduces the Java Agent how to dynamically modify the bytecode, has a certain reference value, interested friends can refer to, I hope you can learn a lot after reading this article, the following let the editor take you to understand it.
1. What is Java Agent
Java Agent is a special type of class that can intercept applications running on JVM and modify their bytecode by using Java Instrumentation API. Java Agent is very powerful and dangerous.
Before I begin, I'll explain how Java Agent uses a simple HelloWorld example to intercept classes.
Public class Hello {public static void main (String [] args) {System.out.println ("hello world");}}
As shown in the following figure:
The class loader is responsible for loading the class from binary into memory. When running a compiled HelloWorld application (HelloWorld.class), you can think of Agent as a way to intercept classloader behavior at run time. You might wonder how the java bytecode is reconstructed so that Agent can add the relevant code in the right place. Interestingly, for Java programs, the bytecode structure is very close to the original Java program source code. So, although we don't add tools to the Java program itself, we use a very close representation of it. It is important to note that some non-Java languages can be compiled into Java bytecode (such as Scala, Clojure, and Kotlin), which means that the structure and shape of program bytecode can be very different.
2. Implement Java Agent
JavaAgent is based on facility from the Java platform, and its entry point is the Java.lang instrument package, which provides services that allow Agent to provide tools for programs running on JVM. The package is simple and self-contained because it contains two exception classes, a data class, a class definition, and two interfaces. Of these two methods, if we want to write Java Agent, we only need to implement the classFileTransformer interface.
There are two ways to define an agent.
The first is a static proxy, which means that we build the Agent proxy, package it as a jar file, and when we start the Java application, we pass in a special JVM parameter called java agent. Then we give it the location of the proxy jar on disk, and then JVM works its magic.
$java-javaagent:-jar
We need to add a special listing entry called the pre-main class, which is, of course, a fully qualified name class definition.
Premain-Class: org.example.JavaAgent
This class looks like this.
Public class JavaAgent {/ * * As soon as the JVM initializes, This method will be called. * * @ param agentArgs The list of agent arguments * @ param instrumentation The instrumentation object * @ throws InstantiationException * / public static void premain (String agentArgs, Instrumentation instrumentation) throws InstantiationException {InterceptingClassTransformer interceptingClassTransformer = new InterceptingClassTransformer (); interceptingClassTransformer.init (); instrumentation.addTransformer (interceptingClassTransformer);}}
The premain method accepts two parameters:
The agentArgs- string parameter, any parameter that the user chooses to pass to the Java Agent call as a parameter.
Instrumentation comes from the java.lang instrument package, and we can add a new ClassFileTransformer object that contains the actual logic of Agent.
The second option is called a dynamic proxy.
What we can do is not to detect how the application is started, but to write a small piece of code that receives and connects to an existing JVM and tells it to load an agent.
VirtualMachine vm = VirtualMachine.attach (vmPid); vm.load (agentFilePath); vm.detach ()
This parameter agentFilePath is exactly the same as the parameter in the static proxy method. It must be the file name of the agent jar, so there is no input stream and no bytes. There are two warnings for this approach. The first is that this is a private API that lives in com.sun space and is usually suitable for hot implementations. The second problem is that when you use Java9 for sorting, you can no longer use this code to connect to the JVM it is running.
2.1 Class conversion
This is the interface we need to implement for agent in order to transform the class.
Public interface ClassFileTransformer {byte [] transform (ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte [] classfileBuffer) throws IllegalClassFormatException;}
This is a bit much, but I will explain the necessary parameters in the method signature. The first important thing is the class name. The main purpose of this parameter is to help find and distinguish between the correct class to intercept and other classes. Obviously, you may not want to intercept every class in your application, and the easiest way is to check with conditional statements.
Then there is the classloader, which is mainly used in environments where the basic application does not have a flat class space, and you may not need to look at it, but once you encounter a more complex or modular platform, you need to look at the classloader. ClassfileBuffer is the current definition of the pre-detection class. To intercept it, you need to use the library to read the byte array and intercept the code, and then you must convert back to the bytecode again before returning.
There are several bytecode generation libraries. You need to do research and decide whether it is a high-level API or a low-level API, community size, and license. The demo I put below is Javassist, because I think it has a good balance between high-level and low-level API and is a triple license, so almost anyone can use it. this
Is the body of the ClassFileTransformer implementation.
@ Overridepublic byte [] transform (ClassLoader loader,..) Throws.. {byte [] byteCode = classfileBuffer; / / If you wanted to intercept all the classs then you can remove this conditional check. If (className.equals ("Example")) {try {ClassPool classPool = scopedClassPoolFactory.create (loader, rootPool, ScopedClassPoolRepositoryImpl.getInstance ()); CtClass ctClass = classPool.makeClass (new ByteArrayInputStream (classfileBuffer)); CtMethod [] methods = ctClass.getDeclaredMethods () For (CtMethod method: methods) {if (method.equals ("main")) {method.insertAfter ("System.out.println (\" Logging using Agent\ "););}} byteCode = ctClass.toBytecode (); ctClass.detach () } catch (Throwable ex) {log.log (Level.SEVERE, "Error in transforming the class:" + className, ex);}} return byteCode;} ctClass.detach ();} catch (Throwable ex) {log.log (Level.SEVERE, "Error in transforming the class:" + className, ex);}} return byteCode;}
Here, from the class pool, we can directly bypass classfileBuffer to get the class, because I want to use the main method. We loop through all the methods in the class definition to get the class we want. We don't need to use bytecodes at all. We can simply pass it some legitimate Java code, and then Javassist will compile it, generate new bytecode, and give a definition.
There are three ways to insert some Java code into a method. InsertAfter (..) inserts a bytecode at the end of the body. It inserts a bytecode at the end of the text. InsertAt (..) inserts a bytecode in the specified line of the body, and insertBefore (..) inserts a bytecode at the beginning of the text.
2.2 use the Java agent for actual operation
Download the sample application and Java Agent from the specified link.
Use the entry path and execute the command mvn clean install to build the two repo
You will now get the jar file in the target. Copy the path to the .jar file in the sample application, and copy the path to the-dependencies.jar file in Java Agent.
First, use the command $java-jar to run the application only with the sample application and observe the output. Hi I am main . It will be printed in the console.
Then, use the command $java-javaagent:-jar and observe the output. A log record using the agent will be printed separately in the console. This ensures that the java agent is intercepted and added to the body of the main method.
In summary, if you want to implement Java Agent, do the following:
1. You need to create two Java classes. One is the premain method (JavaAgent), and the other is the class that extends ClassFileTransformer (CustomTransformer)
two。 Within the body of the premain method, you need to add an object of the class that extends ClassFileTransformer
3. Then, you need to add logic to the override method transform in CustomTransformer.
4. When converting bytecode within a conversion method, you may need to use bytecode to generate libraries according to the purpose.
5. You need to specify the premain class in the listing and build the jar.
6. Use the javaagent tag to load the agent into the application you want to intercept.
Thank you for reading this article carefully. I hope the article "how to dynamically modify the bytecode in Java" shared by the editor will be helpful to everyone. At the same time, I also hope that you will support and pay attention to the industry information channel. More related knowledge is waiting for you to learn!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.