2016-01-17 45 views
3

我需要使用ASM找到一個方法,這是內部的局部變量修改局部變量:使用ASM

String var4 = "hello!"; 

我已經創建了三個班。一個沒有轉變,一個延伸ClassVisitor,以及一個擴展MethodVisitor中,像這樣:

變壓器入口點(Transformationer.java)

package RainbowBansTransAgent; 

import java.lang.instrument.ClassFileTransformer; 
import java.lang.instrument.IllegalClassFormatException; 
import java.security.ProtectionDomain; 
import org.objectweb.asm.*; 

public class Transformationer implements ClassFileTransformer { 

    public byte[] transform(String arg1, byte[] arg2){ 
     ClassReader cr = new ClassReader(arg2); 
     ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES); 
     cr.accept(cw, 0); 
     return cw.toByteArray(); 
    } 

    @Override 
    public byte[] transform(ClassLoader arg0, String className, Class<?> arg2, 
      ProtectionDomain arg3, byte[] arg4) 
      throws IllegalClassFormatException { 
     BooleanKeys.transformer_loaded = true; 
     byte[] b = null; 
     String realName = className.replaceAll("/", "."); 
     if(realName.equals("joebkt.PlayerList")){ 
      if(BooleanKeys.returned_bytes){ 
       return null; 
      }else{ 
      BooleanKeys.found_class = true; 
      b = transform(realName, arg4); 
      if(b !=null){ 
       BooleanKeys.returned_bytes = true; 
      } 
      } 
     } 
     else System.out.println("Class name " + realName + " is not what we're looking for!"); 
     return b; 
    } 

} 

ClassVisior擴展(RBClassVisitor.java)

package RainbowBansTransAgent; 

import org.objectweb.asm.ClassVisitor; 
import org.objectweb.asm.MethodVisitor; 
import org.objectweb.asm.Opcodes; 

public class RBClassVisitor extends ClassVisitor{ 

    public RBClassVisitor() { 
     super(Opcodes.ASM5); 
    } 

    @Override 
    public MethodVisitor visitMethod(int access, String name, String desc, 
     String signature, String[] exceptions) { 
     MethodVisitor mv = super.visitMethod(access, name, desc, signature, 
       exceptions); 
     return new RBMethodVisitor(mv); 
    } 

} 

MethodVisitor中擴展(RBMethodVisitor.java)

package RainbowBansTransAgent; 

import org.objectweb.asm.Label; 
import org.objectweb.asm.MethodVisitor; 
import org.objectweb.asm.Opcodes; 

public class RBMethodVisitor extends MethodVisitor { 

    MethodVisitor mv; 

    public RBMethodVisitor(MethodVisitor mv) { 
     super(Opcodes.ASM5, mv); 
     this.mv = mv; 
    } 

    public void visitLineNumber(int line, Label start){ 
     if(line == 409){ 
      mv.visitCode(); 
     } 
    } 

    public void visitCode(){ 

    } 

} 

正如你所看到的,我的visitCode()方法是空的。我知道這是字節碼操作應該發生的方法。

我看到MethodVisitor中有

mv.visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index); 

的方法,但我不知道如何正確使用標籤類。

我的變壓器將讀取一個文件,並將變量更改爲文件的內容。使用ASM,我該怎麼做?

編輯:我想要改變的東西的字節碼說: ldc「hello!」 (java.lang.String中)

,我想將它更改爲: 「再見」

LDC (java.lang.String)

+0

您是否嘗試過查看javac爲您嘗試轉換的方法生成的字節碼,並瞭解其如何工作?這應該讓你更好地理解如何解決這個問題。 (提示:如果您需要更改用於初始化局部變量的字符串,則根本不需要visitLocalVarialble()方法,但您需要了解常量池的用途以及如何使用它。) – yole

+0

您的發佈代碼提醒我關於[this](http://assets.amuniversal.com/4a53f3006d5901301d7d001dd8b71c47)。但是,不僅所有這些發佈的代碼都缺少相關部分,即您嘗試*實際執行某些操作*,您也未能解釋此代碼應該執行的操作。 – Holger

+0

缺少的代碼應該改變一個變量等於什麼。例如'String var4 =「hello!」;'將變成'String var4 =「goodbye!」;' – JD9999

回答

3

如果沒有對Java字節碼如何工作的基本理解,您不應該嘗試執行字節碼轉換。有關它的信息的主要來源是The Java® Virtual Machine Specification。對於初學者,您可以閱讀§3 「Compiling for the Java Virtual Machine」以瞭解特定語言結構如何映射到byte code instructions

最重要的是,您必須瞭解您的轉換類型,即在字節代碼級別上沒有局部變量(從Java語言中知道它的方式)。在棧幀中有一定數量的局部變量存在空間,這是通過數字索引來解決的。如果已經使用包含調試信息的類文件進行編譯,則只會調用您在ASM API中發現的方法visitLocalVariable

如果它被調用,它會告訴你一個局部變量name和它映射的indexLabel參數告訴你變量作用域的範圍,該變量可以用於其他變量。如果您使用這種學習變量的特定索引的方式編寫代碼,則必須記住,此代碼僅適用於包含調試提示的類。

因此在字節代碼層次上,沒有正式聲明變量var4,但只有將字符串"hello"賦值給特定變量,該變量隱式創建該變量(如果該變量不存在)。該分配在字節代碼級別上被執行爲兩條指令,ldc "hello",其次是astore n,其中n是局部變量的索引。實際上,ldc只承載持有字符串的常量池的索引,但ASM會處理此操作,並在遇到此指令時調用visitLdcInsn("hello")。因此,您可以搜索兩個指令序列,這意味着您必須先找到正確的索引(例如,使用調試信息,如果存在的話)。或者,如果字符串"hello"預計僅出現在此單個分配中,則等待出現visitLdcInsn("hello")並將其替換爲不同的常量字符串是最簡單的替換形式。

+0

如果我的字符串不只是引號中的內容?就像if:'String s =「日期是」+ date +「。」'? – JD9999

+0

@ JD9999:如果所有操作數都是編譯時常量,則該字符串將在編譯時進行評估,從而導致上述的雙指令序列。在其他所有情況下,代碼將相當複雜,並可能包含編譯器特定的細微差異。請參閱[本答覆至「字符串類如何覆蓋+運算符?」](http://stackoverflow.com/a/11408526/2711488) – Holger