Android 进阶之路:ASM 修改字节码,这样学就对了

本文详细介绍了使用ASM库解析和修改Android字节码的过程,包括引入ASM、通过Tree API和Core API分析Class文件、利用ClassWriter进行字节码修改,以及如何在方法前后插入代码。通过示例代码展示了如何添加字段和修改方法字节码,是Android进阶学习的重要实践。
摘要由CSDN通过智能技术生成

[](()引入 ASM

好了,下面我们开始正式学习 ASM。

首先我们找到ASM 的官网:

https://asm.ow2.io/

在官网你可以看到目前最新的版本,还有一份详细的 User guide,基本包含了所有 API 的介绍。

看官网上版本迭代目前已经跟新到9.1了,那就试用最新版本吧:

// https://mvnrepository.com/artifact/org.ow2.asm/asm-commons

implementation group: ‘org.ow2.asm’, name: ‘asm-commons’, version: ‘9.1’

[](()尝试分析 Class 文件


从学习的角度来说,在修改 class文件之前,我们可以先学习下怎么读取 class 文件内部的各个部分。

比如我想在编译期间通过编译的*.class的文件,获取其内部的所有方法名称,字段名称。

[](()Tree Api

对于分析class 文件,我们最希望的方式是什么?

肯定是:我给你个 class 文件,然后你给我返回个 ClassNode对象,这个对象最好有个 类似List,List这样的方法或者字段。

恩…想得倒美,ASM 是这么简单的东西吗?

不过,ASM 还真就这么简单,我们来看一个类 ClassNode,这个类上注释是:

A node that represents a class.

用来指代一个 class 文件。

那么我们想要的 class 内部的一切,应该可以通过这个类的 API 直接或者间接的获取。

没错,是的,看一眼:

我们只要通过 class 文件构造这么个 ClassNode 对象,好像就可以为所欲为了。

来看下代码把:

首先我们编写一个 User:

public class User {

private String name;

private int age;

public String getName() {

return name;

}

public int getAge() {

return age;

}

}

然后我们希望获取 User.class 中包含的所有方法以及字段:

public class TreeApiTest {

public static void main(String[] args) throws Exception {

Class clazz = User.class;

String clazzFilePath = Utils.getClassFilePath(clazz);

ClassReader classReader = new ClassReader(new FileInputStream(clazzFilePath));

ClassNode classNode = new ClassNode(Opcodes.ASM5);

classReader.accept(classNode, 0);

List methods = classNode.methods;

List fields = classNode.fields;

System.out.println(“methods:”);

for (MethodNode methodNode : methods) {

System.out.println(methodNode.name + ", " + methodNode.desc);

}

System.out.println(“fields:”);

for (FieldNode fieldNode : fields) {

System.out.println(fieldNode.name + ", " + fieldNode.desc);

}

}

}

上述代码有个辅助类Utils.getClassFilePath方法我贴一下,主要是可以通过Class 对象,找到其在 AS 中具体的路径。

public static String getClassFilePath(Class clazz) {

// file:/Users/zhy/hongyang/repo/BlogDemo/app/build/intermediates/javac/debug/classes/

String buildDir = clazz.getProtectionDomain().getCodeSource().getLocation().getFile();

String fileName = clazz.getSimpleName() + “.class”;

File file = new File(buildDir + clazz.getPackage().getName().replaceAll(“[.]”, “/”) + “/”, fileName);

return file.getAbsolutePath();

}

看下我们代码的流程:

  1. 首先我们拿到 class 文件的路径;

  2. 然后交给 ClassReader

  3. 再构造一个 ClassNode 对象

  4. 调用 ClassReader.accept()方法完成对 class 遍历,并把相关信息记录到 ClassNode 对象中;

这个时候,我们就能够通过 ClassNode 去拿我们所想要的信息了,看一下输出:

methods:

, ()V

getName, ()Ljava/lang/String;

getAge, ()I

fields:

name, Ljava/lang/String;

age, I

到这里,有没有发现,如果只是读取 class 文件,是不是简单得不能再简单了?

上述的 API,称为 Tree Api,即我们分析完成 class 文件,把信息存储到 ClassNode,然后通过 ClassNode 再读取即可,有点类似 xml 文件解析时,把整个 xml 文件读取到内存中的方式。

[](()Core Api

不过大家如果看博客,其实上述写法在博客中并不多见,更多的博客上书写的还是基于“事件驱动”的 API,即解析class 文件过程中,每遇到一个“节点”,把节点信息交给你,我们类似于监听“节点”的解析事件,我们看下代码:

publi

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值