用Javassist修改方法的几个问题
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用Javassist修改方法的几个问题相关的知识,希望对你有一定的参考价值。
最近在练习使用Javassist进行编程,对编译好的Class文件进行一些修改。由于刚学,遇到一些问题,望高手不吝赐教!
问题是这样的,以Javassist下Sample文件夹下的reflect为例,reflect中有一个Person.java文件,把这个文件编译为Person.class文件,并通过javassist.jar库对这个文件进行一些操作。在我的一系列试验中,有这样两个问题一直没有解决:1.在每个方法的开头添加一行代码,该行代码输出该方法的名称;2.所有输入参数的名称、类型;
遇到的问题细处如下:通过CtClass类中的getDeclaredFields和getDeclaredMethods两个方法,可以获取到原Person.class中的所有成员变量和方法,分别表示为CtField和CtMethod的对象,然后再通过这两个类下的getName,getSignature等方法可以把Person的成员变量名,类型以及所有方法名赋值给String类的对象,但是这些变量都是在当前工程下定义的,只能在当前工程下进行输出操作,无法通过insertBefore等把这些变量的值传到Person.class下去。
原代码片段:
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("reflect.Person");
System.out.println("类中成员变量有:");
CtField[] cf = cc.getDeclaredFields();
for (int i = 0; i < cf.length; i++)
System.out.println(cf[i].getName() + "\t" + cf[i].getSignature());
System.out.println("类中的方法有:");
CtMethod[] cm = cc.getDeclaredMethods();
for (int j = 0; j < cm.length; j++)
System.out.println(cm[j].getName());
//cm[j].insertBefore("System.out.println(cm[j].getName());");
错误就在最后一句代码上,显然Person.class下是没有cm[j].getName()的,没法输出。请问在Javassist要达到这样的目的是用什么方法?用什么来表示获取的原方法名就可以传到要修改的Class下了?还有变量的?
望有高手详解,鄙人新手,问题可能比较傻,谢谢!解决了还有附加分数相送。
以通过一种手段来在程序内部来修改授权部分的实现,使真实的授权部分隐藏在其它代码部分,而可视的授权代码并不参与实际的授权授权,这样的话,对于破解者来说,修改表向的代码实现并不能真正修改代码实现,因为真实的实现已经通过其它代码将原始实现替换掉了。
即在调用授权代码之前将授权原代码进行修改,然后调用授权代码时即调用已经修改后的授权代码,而真实的授权代码是查看不了的(通过某种方式注入),这样即达到一种授权方式的隐藏。
可以通过javassist来修改java类的一个方法,来修改一个方法的真实实现。修改的方法可以是动态方法,也可以是静态方法。修改的前提即是当前修改的类还没有被当前jvm加载,如果当前的类已经被加载,则不能修改。
ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = classPool.get("com.develop.Txt");
CtMethod ctMethod = ctClass.getDeclaredMethod("i");
ctMethod.setBody("tryInteger i = null;"
+ "int y = i.intValue();System.out.println(\\"this is a new method\\");");
ctClass.toClass();
上面的方法即是修改一个方法的实现,当调用ctClass.toClass()时,当前类即会被当前的classLoader加载,并实例化类。
需要注意的是,在调用ctClass.toClass()时,会加载此类,如果此类在之前已经被加载过,则会报一个duplicate load的错误,表示不能重复加载一个类。所以,修改方法的实现必须在修改的类加载之前进行。
即使不调用toClass,那么如果当前修改的类已经加载,那么修改方法实现,同样不起作用,即修改一个已经加载的类(不论是修改静态方法,还是修改动态方法)是没有任何效果的。修改之前必须在类加载之前进行。
当然,使用aspectj也可以同样达到修改的效果,不过修改指定的类,则需要为修改这个类添加一个aspect,然后将这个aspect加入配置文件中以使其生效,比起javassist来说,修改一个类还是使用javassist相对简单一点。
for (int j = 0; j < cm.length; j++)
//System.out.println(cm[j].getName());
cm[j].insertBefore();
cm[j].getName();
System.out.println();
是这意思吗
用 javassist 来修改 class 文件
import javassist.ClassPool; import javassist.CtClass; import javassist.CtMethod; public class Test { public static void main(String[] args) throws Exception { ClassPool classPool = ClassPool.getDefault(); // 必须将class文件放在这个工程编译后的class文件中,路径也对应起来 CtClass ctClass = classPool.get("com.ambitionstone.esa2000.pki.AZTPkiServlet"); //设置方法需要的参数,一定要能匹配起来,而且必须引入这些参数类的包 CtClass[] param = new CtClass[4] ; param[0] = classPool.get("javax.servlet.http.HttpServletRequest") ; param[1] = classPool.get("javax.servlet.http.HttpServletResponse") ; param[2] = classPool.get("int") ; param[3] = classPool.get("java.lang.String") ; // 找到需要修改的行所在的方法 CtMethod method = ctClass.getDeclaredMethod("doOnlineValidate", param); // 在这个方法的182行添加关闭文件流的方法 method.insertAt(182, "fin.close();"); // 将文件写到指定的目录,生成之后在test\com\ambitionstone\esa2000\pki\AZTPkiServlet\这个文件夹下面可以找到编译后的类 ctClass.writeFile("D:\\test") ; } }
以上是关于用Javassist修改方法的几个问题的主要内容,如果未能解决你的问题,请参考以下文章