一种新的修改字节码的思路

2018-05-13  本文已影响242人  轻微

博客地址: dim.red

0x00 前言

分析最近的 Crash , 排在前列基本是第三方SDK导致. 遇到这样的问题, 我们需要寻求官方的支持, 但是官方的支持总是来得比较晚. 在Android上, 第三方SDK一般是两种, 一种是jar, 一种是so. so的修改成本过大, 我们放弃它, 我们现在探讨对 jar 的修改.

0x02 问题

在之前 使用AOP来为第三方SDK打CALL 文章 我们使用 AOP 对第三方 SDK 的问题进行修复. 貌似能这样解决这个问题.
但是 AOP 存在局限性:
1、它不够直观. 定义的规则需要通过编译后才能确定.
2、学习成本较高.
3、可操作的范围不够大. 只能切方法.
例子:
在一些因素下,我们升级某个推送SDK版本. 但是发现有大量的NPE异常出现. 通过反编译 jar 定位问题.

  private Handler p = null;
  private void b() {
        if (this.p != null) {
            this.p.removeMessages(2);
        } else {
            this.p = new Handler(Looper.getMainLooper(), new com.xxx.b(this));
        }
        this.p.sendEmptyMessageDelayed(2, 3000L);
   }
  private void c() {
        if (this.p != null) {
            this.p.removeMessages(2);
            this.p = null;
        }
   }
  public void d(){
  ...
    // 代码块A  开始
    if(p != null){
      this.p.sendEmptyMessageDelayed(3, 3000L);
    }
    // 代码块A  结束
  ...
  }

异常发生在方法this.p.sendEmptyMessageDelayed(2, 3000L);.在一个判断空还会出现NPE, 说明这是一个多线程并发下的bug. 通过分析我们需要

  1. 对b c 方法使用synchronized 修饰
  2. 对代码块A 进行 synchronized(this) 包裹

在这种场景下使用AOP将费力不讨好.

0x03 解决方案

我们可以尝试使用一种更简单的方式来处理这件事情. 输入一个原始的 jar , 经过转换生成新的 jar.

0x04 问题

Q : javac 编译失败.
A: 当你 java 调用的方法不在原有的 jar 中, 导致 javac 编译的时候找不到对应的方法,抛出异常.
解决方式:使用 asm 对 jar 中进行指令分析, 对指令 invokestatic, invokevirtual , invokeinterface , invokedynamic ( android 上可以忽略这个指令) 和 getfield , getstatic 指令的分析. 我们可以生成一个空的实现的jar , 来为 javac 编译提供环境支持.

Q : 内部类的问题.
A: 匿名内部的生成的规则是 外部类类名$Number. Number 是在源码中出现的位置. 当你新增和调整位置的时候会导致生成的类和之前的类不匹配. 解决方式. 在对外部类操作的时候, 直接对匿名内部类进行删除. 等 javac 命令的生成新的内部类直接替换进去.

0x05 改进

通过和 idea 结合提供一整套的解决方案.

上一篇 下一篇

猜你喜欢

热点阅读