Skip to content

Commit c90ae8c

Browse files
committed
Create AgentMain(JVM启动后动态Instrument)
1 parent b55c5f6 commit c90ae8c

2 files changed

Lines changed: 275 additions & 0 deletions

File tree

6.JavaAgent/AgentMain/AgentMain.md

Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
# AgentMain(JVM启动后动态Instrument)
2+
3+
## 简介
4+
5+
在 Java SE 5 当中,开发者只能在 premain 当中施展想象力,**所作的 Instrumentation 也仅限与 main 函数执行前,这样的方式存在一定的局限性**
6+
7+
在 Java SE 5 的基础上,Java SE 6 针对这种状况做出了改进,**开发者可以在 main 函数开始执行以后,再启动自己的 Instrumentation 程序**
8+
9+
在 Java SE 6 的 Instrumentation 当中,**有一个跟 premain“并驾齐驱”的“agentmain”方法,可以在 main 函数开始运行之后再运行**。跟 premain 函数一样, 开发者可以编写一个含有“agentmain”函数的 Java 类:
10+
11+
跟 premain 不同的是,agentmain 需要在 main 函数开始运行后才启动
12+
13+
## Attach API
14+
15+
Attach API 不是 Java 的标准 API,而是 Sun 公司提供的一套扩展 API,**用来向目标 JVM ”附着”(Attach)代理工具程序的**。有了它,**开发者可以方便的监控一个 JVM,运行一个外加的代理程序**
16+
17+
18+
19+
Attach API 很简单,只有 2 个主要的类,都在 com.sun.tools.attach 包里面:
20+
21+
**VirtualMachine 代表一个 Java 虚拟机**,也就是程序需要监控的目标虚拟机,提供了 JVM 枚举,**Attach 动作和 Detach 动作(Attach 动作的相反行为,从 JVM 上面解除一个代理)等等** ;
22+
23+
VirtualMachine类,该类允许我们 **通过给attach方法传入一个jvm的pid(进程id),远程连接到jvm上** 。然后我们可以 **通过loadAgent方法向jvm注册一个代理程序agent,在该agent的代理程序中会得到一个Instrumentation实例**,该实例可以 **在class加载前改变class的字节码,也可以在class加载后重新加载**。在调用Instrumentation实例的方法时,这些方法会使用ClassFileTransformer接口中提供的方法进行处理。
24+
25+
**VirtualMachineDescriptor 则是一个描述虚拟机的容器类**,配合 VirtualMachine 类完成各种功能。
26+
27+
28+
29+
30+
31+
## pom.xml
32+
33+
特别注意Agent-Class标签
34+
35+
```xml
36+
<?xml version="1.0" encoding="UTF-8"?>
37+
<project xmlns="http://maven.apache.org/POM/4.0.0"
38+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
39+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
40+
<modelVersion>4.0.0</modelVersion>
41+
42+
<groupId>org.example</groupId>
43+
<artifactId>AgentMainTest</artifactId>
44+
<version>1.0-SNAPSHOT</version>
45+
<packaging>jar</packaging>
46+
47+
<properties>
48+
<maven.compiler.source>8</maven.compiler.source>
49+
<maven.compiler.target>8</maven.compiler.target>
50+
</properties>
51+
<dependencies>
52+
<dependency>
53+
<groupId>com.sunn</groupId>
54+
<artifactId>tools</artifactId>
55+
<version>1.8.0</version>
56+
<scope>system</scope>
57+
<systemPath>D:/JDKV/jdk8u301/lib/tools.jar</systemPath>
58+
</dependency>
59+
<dependency>
60+
<groupId>org.javassist</groupId>
61+
<artifactId>javassist</artifactId>
62+
<version>3.21.0-GA</version>
63+
</dependency>
64+
65+
66+
</dependencies>
67+
68+
<build>
69+
70+
<pluginManagement>
71+
<plugins>
72+
<plugin>
73+
74+
<groupId>org.apache.maven.plugins</groupId>
75+
<artifactId>maven-jar-plugin</artifactId>
76+
<version>2.2</version>
77+
<configuration>
78+
<archive>
79+
<manifestEntries>
80+
<!--改这个为代理类-->
81+
<Agent-Class>AgentMain</Agent-Class>
82+
<Can-Redefine-Classes>true</Can-Redefine-Classes>
83+
<Can-Retransform-Classes>true</Can-Retransform-Classes>
84+
</manifestEntries>
85+
</archive>
86+
<skip>true</skip>
87+
</configuration>
88+
</plugin>
89+
</plugins>
90+
</pluginManagement>
91+
</build>
92+
93+
</project>
94+
```
95+
96+
97+
98+
## 代码示例
99+
100+
101+
102+
先生成一个恶意类,修改下sout以及return值
103+
104+
![](img/2.png)
105+
106+
并且写一个Test类
107+
108+
```Java
109+
public class Test {
110+
public static void main(String[] args) throws InterruptedException {
111+
System.out.println(new TransClass().getNumber());
112+
int count = 0;
113+
while (true) {
114+
Thread.sleep(500);
115+
count++;
116+
int number = new TransClass().getNumber();
117+
System.out.println(number);
118+
if (3 == number || count >= 10) {
119+
break;
120+
}
121+
}
122+
}
123+
}
124+
```
125+
126+
编译后改类名为TransClass.class.2
127+
128+
写个Transformer,把恶意类路径搞进去
129+
130+
```java
131+
import javassist.CannotCompileException;
132+
import javassist.ClassPool;
133+
import javassist.NotFoundException;
134+
135+
import java.io.ByteArrayOutputStream;
136+
import java.io.File;
137+
import java.io.FileInputStream;
138+
import java.io.IOException;
139+
import java.lang.instrument.ClassFileTransformer;
140+
import java.lang.instrument.IllegalClassFormatException;
141+
import java.security.ProtectionDomain;
142+
143+
public class Transformer implements ClassFileTransformer {
144+
public static final String classNumberReturns2 = "E:\\AgentMainTest\\target\\classes\\TransClass.class.2";
145+
146+
public Transformer() {
147+
}
148+
149+
public static byte[] getBytesFromFile(String fileName) throws Exception {
150+
FileInputStream fileInputStream = new FileInputStream(new File(fileName));
151+
byte[] bytes = new byte[1024];
152+
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
153+
154+
int a;
155+
while((a = fileInputStream.read(bytes)) != -1) {
156+
outputStream.write(bytes, 0, a);
157+
}
158+
159+
return outputStream.toByteArray();
160+
}
161+
162+
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
163+
if (!className.equals("TransClass")) {
164+
return null;
165+
} else {
166+
try {
167+
return getBytesFromFile(classNumberReturns2);
168+
} catch (Exception var7) {
169+
var7.printStackTrace();
170+
return null;
171+
}
172+
}
173+
}
174+
175+
public static void main(String[] args)throws Exception {
176+
System.out.println(getBytesFromFile(classNumberReturns2));
177+
}
178+
}
179+
```
180+
181+
网上那个代码好多地方有问题,pid获得写错等等
182+
183+
写个AgentMain
184+
185+
```Java
186+
import java.lang.instrument.ClassDefinition;
187+
import java.lang.instrument.Instrumentation;
188+
import java.lang.instrument.UnmodifiableClassException;
189+
190+
public class AgentMain {
191+
public static void agentmain(String agentArgs, Instrumentation inst) throws ClassNotFoundException, UnmodifiableClassException, InterruptedException {
192+
inst.addTransformer(new Transformer (), true);
193+
Class[] loadedClass = inst.getAllLoadedClasses();
194+
for (Class clazz : loadedClass){
195+
String className = clazz.getName();
196+
if (inst.isModifiableClass(clazz)){
197+
if (className.equals("TransClass")){
198+
try {
199+
inst.retransformClasses(clazz);
200+
} catch (UnmodifiableClassException e) {
201+
e.printStackTrace();
202+
}
203+
}
204+
205+
}
206+
}
207+
}
208+
}
209+
```
210+
211+
最后搞个AttachTest
212+
213+
```Java
214+
import com.sun.tools.attach.VirtualMachine;
215+
import com.sun.tools.attach.VirtualMachineDescriptor;
216+
217+
import java.util.List;
218+
219+
public class AttachTest {
220+
// 一个运行 Attach API 的线程子类
221+
// 每隔半秒时间检查一次所有的 Java 虚拟机
222+
static class AttachThread extends Thread {
223+
224+
private final List<VirtualMachineDescriptor> listBefore;
225+
226+
private final String jar;
227+
228+
AttachThread(String attachJar, List<VirtualMachineDescriptor> vms) {
229+
listBefore = vms; // 记录程序启动时的 VM 集合
230+
jar = attachJar;
231+
}
232+
233+
public void run() {
234+
VirtualMachine vm = null;
235+
List<VirtualMachineDescriptor> listAfter = null;
236+
try {
237+
int count = 0;
238+
while (true) {
239+
listAfter = VirtualMachine.list();
240+
for (VirtualMachineDescriptor vmd : listAfter) {
241+
242+
if (vmd.displayName().equals("Test")) {
243+
System.out.println("进程ID:" + vmd.id() + ",进程名称:" + vmd.displayName());
244+
System.out.println("捕捉到Test进程,准备Hook");
245+
vm = VirtualMachine.attach(vmd.id());
246+
break;
247+
}
248+
}
249+
Thread.sleep(500);
250+
count++;
251+
if (null != vm || count >= 10) {
252+
break;
253+
}
254+
}
255+
vm.loadAgent(jar);
256+
vm.detach();
257+
} catch (Exception e) {
258+
259+
}
260+
}
261+
}
262+
public static void main(String[] args) throws InterruptedException {
263+
new AttachThread("E:\\AgentMainTest\\target\\AgentMainTest-1.0-SNAPSHOT.jar", VirtualMachine.list()).start();
264+
}
265+
}
266+
```
267+
268+
运行这个AttachTest后,再运行
269+
270+
```bash
271+
java -cp AgentMainTest-1.0-SNAPSHOT.jar Test
272+
```
273+
274+
![](img/1.png)
275+

6.JavaAgent/AgentMain/img/2.png

79.5 KB
Loading

0 commit comments

Comments
 (0)