Java运行时修改字节码技术
Java运行时动态修改字节码技术,常用的有javassist asm
来实现。不过最近在分析openrasp-java
这块时,程序使用的javassist
来动态插桩关键类,达到监控某些程序的行为,OpenRasp
使用这个技术来实现了监控程序的行为。为了分析OpenRasp
和理解其使用的技术原理,先做一个java动态修改指令基础知识的补充。
第一个程序
有如下程序
package com.company;
import java.net.URL;
import java.io.File;
import java.net.URLDecoder;
import java.util.Set;
public class Test1 {
private String aa="heh";
public Test1(){
}
@Override
public String toString() {
return "Test1{" +
"aa='" + aa + '\'' +
'}';
}
}
正常情况下调用toString()
方法,会得到如下
InsertCode:Test1{
aa='heh'}
如果想要在toString()
方法前插入某一个方法块,输出如下内容
方法调用前 ----->>aa
方法调用前 ----->>aaa
方法调用后 ---->> bbbb
InsertCode:Test1{
aa='heh'}
可以借用javassist
工具类操作对应的字节码。
动态修改字节码–常用javassist类(这里是根据写的样例记录的,不是针对所有情况)
要想在动态修改程序行为,则需要使用javassist
内的三个主要类
ClassPool --> 是CtClass的一个容器 要想获得一个类对象,必须
CtClass -->和java的Class类似
CtMethod -->和java的Method类似
- ClassPool --> 是CtClass的一个容器 要想获得一个类对象,必须通过这个对象获取
CtClass ctClzz =classPool.get("完整的类名,例如com.test.demn.A");
如果当前的classPool内没有这个类,则会报javassist.NotFoundException: com.company.Test1 后续会提到
#参考
http://javadox.com/org.javassist/javassist/3.18.1-GA/javassist/ClassPool.html
- CtClass -->和java的Class类似代表了一个类对象,从
ClassPool
内获取到
参考
http://javadox.com/org.javassist/javassist/3.18.1-GA/javassist/CtClass.html
- CtMethod -->和java的Method类似代表的一个方法对象,继承自CtBeHavior
参考
http://javadox.com/org.javassist/javassist/3.18.1-GA/javassist/CtMethod.html
在知道了这几个类之后,接着就是动手尝试修改com.company.Test1
的toString()
方法。
插入代码到toString()方法
编写TestInsetOPs类
package com.company;
import javassist.*;
import org.junit.Test;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.LinkedList;
//测试插入程序
public class TestInsertOps {
private static ClassPool insertCode() {
try {
//todo 两个方式回去ClassPool 1)ClassPool.getDefault(); 内部会自动调用appendSystemPath方法 2)可以直接new,不过要手动 appendSystemPath
ClassPool pool = new ClassPool();// ClassPool.getDefault();
pool.appendSystemPath();//如果添加到系统环境中内程序可以执行,否则会javassist.NotFoundException: com.company.Test1
CtClass clazz = pool.get("com.company.Test1");
CtMethod method = clazz.getDeclaredMethod("toString");
method.insertBefore("{ System.out.println(\"方法调用前 ----->>aaa \"); }");
method.insertAfter("{ System.out.println(\"方法调用后 ---->> bbbb\"); }");
return pool;
} catch (NotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (CannotCompileException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null