动态附加 Java 代理不起作用
Dynamically attaching a Java agent not working
我为编码挑战编写了以下代码。该程序的目的是附加到另一个 java 进程并在程序启动后加载检测引擎。这通常在 Java 中通过在代理 jar 文件中实施 agentmain 方法来完成。我在下面的代码中生成了这一切:
错误出现在 JVM 运行 玩具程序上,线索是它认为 Agent class 文件名出于某种原因是 Agent$1。我注释掉了我对 ByteBuddy 的使用,并尝试使用内置库加载代理文件,但它仍然失败。
heartbeat : 373 pid = 21992
heartbeat : 374 pid = 21992
Exception in thread "Attach Listener" java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at java.instrument/sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:491)
at java.instrument/sun.instrument.InstrumentationImpl.loadClassAndCallAgentmain(InstrumentationImpl.java:513)
Caused by: java.lang.NoClassDefFoundError: Agent
at Agent.agentmain(Agent.java:8)
... 6 more
Caused by: java.lang.ClassNotFoundException: Agent
... 7 more
Agent failed to start!
import com.sun.tools.attach.VirtualMachine;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.agent.ByteBuddyAgent;
import net.bytebuddy.dynamic.loading.ClassReloadingStrategy;
import net.bytebuddy.implementation.FixedValue;
import java.io.*;
import java.lang.instrument.Instrumentation;
import static net.bytebuddy.matcher.ElementMatchers.named;
public class Thief {
public static void main(String[] args) throws Exception {
Thief thief = new Thief();
VirtualMachine jvm = VirtualMachine.attach("21992");
File agentFile = new File("agent.jar");
jvm.loadAgent(agentFile.getAbsolutePath());
//thief.guessSecurityCode("24472");
}
public String guessSecurityCode(final String pid) throws Exception {
File jarFile = createAgent();
ByteBuddyAgent.attach(jarFile, pid);
return "0000";
}
private static String generateSimpleAgent2() {
return "import java.lang.instrument.ClassFileTransformer;" + "\n" +
"import java.lang.instrument.Instrumentation;" + "\n" +
"import java.security.ProtectionDomain;" + "\n" +
"\n\n" +
"public class Agent {" +"\n" +
" public static void agentmain(String argument, Instrumentation inst) {" +"\n" +
" }" +"\n" +
"}" +"\n";
}
private static String generateSimpleAgent() {
return "import java.lang.instrument.ClassFileTransformer;" + "\n" +
"import java.lang.instrument.Instrumentation;" + "\n" +
"import java.security.ProtectionDomain;" + "\n" +
"\n\n" +
"public class Agent {" +"\n" +
" public static void agentmain(String argument, Instrumentation inst) {" +"\n" +
" inst.addTransformer(new ClassFileTransformer() {" +"\n" +
" @Override" +"\n" +
" public byte[] transform(" +"\n" +
" ClassLoader loader," +"\n" +
" String className," +"\n" +
" Class<?> classBeingRedefined," +"\n" +
" ProtectionDomain protectionDomain," +"\n" +
" byte[] classFileBuffer) {" +"\n" +
" System.out.println(\"transform on : \" +className);" +"\n" +
" return classFileBuffer;" +"\n" +
" }" +"\n" +
" });" +"\n" +
" }" +"\n" +
"}" +"\n";
}
private static String generateAgentManifest() {
return String.join("\n", "Agent-Class: Agent",
"Can-Retransform-Classes: true",
"Can-Redefine-Classes: true",
"Premain-Class: Agent"
);
}
private static String generateAgentManifest2() {
return String.join("\n",
"Manifest-Version: 1.0",
"Agent-Class: Agent",
"Permissions: all-permissions"
);
}
private static String generateTransformer() {
return String.join("\n",
"import java.lang.instrument.ClassFileTransformer;",
"import java.security.ProtectionDomain;",
"import java.util.Arrays;",
"public class Transformer implements ClassFileTransformer {",
" public byte[] transform(ClassLoader loader, String className, Class<?> cls, ProtectionDomain dom, byte[] buf) {",
" return null;",
" }",
"}"
);
}
private static void writeFile(String path, String data) throws IOException {
final PrintWriter out = new PrintWriter(path);
out.print(data);
out.close();
}
private static void runCommand(String cmd) throws Exception {
System.out.println("[commmand] " + cmd);
String s;
Process p = Runtime.getRuntime().exec(cmd);
BufferedReader out = new BufferedReader(new InputStreamReader(p.getInputStream()));
while ((s = out.readLine()) != null) {
System.out.println("[out] " + s);
}
out = new BufferedReader(new InputStreamReader(p.getErrorStream()));
while ((s = out.readLine()) != null) {
System.out.println("[err] " + s);
}
p.waitFor();
System.out.println("[exit status] " + p.exitValue());
p.destroy();
}
private static File createAgent() throws Exception {
writeFile("Agent.java", generateSimpleAgent2());
writeFile("Transformer.java", generateTransformer());
writeFile("manifest.mf", generateAgentManifest2());
runCommand("javac Agent.java Transformer.java");
runCommand("jar -cfm agent.jar manifest.mf Agent.class Transformer.class");
return new File("agent.jar");
}
}
有人知道我做错了什么吗?
Agent
是匿名的class,所以一定是你在这里创建的:
new ClassFileTransformer() { ... }
至于为什么找不到,怎么解决我不知道,或许你可以在引用之前为它生成一个真实的名字class?
与 generateSimpleAgent2()
一起使用。它不会以同样的方式失败,就像您在 ewramner 的回答下的评论中所说的那样。那应该给你一个线索。此外,如果您只是阅读异常并相信它告诉您的内容,您就知道该怎么做。再次:
java.lang.ClassNotFoundException: Agent
即,generateSimpleAgent()
创建两个 class 文件,一个用于代理本身,一个用于其匿名 class。这正是 javac 所期望的。只需检查输出目录,您就会看到它。只需将缺少的 class 文件添加到 JAR 中即可:
runCommand("jar -cfm agent.jar manifest.mf Agent.class Agent.class Transformer.class");
现在带有匿名转换器的代理 class 也可以工作了。我测试了。
我为编码挑战编写了以下代码。该程序的目的是附加到另一个 java 进程并在程序启动后加载检测引擎。这通常在 Java 中通过在代理 jar 文件中实施 agentmain 方法来完成。我在下面的代码中生成了这一切:
错误出现在 JVM 运行 玩具程序上,线索是它认为 Agent class 文件名出于某种原因是 Agent$1。我注释掉了我对 ByteBuddy 的使用,并尝试使用内置库加载代理文件,但它仍然失败。
heartbeat : 373 pid = 21992
heartbeat : 374 pid = 21992
Exception in thread "Attach Listener" java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at java.instrument/sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:491)
at java.instrument/sun.instrument.InstrumentationImpl.loadClassAndCallAgentmain(InstrumentationImpl.java:513)
Caused by: java.lang.NoClassDefFoundError: Agent
at Agent.agentmain(Agent.java:8)
... 6 more
Caused by: java.lang.ClassNotFoundException: Agent
... 7 more
Agent failed to start!
import com.sun.tools.attach.VirtualMachine;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.agent.ByteBuddyAgent;
import net.bytebuddy.dynamic.loading.ClassReloadingStrategy;
import net.bytebuddy.implementation.FixedValue;
import java.io.*;
import java.lang.instrument.Instrumentation;
import static net.bytebuddy.matcher.ElementMatchers.named;
public class Thief {
public static void main(String[] args) throws Exception {
Thief thief = new Thief();
VirtualMachine jvm = VirtualMachine.attach("21992");
File agentFile = new File("agent.jar");
jvm.loadAgent(agentFile.getAbsolutePath());
//thief.guessSecurityCode("24472");
}
public String guessSecurityCode(final String pid) throws Exception {
File jarFile = createAgent();
ByteBuddyAgent.attach(jarFile, pid);
return "0000";
}
private static String generateSimpleAgent2() {
return "import java.lang.instrument.ClassFileTransformer;" + "\n" +
"import java.lang.instrument.Instrumentation;" + "\n" +
"import java.security.ProtectionDomain;" + "\n" +
"\n\n" +
"public class Agent {" +"\n" +
" public static void agentmain(String argument, Instrumentation inst) {" +"\n" +
" }" +"\n" +
"}" +"\n";
}
private static String generateSimpleAgent() {
return "import java.lang.instrument.ClassFileTransformer;" + "\n" +
"import java.lang.instrument.Instrumentation;" + "\n" +
"import java.security.ProtectionDomain;" + "\n" +
"\n\n" +
"public class Agent {" +"\n" +
" public static void agentmain(String argument, Instrumentation inst) {" +"\n" +
" inst.addTransformer(new ClassFileTransformer() {" +"\n" +
" @Override" +"\n" +
" public byte[] transform(" +"\n" +
" ClassLoader loader," +"\n" +
" String className," +"\n" +
" Class<?> classBeingRedefined," +"\n" +
" ProtectionDomain protectionDomain," +"\n" +
" byte[] classFileBuffer) {" +"\n" +
" System.out.println(\"transform on : \" +className);" +"\n" +
" return classFileBuffer;" +"\n" +
" }" +"\n" +
" });" +"\n" +
" }" +"\n" +
"}" +"\n";
}
private static String generateAgentManifest() {
return String.join("\n", "Agent-Class: Agent",
"Can-Retransform-Classes: true",
"Can-Redefine-Classes: true",
"Premain-Class: Agent"
);
}
private static String generateAgentManifest2() {
return String.join("\n",
"Manifest-Version: 1.0",
"Agent-Class: Agent",
"Permissions: all-permissions"
);
}
private static String generateTransformer() {
return String.join("\n",
"import java.lang.instrument.ClassFileTransformer;",
"import java.security.ProtectionDomain;",
"import java.util.Arrays;",
"public class Transformer implements ClassFileTransformer {",
" public byte[] transform(ClassLoader loader, String className, Class<?> cls, ProtectionDomain dom, byte[] buf) {",
" return null;",
" }",
"}"
);
}
private static void writeFile(String path, String data) throws IOException {
final PrintWriter out = new PrintWriter(path);
out.print(data);
out.close();
}
private static void runCommand(String cmd) throws Exception {
System.out.println("[commmand] " + cmd);
String s;
Process p = Runtime.getRuntime().exec(cmd);
BufferedReader out = new BufferedReader(new InputStreamReader(p.getInputStream()));
while ((s = out.readLine()) != null) {
System.out.println("[out] " + s);
}
out = new BufferedReader(new InputStreamReader(p.getErrorStream()));
while ((s = out.readLine()) != null) {
System.out.println("[err] " + s);
}
p.waitFor();
System.out.println("[exit status] " + p.exitValue());
p.destroy();
}
private static File createAgent() throws Exception {
writeFile("Agent.java", generateSimpleAgent2());
writeFile("Transformer.java", generateTransformer());
writeFile("manifest.mf", generateAgentManifest2());
runCommand("javac Agent.java Transformer.java");
runCommand("jar -cfm agent.jar manifest.mf Agent.class Transformer.class");
return new File("agent.jar");
}
}
有人知道我做错了什么吗?
Agent
是匿名的class,所以一定是你在这里创建的:
new ClassFileTransformer() { ... }
至于为什么找不到,怎么解决我不知道,或许你可以在引用之前为它生成一个真实的名字class?
与 generateSimpleAgent2()
一起使用。它不会以同样的方式失败,就像您在 ewramner 的回答下的评论中所说的那样。那应该给你一个线索。此外,如果您只是阅读异常并相信它告诉您的内容,您就知道该怎么做。再次:
java.lang.ClassNotFoundException: Agent
即,generateSimpleAgent()
创建两个 class 文件,一个用于代理本身,一个用于其匿名 class。这正是 javac 所期望的。只需检查输出目录,您就会看到它。只需将缺少的 class 文件添加到 JAR 中即可:
runCommand("jar -cfm agent.jar manifest.mf Agent.class Agent.class Transformer.class");
现在带有匿名转换器的代理 class 也可以工作了。我测试了。