Java 通过修改 .class 修复 BUG

当源代码不可用时,通过反编译分析.class文件定位到错误的固定数值,使用二进制查看器进行修改,实现BUG的紧急修复。详细步骤包括查找、定位、修改和验证。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题来源

下载现场部署的代码的源代码,虽然只有 .class 文件,但是也能够通过反编译获得一些信息,果不其然,其中第一次查询的固定的某个 id 与现在项目中对应的 id 值不一致,然后在部署现场执行 sql 语句发现没有数据,定位到原因在此。

解决办法

现在我这里的源代码已经被改得面目全非,要改回去是不可能的了。由于是在项目中写的一个固定数字,那么通过分析现场获取过来的项目的 .class 文件,就能够定位到,然后通过二进制查看器,对其进行二进制级别的修改,就能够达到修复这个 BUG 的目的。

解决过程:

1、获取 AServiceImp.class 文件,通过反编译查看器得到需要修改的代码如下:

public PageDataModel queryCount(String startTime, String endTime) {
    List resultList = new ArrayList();
    List aList = this.ADao.queryA(1, 356);
    
    ....
    
    Double timeTotal = Double.valueOf(0.0D);
    Double sizeTotal = Double.valueOf(0.0D);
    Integer countTotal = Integer.valueOf(0);
    List footerList = new ArrayList();
    
    ....
    
    return new PageDataModel(Integer.valueOf(0), resultList, footerList);
  }

其中需要修改的是:List aList = this.ADao.queryA(1, 356);356 修改为 610

2、通过 javap -verbose AServiceImp 命令,输出 AServiceImpl.class 中的字节码内容,并查找上述程序段的字节码全部内容如下

 public com.example.web.PageDataModel queryCount(java.lang.String, java.lang.String);
    descriptor: (Ljava/lang/String;Ljava/lang/String;)Lcom/example/web/PageDataModel;
    flags: ACC_PUBLIC
    Code:
      stack=9, locals=13, args_size=3
         0: aload_1
         1: ifnull        26
         4: aload_2
         5: ifnull        26
         8: aload_1
         9: ldc           #4                  // String
        11: invokevirtual #5                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z
        14: ifne          26
        17: aload_2
        18: ldc           #4                  // String
        20: invokevirtual #5                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z
        23: ifeq          33
        26: aconst_null
        27: astore_1
        28: aconst_null
        29: astore_2
        30: goto          73
        33: new           #8                  // class java/lang/StringBuilder
        36: dup
        37: invokespecial #9                  // Method java/lang/StringBuilder."<init>":()V
        40: aload_1
        41: invokevirtual #10                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        44: ldc           #11                 // String  00:00:00
        46: invokevirtual #10                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        49: invokevirtual #12                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        52: astore_1
        53: new           #8                  // class java/lang/StringBuilder
        56: dup
        57: invokespecial #9                  // Method java/lang/StringBuilder."<init>":()V
        60: aload_2
        61: invokevirtual #10                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        64: ldc           #13                 // String  23:59:59
        66: invokevirtual #10                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        69: invokevirtual #12                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        72: astore_2
        73: new           #17                 // class java/util/ArrayList
        76: dup
        77: invokespecial #18                 // Method java/util/ArrayList."<init>":()V
        80: astore_3
        81: aload_0
        82: getfield      #24                 // Field ADao:Lcom/example/web/ADao;
        85: iconst_1
        86: sipush        356
        89: invokevirtual #155                // Method com/example/web/dao/ADao.queryA:(II)Ljava/util/List;
        92: astore        4
        94: aload         4
        96: invokeinterface #29,  1           // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
       101: astore        5
       103: aload         5
       105: invokeinterface #30,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
       110: ifeq          397
       113: aload         5
       115: invokeinterface #31,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
       120: checkcast     #87                 // class com/example/web/AInfo
       123: astore        6
       125: aload_0
       126: getfield      #24                 // Field statisticsDao:Lcom/example/web/dao/ADao;
       129: iconst_2
       130: aload         6
       
       ....

其中 356 所在的位置为:

85: iconst_1
86: sipush        356
89: invokevirtual #155                // Method

3、使用二进制查看器打开 AServiceImpl.class 文件。

4、查阅第 2 步中 356 前后的助记符所对应的十六进制字节码:

iconst_1       ---    0x04
sipush         ---    0x11
invokevirtual  ---    0xb6

5、由于 iconst_1sipush 之间没有任何间隔,那么就可以通过 0x040x11 在二进制查看器中同时进行搜索,然后比对后面几个二级制数字中是否出现 0xb6,查找结果如下:

04 11 01 64 b6
04 11 13 88 b6

计算在 04 11b6 之间的十六进制数字得到:

0x0164 = 356
0x1388 = 5000

可以得出:04 11 01 64 b6 就是需要修改的 356 这个数字,然后把 610 转换为十六进制为 0x0262,最后在二进制查看器中将 0x0164 修改为 0x0262 即可。

6、验证,保存上一步修改的文件,此时文件依然为 AServiceImpl.class 文件,然后通过 javap -verbose AServiceImpl,找到 sipush 对应的那一行,可以看到此时后面的数据已经变成了 610,修改成功。

85: iconst_1
86: sipush        610
89: invokevirtual 

替换现场中的 .class 文件,能看到数据了,修改成功。

总结

本文只是给出了修改固定值的一种便捷方法,不适用于普遍的情况,但是在本文给出的前提条件下,不失为一种巧妙的解决方式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值