天天看點

使用javassist對.class檔案進行修改

最近重新再看<inside jvm>,對java編譯成的位元組碼結構很感興趣,希望找個工具能夠對.class檔案進行的解析和檢視。沒找到,倒發現javaassist可以對位元組碼進行操作和修改。此工具是jboss項目的一部分,jboss實作aop的基礎。呵呵,開眼界了,原來我們可以直接對位元組碼檔案進行修改,哪怕不知道源檔案(跟反編譯完全不同)。一個簡單例子:

import javassist.*;

class hello {

    public void say() {

        system.out.println("hello");

    }

}

public class test {

    public static void main(string[] args) throws exception {

        classpool cp = classpool.getdefault();

        ctclass cc = cp.get("hello");

        ctmethod m = cc.getdeclaredmethod("say");

        m.setbody("{system.out.println(/"shit/");}");

        m.insertbefore("system.out.println(/"fuck/");");

        class c = cc.toclass();

        hello h = (hello)c.newinstance();

        h.say();

編譯運作此檔案,輸出:

fuck

shit

我們在

 ctmethod m = cc.getdeclaredmethod("say");

  m.setbody("{system.out.println(/"shit/");}");

  m.insertbefore("system.out.println(/"fuck/");");

修改了say()方法,改成了

system.out.println("fuck");

system.out.println("shit");

這裡的classpool是ctclass的容器,它讀取class檔案,并根據要求儲存ctclass的結構以便日後使用,預設狀态下是從目前的類裝載器獲得,當然你可以指定:

pool.insertclasspath("/usr/local/javalib");

當然,不僅僅是修改方法,你還可以建立一個class,利用makeclass()方法,如:

classpool pool = classpool.getdefault();

ctclass cc = pool.makeclass("point");

還可以新增方法,下面是sample裡的一個例子,同樣的:

package sample;

import java.lang.reflect.*;

/*

   a very simple sample program

   this program overwrites sample/test.class (the class file of this

   class itself) for adding a method g().  if the method g() is not

   defined in class test, then this program adds a copy of

   f() to the class test with name g().  otherwise, this program does

   not modify sample/test.class at all.

   to see the modified class definition, execute:

   % javap sample.test

   after running this program.

*/

    public int f(int i) {

     i++;

    return i;

 classpool pool = classpool.getdefault();

 ctclass cc = pool.get("sample.test");

 test test=new test();

 class c=test.getclass();

 method []method=c.getdeclaredmethods();

 for(int i=0;i<method.length;i++){

  system.out.println(method[i]);

 }

 try {

     cc.getdeclaredmethod("g");

     system.out.println("g() is already defined in sample.test.");

 catch (notfoundexception e) {

     /* getdeclaredmethod() throws an exception if g()

      * is not defined in sample.test.

      */

     ctmethod fmethod = cc.getdeclaredmethod("f");

     ctmethod gmethod = ctnewmethod.copy(fmethod, "g", cc, null);

     cc.addmethod(gmethod);

     cc.writefile(); // update the class file

     system.out.println("g() was added.");

第一次運作時,因為test裡并沒有g()方法,是以執行

 ctmethod fmethod = cc.getdeclaredmethod("f");

     ctmethod gmethod = ctnewmethod.copy(fmethod, "g", cc, null);  //把f方法複制給g

     cc.writefile(); //更新class檔案

列印:g() was added

第2次運作時,因為以上步驟已經在class檔案中增加了一個g方法,是以

 system.out.println("g() is already defined in sample.test.");

列印:g() is already defined in sample.test

javassist不僅能修改你自己的class檔案,而且可以同樣修改jdk自帶的類庫(廢話,類庫也是人寫的^_^)具體請看它的tutorial

文章轉自莊周夢蝶  ,原文釋出時間5.17

繼續閱讀