Android字节码插桩——详细讲解 附带Demo

Posted 薛瑄

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android字节码插桩——详细讲解 附带Demo相关的知识,希望对你有一定的参考价值。

前言

什么是字节码插桩

字节码插桩就是在构建的过程中,通过修改已经编译完成的字节码文件,也就是class文件,来实现功能的添加

从技术上来说,字节码插桩是自定义Gradle插件、ASM、Java字节码、切面编程的综合应用

字节码插桩可以做什么

举个例子,APP全量统计的时候,经常需要建立很多埋点。这是个很大重复性工作,那么可以通过字节码插桩,在apk打包之前,对class文件需要的地方进行埋点。这样就可以实现无埋点的全量统计。

下面我们来逐一介绍用到的知识,可能需要学习很多东西,学完这些将会有很大的收货!!

一、切面编程 AOP

AOP(Aspect Oriented Program的首字母缩写)是一种面向切面编程的思想。这种编程思想是相对于OOP(ObjectOriented Programming即面向对象编程)来说的。

先来说一下大家熟悉的面向对象编程:面向对象的特点是继承、多态和封装。而封装就要求将功能分散到不同的对象中去,这在软件设计中往往称为职责分配。实际上也就是说,让不同的类设计不同的方法。这样代码就分散到一个个的类中去了。这样做的好处是降低了代码的复杂程度,使类可重用。

但是存在一个问题,如果每个类中都需要同样的功能,例如日志,统计等。这个是面向对象的编程天生的缺点,就是分散代码的同时,也增加了代码的重复性。按照OOP的思想,我们需要在各个模块里面都添加统计代码。但是如果按照AOP的思想,可以将统计的地方抽象成切面,只需要在切面里面添加统计代码就OK了。

字节码插桩是AOP编程一种很好的实现方式,在后台开发的Spring框架中已经在使用切面编程来添加操作日志记录

二、APK打包流程

在APK打包的时候,我们要对字节码进行修改,那么就需要了解整个打包流程,知道在哪个过程中可以获取到字节码。

官网的打包流程介绍的不是很详细,下图介绍了详细的打包流程。

apk打包使用的工具是gradle,android 提供了Gradle插件 com.android.tools.build:gradle,使得我们可以轻松执行这个打包流程

经过“Java Compiler步骤”,也就是代码编译,系统便生成了.class文件。这些class文件经过dex步骤再次转化成Android识别的.dex文件。

既然我们要做字节码插桩,就必须hook打包流程,在dex步骤之前对class字节码进行扫描与重新编织,然后将编织好的class文件交给dex过程。这样就实现了所谓的无埋点。

那么问题来了,怎么才能在打包流程中,添加我们想要执行的操作。也就是说如何才能拦截住打包流程呢? 我们下面分解

三、自定义Gradle插件

