javassist入门
javassist是用来生成class字节码的类库
0x01 添加依赖
1 2 3 4 5
| <dependency> <groupId>org.javassist</groupId> <artifactId>javassist</artifactId> <version>3.27.0-GA</version> </dependency>
|
0x02 生成字节码
下面来看代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
| import javassist.*;
import java.io.FileOutputStream;
public class javassist_test { public static void createClass() throws Exception{ ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.makeClass("test"); cc.setSuperclass(pool.get("com.test.superclass")); cc.setInterfaces(new CtClass[]{pool.get("com.test.aa"),pool.get("java.io.Serializable")}); String cmd = "System.out.println(\"test\");"; cc.makeClassInitializer().insertBefore(cmd);
String randomClassName = "EvilTest"; cc.setName(randomClassName); CtConstructor ctConstructor = new CtConstructor(new CtClass[]{}, cc); ctConstructor.setBody("{System.out.println(0);}"); cc.addConstructor(ctConstructor);
CtField param = new CtField(pool.get("java.lang.String"), "name", cc); param.setModifiers(Modifier.PRIVATE); cc.addField(param, CtField.Initializer.constant("")); CtConstructor ctConstructor2 = new CtConstructor(new CtClass[]{pool.get("java.lang.String")}, cc); ctConstructor2.setBody("{$0.name = $1;}"); cc.addConstructor(ctConstructor2); CtMethod ctMethod = new CtMethod(pool.get("java.lang.String"), "PrintName", new CtClass[]{}, cc); ctMethod.setBody("{String name = \"aaa\";System.out.println(name);return name;}"); cc.addMethod(ctMethod);
cc.writeFile();
}
public static void main(String[] args) { try { createClass(); } catch (Exception e){ e.printStackTrace(); } } }
|
下面为生成的字节码文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
import com.test.aa; import com.test.superclass; import java.io.Serializable;
public class EvilCat extends superclass implements aa, Serializable { private String name = "";
static { System.out.println("test"); }
public EvilCat() { System.out.println(1); }
public EvilCat(String var1) { this.name = var1; }
public String PrintName() { String var1 = "aaa"; System.out.println(var1); return var1; } }
|
当时还有一点想不明白
该怎么导入外部类
搜索了之后发现有2种方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import javassist.*;
public class javassist_import { public static void createClass() throws Exception{ ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.makeClass("test");
pool.importPackage("java.util");
CtMethod ctMethod = new CtMethod(pool.get("java.util.HashMap"), "PrintHashMap", new CtClass[]{}, cc); ctMethod.setBody("{HashMap hashMap = new HashMap();return hashMap;}"); cc.writeFile();
}
public static void main(String[] args) { try { createClass(); } catch (Exception e){ e.printStackTrace(); } } }
|
使用importPackage方法导入包,这样下面的类都可以直接使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
import java.util.HashMap;
public class Cat { public HashMap PrintHashMap() { HashMap var1 = new HashMap(); return var1; }
public Cat() { } }
|
这里还有一段小插曲,当时一直找不到改怎么写,就去问一位师傅,师傅去查了GPT说有这个方法
不过当时GPT的示例代码上面显示该方法在CtClass下,去查了官方文档发现没有这方法,然后就懵了
还以为GPT开始编造方法了
后面师傅说有些文章也是有写这方法,再去搜索后发现是ClassPool类的方法
当时继续问GPT还说不好意思说错了没有这个方法,然后给了第二种方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import javassist.*;
public class javassist_import { public static void createClass() throws Exception{ ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.makeClass("test");
CtMethod ctMethod = new CtMethod(pool.get("java.util.HashMap"), "PrintHashMap", new CtClass[]{}, cc); ctMethod.setBody("{java.util.HashMap hashMap = new java.util.HashMap();return hashMap;}"); cc.writeFile();
}
public static void main(String[] args) { try { createClass(); } catch (Exception e){ e.printStackTrace(); } } }
|
直接写全包名是相同的效果
最后是修改类
首先创建一个测试的类
1 2 3 4 5 6 7 8 9 10 11
| package com.test;
public class test {
public test(){ System.out.println("0"); } public static void main(String[] args) { System.out.println("HelloWorld"); } }
|
然后使用javassist修改测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| import javassist.*; import java.io.FileOutputStream;
public class javassist_test {
public static void resetClass() throws Exception{ ClassPool pool = ClassPool.getDefault(); CtClass ctClass = pool.get("com.test.test"); CtConstructor constructor = ctClass.getConstructors()[0]; constructor.insertBefore("System.out.print(\"211\");"); constructor.insertAfter("{System.out.print(\"000\");}"); ctClass.makeClassInitializer().setBody("{System.out.print(\"000\");}"); new FileOutputStream("2.class").write(ctClass.toBytecode()); } public static void main(String[] args) { try { resetClass(); } catch (Exception e){ e.printStackTrace(); } } }
|
上面就是一些javassist的基础用法
修改类的方法还有一些,比如函数过大不可能重写,有办法通过匹配来指定的内容
这里没用到先不写了后面用到再写