Java/Kotlin 代码转 ASM API 工具指南
目前虽然没有完全自动化的转换工具,但有一些实用工具和方法可以帮助开发者将 Java 或 Kotlin 代码转换为基于 ASM API 的字节码操作代码。以下是常用工具和方法的详细介绍:
1. ASM Bytecode Viewer
安装方法:
- 在 Android Studio 或 IntelliJ IDEA 中,通过插件市场搜索"ASM Bytecode Viewer"或"ASM Bytecode Viewer Support Kotlin"
- 安装完成后,重启 IDE
使用方法:
- 右键点击需要转换的 Java 或 Kotlin 文件或方法
- 选择"ASM Bytecode Viewer"
- 插件会生成对应的 ASM 字节码操作代码,并显示在一个新的窗口中
特点:
- 支持 Java 和 Kotlin
- 可以直接生成 ASM 的
ClassVisitor和MethodVisitor代码 - 适合初学者快速理解和使用 ASM
2. ASMifier
使用方法:
- 编译 Java 或 Kotlin 代码,生成
.class文件 - 使用以下命令运行
ASMifier:
java -classpath "asm-all-4.0.jar" org.objectweb.asm.util.ASMifier <path_to_class_file>
其中asm-all-4.0.jar是 ASM 的 JAR 文件,<path_to_class_file>是目标.class文件的路径
特点:
- 官方工具,生成的代码准确可靠
- 适合对字节码有一定了解的开发者
3. JD-GUI
使用方法:
- 下载并安装JD-GUI
- 打开 JD-GUI,将
.class文件拖入工具中 - 查看反编译后的 Java 代码,了解字节码的结构
特点:
- 提供直观的字节码反编译视图
- 可以帮助开发者更好地理解字节码结构
- 虽然不直接生成 ASM 代码,但可作为辅助分析工具
4. Kotlin Bytecode Viewer
使用方法:
- 在 Android Studio 中选中需要查看的 Kotlin 文件
- 点击菜单栏中的 Tools > Kotlin > Show Kotlin Bytecode
- 在弹出的字节码窗口中,点击 Decompile 按钮,查看反编译后的 Java 代码
特点:
- 内置工具,无需额外安装
- 支持 Kotlin 代码的字节码查看
- 适合 Kotlin 开发者快速查看字节码
5. ASM Bytecode Outline
安装方法:
- 在 Android Studio 或 IntelliJ IDEA 中,通过插件市场搜索"ASM Bytecode Outline"
- 安装完成后,重启 IDE
使用方法:
- 右键点击需要转换的文件或方法,选择"Show Bytecode Outline"
- 在弹出的窗口中选择"ASMified"标签,查看生成的 ASM 代码
特点:
- 提供详细的字节码分析
- 支持 Java 和 Kotlin
- 比 ASM Bytecode Viewer 功能更全面
总结
根据你的具体需求,可以选择以下工具:
- 初学者友好:ASM Bytecode Viewer
- 官方工具:ASMifier
- 辅助分析:JD-GUI
- Kotlin 专用:Kotlin Bytecode Viewer
- 功能全面:ASM Bytecode Outline
这些工具各有侧重,可以根据开发阶段和需求灵活组合使用。
手动转换
手动转换需要你熟悉Java字节码指令和ASM的API规则,步骤如下:
1. 将Java代码编译成字节码
使用javac命令将Java源代码编译成.class文件,例如:
javac YourJavaFile.java
2. 反编译字节码文件
使用javap工具反编译字节码文件,查看对应的助记符指令,例如:
javap -c YourJavaFile.class
3. 根据指令编写ASM代码
下面以一个简单的Java类为例:
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
其对应的字节码指令(使用javap -c HelloWorld查看)如下:
Compiled from "HelloWorld.java"
public class HelloWorld {
public HelloWorld();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String Hello, World!
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
}
下面是使用ASM API实现相同功能的代码:
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
public class AsmExample {
public static byte[] generateClass() {
// 创建ClassWriter对象,用于生成类的字节码
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
// 定义类的基本信息,这里定义一个名为HelloWorld的公共类,继承自java.lang.Object
cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, "HelloWorld", null, "java/lang/Object", null);
// 定义类的构造方法
MethodVisitor constructor = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
constructor.visitCode();
// 加载this引用
constructor.visitVarInsn(Opcodes.ALOAD, 0);
// 调用父类的构造方法
constructor.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
// 返回
constructor.visitInsn(Opcodes.RETURN);
constructor.visitMaxs(1, 1);
constructor.visitEnd();
// 定义main方法
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
mv.visitCode();
// 获取System.out静态字段
mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
// 加载字符串常量"Hello, World!"
mv.visitLdcInsn("Hello, World!");
// 调用PrintStream的println方法
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
// 返回
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(2, 1);
mv.visitEnd();
// 结束类的生成
cw.visitEnd();
return cw.toByteArray();
}
public static void main(String[] args) {
byte[] bytecode = generateClass();
// 这里可以将bytecode写入文件或加载到JVM中
}
}
上述代码中:
ClassWriter用于生成类的字节码。MethodVisitor用于访问和修改方法体的字节码。Opcodes类提供了JVM操作码的常量。
手动转换对于复杂的代码来说会比较困难,需要对字节码指令有深入的了解。
知名框架中的使用场景举例
TheRouter中使用ASM插入字节码的实战案例:
com.therouter.plugin.AddCodeVisitor中有数个根据采集数据循环插入代码的案例。 asm插入代码的目标类是TheRouterServiceProvideInjecter,我们对比源码,字节码,修改后的字节码。
1、TheRouterServiceProvideInjecter.kt顶层代码
TheRouterServiceProvideInjecter 并非严格意义上的类,而是 Kotlin 文件顶层代码。
函数和属性会被编译到由 @file:JvmName 指定的类里TheRouterServiceProvideInjecter final类。而且都是public static修饰的,方法还多一个final修饰。
// 指定该文件在 Java 中对应的类名
@file:JvmName("TheRouterServiceProvideInjecter")
// 声明该文件所属的包名
package a
import android.content.Context
import androidx.annotation.Keep
import com.therouter.flow.Digraph
/**
* 标记该类或方法在混淆时不被移除或重命名
*/
@Keep
/**
* 内部变量,用于标记是否使用 ASM 进行字节码操作
*/
@JvmField
internal var asm = false
/**
* 该函数可能用于执行特定的初始化或者拦截操作,当前为空实现。
*/
fun trojan() {}
/**
* 该函数可能用于执行自动注入逻辑,但传入的对象参数未被使用,当前为空实现。
*/
fun autowiredInject(obj: Any?) {}
/**
* 该函数可能用于添加流程任务,但传入的上下文和有向无环图参数均未被使用,当前为空实现。
*/
fun addFlowTask(context: Context?, digraph: Digraph) {}
/**
* 该函数可能用于初始化默认的路由映射表,当前为空实现。
*/
fun initDefaultRouteMap() {}
2、TheRouterServiceProvideInjecter.class字节码,javac或者kotlinc阶段产生。
PS hll-wp-therouter-android\router\build\tmp\kotlin-classes\debug\a> javap -p -v TheRouterServiceProvideInjecter.class > xx.txt
Classfile hll-wp-therouter-android/router/build/tmp/kotlin-classes/debug/a/TheRouterServiceProvideInjecter.class
Last modified 2025年5月14日; size 1497 bytes
MD5 checksum c763ff556e739f951790ee7db28f96ad
Compiled from "TheRouterServiceProvideInjecter.kt"
public final class a.TheRouterServiceProvideInjecter
minor version: 0
major version: 55
flags: (0x0031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER
this_class: #2 // a/TheRouterServiceProvideInjecter
super_class: #4 // java/lang/Object
interfaces: 0, fields: 1, methods: 5, attributes: 3
Constant pool:
#1 = Utf8 a/TheRouterServiceProvideInjecter
#2 = Class #1 // a/TheRouterServiceProvideInjecter
#3 = Utf8 java/lang/Object
#4 = Class #3 // java/lang/Object
#5 = Utf8 trojan
#6 = Utf8 ()V
#7 = Utf8 autowiredInject
#8 = Utf8 (Ljava/lang/Object;)V
#9 = Utf8 Lorg/jetbrains/annotations/Nullable;
#10 = Utf8 obj
#11 = Utf8 Ljava/lang/Object;
#12 = Utf8 addFlowTask
#13 = Utf8 (Landroid/content/Context;Lcom/therouter/flow/Digraph;)V
#14 = Utf8 Lorg/jetbrains/annotations/NotNull;
#15 = Utf8 digraph
#16 = String #15 // digraph
#17 = Utf8 kotlin/jvm/internal/Intrinsics
#18 = Class #17 // kotlin/jvm/internal/Intrinsics
#19 = Utf8 checkNotNullParameter
#20 = Utf8 (Ljava/lang/Object;Ljava/lang/String;)V
#21 = NameAndType #19:#20 // checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
#22 = Methodref #18.#21 // kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
#23 = Utf8 context
#24 = Utf8 Landroid/content/Context;
#25 = Utf8 Lcom/therouter/flow/Digraph;
#26 = Utf8 initDefaultRouteMap
#27 = Utf8 <clinit>
#28 = Utf8 asm
#29 = Utf8 Z
#30 = Utf8 Landroidx/annotation/Keep;
#31 = Utf8 Lkotlin/jvm/JvmField;
#32 = Utf8 Lkotlin/jvm/JvmName;
#33 = Utf8 name
#34 = Utf8 TheRouterServiceProvideInjecter
#35 = Utf8 Lkotlin/Metadata;
#36 = Utf8 mv
#37 = Integer 1
#38 = Integer 9
#39 = Integer 0
#40 = Utf8 k
#41 = Integer 2
#42 = Utf8 xi
#43 = Integer 48
#44 = Utf8 d1
#45 = Utf8 \u0000$\n\u0000\n\u0002\u0010\u000b\n\u0000\n\u0002\u0010\u0002\n\u0000\n\u0002\u0018\u0002\n\u0000\n\u0002\u0018\u0002\n\u0002\b\u0002\n\u0002\u0010\u0000\n\u0002\b\u0003\u001a\u0018\u0010\u0002\u001a\u00020\u00032\b\u0010\u0004\u001a\u0004\u0018\u00010\u00052\u0006\u0010\u0006\u001a\u00020\u0007\u001a\u0010\u0010\b\u001a\u00020\u00032\b\u0010\t\u001a\u0004\u0018\u00010\n\u001a\u0006\u0010\u000b\u001a\u00020\u0003\u001a\u0006\u0010\f\u001a\u00020\u0003"\u0012\u0010\u0000\u001a\u00020\u00018\u0000@\u0000X\u0081\u000e?\u0006\u0002\n\u0000¨\u0006\r
#46 = Utf8 d2
#47 = Utf8
#48 = Utf8 router_debug
#49 = Utf8 TheRouterServiceProvideInjecter.kt
#50 = Utf8 RuntimeInvisibleAnnotations
#51 = Utf8 Code
#52 = Utf8 LineNumberTable
#53 = Utf8 LocalVariableTable
#54 = Utf8 RuntimeInvisibleParameterAnnotations
#55 = Utf8 SourceFile
#56 = Utf8 RuntimeVisibleAnnotations
{
public static boolean asm;
descriptor: Z
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
RuntimeInvisibleAnnotations:
0: #30()
androidx.annotation.Keep
1: #31()
kotlin.jvm.JvmField
public static final void trojan();
descriptor: ()V
flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
Code:
stack=0, locals=0, args_size=0
0: return
LineNumberTable:
line 16: 0
public static final void autowiredInject(java.lang.Object);
descriptor: (Ljava/lang/Object;)V
flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
Code:
stack=0, locals=1, args_size=1
0: return
LineNumberTable:
line 17: 0
LocalVariableTable:
Start Length Slot Name Signature
0 1 0 obj Ljava/lang/Object;
RuntimeInvisibleParameterAnnotations:
parameter 0:
0: #9()
org.jetbrains.annotations.Nullable
public static final void addFlowTask(android.content.Context, com.therouter.flow.Digraph);
descriptor: (Landroid/content/Context;Lcom/therouter/flow/Digraph;)V
flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
Code:
stack=2, locals=2, args_size=2
0: aload_1
1: ldc #16 // String digraph
3: invokestatic #22 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
6: return
LineNumberTable:
line 18: 6
LocalVariableTable:
Start Length Slot Name Signature
0 7 0 context Landroid/content/Context;
0 7 1 digraph Lcom/therouter/flow/Digraph;
RuntimeInvisibleParameterAnnotations:
parameter 0:
0: #9()
org.jetbrains.annotations.Nullable
parameter 1:
0: #14()
org.jetbrains.annotations.NotNull
public static final void initDefaultRouteMap();
descriptor: ()V
flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
Code:
stack=0, locals=0, args_size=0
0: return
LineNumberTable:
line 19: 0
static {};
descriptor: ()V
flags: (0x0008) ACC_STATIC
Code:
stack=0, locals=0, args_size=0
0: return
}
SourceFile: "TheRouterServiceProvideInjecter.kt"
RuntimeVisibleAnnotations:
0: #35(#36=[I#37,I#38,I#39],#40=I#41,#42=I#43,#44=[s#45],#46=[s#28,s#47,s#12,s#47,s#23,s#24,s#15,s#25,s#7,s#10,s#47,s#26,s#5,s#48])
kotlin.Metadata(
mv=[1,9,0]
k=2
xi=48
d1=["\u0000$\n\u0000\n\u0002\u0010\u000b\n\u0000\n\u0002\u0010\u0002\n\u0000\n\u0002\u0018\u0002\n\u0000\n\u0002\u0018\u0002\n\u0002\b\u0002\n\u0002\u0010\u0000\n\u0002\b\u0003\u001a\u0018\u0010\u0002\u001a\u00020\u00032\b\u0010\u0004\u001a\u0004\u0018\u00010\u00052\u0006\u0010\u0006\u001a\u00020\u0007\u001a\u0010\u0010\b\u001a\u00020\u00032\b\u0010\t\u001a\u0004\u0018\u00010\n\u001a\u0006\u0010\u000b\u001a\u00020\u0003\u001a\u0006\u0010\f\u001a\u00020\u0003"\u0012\u0010\u0000\u001a\u00020\u00018\u0000@\u0000X\u0081\u000e?\u0006\u0002\n\u0000¨\u0006\r"]
d2=["asm","","addFlowTask","","context","Landroid/content/Context;","digraph","Lcom/therouter/flow/Digraph;","autowiredInject","obj","","initDefaultRouteMap","trojan","router_debug"]
)
RuntimeInvisibleAnnotations:
0: #32(#33=s#34)
kotlin.jvm.JvmName(
name="TheRouterServiceProvideInjecter"
)
3、两种反编译工具回放TheRouterServiceProvideInjecter.class字节码对应kotlin或者java源码。
// IntelliJ API Decompiler stub source generated from a class file
// Implementation of methods is not available
package a
@field:androidx.annotation.Keep @field:kotlin.jvm.JvmField internal var asm: kotlin.Boolean /* compiled code */
public fun addFlowTask(context: android.content.Context?, digraph: com.therouter.flow.Digraph): kotlin.Unit { /* compiled code */ }
public fun autowiredInject(obj: kotlin.Any?): kotlin.Unit { /* compiled code */ }
public fun initDefaultRouteMap(): kotlin.Unit { /* compiled code */ }
public fun trojan(): kotlin.Unit { /* compiled code */ }
package a;
import android.content.Context;
import androidx.annotation.Keep;
import com.therouter.flow.Digraph;
import kotlin.Metadata;
import kotlin.jvm.JvmField;
import kotlin.jvm.JvmName;
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/* compiled from: TheRouterServiceProvideInjecter.kt */
@Metadata(mv = {1, 9, 0}, k = 2, xi = 48, d1 = {"��$\n��\n\u0002\u0010\u000b\n��\n\u0002\u0010\u0002\n��\n\u0002\u0018\u0002\n��\n\u0002\u0018\u0002\n\u0002\b\u0002\n\u0002\u0010��\n\u0002\b\u0003\u001a\u0018\u0010\u0002\u001a\u00020\u00032\b\u0010\u0004\u001a\u0004\u0018\u00010\u00052\u0006\u0010\u0006\u001a\u00020\u0007\u001a\u0010\u0010\b\u001a\u00020\u00032\b\u0010\t\u001a\u0004\u0018\u00010\n\u001a\u0006\u0010\u000b\u001a\u00020\u0003\u001a\u0006\u0010\f\u001a\u00020\u0003"\u0012\u0010��\u001a\u00020\u00018��@��X\u0081\u000e¢\u0006\u0002\n��¨\u0006\r"}, d2 = {"asm", "", "addFlowTask", "", "context", "Landroid/content/Context;", "digraph", "Lcom/therouter/flow/Digraph;", "autowiredInject", "obj", "", "initDefaultRouteMap", "trojan", "router_debug"})
@JvmName(name = "TheRouterServiceProvideInjecter")
/* loaded from: TheRouterServiceProvideInjecter.class */
public final class TheRouterServiceProvideInjecter {
@Keep
@JvmField
public static boolean asm;
public static final void trojan() {
}
public static final void autowiredInject(@Nullable Object obj) {
}
public static final void addFlowTask(@Nullable Context context, @NotNull Digraph digraph) {
Intrinsics.checkNotNullParameter(digraph, "digraph");
}
public static final void initDefaultRouteMap() {
}
}
4、ASM插入代码后的TheRouterServiceProvideInjecter.class字节码反编译
package a;
import android.content.Context;
import com.therouter.TheRouter;
import com.therouter.app.navigator.NavigatorFragment2__TheRouter__Autowired;
import com.therouter.app.navigator.NavigatorFragmentActivity__TheRouter__Autowired;
import com.therouter.app.navigator.NavigatorFragment__TheRouter__Autowired;
import com.therouter.app.navigator.NavigatorTargetActivity2__TheRouter__Autowired;
import com.therouter.app.navigator.NavigatorTargetActivity__TheRouter__Autowired;
import com.therouter.app.navigator.ObjectTargetActivity__TheRouter__Autowired;
import com.therouter.app.test.Test2Activity__TheRouter__Autowired;
import com.therouter.demo.shell.MainActivity__TheRouter__Autowired;
import com.therouter.flow.Digraph;
import kotlin.Metadata;
@Metadata(d1 = {"\u0000$\n\u0000\n\u0002\u0010\u000b\n\u0000\n\u0002\u0010\u0002\n\u0000\n\u0002\u0018\u0002\n\u0000\n\u0002\u0018\u0002\n\u0002\b\u0002\n\u0002\u0010\u0000\n\u0002\b\u0003\u001a\u0018\u0010\u0002\u001a\u00020\u00032\b\u0010\u0004\u001a\u0004\u0018\u00010\u00052\u0006\u0010\u0006\u001a\u00020\u0007\u001a\u0010\u0010\b\u001a\u00020\u00032\b\u0010\t\u001a\u0004\u0018\u00010\n\u001a\u0006\u0010\u000b\u001a\u00020\u0003\u001a\u0006\u0010\f\u001a\u00020\u0003"\u0012\u0010\u0000\u001a\u00020\u00018\u0000@\u0000X\u0081\u000e¢\u0006\u0002\n\u0000¨\u0006\r"}, d2 = {"asm", "", "addFlowTask", "", "context", "Landroid/content/Context;", "digraph", "Lcom/therouter/flow/Digraph;", "autowiredInject", "obj", "", "initDefaultRouteMap", "trojan", "router_debug"}, k = 2, mv = {1, 9, 0}, xi = 48)
/* loaded from: classes.dex */
public final class TheRouterServiceProvideInjecter {
public static boolean asm = true;
public static final void addFlowTask(Context context, Digraph digraph) {
try {
ServiceProvider__TheRouter__1173000296.addFlowTask(context, digraph);
} catch (Throwable th) {
th.printStackTrace();
}
try {
ServiceProvider__TheRouter__1246883483.addFlowTask(context, digraph);
} catch (Throwable th2) {
th2.printStackTrace();
}
try {
ServiceProvider__TheRouter__2113935708.addFlowTask(context, digraph);
} catch (Throwable th3) {
th3.printStackTrace();
}
try {
ServiceProvider__TheRouter__794287541.addFlowTask(context, digraph);
} catch (Throwable th4) {
th4.printStackTrace();
}
}
public static final void autowiredInject(Object obj) {
try {
NavigatorFragment2__TheRouter__Autowired.autowiredInject(obj);
} catch (Throwable th) {
th.printStackTrace();
}
try {
NavigatorFragmentActivity__TheRouter__Autowired.autowiredInject(obj);
} catch (Throwable th2) {
th2.printStackTrace();
}
try {
NavigatorFragment__TheRouter__Autowired.autowiredInject(obj);
} catch (Throwable th3) {
th3.printStackTrace();
}
try {
NavigatorTargetActivity2__TheRouter__Autowired.autowiredInject(obj);
} catch (Throwable th4) {
th4.printStackTrace();
}
try {
NavigatorTargetActivity__TheRouter__Autowired.autowiredInject(obj);
} catch (Throwable th5) {
th5.printStackTrace();
}
try {
ObjectTargetActivity__TheRouter__Autowired.autowiredInject(obj);
} catch (Throwable th6) {
th6.printStackTrace();
}
try {
Test2Activity__TheRouter__Autowired.autowiredInject(obj);
} catch (Throwable th7) {
th7.printStackTrace();
}
try {
MainActivity__TheRouter__Autowired.autowiredInject(obj);
} catch (Throwable th8) {
th8.printStackTrace();
}
}
public static final void initDefaultRouteMap() {
try {
RouterMap__TheRouter__582536616.addRoute();
} catch (Throwable th) {
th.printStackTrace();
}
try {
RouterMap__TheRouter__881103503.addRoute();
} catch (Throwable th2) {
th2.printStackTrace();
}
}
public static final void trojan() {
try {
TheRouter.getRouterInject().privateAddInterceptor(new ServiceProvider__TheRouter__1173000296());
} catch (Throwable th) {
th.printStackTrace();
}
try {
TheRouter.getRouterInject().privateAddInterceptor(new ServiceProvider__TheRouter__1246883483());
} catch (Throwable th2) {
th2.printStackTrace();
}
try {
TheRouter.getRouterInject().privateAddInterceptor(new ServiceProvider__TheRouter__2113935708());
} catch (Throwable th3) {
th3.printStackTrace();
}
try {
TheRouter.getRouterInject().privateAddInterceptor(new ServiceProvider__TheRouter__794287541());
} catch (Throwable th4) {
th4.printStackTrace();
}
}
}
5、AMS API修改字节码
package com.therouter.plugin;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.commons.AdviceAdapter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* AddCodeVisitor 类用于通过 ASM 修改字节码,注入 TheRouter 相关逻辑
*/
public class AddCodeVisitor extends ClassVisitor {
private List<String> serviceProvideList; // 存储服务提供者类名列表
private Map<String, String> serviceProvideMap; // 存储服务提供者类名及其版本信息
private List<String> autowiredList; // 存储自动注入类名列表
private List<String> routeList; // 存储路由类名列表
private final boolean isIncremental; // 是否增量编译
/**
* 构造函数,初始化 ASM 访问器
* @param cv ASM ClassVisitor 对象
* @param serviceProvideMap 服务提供者类名及其版本信息
* @param autowiredSet 自动注入类名集合
* @param routeSet 路由类名集合
* @param incremental 是否增量编译
*/
public AddCodeVisitor(ClassVisitor cv, Map<String, String> serviceProvideMap, Set<String> autowiredSet, Set<String> routeSet, boolean incremental) {
super(Opcodes.ASM7, cv);
this.serviceProvideList = new ArrayList<>(serviceProvideMap.keySet());
this.serviceProvideMap = serviceProvideMap;
this.autowiredList = new ArrayList<>(autowiredSet);
this.routeList = new ArrayList<>(routeSet);
this.isIncremental = incremental;
Collections.sort(this.serviceProvideList);
Collections.sort(this.autowiredList);
Collections.sort(this.routeList);
}
/**
* 访问字段,修改特定字段的值
* @param access 字段的访问修饰符
* @param name 字段名
* @param descriptor 字段描述符
* @param signature 字段签名
* @param value 字段初始值
* @return FieldVisitor 对象
*/
@Override
public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
if ("asm".equals(name) && "Z".equals(descriptor)) {
return super.visitField(access, name, descriptor, signature, true);
}
return super.visitField(access, name, descriptor, signature, value);
}
/**
* 访问方法,注入 TheRouter 相关逻辑
* @param access 方法的访问修饰符
* @param methodName 方法名
* @param desc 方法描述符
* @param signature 方法签名
* @param exceptions 方法抛出的异常
* @return MethodVisitor 对象
*/
@Override
public MethodVisitor visitMethod(int access, String methodName, String desc, String signature, String[] exceptions) {
MethodVisitor mv = cv.visitMethod(access, methodName, desc, signature, exceptions);
mv = new AdviceAdapter(Opcodes.ASM7, mv, access, methodName, desc) {
@Override
protected void onMethodEnter() {
super.onMethodEnter();
if (!"<init>".equals(methodName)) {
if ("trojan".equals(methodName)) {
// 注入拦截器逻辑
// 遍历服务提供者类名列表
for (String serviceProviderClassName : serviceProvideList) {
// 检查服务提供者类名是否以 "a/" 开头,如果不是则添加 "a/" 前缀
if (!serviceProviderClassName.startsWith("a/")) {
serviceProviderClassName = "a/" + serviceProviderClassName;
}
// 后边asm想要插入的代码,
// try {
// // 调用 com.therouter.TheRouter 类的静态方法 getRouterInject 获取 RouterInject 实例
// com.therouter.inject.RouterInject routerInject = com.therouter.TheRouter.getRouterInject();
// // 创建一个指定服务提供者类的新对象
// Class<?> serviceProviderClass = Class.forName(serviceProviderClassName.replace('/', '.'));
// com.therouter.inject.Interceptor interceptor = (com.therouter.inject.Interceptor) serviceProviderClass.getDeclaredConstructor().newInstance();
// // 调用 RouterInject 实例的 privateAddInterceptor 方法,将新创建的服务提供者对象作为拦截器添加
// routerInject.privateAddInterceptor(interceptor);
// } catch (Throwable e) {
// // 打印异常堆栈信息
// e.printStackTrace();
// }
// 定义 Label 用于标记 try-catch 块的不同位置
// try 块的起始位置
Label tryStart = new Label();
// try 块的结束位置
Label tryEnd = new Label();
// catch 块的起始位置
Label labelCatch = new Label();
// try-catch 块结束后的位置
Label tryCatchBlockEnd = new Label();
// 使用 ASM 访问器定义一个 try-catch 块,捕获所有 Throwable 类型的异常
mv.visitTryCatchBlock(tryStart, tryEnd, labelCatch, "java/lang/Throwable");
// 标记 try 块的起始位置
mv.visitLabel(tryStart);
// 调用 com.therouter.TheRouter 类的静态方法 getRouterInject 获取 RouterInject 实例
mv.visitMethodInsn(INVOKESTATIC, "com/therouter/TheRouter", "getRouterInject", "()Lcom/therouter/inject/RouterInject;", false);
// 创建一个指定服务提供者类的新对象
mv.visitTypeInsn(NEW, serviceProviderClassName);
// 复制栈顶元素,用于后续的构造函数调用
mv.visitInsn(DUP);
// 调用指定服务提供者类的无参构造函数进行初始化
mv.visitMethodInsn(INVOKESPECIAL, serviceProviderClassName, "<init>", "()V", false);
// 调用 RouterInject 实例的 privateAddInterceptor 方法,将新创建的服务提供者对象作为拦截器添加
mv.visitMethodInsn(INVOKEVIRTUAL, "com/therouter/inject/RouterInject", "privateAddInterceptor", "(Lcom/therouter/inject/Interceptor;)V", false);
// 标记 try 块的结束位置
mv.visitLabel(tryEnd);
// 跳转到 try-catch 块结束后的位置
mv.visitJumpInsn(GOTO, tryCatchBlockEnd);
// 标记 catch 块的起始位置
mv.visitLabel(labelCatch);
// 将捕获到的异常对象存储到局部变量表索引为 1 的位置
mv.visitVarInsn(ASTORE, 1);
// 从局部变量表索引为 1 的位置加载异常对象到操作数栈
mv.visitVarInsn(ALOAD, 1);
// 调用异常对象的 printStackTrace 方法打印异常堆栈信息
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Throwable", "printStackTrace", "()V", false);
// 标记 try-catch 块结束后的位置
mv.visitLabel(tryCatchBlockEnd);
}
}
if ("addFlowTask".equals(methodName)) {
// 注入任务依赖逻辑
for (String serviceProviderClassName : serviceProvideList) {
if (!serviceProviderClassName.startsWith("a/")) {
serviceProviderClassName = "a/" + serviceProviderClassName;
}
String aptVersion = serviceProvideMap.get(serviceProviderClassName);
if (aptVersion == null) {
aptVersion = serviceProvideMap.get(serviceProviderClassName.substring(2));
}
if (aptVersion != null && !aptVersion.equals("0.0.0")) {
// 后边asm想要插入的代码
// try {
// // 调用指定服务提供者类的静态方法 addFlowTask
// // 传入 Android 上下文对象和有向无环图对象作为参数
// Class<?> clazz = Class.forName(serviceProviderClassName.replace('/', '.'));
// Method addFlowTaskMethod = clazz.getMethod("addFlowTask", Context.class, Digraph.class);
// addFlowTaskMethod.invoke(null, context, digraph);
// } catch (Throwable e) {
// // 打印异常堆栈信息
// e.printStackTrace();
// }
// 定义 Label 用于标记 try-catch 块的不同位置
Label label0 = new Label();
Label label1 = new Label();
// catch 块的起始位置
Label label2 = new Label();
// catch 块的结束位置
Label tryCatchBlockEnd = new Label();
// 使用 ASM 访问器定义一个 try-catch 块,捕获所有 Throwable 类型的异常
// 起始位置为 label0,结束位置为 label1,异常处理位置为 label2
mv.visitTryCatchBlock(label0, label1, label2, "java/lang/Throwable");
// 标记 try 块的起始位置
mv.visitLabel(label0);
// 从局部变量表索引为 0 的位置加载对象引用到操作数栈
mv.visitVarInsn(ALOAD, 0);
// 从局部变量表索引为 1 的位置加载对象引用到操作数栈
mv.visitVarInsn(ALOAD, 1);
// 调用指定服务提供者类的静态方法 addFlowTask
// 传入 Android 上下文对象和有向无环图对象作为参数
mv.visitMethodInsn(INVOKESTATIC, serviceProviderClassName, "addFlowTask", "(Landroid/content/Context;Lcom/therouter/flow/Digraph;)V", false);
// 标记 try 块的结束位置
mv.visitLabel(label1);
// 跳转到 try-catch 块结束后的位置
mv.visitJumpInsn(GOTO, tryCatchBlockEnd);
// 标记 catch 块的起始位置
mv.visitLabel(label2);
// 将捕获到的异常对象存储到局部变量表索引为 2 的位置
mv.visitVarInsn(ASTORE, 2);
// 从局部变量表索引为 2 的位置加载异常对象到操作数栈
mv.visitVarInsn(ALOAD, 2);
// 调用异常对象的 printStackTrace 方法打印异常堆栈信息
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Throwable", "printStackTrace", "()V", false);
// 标记 try-catch 块结束后的位置
mv.visitLabel(tryCatchBlockEnd);
}
}
}
if ("autowiredInject".equals(methodName)) {
// 注入自动注入逻辑
for (String autowiredClassName : autowiredList) {
// 下边是要插入的代码
// try {
// // 获取当前方法的第一个参数(即调用 autowiredInject 方法时传入的对象)
// Object obj = getThisMethodFirstParameter();
// // 加载自动注入类
// Class<?> clazz = Class.forName(autowiredClassName);
// // 获取自动注入类的静态方法 autowiredInject
// java.lang.reflect.Method autowiredMethod = clazz.getMethod("autowiredInject", Object.class);
// // 调用静态方法 autowiredInject 进行自动注入操作
// autowiredMethod.invoke(null, obj);
// } catch (Throwable e) {
// // 打印异常堆栈信息
// e.printStackTrace();
// }
// 定义 Label 用于标记 try-catch 块的不同位置
// try 块的起始位置
Label tryStart = new Label();
// try 块的结束位置
Label tryEnd = new Label();
// catch 块的起始位置
Label labelCatch = new Label();
// try-catch 块结束后的位置
Label labelInvoke = new Label();
// 使用 ASM 访问器定义一个 try-catch 块,捕获所有 Throwable 类型的异常
mv.visitTryCatchBlock(tryStart, tryEnd, labelCatch, "java/lang/Throwable");
// 标记 try 块的起始位置
mv.visitLabel(tryStart);
// 从局部变量表索引为 0 的位置加载对象引用到操作数栈
mv.visitVarInsn(ALOAD, 0);
// 调用指定自动注入类的静态方法 autowiredInject
// 传入一个对象作为参数进行自动注入操作
mv.visitMethodInsn(INVOKESTATIC, autowiredClassName.replace('.', '/'), "autowiredInject", "(Ljava/lang/Object;)V", false);
mv.visitLabel(tryEnd);
mv.visitJumpInsn(GOTO, labelInvoke);
mv.visitLabel(labelCatch);
mv.visitVarInsn(ASTORE, 1);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Throwable", "printStackTrace", "()V", false);
mv.visitLabel(labelInvoke);
}
}
// 注入路由初始化逻辑
if ("initDefaultRouteMap".equals(methodName)) {
for (String route : routeList) {
// 下边是要插入的代码
// try {
// // 处理类名,移除 .class 后缀并将 . 替换为 /
// String className = route.replace(".class", "").replace('.', '/');
// // 将类名转换为 Java 类名格式
// String javaClassName = className.replace('/', '.');
// // 加载路由类
// Class<?> routeClass = Class.forName(javaClassName);
// // 获取路由类的静态方法 addRoute
// java.lang.reflect.Method addRouteMethod = routeClass.getMethod("addRoute");
// // 调用静态方法 addRoute 进行路由初始化
// addRouteMethod.invoke(null);
// } catch (Throwable e) {
// // 打印异常堆栈信息
// e.printStackTrace();
// }
Label tryStart = new Label();
Label tryEnd = new Label();
Label labelCatch = new Label();
Label tryCatchBlockEnd = new Label();
mv.visitTryCatchBlock(tryStart, tryEnd, labelCatch, "java/lang/Throwable");
mv.visitLabel(tryStart);
String className = route.replace(".class", "").replace('.', '/');
mv.visitMethodInsn(INVOKESTATIC, className, "addRoute", "()V", false);
mv.visitLabel(tryEnd);
mv.visitJumpInsn(GOTO, tryCatchBlockEnd);
mv.visitLabel(labelCatch);
mv.visitVarInsn(ASTORE, 1);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Throwable", "printStackTrace", "()V", false);
mv.visitLabel(tryCatchBlockEnd);
}
}
if (!isIncremental) {
mv.visitInsn(RETURN);
}
}
}
};
return mv;
}
}