booster功能分析(1)

1,179 阅读3分钟

1、代码整体结构

buildSrc是gradle界定的用来做统一编译的一种配置方式,代码中除了booster-android-instrument方面的代码都适用这个方式来进行统一依赖。

并且自定义了个plugin来做相对应的一些内容生成,对项目中的java、groovy、kotlin依赖自定义的task:generateBuildProps来生成相对应的build.java文件用来保存一些信息,比如下方的:

booster-gradle-api、booster-gradle-v3_0 booster-gradle-v3_2、booster-gradle-v3_3 是一些扩展类

booster-gradle-plugin、booster-transform-asm 这里是booster的主要入口,用来进行插庄等操作都需要通过这里来进入

booster-android-api、booster-android-instrument这些就是提供插庄所需要的类,比如booster-android-instrument-toast对toast进行功能修改,来防止一些系统导致的toast错误

2、功能细分

(1)ToastBugFix

通过asm的方式替换系统的实现来完成系统bug修复的功能。具体实现如下:

如上面所看到,通过遍历class的节点中是否有包名等于当前所要插庄的代码则返回,否则进行方法遍历查找,如果等于系统的toast,则进行代码的插庄替换,具体看MethodInsnNode.optimize这个方法里面。接下来看替换的toast做了什么操作

代码中可以看出,在替换实现的方式内部,当sdk版本号大于25的时候会执行workaround的方法,会通过反射的方式去获取到toast中的handler和mShow方法,再通过反射去添加一个回调,CaughtCallback,这个callback做了下面的操作:

看完代码是不是觉得很简单,只是在里面加了个try catch就可以修复系统的bug。

(2)FinalizerWatchdog finalize time out 的解决方案

val method = klass.methods?.find {
    "${it.name}${it.desc}" == "attachBaseContext(Landroid/content/Context;)V"} ?:klass.defaultAttachBaseContext
    
       method.instructions?.findAll(RETURN, ATHROW)?.forEach {
    method.instructions?.insertBefore(it, MethodInsnNode(INVOKESTATIC, FINALIZER_WATCHDOG_DAEMON_KILLER, "kill", "()V", false))
    logger.println(" + $FINALIZER_WATCHDOG_DAEMON_KILLER.kill()V before @${if (it.opcode == ATHROW) "athrow" else "return"}: ${klass.name}.${method.name}${method.desc} ")
    }

FinalizerWatchdogDaemonKiller这个的职责就是最多尝试10次,查找java.lang.Daemons$FinalizerWatchdogDaemon这个类,去做反射操作获取他的getinstance,并设置他的thread为空,如果失败就调用stop方法来执行。 但是反射如果上面那个链接所说的,Android 9.0 版本开始限制 Private API 调用,不能再使用反射调用 Daemons 以及 FinalizerWatchdogDaemon 类方法,所以这块应该还可以再优化。

(3)Thread

主要是通过查找以下几种类型对thread的调用来进行代码插庄操作

1、 transformInvokeVirtual,检测是调用Thread.start或者Thread.setName,则进行插庄替换实现进行线程的名字替换

if (context.klassPool.get(THREAD).isAssignableFrom(this.owner)) {
        when ("${this.name}${this.desc}") {
            "start()V" -> {
                method.instructions.insertBefore(this, LdcInsnNode(makeThreadName(klass.className)))
                method.instructions.insertBefore(this, MethodInsnNode(Opcodes.INVOKESTATIC, SHADOW_THREAD, "setThreadName", "(Ljava/lang/Thread;Ljava/lang/String;)Ljava/lang/Thread;", false))
                logger.println(" + $SHADOW_THREAD.makeThreadName(Ljava/lang/String;Ljava/lang/String;) => ${this.owner}.${this.name}${this.desc}: ${klass.name}.${method.name}${method.desc}")
                this.owner = THREAD
            }
            "setName(Ljava/lang/String;)V" -> {
                method.instructions.insertBefore(this, LdcInsnNode(makeThreadName(klass.className)))
                method.instructions.insertBefore(this, MethodInsnNode(Opcodes.INVOKESTATIC, SHADOW_THREAD, "makeThreadName", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false))
                logger.println(" + $SHADOW_THREAD.makeThreadName(Ljava/lang/String;Ljava/lang/String;) => ${this.owner}.${this.name}${this.desc}: ${klass.name}.${method.name}${method.desc}")
                this.owner = THREAD
            }
        }
    }

2、transformInvokeSpecial,符合以下一种情况的方法,也进行名字的重新设定

