Java字节码修改框架ASM

简介:

字节码相对Java的意义类似汇编相对C的意义,底层了解的越多越深入,程序就越神奇,一切想法皆有可能实现。学习了下字节码框架ASM,总结分享下:


API概述。


一、ASM库提供了两类API接口模型来产生或者修改类字节码:

1)核心API: 基于事件,每个事件代表类的一个元素,如头事件、方法事件、字段事件等。特点是更快耗费更少的内存。
2)树型API: 基于对象树状结构,字段方法等都可以看做对象树的一部分。使用相对简单,但耗费内存。

二、API包结构大致如下:

1)事件、解析器、生产器类API在包路径org.objectweb.asmorg.objectweb.asm.signature内。
2)开发和调试中可以用到的一些核心API在包路径org.objectweb.asm.util内。
3)基于对象树的APIorg.objectweb.asm.tree内,同时包括了和事件类API互相转换的工具。
4org.objectweb.asm.tree.analysis为对象树类API提供了类分析框架。


核心类AP I- class


一、类结构
1)虚拟机内部定义的类结构图:
_
2)类名称在虚拟机内部标示有所不同,如java.lang.String内部标示为java/lang/String
3)编译后的class的字段类型如下:
_
4)方法描述内部标识如下:
_

二、类相关接口API:

1ClassReader可以看作事件的产生器,能够读取解析类的二进制字节数组,边解析边把类的字段或者方法等信息以事件传递给ClassVisitor的相关visitXXX方法。常见代码:

// ClassPrinterClassVisitor的实例,在内部的visitXXX方法内部定义自己的业务逻辑,如打印输出

ClassPrinter cp = new ClassPrinter();

ClassReader cr = new ClassReader("java.lang.Runnable");

cr.accept(cp0);

2ClassWriter可以看做时间的一个消费器,是ClassVisitor的一个子类实现,可以直接构建出类的二进制数组标识形式,截取下例子:

ClassWriter cw = new ClassWriter(0);

// public interface Comparable extends Mesurable {

cw.visit(V1_5, ACC_PUBLIC + ACC_ABSTRACT + ACC_INTERFACE,

"pkg/Comparable"null"java/lang/Object",

new String[] { "pkg/Mesurable" });

// int LESS = -1;

cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "LESS""I",

nullnew Integer(-1)).visitEnd();

// int EQUAL = 0;

cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "EQUAL""I",

nullnew Integer(0)).visitEnd();

// int GREATER = 1;

cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "GREATER""I",

nullnew Integer(1)).visitEnd();

// int compareTo(Object o);

cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, "compareTo",

"(Ljava/lang/Object;)I"nullnull).visitEnd();

cw.visitEnd();

byte[] b = cw.toByteArray();

3ClassVisitor可以将接收到的所有监听到事件的方法调用传递给另外一个ClassVisitor,可以看做一个事件过滤器,针对ClassVisitor添加自己的业务逻辑就可以实现神奇的字节码修改:

// 转换前的字节码

byte[] b1 = ...;

ClassReader cr = new ClassReader(b);

ClassWriter cw = new ClassWriter(cr, 0);

// 在这里面定义你想要的业务,一切皆有可能

ClassVisitor cv = new ChangeVersionAdapter(cw);

cr.accept(cv, 0);

// 转换后的字节码

byte[] b2 = cw.toByteArray();

4ClassVisitor内可以实现删除类成员(如visitMethod返回null删除方法)、添加类成员(visitEnd添加visitField),ClassVisitor也可以是个调用链。

三、开发工具API:

1Type提供了内部类型和java类型的转换,如前面提到的String转换内部类名可以这样实现:

// 仅用于类或者接口,java/lang/String

String internalName = Type.getType(String.class).getInternalName();

String转换为内部描述可以这样:

// 转换内部描述:Ljava/lang/String;

String internalDesc = Type.getType(String.class).getDescriptor();

String intDesc = Type.INT_TYPE.getDescriptor();

方法参数和返回值示例:

// 返回Type.INT_TYPE

Type.getArgumentTypes("(I)V");

// Type.VOID_TYPE

Type.getReturnType("(I)V")

2TraceClassVisitor可以文本形式打印出转换后的字节码到底是什么样子的:

ClassWriter cw = new ClassWriter(0);

