2016-12-14 93 views
0

使用Javassist 3.20.0.GA,我試圖將代碼注入一個空循環。使用Javassist,如何將代碼添加到空循環?

我試過的一切,我試圖創建修改後的類的新實例,我一直運行到一個java.lang.VerifyError錯誤。

我試圖將問題隔離到無法成功運行的小程序。

import javassist.ClassPool; 
import javassist.CtClass; 
import javassist.CtMethod; 
import javassist.bytecode.Bytecode; 
import javassist.bytecode.CodeAttribute; 
import javassist.bytecode.CodeIterator; 
import javassist.bytecode.InstructionPrinter; 
import javassist.bytecode.MethodInfo; 
import javassist.compiler.Javac; 

public class EmptyLoopTest { 
    private void testEmptyLoopModification() throws Exception { 
     ClassPool cp = ClassPool.getDefault(); 
     CtClass cc = cp.get(EmptyLoopTest.class.getName() + "$EmptyLoopClass"); 
     CtMethod m = cc.getDeclaredMethod("emptyLoopMethod"); 
     printMethod("Before modifications", m); 

     MethodInfo methodInfo = m.getMethodInfo(); 
     CodeAttribute ca = methodInfo.getCodeAttribute(); 
     CodeIterator ci = ca.iterator(); 

     Javac jv = new Javac(cc); 
     jv.compileStmnt("System.out.println(\"injected into loop\");"); 

     Bytecode b = jv.getBytecode(); 
     adjustCodeAttributeIfNeeded(b, ca); 
     ci.insertAt(0, b.get()); 
     printMethod("After modifications", m); 
     Class c = cc.toClass(); 

     logInfo("Attempting to create instance of modified class"); 
     c.newInstance(); 
    } 
    private void adjustCodeAttributeIfNeeded(Bytecode b, CodeAttribute ca){ 
     int locals = b.getMaxLocals(); 
     int stack = b.getMaxStack(); 
     if(stack > ca.getMaxStack()) { 
      ca.setMaxStack(stack); 
     } 
     if(locals > ca.getMaxLocals()) { 
      ca.setMaxLocals(locals); 
     } 
    } 

    private void printMethod(String title, CtMethod m){ 
     logInfo(title); 
     InstructionPrinter instructionPrinter = new InstructionPrinter(System.out); 
     instructionPrinter.print(m); 
    } 
    private void logInfo(String message){ 
     System.out.println(""); 
     System.out.println("------" + message); 
    } 

    public class EmptyLoopClass { 
     public void emptyLoopMethod() { 
      for(;;){ 
      } 
     } 
    } 

    public static void main(String[] args) throws Exception { 
     // have errors written to sysout so all output from this program is in order 
     System.setErr(System.out); 
     new EmptyLoopTest().testEmptyLoopModification(); 
    } 
} 

當我運行這個程序,以下是寫入到控制檯..

------Before modifications 
0: goto 0 

------After modifications 
0: getstatiC#32 = Field java.lang.System.out(Ljava/io/PrintStream;) 
3: ldC#34 = "injected into loop" 
5: invokevirtual #40 = Method java.io.PrintStream.println((Ljava/lang/String;)V) 
8: goto 8 

------Attempting to create instance of modified class 
Exception in thread "main" java.lang.VerifyError: Expecting a stackmap frame at branch target 8 
Exception Details: 
    Location: 
    EmptyLoopTest$EmptyLoopClass.emptyLoopMethod()V @8: goto 
    Reason: 
    Expected stackmap frame at this location. 
    Bytecode: 
    0x0000000: b200 2012 22b6 0028 a700 00    
    Stackmap Table: 
    same_frame(@0) 

    at java.lang.Class.getDeclaredConstructors0(Native Method) 
    at java.lang.Class.privateGetDeclaredConstructors(Class.java:2671) 
    at java.lang.Class.getConstructor0(Class.java:3075) 
    at java.lang.Class.newInstance(Class.java:412) 
    at EmptyLoopTest.testEmptyLoopModification(EmptyLoopTest.java:35) 
    at EmptyLoopTest.main(EmptyLoopTest.java:68) 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
    at java.lang.reflect.Method.invoke(Method.java:498) 
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147) 

Process finished with exit code 1 

如果按預期工作一切都被我所期待的修改轉到0,而不是後的GOTO指令當前顯示的內容是goto 8.這就好像調用javassist.bytecode.CodeIterator#insertAt時,StackMapTable沒有適當調整。

任何人都可以發現我在這裏做錯了嗎?

感謝,

埃裏克

回答

0
  1. 創建一流這樣c.newInstance();所以它應該有0參數構造函數 - 是靜態內部類

    public static class EmptyLoopClass { 
    public void emptyLoopMethod() { 
        for(;;){ 
        } 
    } 
    
  2. 你代碼修改後應調用methodInfo.rebuildStackMap(cp);。 JVM使用StackMap來驗證類文件。

  3. 反正你用字節碼

    3: ldC#34 = "injected into loop" 
        5: invokevirtual #40 = Method java.io.PrintStream.println((Ljava/lang/String;)V) 
        8: goto 8 
    

    此代碼是不是在循環中結束了,這是循環之前。實際循環是單指令8: goto 8,如果要在其中注入指令,則必須生成一些額外的循環代碼。