通过修改字节修改方法体

先贴出示例中的代码:

复制代码
public class Test{
    public static void main(String[] args){
    System.out.println("123");
    System.out.println("345");
    }
    
    public void m1(){
        System.out.println("m1");
    }
    
    public void m2(){
        System.out.println("m2");
    }
}
复制代码

Test2中先后调用了Test的m1,m2方法,此处通过修改字节码删除对m1方法的调用。

public class Test2{
    public static void main(String[] args){
        System.out.println("test2");
        Test t1 = new Test();
        t1.m1();
        t1.m2();
        }
}

 

1、找到相对应的方法调用的字节码指令,通过JclassLib或者在命令行通过javap -verbose Test2.class来查看。

此处为16行和17行的aload_1和invokespecial #7,点击此处#7,可确定调用的方法为Test.m1(),如下所示:

此处控制台内容没有全部显示

 

贴出命令行中未截出的main函数中的方法调用字节码指令

 

2、通过虚拟机字节码指令集去查询对应的16进制字节码为:2B B6 00 07,此处00 07为B6(调用示例方法)的参数,即为调用Test的m1方法。

3、在UE中选中后右键进行选中16进制插入/删除,删除对应的字节码。此处选择删除,要删除的字节数即为4

 

4、在删除对应的字节码后,仍有3处需修改。

     4.1 此Code属性对一个的attribute_length,在原值的基础上减4

      4.2 修改此Code属性对应的code_length,从19修改为15,同样见上图。

      43 修改此Code属性对应的LinerNumberTable中的对应的字节码行号,因字节码减少了,如不修改会报错

如上图所示,将00 18和 00 14此两次大于所删除的字节码行号(此处为15和16)的两处行号分别减4。保存,OK!重新运行java Test2,如下图所示,上面异常是在没有修改字节码行号记录表时所报的异常。

 

 如果方法体内代码比较多,如此修改比较繁琐,此时采用jClassLib程序来实现,要修改的点和上述方式基本相同。代码如下:

复制代码
package com.xue.jvm;

import java.io.DataInput;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;
import org.gjt.jclasslib.io.ClassFileWriter;
import org.gjt.jclasslib.structures.AttributeInfo;
import org.gjt.jclasslib.structures.CPInfo;
import org.gjt.jclasslib.structures.ClassFile;
import org.gjt.jclasslib.structures.MethodInfo;
import org.gjt.jclasslib.structures.attributes.CodeAttribute;
import org.gjt.jclasslib.structures.attributes.LineNumberTableAttribute;
import org.gjt.jclasslib.structures.attributes.LineNumberTableEntry;
import org.testng.annotations.Test;

import com.alibaba.druid.util.StringUtils;

public class TestJclasslib {
    
    private static Logger log = Logger.getLogger(TestJclasslib.class);
    //要展示盒修改的方法名
    private static String methodName="main";
    //Code对应的常量池index
    private static int codeConstantIndex ;
    //LineNumberTable对应的常量池index
    private static int lineNumberTableConstantIndex;
    
    private static String classFileName = "D:\\Test2.class";
    
    private static InputStream fis;
    
    private static ClassFile cf = new ClassFile();
    
    private String deletedByteStr = "43,-74,0,7";
    //被删除的代码的字节行号,在调整字节码行号表时使用
    private int deletedCodeNumber = 12;
    //删除的代码字节数
    private int deletedCodeCount = 4;
    
