使用jclasslib修改字节码/源码

  查看源码很简单,一些常用IDE里如idea、eclipse都提供了查看class文件源码的功能,虽然跟源码有些出入(解语法糖等),但功能实现上是一致的,且比源码更贴近于JVM运行时的情况。
  有时候我们需要修改源码以满足使用要求,对于java代码生成的字节码重新源码就比较简单了,一种方式是继承然后重写待修改的功能,另一种方式是直接创建一个同名类文件,把反编译的源码复制进去,修改后,将新生成的class文件替换原jar包中的class文件,但有些字节码是由其它语言生成的,反编译后的文件并不能满足java编译语法,也就无法编译成新的class文件,对于这种情况,有种通用的方式就是直接修改字节码来实现。
  网上有很多修改字节码的文章,但大都是修改常量池来输出不同的值,本文使用jclasslib直接修改字节码中的源码逻辑,为方便演示,本文的demo可能比较简单,但是这种方法可以用到更复杂的类中,如有需要欢迎留言探讨。

1、java源码

package com.zhanghao.test.jclasslib;

public class JclasslibTest {
    public static void main(String[] args) {
        int a = 1;
        int b = 2;
        printMin(a, b);
    }

    private static void printMin(int a, int b) {
        int min = a <= b ? a : b;
        System.out.println(min);
    }
}

输出两个参数中的较小值:1
目标:通过修改字节码的方式使printMin方法输出较大值:2

2、class文件反编译结果

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.zhanghao.test.jclasslib;

public class JclasslibTest {
    public JclasslibTest() {
    }

    public static void main(String[] args) {
        int a = 1;
        int b = 2;
        printMin(a, b);
    }

    private static void printMin(int a, int b) {
        int min = a <= b ? a : b;
        System.out.println(min);
    }
}

  可以看出,生成的class文件只是比java源码多加了一个默认的无参构造方法(因为demo比较简单,所以未体现编译优化的多种策略)

3、下载安装jclasslib

https://github.com/ingokegel/jclasslib/releases

4、使用jclasslib打开class文件

在这里插入图片描述
字节码各部分名称已经很明确类,这里不在过多解释,看我们需要修改的位置Methods->printMin
在这里插入图片描述
printMin方法对应的字节码一共为19行
0:将第一个参数入栈(参数a=1)
1:将第二个参数入栈(参数b=2)
2:比较栈顶两int型数值的大小,当结果大于0时跳转到第9行字节码指令(判断条件进入相应逻辑块)
5:将第一个参数入栈(选择a)
6:无条件跳转到第10行字节码指令
9:将第二个参数入栈(选择b)
10:将栈顶int型数值存入第三个本地变量(存储选择结果c)
11:获取指定类的静态域,并将其压入栈顶(获取待运行的实例及方法)
14:将第三个参数入栈(将c入栈以供11中的实例方法运行参数)
15:调用实例方法
18:从当前方法返回void
更多字节码指令可参考 虚拟机字节码指令表
参照虚拟机字节码指令表,如要实现将pringMin输出参数中的较大值,只要把第三条指令if_icmpgt修改成if_icmple即可。

助记符if_icmpgtif_icmple
指令含义比较栈顶两int型数值的大小,当结果大于0时跳转比较栈顶两int型数值的大小,当结果小于或等于0时跳转
字节码0xa30xa4
有符号型十进制数-93-92

5、修改字节码

package com.zhanghao.test.jclasslib;

import com.alibaba.fastjson.JSON;
import java.io.*;
import org.gjt.jclasslib.io.ClassFileWriter;
import org.gjt.jclasslib.structures.AttributeInfo;
import org.gjt.jclasslib.structures.ClassFile;
import org.gjt.jclasslib.structures.MethodInfo;
import org.gjt.jclasslib.structures.attributes.CodeAttribute;