if (this.owner == THREAD && this.name == "<init>") {
            when (this.desc) {
                "()V",
                "(Ljava/lang/Runnable;)V",
                "(Ljava/lang/ThreadGroup;Ljava/lang/Runnable;)V" -> {
                    method.instructions.insertBefore(this, LdcInsnNode(makeThreadName(klass.className)))
                    val r = this.desc.lastIndexOf(')')
                    val desc = "${this.desc.substring(0, r)}Ljava/lang/String;${this.desc.substring(r)}"
                    logger.println(" + $SHADOW_THREAD.makeThreadName(Ljava/lang/String;Ljava/lang/String;) => ${this.owner}.${this.name}${this.desc}: ${klass.name}.${method.name}${method.desc}")
                    logger.println(" * ${this.owner}.${this.name}${this.desc} => ${this.owner}.${this.name}$desc: ${klass.name}.${method.name}${method.desc}")
                    this.desc = desc
                }
                "(Ljava/lang/String;)V",
                "(Ljava/lang/ThreadGroup;Ljava/lang/String;)V",
                "(Ljava/lang/Runnable;Ljava/lang/String;)V",
                "(Ljava/lang/ThreadGroup;Ljava/lang/Runnable;Ljava/lang/String;)V" -> {
                    method.instructions.insertBefore(this, LdcInsnNode(makeThreadName(klass.className)))
                    method.instructions.insertBefore(this, MethodInsnNode(Opcodes.INVOKESTATIC, SHADOW_THREAD, "makeThreadName", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false))
                    logger.println(" + $SHADOW_THREAD.makeThreadName(Ljava/lang/String;Ljava/lang/String;) => ${this.owner}.${this.name}${this.desc}: ${klass.name}.${method.name}${method.desc}")
                }
                "(Ljava/lang/ThreadGroup;Ljava/lang/Runnable;Ljava/lang/String;J)V" -> {
                    method.instructions.insertBefore(this, InsnNode(Opcodes.POP2)) // discard the last argument: stackSize
                    method.instructions.insertBefore(this, LdcInsnNode(makeThreadName(klass.className)))
                    method.instructions.insertBefore(this, MethodInsnNode(Opcodes.INVOKESTATIC, SHADOW_THREAD, "makeThreadName", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false))
                    logger.println(" + $SHADOW_THREAD.makeThreadName(Ljava/lang/String;Ljava/lang/String;) => ${this.owner}.${this.name}${this.desc}: ${klass.name}.${method.name}${method.desc}")
                    this.desc = "(Ljava/lang/ThreadGroup;Ljava/lang/Runnable;Ljava/lang/String;)V"
                }
            }
        }

3、transformInvokeStatic,如果是通过线程池方式创建的线程,统一替换成ShadowExecutors来创建线程,保证所产生的线程名字等信息能够统一

when (this.owner) {
            EXECUTORS -> {
                when (this.name) {
                    //往opcode栈push String常量值 "\u200B"+类名
                    "defaultThreadFactory" -> {
                        val r = this.desc.lastIndexOf(')')
                        val desc = "${this.desc.substring(0, r)}Ljava/lang/String;${this.desc.substring(r)}"
                        logger.println(" * ${this.owner}.${this.name}${this.desc} => $SHADOW_EXECUTORS.${this.name}$desc: ${klass.name}.${method.name}${method.desc}")
                        this.owner = SHADOW_EXECUTORS
                        this.desc = desc
                        method.instructions.insertBefore(this, LdcInsnNode(makeThreadName(klass.className)))
                    }
                    "newCachedThreadPool",
                    "newFixedThreadPool",
                    "newSingleThreadExecutor",
                    "newSingleThreadScheduledExecutor",
                    "newScheduledThreadPool" -> {
                        val r = this.desc.lastIndexOf(')')
                        val name = this.name.replace("new", "newOptimized")
                        val desc = "${this.desc.substring(0, r)}Ljava/lang/String;${this.desc.substring(r)}"
                        logger.println(" * ${this.owner}.${this.name}${this.desc} => $SHADOW_EXECUTORS.$name$desc: ${klass.name}.${method.name}${method.desc}")
                        this.owner = SHADOW_EXECUTORS
                        this.name = name
                        this.desc = desc
                        method.instructions.insertBefore(this, LdcInsnNode(makeThreadName(klass.className)))
                    }
                }
            }
        }

(4)ActivityThread 获取handler的子类H,通过ActivityThreadHooker往里面埋入hook方法,来设置callback,用自定义callback的来替换系统的callback,以此来捕获系统导致的异常

override fun transform(context: TransformContext, klass: ClassNode): ClassNode {
        if (!this.applications.contains(klass.className)) {
            return klass
        }

        mapOf(
            "<clinit>()V" to klass.defaultClinit,
            "<init>()V" to klass.defaultInit,
            "onCreate()V" to klass.defaultOnCreate
        ).forEach { (unique, defaultMethod) ->
            val method = klass.methods?.find {
                "${it.name}${it.desc}" == unique
            } ?: defaultMethod.also {
                klass.methods.add(it)
            }
            method.instructions?.findAll(RETURN, ATHROW)?.forEach {
                method.instructions?.insertBefore(it, MethodInsnNode(INVOKESTATIC, ACTIVITY_THREAD_HOOKER, "hook", "()V", false))
                logger.println(" + $ACTIVITY_THREAD_HOOKER.hook()V before @${if (it.opcode == ATHROW) "athrow" else "return"}: ${klass.name}.${method.name}${method.desc}")
            }
        }

        return klass
    }

未完待续