    public static void main(String[] args) throws Exception{
        initParams();
        log.info("Method info");
        MethodInfo[] methods = cf.getMethods();
        for(int i=0;i<methods.length;i++){
            log.info(methods[i].getName());
            //根据方法名找出对应的方法
            if(methodName.equals(methods[i].getName())){
                MethodInfo mi = methods[i];
                //方法体属性,包括Code属性
                AttributeInfo[] attributes = mi.getAttributes();
                for(AttributeInfo ai : attributes){
                    log.info("AttributeInfo.name:"+ai.getName());
                    //找出此方法的Code,即为方法体代码
                    if(ai.getAttributeNameIndex()==codeConstantIndex){
                        CodeAttribute codeAttribute = (CodeAttribute)ai;
                        int attributeLength = codeAttribute.getAttributeLength();
                        log.info("attributeLength:"+attributeLength);
                        for(byte b:codeAttribute.getCode()){
                            log.info(b);
                        }
                        AttributeInfo[] codeAis = codeAttribute.getAttributes();
                        log.info("code attributeInfo length:"+codeAis.length);
                        for(AttributeInfo codeAi:codeAis){
                            log.info(codeAi.getAttributeNameIndex());
                            //此处找出对应LineNumberTalbe,显示对应行对应关系。
                            if(codeAi.getAttributeNameIndex()==lineNumberTableConstantIndex){
                                LineNumberTableAttribute lnta = (LineNumberTableAttribute) codeAi;
                                LineNumberTableEntry[] lineNumberTableEntrys = lnta.getLineNumberTable();
                                for(LineNumberTableEntry lineNumberTableEntry:lineNumberTableEntrys){
                                    log.info(lineNumberTableEntry.getStartPc()+"="+lineNumberTableEntry.getLineNumber());
                                }
                            }
                        }
                    }
                }
            }
            
            
        }
        
        fis.close();
//        File f = new File("D:\\com\\xue\\test\\Test2.class");
//        ClassFileWriter.writeToFile(f, cf);
    }
    /**
     * 此方法用来修改class文件,删除class文件中的方法调用,修改对应的attribute_length,修改对应的code_length,修改LineNumberTable
     * @throws Exception 
     */
    @Test
    public void updateClassFile() throws Exception{
        
        //将需要用到的参数初始化
        initParams();
        
        MethodInfo[] methods = cf.getMethods();
        for(int i=0;i<methods.length;i++){
            log.info(methods[i].getName());
            //根据方法名找出对应的方法
            if(methodName.equals(methods[i].getName())){
                MethodInfo mi = methods[i];
                //方法体属性,包括Code属性
                AttributeInfo[] attributes = mi.getAttributes();
                for(AttributeInfo ai : attributes){
                    log.info(ai.getName());
                    //找出此方法的Code,即为方法体代码
                    if(ai.getAttributeNameIndex()==codeConstantIndex){
                        CodeAttribute codeAttribute = (CodeAttribute)ai;
                        int attributeLength = codeAttribute.getAttributeLength();
                        //此处attributeLength为动态计算所得,不需要更改,直接更改code数组
                        log.info("attributeLength:"+attributeLength);
                        byte[] changedCode = changeCode(codeAttribute.getCode());
                        codeAttribute.setCode(changedCode);
                        AttributeInfo[] codeAis = codeAttribute.getAttributes();
                        log.info("code attributeInfo length:"+codeAis.length);
                        for(AttributeInfo codeAi:codeAis){
                            log.info(codeAi.getAttributeNameIndex());
                            //此处找出对应LineNumberTalbe,显示对应行对应关系。
                            if(codeAi.getAttributeNameIndex()==lineNumberTableConstantIndex){
                                LineNumberTableAttribute lnta = (LineNumberTableAttribute) codeAi;
                                LineNumberTableEntry[] lineNumberTableEntrys = lnta.getLineNumberTable();
                                for(LineNumberTableEntry lineNumberTableEntry:lineNumberTableEntrys){
                                    int startPc = lineNumberTableEntry.getStartPc();
                                    if(lineNumberTableEntry.getStartPc()>deletedCodeNumber){
                                        lineNumberTableEntry.setStartPc(startPc-deletedCodeCount);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        fis.close();
        File f = new File("D:\\com\\xue\\test\\Test2.class");
        ClassFileWriter.writeToFile(f, cf);
    }
    

    public byte[] changeCode(byte[] code){
        log.info(deletedByteStr);
        StringBuffer sb = new StringBuffer();
        for(byte b:code){
            sb.append(b).append(",");
        }
        log.info(sb);
        sb.delete(sb.indexOf(deletedByteStr), sb.indexOf(deletedByteStr)+deletedByteStr.length());
        log.info(sb);
        String[] bStrs = sb.toString().split(",");
        List<Byte> bytes = new ArrayList<Byte>();
        for(String bs:bStrs){
            if(!StringUtils.isEmpty(bs)){
                bytes.add(Byte.valueOf(bs));
            }
        }
        byte[] result = new byte[bytes.size()];
        for(int i=0;i<bytes.size();i++){
            result[i] = bytes.get(i);
        }
        return result;
    }
    
    private static void initParams() throws Exception{
        File file = new File(classFileName);
        fis = new FileInputStream(file);
        
        DataInput di = new DataInputStream(fis);
        cf.read(di);
        
        CPInfo[] infos = cf.getConstantPool();
        log.info(infos.length);

        for(int i=0;i<infos.length;i++){
            if(infos[i]!=null){
                System.out.print(i);
                System.out.print("=");
                System.out.print(infos[i].getVerbose());
                System.out.print("=");
                System.out.println(infos[i].getTagVerbose());
                
                if(infos[i].getVerbose().equals("Code")){
                    codeConstantIndex=i;
                }
                if(infos[i].getVerbose().equals("LineNumberTable")){
                    lineNumberTableConstantIndex=i;
                }
            }
            
        }
    }
}
复制代码

 关于Java类文件的结构参考:Orclae Class File Format

posted @   皓若繁星  阅读(1553)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示