整个打包流程是由Android Gradle插件 com.android.tools.build:gradle提供的,在1.5.0-beta1 及以后的版本,添加了Transform API ,允许第三方Gradle 插件,在打包为dex 之前,可以对class进行操作。(详见官网

那么由此引出两个知识点,介绍这两个的篇幅有点长,所以列出一下网上比较好的文章

3.1、自定义Gradle插件

自定义Gradle插件 官方文档:Developing Custom Gradle Plugins

在AndroidStudio中自定义Gradle插件
拥抱 Android Studio 之五:Gradle 插件开发

对以上两边文章,有两点补充:

1、关于自定义Gradle插件 ,网上中文文档,基本都还是在介绍使用使用groovy,现在已经支持使用kotlin来编写Gradle插件。两者只在目录方面有些差异。最后的demo就是使用kotlin来编写Gradle插件的

2、在build.gradle 文件中,通常会出现 apply plugin: 'com.android.application' 这里apply plugin 的是groovy 语言调用函数的方式,这句代码会调用com.android.application插件的apply() 函数

3.2、如何使用Transform API

Transform详解

四、Java字节码

Java 字节码(英语:Java bytecode)是Java虚拟机执行的一种指令格式。通俗来讲字节码就是经过javac命令编译之后生成的Class文件。Class文件包含了Java虚拟机指令集和符号表以及若干其他的辅助信息。Class文件是一组以8位字节为基础单位的二进制流,所有数据项目严格按照顺序紧凑的排列在Class文件之中,中间没有任何分隔符,这使得整个Class文件中存储的内容几乎全是程序运行时的必要数据。

通俗的说就是,Java代码编译后生成的Class文件,这个文件是二进制。这个文件有个规定,第几位到第几位是什么数据,

4.1、关于字节码几个重要的内容::

1、 Class文件中使用全限定名来表示一个类的引用,全限定名很容易理解,即把类名所有“.”换成了“/”

例如:
android.widget.TextView 的全限定名 android/widget/TextView

2、描述符
Class文件中使用描述符,描述字段的数据类型、方法的参数列表(包括数量、类型以及顺序)和返回值。

  1. 基本数据类型(byte char double float int long short boolean)以及代表无返回值的void类型都用一个大写字符( Type Signature)来表示
  2. 对象类型则用字符“L”加对象的全限定名来表示,一般对象类型末尾都会加一个“;”来表示全限定名的结束。

类型签名

Type SignatureJava Type
Zboolean
Bbyte
Cchar
Sshort
Iint
Jlong
Ffloat
Ddouble
Lfully-qualified-class ;fully-qualified-class
[ typetype[]
( arg-types ) ret-typemethod type

举例:

java.lang.String[][]
将会被表示成
[[java/lang/String;

int[]
将会被表示成
[I;

void init()
会被描述成
()V

void setText(String s)
会被描述成
(Ljava/lang/String)V;

java.lang.String toString()
会被描述成
()Ljava/lang/String;

4.2、Java字节码

一文让你明白Java字节码

看了字节码,相信你一定有疑问,是不是对class文件修改很复杂呀?其实根本没有这么复杂,而且已经有很多支持字节码编织的框架,学习字节码,是为了在使用框架修改字节码时更上手一点

五、 Java 字节码编织框架——ASM

什么是ASM?

ASM 是一个 Java 字节码操控框架。它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class 文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。

看完下面这篇文章,基本可以直接上手使用ASM框架了,通过这个框架,我们可以修改class文件,增加函数,修改函数、各种逻辑等等编程操作。

AOP 的利器:ASM 3.0 介绍,这篇文章有点过时,但是基本的原理没变,所以可以学习其思想

ASM官方文档

虽然有了ASM这种框架,可以很方便的修改class文件,但是如果不熟悉框架的使用,写起来还是有点吃力
人类总是懒惰的,试图找出一些捷径,于是有了一款Idea插件——ASM Bytecode Outline

如果想深入学习ASM,可以查看这个系列的文章:
1.1 ASM-简介-目的

ASM Bytecode Outline

插件ASM Bytecode Outline,可以把java代码转为ASM框架 的代码,那么我们可以先修改好一个类的代码,把代码转为ASM框架的代码,然后把需要的代码复制到,这样就可以在自定义的gradle plugin 中批量自动去修改目标类了。

六、实战项目

这个demo简单实现了,在Activity的两个生命周期中插入一行Toast代码

demo地址:https://github.com/JantHsueh/AndoridAop

demo中有两次地方,必须改为自己的地址,需要更改的地址的都添加了注释

1、AndroidAOP\\traceplugin\\gradle.properties 文件中
2、根目录的build.gradle 文件

代码不是很复杂,上面那些知识学会了,上手很容易,代码就不贴了,下面看下最后的效果

请点赞、收藏,感谢大家的支持,有任何疑问可在评论区回复

参考:
Android字节码插桩采坑笔记
通过自定义 Gradle 插件修改编译后的 class 文件

ASM官方文档
https://www.sharezer.com/archives/1574

JVM的类型签名对照表

以上是关于Android字节码插桩——详细讲解 附带Demo的主要内容,如果未能解决你的问题,请参考以下文章

字节码插桩之Java Agent

字节码插桩之Java Agent

字节码插桩AOP 技术 ( “字节码插桩“ 技术简介 | AspectJ 插桩工具 | ASM 插桩工具 )

Android-ASM字节码插桩与APT原理补充

Android-ASM字节码插桩与APT原理补充

Android Gradle 中的字节码插桩之ASM