记一次BUG查找

缘起

[#1314729 java.lang.NullPointerException]
Attempt to invoke virtual method 'void android.webkit.WebView.loadUrl(java.lang.String)' on a null object reference
com.hao.ad.e.e$1.run(FragmentHelper.java:22)

很明显,就是一个普通的空指针异常,但是不同之处在于反编译出来的包里面竟然找不到com.hao.ad这个包,公司开发组一直也没有解决掉这个问题。

发展

本人接到任务,优化APP的启动,在Profiler中又再次发现了这个奇怪的包,关键是他的启动占据了我们宝贵的UI线程接近1s的时间:

com.hao.ad启动占据1S的宝贵时间

可以推测com.hao.ad是通过context获取到mainLooper,再构造一个handler绑定mainLooper,最后在通过handler.post(runnable)的方式,将他的代码运行到UI线程的。

初见

通过经典的二分查找,在众多第三方库中找到了MainSDK.init(context,···)这个函数,将其注释掉之后发现启动的trace中就没有com.hao.ad这个包的运行代码了。到这里问题就解决了,召集产品确认是否把这个SDK关掉就万事大吉了。

深入

作为码农的我怎么能放弃这次学习的机会呢,必然要到源码里一探究竟的:

// NoeSdk_1_5.0.0.jar!/com/boh/ejskhc/MainSDK.class
class MainSDK extends Thread(){
    // ···
    (new MainSDK()).start();
    // ···
}

可见,完美符合预期,的确是在一个线程中跑的,那么怎么post到UI线程的呢,在他的包里面继续寻找:

// NoeSdk_1_5.0.0.jar!/com/boh/ejskhc/h.class
Runnable var5 = new Runnable() {
                    public final void run() {
                        try {
                            h.a;
                            int var1x = (new f(i.b(var2.a))).a(var2, var1);
                            com.tao.admin.loglib.b.a(com.tao.admin.loglib.a.a().d() ? ": ".concat(String.valueOf(var1x)) : "");
                        } catch (Exception var2x) {
                        }
                    }
                };
                b.submit(var5);

再一次完美的对应上预期了,注意到这里有一段拼接字符串的操作,难不成是拼接com.hao.ad?继续探究下代码,就从var1x出发:

// NoeSdk_1_5.0.0.jar!/com/boh/ejskhc/f.class
static String b(int var0) {
        return "m" + var0 + ".jar";
    }
f(String var1) throws NullPointerException {
        var1 = MainSDK.a().getFilesDir().getAbsolutePath() + "/" + var1;
        this.a = new File(var1);
    }

又一次对上了,通过拼接了一个绝对路径/m数字.jar字符串打开了一个jar包,那么就应该出现DexClassLoader来记载这个包,继续寻找:

// NoeSdk_1_5.0.0.jar!/com/boh/ejskhc/f.class
  File var3 = MainSDK.a().getDir("dex", 0);
            Class var6;
            Object var7 = (var6 = (new DexClassLoader(this.a.toString(), var3.getAbsolutePath(), (String)null, ClassLoader.getSystemClassLoader().getParent())).loadClass(var1.d + "." + var1.e)).newInstance();
            Class[] var4;
            (var4 = new Class[2])[0] = Context.class;
            var4[1] = Map.class;
            return (Integer)var6.getMethod("load", var4).invoke(var7, MainSDK.a(), var2);

完美!基本确认就是这个问题了,还差最后一步,找到这个绝对路径/m数字.jar,给本次侦探盖棺定论!

反击

导出所有位于我们/data/data/包名/目录下的文件,寻找m数字.jar这个文件:

导出包名下所有文件

经过查找,找到了两个名称符合的文件m1.jar&&m7.jar
对这两个文件进行反编译,查看里面的dex文件:
m7.har中包含com.hao.ad

完美!

结案

抓贼要抓脏,一步步分析下来确认了就是这个第三方合作的SDK偷偷运行线程动态添加dex文件,并且把代码post到UI线程,不仅拖慢了我们APP的启动,还不定时的报一个崩溃!

总结

本次探案过程用到了这些相关工具好相关知识:

  1. profiler,启动过程分析
  2. DexClassLoader,动态dex加载
  3. 反射
  4. 二分查找
  5. adb相关
  6. apktool

你可能感兴趣的:(记一次BUG查找)