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
相关产品推荐
相关产品推荐

