用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相对简单一点。

参考技术A CtMethod[] cm = cc.getDeclaredMethods();
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修改方法的几个问题的主要内容,如果未能解决你的问题,请参考以下文章

Javassist:使用-javaagent方式实现修改方法内容(打包和非打包方式执行)

用 javassist 来修改 class 文件

Javassist 通用工具之 CodeInjector

Javassist 代码转换

Javassist 代码转换

Javassist使用