public class JclasslibModify {
    public static void main(String[] args) throws Exception {
        String filePath = "/Users/zhanghao/Desktop/jclasslib/JclasslibTest.class";
        FileInputStream fis = new FileInputStream(filePath);

        DataInput di = new DataInputStream(fis);
        ClassFile cf = new ClassFile();
        cf.read(di);
        System.out.println(JSON.toJSONString(cf));
        MethodInfo[] methodInfos = cf.getMethods();
        MethodInfo methodInfo = methodInfos[2];
        AttributeInfo[] attributeInfos = methodInfo.getAttributes();
        CodeAttribute codeAttribute = (CodeAttribute) attributeInfos[0];
        byte[] bytes = codeAttribute.getCode();
        bytes[2] = -92;

        fis.close();
        File f = new File(filePath);
        ClassFileWriter.writeToFile(f, cf);
    }
}

读取class文件,可通过debug或者输出json的方式查看ClassFile的类结构,修改类结构中字节码然后重写文件。
需要用到jclasslib.jar,下载链接:jclasslib.jar

6、查看修改后的结果

package com.zhanghao.test;

public class JclasslibTest {
    public JclasslibTest() {
    }

    public static void main(String[] args) {
        int a = 1;
        int b = 2;
        printMin(a, b);
    }

    private static void printMin(int a, int b) {
        int min = a > b ? a : b;
        System.out.println(min);
    }
}

在这里插入图片描述

本文只是简单的阐述修改字节码的方法,对于实际项目中需要修改字节码时,情况会更为复杂,但是换汤不换药,可以先用java生成待修改部分的字节码,然后替换掉原字节码中相应的字节码块,其它文件也需要相应修改,比如常量池等

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
修改jar包中变量 或者长量 步骤如下: 1.用jd-gui打开该jar 怎么打开不用我说了吧? 打开之后找到 要修改提示信息 的class文件 这里找到的是GenEntity 找到对应需要修改的代码所在的方法名 我这里包含提示信息的这段代码在方法 getAllDataBase() 如下图: 2.用jd-gui或者winrar把GenEntity.class 解压出来到C盘 (目录无所谓) 3.双击桌面上安装好了的 jc lasslib bytecode viewer ,点击软件的 File -- Open Class File 打开你刚解压出来的class文件 4.点击methods--getAllDataBase--Code methods是表示方法不用说吧 getAllDataBase是刚在jd-gui里面找到的方法名 Code包含了getAllDataBase方法里所有的信息 找到"不能打开数据库连接,请检查!" 这里这个工具没有提供搜索功能 如果这里很多内容 那么你可以点击 copy to clipboard把这里的内容复制到一个文本文件里面然后再搜索 这里找到的是第82行 5.点击第 82行后面的 #34 会跳转到Constant Pool常量池的第34个常量 6.再点右边的 cp info #362 会跳转到第362个常量 这里能看到String: 不能打开数据库连接,请检查! 也就是最后输出的信息 7.找到GenEntity.class的关键常量了现在就该修改它了 在eclipse里面新建一个Test来处理GenEntity.class [java] view plain copy import java.io.*; import org.gjt.jclasslib.io.ClassFileWriter; import org.gjt.jclasslib.structures.CPInfo; import org.gjt.jclasslib.structures.ClassFile; import org.gjt.jclasslib.structures.constants.ConstantUtf8Info; public class Test { public static void main(String[] args) throws Exception { String filePath = "C:\\GenEntity.class"; FileInputStream fis = new FileInputStream(filePath); DataInput di = new DataInputStream(fis); ClassFile cf = new ClassFile(); cf.read(di); CPInfo[] infos = cf.getConstantPool(); int count = infos.length; for (int i = 0; i < count; 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(i == 362){ ConstantUtf8Info uInfo = (ConstantUtf8Info)infos[i]; uInfo.setBytes("芝麻不开门!".getBytes()); infos[i]=uInfo; } } } cf.setConstantPool(infos); fis.close(); File f = new File(filePath); ClassFileWriter.writeToFile(f, cf); } } 这里需要注意"C:\\GenEntity.class"是我存放class的目录 if(i == 362) 这里是刚我在第七步找到的常量序号 "芝麻不开门!"这里是我想修改的文字信息! 运行Test.java 会有如下提示信息 没有报错就证明正常的 如果报错那么就有问题 9.把C盘刚修改后的GenEntity.class替换掉原来的GenEntity.class 怎么替换不用我说了吧.. 用winrar打开.jar 然后把GenEntity.class拖进去覆盖就行了 10.运行效果..

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值