Android Hook以及插桩技术

Posted by pxfile Blog on June 10, 2018

Android-Hook以及插桩技术

Android AOP之字节码插桩

插桩的概念是以静态的方式修改第三方的代码,也就是从编译阶段,对源代码(中间代码)进行编译,而后重新打包,是静态的篡改; 而hook则不需要再编译阶段修改第三方的源码或中间代码,是在运行时通过反射的方式修改调用,是一种动态的篡改

  • 插桩的概念:

插桩就是在代码中插入一段我们自定义的代码。

  • 插桩的目的:

将程序中插入我们自定义的代码编译到可执行文件中, 该程序的运行过程中就会执行我们自定义的代码,实现我们想要增加的功能需求。

面向切向编程(Aspect Oriented Programming),相对于面向对象编程(ObjectOriented Programming)而言。   OOP的精髓是把功能或问题模块化,每个模块处理自己的家务事。但在现实世界中,并不是所有问题都能完美得划分到模块中,有些功能是横跨并嵌入众多模块里的,比如下图所示的例子。

图1-1 AOP概念说明示例

上图是一个APP模块结构示例,按照照OOP的思想划分为“视图交互”,“业务逻辑”,“网络”等三个模块,而现在假设想要对所有模块的每个方法耗时(性能监控模块)进行统计。这个性能监控模块的功能就是需要横跨并嵌入众多模块里的,这就是典型的AOP的应用场景。

AOP的目标是把这些横跨并嵌入众多模块里的功能(如监控每个方法的性能) 集中起来,放到一个统一的地方来控制和管理。如果说,OOP如果是把问题划分到单个模块的话,那么AOP就是把涉及到众多模块的某一类问题进行统一管理。

我们在开发无埋点数据收集是同样也遇到了很多需要横跨并嵌入众多模块里的场景

Android AOP方式概述

AOP从实现原理上可以分为运行时AOP和编译时AOP,对于Android来讲运行时AOP的实现主要是hook某些关键方法,编译时AOP主要是在Apk打包过程中对class文件的字节码进行扫描更改。Android主流的aop 框架有:

  • Dexposed,Xposed等(运行时)
  • aspactJ(编译时)

除此之外,还有一些非框架的但是能帮助我们实现 AOP的工具类库:

  • java的动态代理机制(对java接口有效)
  • ASM,javassit等字节码操作类库
  • (偏方)DexMaker:Dalvik 虚拟机上,在编译期或者运行时生成代码的 Java API。
  • (偏方)ASMDEX(一个类似 ASM 的字节码操作库,运行在Android平台,操作Dex字节码)

Android AOP方式对比选择

Dexposed,Xposed的缺陷很明显,xposed需要root权限,Dexposed只对部分系统版本有效。   与之相比aspactJ没有这些缺点,但是aspactJ作为一个AOP的框架来讲对于我们来讲太重了,不仅方法数大增,而且还有一堆aspactJ的依赖要引入项目中(这些代码定义了aspactJ框架诸如切点等概念)。更重要的是我们的目标仅仅是按照一些简单的切点(用户点击等)收集数据,而不是将整个项目开发从OOP过渡到AOP。   AspactJ对于我们想要实现的数据收集需求太重了,但是这种编译期操作class文件字节码实现AOP的方式对我们来说是合适的。   因此我们实现Android上AOP的方式确定为:

  • 采用编译时的字节码操作的做法
  • 自己hook Android编译打包流程并借助ASM库对项目字节码文件进行统一扫描,过滤以及修改。

在具体讲解实现技术之前,先看一下无埋点数据收集需求遇到的三个需要AOP的场景。

AOP实现概述

AOP的实现,实现的大致流程如下图所示:

图3-1 Android AOP实现流程

关键有以下几点:

A、字节码插桩入口(图3-1 中1,3两个环节)。   我们知道Android程序从Java源代码到可执行的Apk包,中间有(但不止有)两个环节:

  • javac:将源文件编译成class格式的文件
  • dex:将class格式的文件汇总到dex格式的文件中

我们要想对字节码进行修改,只需要在javac之后,dex之前对class文件进行字节码扫描,并按照一定规则进行过滤及修改就可以了,这样修改过后的字节码就会在后续的dex打包环节被打到apk中,这就是我们的插桩入口(更具体的后面还会详述)。

B、bytecode manipulate(上图3-1 中第二个环节),这个环节主要做:

  1. 字节码扫描,并按照一定规则进行过滤出哪些类的class文件需要进行字节码修改
  2. 对筛选出来的类进行字节码修改操作

最后B步骤修改过字节码的class文件,将连同资源文件,一起打入Apk中,得到最终可以在Android平台可以运行的APP。

总结

简单列举了无埋点数据收集SDK中需要AOP的应用情景,最后介绍了实现的技术细节,主要有两点:

  1. 通过hook dx.jar的方式获得插桩入口(可以和transfrom api配合使用)
  2. 使用ASM库修改字节码,此部分简要介绍了关于字节码的一些基本概念以及执行引擎,最后以View.OnClickListener为例进行了实践。