赞
踩
目录
字节码的抽象级别 | 描述 | 例子 |
---|---|---|
低级 | 库需要直接在字节码级别上进行操作。通常,它们提供大多数功能丰富的功能,但与其他字节码操作工具相比,它们的使用也最复杂。 | ASM(ASM –主页) |
中级水平 | 库提供了字节码的某种抽象级别,并简化了其修改。例如,代替修改字节码,可以使用类似于Java的语法进行更改,然后将其编译为字节码,然后由使用的库修改为原始字节码。通常,它们缺少修改后的代码验证的功能-这意味着,错误可能在修改准备过程中被忽略,然后在运行时被发现。 | Javassist(jboss-javassist的Javassist) |
高水平 | 库使用高级指令进行操作,并且通常配备有用于语法验证的工具集。不幸的是,从修改后的字节码进行的最高抽象化通常会导致某些功能的丧失,这些功能仅在直接修改字节码时可用。 | AspectJ(AspectJ项目) CGLib(CGLib GitHub,基于ASM) |
Javassist (JAVA programming Assistant,Java编程助手) 是一个用Java编辑字节码的类库。它使Java程序可以在运行时定义新类,并在JVM加载它时修改类文件。
与其他类似的字节码编辑器不同,Javassist提供两个级别的API:源代码级别和字节码级别。
如果使用源代码级API,则可以在不了解Java字节码规范的情况下编辑类文件。整个API仅使用Java语言的词汇表进行设计。甚至可以以源文本的形式指定插入的字节码。Javassist可以即时对其进行编译。
另一方面,字节码级API允许用户像其他编辑器一样直接编辑类文件。
我们常用到的动态特性主要是反射,在运行时查找对象属性、方法,修改作用域,通过方法名称调用方法等。在线的应用不会频繁使用反射,因为反射的性能开销较大。其实还有一种和反射一样强大的特性,但是开销却很低,它就是Javassist。
类似的技术还有:bcel,asm等,他们相对于Javassit,偏向底层,效率较高,但编码难度更高(需要了解JVM指令)。
Javassist是Jboss的一个子项目,其特点是简单:不需要了解底层JVM指令,直接用Java代码编写,容易理解,并且现在生成代码效率和以上两种技术相差已经很小。目前,最新版本 3.27.0-GA (2020年03月19日).
.
├── build.xml
├── javassist.jar
├── License.html
├── pom.xml
├── Readme.html
├── README.md
├── sample
│ ├── duplicate
│ ├── evolve
│ ├── hotswap
│ ├── preproc
│ ├── reflect
│ ├── rmi
│ ├── Test.java
│ └── vector
├── src
│ ├── main
│ └── test
└── tutorial
├── brown.css
├── tutorial2.html
├── tutorial3.html
└── tutorial.html
CtClass对象的容器。
ClassPool
,单例模式,一般通过该方法创建我们的ClassPool;ClassPath
加到类搜索路径的末尾位置或插入到起始位置。通常通过该方法写入额外的类搜索路径,以解决多个类加载器环境中找不到类问题;类
toClass
方法是通过调用本方法实现。需要注意的是一旦调用该方法,则无法继续修改已经被加载的CtClass;.class
文件;类加载器
字段
方法
对于setBody $0代表this $1、$2、...代表方法的第几个参数
setBody("{$0.name = $1;}");
符号 | 含义 |
---|---|
$0, $1, $2, ... | this,第几个参数 |
$args | 参数列表. $args的类型是Object[]. |
$$ | 所有实参.例如, m($$) 等价于 m($1,$2,...) |
$cflow(...) | cflow变量 |
$r | 结果类型. 用于表达式转换. |
$w | 包装类型. 用于表达式转换. |
$_ | 结果值 |
$sig | java.lang.Class列表,代表正式入参类型 |
$type | java.lang.Class对象,代表正式入参值. |
$class | java.lang.Class对象,代表传入的代码段. |
新建Java项目,导入javassist.jar。
Main无所谓,是我建项目时选择了Hello World模板
Test.java
- package bupt.edu.cn;
- import javassist.*;
-
- import java.lang.reflect.Method;
-
- public class Test {
- public static void createStudent() throws Exception {
- ClassPool pool = ClassPool.getDefault();
- CtClass cc = pool.makeClass("E:\\Workspace\\IDEA_workspace\\JavassistTest\\src\\bupt.edu.cn.Student");
-
- // 字段名为name
- CtField param = new CtField(pool.get("java.lang.String"),"name", cc);
- // 访问级别是 private
- param.setModifiers(Modifier.PRIVATE);
- // 初始值是 "Frankyu"
- cc.addField(param, CtField.Initializer.constant("Frankyu"));
-
- // 生成 getter、setter 方法
- cc.addMethod(CtNewMethod.setter("setName", param));
- cc.addMethod(CtNewMethod.getter("getName", param));
-
- // 添加无参的构造函数
- CtConstructor cons = new CtConstructor(new CtClass[]{}, cc);
- cons.setBody("{name = \"yubo\";}");
- cc.addConstructor(cons);
-
- // 5. 添加有参的构造函数
- cons = new CtConstructor(new CtClass[]{pool.get("java.lang.String")}, cc);
- // $0=this / $1,$2,$3... 代表方法参数
- cons.setBody("{$0.name = $1;}");
- cc.addConstructor(cons);
-
- // 6. 创建一个名为printName方法,无参数,无返回值,输出name值
- CtMethod ctMethod = new CtMethod(CtClass.voidType, "printName", new CtClass[]{}, cc);
- ctMethod.setModifiers(Modifier.PUBLIC);
- ctMethod.setBody("{System.out.println(name);}");
- cc.addMethod(ctMethod);
-
- //这里会将这个创建的类对象编译为.class文件
- cc.writeFile("");
- }
- public static void usingStudent() throws Exception{
- ClassPool pool = ClassPool.getDefault();
- CtClass cc = pool.getCtClass("E:\\Workspace\\IDEA_workspace\\JavassistTest\\src\\bupt.edu.cn.Student");
- // 实例化
- Object student = cc.toClass().newInstance();
- // 设置值
- Method setName = student.getClass().getMethod("setName", String.class);
- setName.invoke(student, "junjie");
- // 输出值
- Method execute = student.getClass().getMethod("printName");
- execute.invoke(student);
- }
- public static void main(String[] args) throws Exception{
- createStudent();
- usingStudent();
- }
- }
注意:.class文件的绝对路径需要修改
Student.class
- //
- // Source code recreated from a .class file by IntelliJ IDEA
- // (powered by Fernflower decompiler)
- //
-
- package E:\Workspace\IDEA_workspace\JavassistTest\src\bupt.edu.cn;
-
- public class Student {
- private String name = "Frankyu";
-
- public void setName(String var1) {
- this.name = var1;
- }
-
- public String getName() {
- return this.name;
- }
-
- public Student() {
- this.name = "yubo";
- }
-
- public Student(String var1) {
- this.name = var1;
- }
-
- public void printName() {
- System.out.println(this.name);
- }
- }
Student.java
- package bupt.edu.cn;
-
- import java.util.Random;
-
- public class Student {
- public int getAge() {
- return age;
- }
-
- public void setAge(int age) {
- this.age = age;
- }
-
- private int age;
-
- public int getGrade() {
- return grade;
- }
-
- public void setGrade(int grade) {
- this.grade = grade;
- }
-
- private int grade;
-
- @Override
- public String toString() {
- return "Student{" +
- "age=" + age +
- ", grade=" + grade +
- '}';
- }
-
- public void display() {
- System.out.println(this.toString());
- }
- public void learn() throws Exception{
- // long start = System.currentTimeMillis();
- Random rand = new Random();
- int time = rand.nextInt(1000)+3000;
- Thread.sleep(time);
- // long end = System.currentTimeMillis();
- // System.out.println(end-start);
- }
- public static void main(String[] args) {
- Student s = new Student();
- s.display();
- try{
- s.learn();
- }
- catch (Exception e){
-
- }
-
- }
- }
Monitor.java
- import bupt.edu.cn.Student;
- import javassist.*;
-
- public class Monitor {
- final static ClassPool pool = ClassPool.getDefault();
- final static String classname = "bupt.edu.cn.Student";
- public void studentLearnMonitor() throws Exception{
- CtClass ss = pool.getCtClass(classname);
- CtClass.debugDump="./dump";
- String methodname = "learn";
- CtMethod learn_ori = ss.getDeclaredMethod(methodname);
- //拷贝一份learn方法
- CtMethod learn_cp = CtNewMethod.copy(learn_ori,learn_ori.getName()+"_cp",ss,null);
- //添加拷贝后的方法
- ss.addMethod(learn_cp);
- //修改learn方法:原代码前后添加时间
- String src = "{"+
- "long start = System.currentTimeMillis();" +
- learn_ori.getName()+"_cp($$);"+
- "long end = System.currentTimeMillis();"+
- "System.out.println(end-start);"+
- "}";
- learn_ori.setBody(src);
- ss.toClass();
- //生成.class文件,主要用于调试,查看是否有代码片段被忽略
- //ss.writeFile();
- Student s = new Student();
- s.learn();
- }
-
- public void studentDisplayMonitor() throws Exception{
- CtClass ss = pool.getCtClass(classname);
- CtClass.debugDump="./dump";
- //添加字段name
- CtField param = new CtField(pool.get("java.lang.String"),"name", ss);
- // 访问级别是 private
- param.setModifiers(Modifier.PRIVATE);
- // 初始值是 "Frankyu"
- ss.addField(param, CtField.Initializer.constant("Frankyu"));
- String methodname = "display";
- CtMethod display = ss.getDeclaredMethod(methodname);
- String src = "{"+
- "System.out.println($0.age);" +
- "$0.name=\"frankyu\";" +
- "System.out.println($0.name);"+
- "}";
- display.insertBefore(src);
- if(true){
- System.out.println("Hello javassist");
- }
- src ="if(true){" +
- "System.out.println(\"Hello javassist\");" +
- "}";
- display.insertAt(7,src);
- src = "{"+
- "System.out.println($0.grade);"+
- "}";
- display.insertAfter(src);
- ss.toClass();
- //ss.writeFile();
- Student s = new Student();
- s.display();
- }
-
- public static void main(String[] args) throws Exception{
- Monitor m = new Monitor();
- // 由于类冻结问题,两个方法不可同时调用
- // m.studentLearnMonitor();
- m.studentDisplayMonitor();
- }
-
- }
studentLearnMonitor对learn方法插桩,通过拷贝的方式,使start和end属于同一个代码块,解决不同代码块之间变量无法使用问题。
studentDisplayMonitor对display方法插桩,演示了添加变量,访问变量,修改变量,方法前插,后插,任意位置插。
调用studentDisplayMonitor时,dump目录下,Student.class部分代码
- public void display() {
- System.out.println(this.age);
- this.name = "frankyu";
- System.out.println(this.name);
- if (true) {
- System.out.println("Hello javassist");
- }
-
- System.out.println(this.toString());
- Object var2 = null;
- System.out.println(this.grade);
- }
调用studentLearnMonitor时,dump目录下,Student.class部分代码
- public void learn() throws Exception {
- long var1 = System.currentTimeMillis();
- this.learn_cp();
- long var3 = System.currentTimeMillis();
- System.out.println(var3 - var1);
- }
类
字段
方法
代码属性
代码指令迭代器
void begin()
void move(int index)
boolean hasNext()
int next()
int byteAt(int index)
int u16bitAt(int index)
int write(byte[] code, int index)
void insert(int index, byte[] code)
更多内容查看:网络安全-自学笔记
喜欢本文的请动动小手点个赞,收藏一下,有问题请下方评论,转载请注明出处,并附有原文链接,谢谢!如有侵权,请及时联系。如果您感觉有所收获,自愿打赏,可选择支付宝18833895206(小于),您的支持是我不断更新的动力。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。