// printWriter会输出转换后的字节码

TraceClassVisitor cv = new TraceClassVisitor(cw, printWriter);

cv.visit(...);

...

cv.visitEnd();

byte b[] = cw.toByteArray();

3)转换后的字节码是否格式合法可以用CheckClassAdapter校验:

ClassWriter cw = new ClassWriter(0);

TraceClassVisitor tcv = new TraceClassVisitor(cw, printWriter);

// 发现问题会抛出异常

CheckClassAdapter cv = new CheckClassAdapter(tcv);

cv.visit(...);

...

cv.visitEnd();

byte b[] = cw.toByteArray();

4)如果有的类实在复杂到你很难写出字节码来生成它,ASM提供自动把复杂类打印出字节码的工具ASMifier,通过命令行方式输出,太强大了:

java -classpath asm.jar:asm-util.jar \

org.objectweb.asm.util.ASMifier \

java.lang.Runnable


核心类AP I- method


一、方法结构

1)方法执行模型:Java代码在线程内执行,每个线程有自己响应的栈stack,每个栈由栈帧frame构成,每个frame代表一个方法调用。每个frame包括本地变量和操作栈,本地变量用序号访问,变量使用slot存储,除了longdouble需要2slot外其他变量都有一个slot存储。一个frame构成如下:
_
2)字节码指令由操作码和参数构成,可以分为两类:一类是将值在本地变量和操作数栈来回复制移动的,如ILOAD\LLOAD\FLOAD\DLOAD\ALOAD\ISTORE\LSTORE\FSTORE等。另一类是操作操作数栈上value的,包括一大批的指令,操作栈stack的、定义常量的、算术计算的、类型转换的、操作类、操作字段、操作方法、操作数组、跳转、返回。

二、方法接口API和组件

1MethodVisitor可以被ClassVisitorvisitMethod方法返回,用于方法的生成和转化。MethodVisitor的内部方法严格按照如下顺序调用:
visitMethod_
多个MethodVisitor时,每个都是一个独立的转换方法实例,因此多个MethodVisitor之间可以交替执行。类似类APIMethodVisitor使用到ClassReaderClassWriterClassWriter参数不同意义不同:

// ASM不进行任何自动计算,需要我们自己计算栈帧、本地变量、操作数栈的大小

new ClassWriter(0);

2)一个生产getF()方法的指令如下:

mv.visitCode();

mv.visitVarInsn(ALOAD, 0);

mv.visitFieldInsn(GETFIELD, "pkg/Bean""f""I");

mv.visitInsn(IRETURN);

mv.visitMaxs(11);

mv.visitEnd();

3MethodVisitorClassVisitor通常配合使用来转换方法,可以用于删除方法指令、执行状态无关的转换、执行状态相关的转换(一般需要定义状态属性到MethodVisitor内记忆)及其他更复杂的转换。

三、方法开发工具API

1)基本的TypeTraceClassVisitorCheckClassAdapterASMifier同样适用。
2AnalyzerAdapter记录了visitFrame调用后栈帧映射的信息,必须要和ClassReader#EXPAND_FRAMES配合使用。每个visitX指令都被代理到调用链,并通过本地变量locals和栈stack模拟出队栈帧映射的影响,这样后面的visitor可以得到栈帧映射的当前状态。
3LocalVariablesSorter可以对方法内的变量自动编号,对于需要修改字节码添加变量的场景非常适用。还可以喝AnalyzerAdapter配合构造成过滤链配合使用。
4)需要在方法开始和结束为止添加指令的场景适合使用AdviceAdapter,而且使用与构造方法。


 

