Java安全笔记(4)-Java Agent
学习背景
- Agent本来就在学习的清单上
- 更深入理解下草师傅说的代理模式
- study the world
AOP
AOP为Aspect Oriented Programming的缩写,意为: 面向切面编程,在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。
以下引用来自su18和天下大木头师傅的博客:
JDK 1.5 开始,Java新增了Instrumentation(Java Agent API)和JVMTI(JVM Tool Interface)功能,允许JVM在加载某个class文件之前对其字节码进行修改,同时也支持对已加载的class(类字节码)进行重新加载(Retransform)。
开发者可以在一个普通Java程序(带有main函数的Java类)运行时,通过
–javaagent
参数指定一个特定的jar文件(包含Instrumentation代理)来启动Instrumentation的代理程序。在类的字节码载入jvm前会调用ClassFileTransformer的transform方法,从而实现修改原类方法的功能,实现AOP。
通过java.lang.instrument实现的工具我们称之为Java Agent,Java Agent能够在不影响正常编译的情况下来修改字节码,即动态修改已加载或者未加载的类,包括类的属性、方法,Agent内存马的实现就是利用了这一特性使其动态修改特定类的特定方法,将我们的恶意方法添加进去。
说白了Java Agent只是一个Java类而已,只不过普通的Java类是以main函数作为入口点的,Java Agent的入口点则是premain和agentmain
Java Agent 支持两种方式进行加载:
- 实现 premain 方法,在启动时进行加载 (该特性在 jdk 1.5 之后才有)
- 实现 agentmain 方法,在启动后进行加载 (该特性在 jdk 1.6 之后才有)
premain
首先创建一个premain的agent:
import java.lang.instrument.Instrumentation;
public class DemoTest {
public static void premain(String agentArgs, Instrumentation inst) throws Exception{
System.out.println(agentArgs);
for(int i=0;i<5;i++){
System.out.println("premain method is invoked!");
}
}
}
然后通过mvn或者mf文件打生成jar包:
Manifest-Version: 1.0
Premain-Class: DemoTest
利用javac生成class文件之后打包:jar cvfm agent.jar agent.mf DemoTest.class
然后再创建一个普通的类Hello.java
和mf文件:
public class Hello {
public static void main(String[] args) {
System.out.println("Hello,Java");
}
}
mf文件:
Manifest-Version: 1.0
Main-Class: Hello
同样打包为jar: jar cvfm hello.jar hello.mf Hello.class
接下来运行的时候增加-javaagent:agent.jar: java -javaagent:agent.jar=Hello -jar hello.jar
agentmain
首先对这种方式有一个初步的认识,需要三个文件:
App.java
循环输出,模拟运行的应用
package agentmain.demo1;
import java.lang.management.ManagementFactory;
public class App {
public static void main(String[] args) throws InterruptedException {
while (true){
String name = ManagementFactory.getRuntimeMXBean().getName();
String pid = name.split("@")[0];
System.out.println("Pid is:" + pid);
for(int i=0;i<500;i++){
System.out.println("App is Running!");
Thread.sleep(2000);
}
}
}
}
AgentDemo.java
实现agentmain
功能,用于agent的功能实现
package agentmain.demo1;
import java.lang.instrument.Instrumentation;
public class AgentDemo {
public static void agentmain(String agentArgs, Instrumentation inst){
System.out.println("Surprise! I'm Agent Smith");
}
}
Inject.java
通过VirtualMachine的loadAgent
方式,把Agent注入正在运行的App里:
package agentmain.demo1;
import com.sun.tools.attach.VirtualMachine;
public class Inject {
public static void main(String[] args) throws Exception {
String pid = args[0];
String jarName = args[1];
VirtualMachine vm = VirtualMachine.attach(pid);
vm.loadAgent(jarName);
vm.detach();
}
}
以上三个java文件通过IDEA打包为Jar文件,首先运行App.jar:
java -cp JavaAgentDemo.jar agentmain.demo1.App
然后使用Inject.jar注入进程:
java -cp JavaAgentDemo.jar agentmain.demo1.Inject 22932 /var/tmp/AgentDemo.jar
可以看到App.jar的输出被动态的改变了: