You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android环境下使用ASM/Byte Buddy查看字节码遇ClassNotFound异常求助

Android环境下实现字节码检查与方法操作码打印

需求

  • 检查Java方法的字节码是否被篡改或操纵
  • 在Android环境中列出/打印类中方法的操作码

遇到的问题

尝试用ASM的ClassReader读取java.lang.Object时抛出ClassNotFound异常,代码如下:

try {
    ClassReader classreader = new ClassReader("java.lang.Object");
} catch (IOException e) {
    throw new RuntimeException(e);
}

已引入的依赖(build.gradle):

implementation("net.bytebuddy:byte-buddy-android:1.14.12")

implementation("org.ow2.asm:asm:9.4")
implementation("org.ow2.asm:asm-util:9.2")

解决方案

1. 修复ClassNotFound异常

Android的类加载机制和标准JVM不同,系统核心类(如java.lang.Object)由BootClassLoader加载,直接通过类名构造ClassReader无法获取类字节流。正确做法是通过Class对象获取输入流:

try {
    // 获取目标类的Class对象
    Class<?> clazz = Class.forName("java.lang.Object");
    // 从类加载器中读取类的字节流
    InputStream is = clazz.getClassLoader().getResourceAsStream("java/lang/Object.class");
    if (is != null) {
        ClassReader classReader = new ClassReader(is);
        // 后续处理逻辑
        is.close();
    } else {
        throw new RuntimeException("无法获取类字节流");
    }
} catch (ClassNotFoundException | IOException e) {
    throw new RuntimeException(e);
}

2. 打印方法操作码(基于ASM)

使用ASM的ClassVisitor和MethodVisitor遍历类方法,输出操作码:

import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.util.Printer;

public class ByteCodePrinter extends ClassVisitor {

    public ByteCodePrinter() {
        super(Opcodes.ASM9);
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
        System.out.println("方法:" + name + " " + descriptor);
        return new MethodVisitor(Opcodes.ASM9) {
            @Override
            public void visitInsn(int opcode) {
                // 打印操作码对应的名称
                System.out.println("  操作码:" + Printer.OPCODES[opcode]);
                super.visitInsn(opcode);
            }

            // 处理带变量的指令
            @Override
            public void visitVarInsn(int opcode, int var) {
                System.out.println("  操作码:" + Printer.OPCODES[opcode] + " 变量索引:" + var);
                super.visitVarInsn(opcode, var);
            }

            // 处理字段访问指令
            @Override
            public void visitFieldInsn(int opcode, String owner, String name, String descriptor) {
                System.out.println("  操作码:" + Printer.OPCODES[opcode] + " 字段:" + owner + "." + name);
                super.visitFieldInsn(opcode, owner, name, descriptor);
            }
        };
    }
}

调用示例:

try {
    Class<?> clazz = Class.forName("java.lang.Object");
    InputStream is = clazz.getClassLoader().getResourceAsStream("java/lang/Object.class");
    if (is != null) {
        ClassReader classReader = new ClassReader(is);
        ByteCodePrinter printer = new ByteCodePrinter();
        classReader.accept(printer, 0);
        is.close();
    }
} catch (Exception e) {
    e.printStackTrace();
}

3. 注意事项

  • Android的应用沙箱和SELinux可能限制对部分系统核心类的字节流读取,建议先使用自定义类测试功能
  • Byte Buddy更擅长动态生成和修改类,若仅需打印操作码,ASM的轻量性更适合当前需求

参考资料内容(原链接翻译)

该资料介绍了查看Java类字节码的多种方式:

  • 使用javap命令行工具反编译类文件
  • 通过ASM库编程式读取和分析字节码
  • 对比不同字节码操作框架的特性与适用场景

内容的提问来源于stack exchange,提问作者localacct

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 19:13:21