泉石
+关注
目录
打赏
0
0
0
0
6
分享
相关文章
在Java中使用Seata框架实现分布式事务的详细步骤
通过以上步骤,利用 Seata 框架可以实现较为简单的分布式事务处理。在实际应用中,还需要根据具体业务需求进行更详细的配置和处理。同时,要注意处理各种异常情况,以确保分布式事务的正确执行。
在Java中实现分布式事务的常用框架和方法
总之,选择合适的分布式事务框架和方法需要综合考虑业务需求、性能、复杂度等因素。不同的框架和方法都有其特点和适用场景,需要根据具体情况进行评估和选择。同时,随着技术的不断发展,分布式事务的解决方案也在不断更新和完善,以更好地满足业务的需求。你还可以进一步深入研究和了解这些框架和方法,以便在实际应用中更好地实现分布式事务管理。
Java 集合框架中的老炮与新秀:HashTable 和 HashMap 谁更胜一筹?
嗨,大家好,我是技术伙伴小米。今天通过讲故事的方式,详细介绍 Java 中 HashMap 和 HashTable 的区别。从版本、线程安全、null 值支持、性能及迭代器行为等方面对比,帮助你轻松应对面试中的经典问题。HashMap 更高效灵活,适合单线程或需手动处理线程安全的场景;HashTable 较古老,线程安全但性能不佳。现代项目推荐使用 ConcurrentHashMap。关注我的公众号“软件求生”,获取更多技术干货!
62 3
|
1月前
|
java语言后台管理ruoyi后台管理框架-登录提示“无效的会话,或者会话已过期,请重新登录。”-扩展知识数据库中密码加密的方法-问题如何解决-以及如何重置若依后台管理框架admin密码-优雅草卓伊凡
java语言后台管理ruoyi后台管理框架-登录提示“无效的会话,或者会话已过期,请重新登录。”-扩展知识数据库中密码加密的方法-问题如何解决-以及如何重置若依后台管理框架admin密码-优雅草卓伊凡
122 3
java语言后台管理ruoyi后台管理框架-登录提示“无效的会话,或者会话已过期,请重新登录。”-扩展知识数据库中密码加密的方法-问题如何解决-以及如何重置若依后台管理框架admin密码-优雅草卓伊凡
SaaS云计算技术的智慧工地源码,基于Java+Spring Cloud框架开发
智慧工地源码基于微服务+Java+Spring Cloud +UniApp +MySql架构,利用传感器、监控摄像头、AI、大数据等技术,实现施工现场的实时监测、数据分析与智能决策。平台涵盖人员、车辆、视频监控、施工质量、设备、环境和能耗管理七大维度,提供可视化管理、智能化报警、移动智能办公及分布计算存储等功能,全面提升工地的安全性、效率和质量。
Java中的Fork/Join框架详解
Fork/Join框架是Java并行计算的强大工具,尤其适用于需要将任务分解为子任务的场景。通过正确使用Fork/Join框架,可以显著提升应用程序的性能和响应速度。在实际应用中,应结合具体需求选择合适的任务拆分策略,以最大化并行计算的效率。
66 23
Java 集合框架优化:从基础到高级应用
《Java集合框架优化:从基础到高级应用》深入解析Java集合框架的核心原理与优化技巧,涵盖列表、集合、映射等常用数据结构,结合实际案例,指导开发者高效使用和优化Java集合。
85 4
Java哪个框架适合开发API接口?
在快速发展的软件开发领域,API接口连接了不同的系统和服务。Java作为成熟的编程语言,其生态系统中出现了许多API开发框架。Magic-API因其独特优势和强大功能,成为Java开发者优选的API开发框架。本文将从核心优势、实际应用价值及未来展望等方面,深入探讨Magic-API为何值得选择。
169 2
你不可不知道的JAVA EE 框架有哪些?
本文介绍了框架的基本概念及其在编程领域的应用,强调了软件框架作为通用、可复用的软件环境的重要性。文章分析了早期Java EE开发中使用JSP+Servlet技术的弊端,包括可维护性差和代码重用性低等问题,并阐述了使用框架的优势,如提高开发效率、增强代码规范性和可维护性及提升软件性能。最后,文中详细描述了几种主流的Java EE框架,包括Spring、Spring MVC、MyBatis、Hibernate和Struts 2,这些框架通过提供强大的功能和支持,显著提升了Java EE应用的开发效率和稳定性。
331 1
Spring 框架的介绍(Java EE 学习笔记02)
Spring是一个由Rod Johnson开发的轻量级Java SE/EE一站式开源框架,旨在解决Java EE应用中的多种问题。它采用非侵入式设计,通过IoC和AOP技术简化了Java应用的开发流程,降低了组件间的耦合度,支持事务管理和多种框架的无缝集成,极大提升了开发效率和代码质量。Spring 5引入了响应式编程等新特性,进一步增强了框架的功能性和灵活性。
91 0

热门文章

最新文章