JAVA ASM总结篇-03

MethodVisitor

ClassVisitor的visitMethod能够访问到类中某个方法的一些入口信息,那么针对具体方法中字节码的访问是由MethodVisitor来进行的

访问顺序如下,其中visitCode和visitMaxs仅调用一次,标志方法字节码访问的开始和结束

 MethodVisitor如何获得:

1.ClassReader中传入的ClassVisitor中返回的MethodVisitor

2.直接调用ClassWriter.visitMethod返回MethodVisitor

创建ClassWriter的时候:
1.new ClassWriter(0) 

自己计算帧、操作数栈大小和局部变量表大小,即自己调用visitMaxs

2.new ClassWriter(COMUPTE_MAXS)

自动计算帧、操作数栈大小和局部变量表大小,但是仍需要调用visitMaxs,里面的参数自动忽略,优点是简单,缺点是程序运行性能降低(10%)

3.new ClassWriter(COMPUTE_FRAMES)

与2类似,改进是不用再调用visitFrame,性能只下降2的一半

常用api:

visitFieldInsn : 访问某个成员变量的指令,支持GETSTATIC, PUTSTATIC, GETFIELD or PUTFIELD.
visitFrame :访问当前局部变量表和操作数栈中元素的状态,参数就是局部变量表和操作数栈的内容
visitIincInsn : 访问自增指令
visitVarInsn :访问局部变量指令,就是取局部变量变的值放入操作数栈
visitMethodInsn :访问方法指令,就是调用某个方法,支持INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC or INVOKEINTERFACE.
visitInsn : 访问无操作数的指令,例如nop,duo等等

visitTypeInsn:访问type指令,即将一个类的全限定名作为参数然后new一个对象压入操作数栈中

https://www.javadoc.io/doc/org.ow2.asm/asm/4.0/org/objectweb/asm/MethodVisitor.html

生成方法:

比如

package asm;public class bean {private int f;public bean() {}public void setF(int f) {this.f = f;}public int getF() {return this.f;}
}

上面的getF方法就可以用其对应的字节码指令生成

 假设mv是MethodVisitor,即:

mv.visitCode();// 标志开始访问
mv.visitVarIn(ALOAD,0)
mv.visitFieldInsn(GETFIELD,"asm/beam","f","I")
mv.visitInsn(IRETURN)
mv.visitMaxs(1,1) //局部表量表和操作数栈的大小,只要一个this即可
mv.visitEnd

 那么可以动态的生成setF的方法体:

 那么只需要定义一个ClassAdapter,由于要遍历每个方法,因此在visitMethod处判断方法名即可hook指定方法:

package asm;import org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;public class ClassPrint  extends ClassAdapter {public ClassPrint(ClassVisitor classVisitor) {super(classVisitor);}@Overridepublic MethodVisitor visitMethod(int var1, String var2, String var3, String var4, String[] var5) {System.out.println(var2);if (var2.equals("setFf")) {MethodVisitor mv = this.cv.visitMethod(var1, var2, var3, var4, var5);mv.visitVarInsn(Opcodes.ALOAD, 0);mv.visitVarInsn(Opcodes.ILOAD, 1);mv.visitFieldInsn(Opcodes.PUTFIELD, "asm/bean", "f", "I");mv.visitInsn(Opcodes.RETURN);mv.visitMaxs(2, 2);mv.visitEnd();return mv;}return    this.cv.visitMethod(var1, var2, var3, var4, var5);}}

生成结果如下:

结合if以及异常的字节码指令分析:

还是以以下代码为例,假设要为setFf生成代码块,先取其字节码指令:

package asm;public class bean {private int f;public void setFf(int f) {if(f>=0){this.f=f;}else {throw     new IllegalArgumentException();}}public int getF(){return f;}
}

字节码指令如下:

这里要引入栈映射帧的概念,就是表示在执行某一条字节码指令之前,帧的状态,即局部变量表和操作数栈的状态,不是每条字节码前面都有栈映射帧,通常在有条件跳转或无条件跳转之后或者抛出异常之前,只要记住有这么个指令即可,具体怎么用可以查doc。ps:直接根据idea给出的字节码指令来写asm代码即可

即对应的重写setFf的asm代码为:

package asm;
import org.objectweb.asm.*;public class ClassPrint  extends ClassAdapter {public ClassPrint(ClassVisitor classVisitor) {super(classVisitor);}@Overridepublic MethodVisitor visitMethod(int var1, String var2, String var3, String var4, String[] var5) {System.out.println(var2);if (var2.equals("setFf")) {MethodVisitor mv = this.cv.visitMethod(var1, var2, var3, var4, var5);mv.visitVarInsn(Opcodes.ILOAD, 1); //f入栈Label l1 = new Label();mv.visitJumpInsn(Opcodes.IFLT,l1); //弹出f和0比较,此时栈空,到label1mv.visitVarInsn(Opcodes.ALOAD,0);//压入thismv.visitVarInsn(Opcodes.ILOAD,1); //压入fmv.visitFieldInsn(Opcodes.PUTFIELD,"asm/bean","f","I"); //弹出this和f,赋值this.f=fLabel l2 = new Label(); //声明labelmv.visitJumpInsn(Opcodes.GOTO,l2); //跳转关联label2mv.visitLabel(l1);//label1起始mv.visitFrame(Opcodes.F_SAME,2,null,0,null); //访问当前帧状态mv.visitTypeInsn(Opcodes.NEW,"java/lang/IllegalArgumentException");//new异常,分配内存但不做初始化操作mv.visitInsn(Opcodes.DUP);//复制栈里元素,再次压入mv.visitMethodInsn(Opcodes.INVOKESPECIAL,"java/lang/IllegalArgumentException","<init>","()V");//弹出一个对象(参数个数为0+1=1),进行初始化操作,构造函数默认为空,此时栈大小为1,实例化结束后再次压入实例化的结果mv.visitInsn(Opcodes.ATHROW);//此时栈中对象已经进行初始化,所以弹出栈顶的异常对象,即抛出异常,栈中实际上还剩余一个thismv.visitLabel(l2); //label2起始mv.visitFrame(Opcodes.F_SAME,2,null,0,null);//访问当前帧状态mv.visitInsn(Opcodes.RETURN);//返回mv.visitMaxs(2, 2);//设置局部表量表和操作数栈大小mv.visitEnd();//访问结束return mv;}return    this.cv.visitMethod(var1, var2, var3, var4, var5);}}

tips:

getfiled 弹一个对象的引用,并将所取的字段的值压入

putfield 需要弹一个值和一个对象引用,将值存储在对象所指定的字段中 

asm不同版本差别

调用思想不变,做好替换即可。

tip:

1.hook调用自己的方法时注意用invokeStatic

2.需要用到源方法中的参数时,直接找到class文件,从字节码指令中找

参考:

ASM4使用指南

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/530.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

不羁联盟怎么参与测试 不羁联盟测试时间+参与测试方法分享

不羁联盟怎么参与测试 不羁联盟测试时间参与测试方法分享 《不羁联盟》是由育碧&#xff08;Ubisoft&#xff09;开发的一款6v6团队合作射击游戏。游戏的背景设定在一个后启示录时代的废土世界中&#xff0c;玩家能够身临其境地感受到废土世界的荒凉和残酷。游戏在内测时候就受…

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台级联时,下级平台未发流是什么原因?

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安…

AUTOCAD输出或打印PDF文件时,如何将图形居中且布满图纸?

AUTOCAD输出或打印PDF文件时,如何将图形居中且布满图纸? 如下图所示,我们打开一份DWG格式的图纸文件,然后点击上方的“打印“图标, 如下图所示, 打印机/绘图仪这里选择“DWG To PDF“; 图纸尺寸:这里以普通的A4纸为例进行说明; 打印比例选择“布满图纸“; 打印偏移…

优维应用级数字化架构管理:让企业运维天堑变通途

在优维科技的产品视角中&#xff0c;数字化架构管理就像是一门精妙的艺术&#xff0c;它将上层应用模型的业务概念以可视化的方式呈现出来&#xff0c;使得业务逻辑和流程变得更加直观、清晰。我们将这样的管理方式理解为“给企业搭起一座桥梁”——在这座桥梁的搭建过程中&…

express服务器 authorization 前端获取不到的问题

服务器生成token 设置在响应头&#xff0c;但是前端获取不到 const token JWT.generate({ id: new Date().getTime(), userName }, 10s) res.header(Authorization, token) axios.interceptors.response.use((response) > {console.log(response);if (response.data?.co…

破解费用管理迷局,企业费用管理从不止于报销

数字化变革浪潮下&#xff0c;各种企业费用报销软件如雨后春笋般不断涌现&#xff0c;企业报销效率大幅提升&#xff0c;部分财务处理流程得到固化和优化&#xff0c;报销早已不再是企业费时费力的财务难题。那么&#xff0c;企业费用管里如何实现呢&#xff1f; 企业费用贯穿于…

ubuntu上安装调试SVN服务

刚成立团队需要临时搭建一台SVN服务器&#xff0c;所以对照网上的一些提示做了下&#xff0c;操作起来不复杂&#xff0c;还是踩了不少坑&#xff0c;顺便原理性了解了下。 主要操作步骤如下&#xff1a; 1&#xff1a;安装svn sudo apt-get install subversion 2: 创建svn版…

NVIDIA智算中心“产品”上市,AI工业革命的iPhone时刻

GTC 2024落下帷幕了&#xff0c;但这个大会的信息仍在AI产业和经济中发酵。咨询机构WIKIBON认为&#xff0c;GTC 2024在整个科技史中的意义超过了当年史蒂夫乔布斯的iPod和iPhone发布。在AI将永久改变人类的共识下&#xff0c;GTC 2024在广度、愿景、生态系统等方面都有着深远影…

Nacos注册中心的使用

一&#xff1a;服务注册 步骤如下&#xff1a; 引入依赖 配置Nacos地址 重启 1、添加依赖 在需要注册的项目的pom.xml中添加依赖&#xff1a; <!--nacos 服务注册发现--> <dependency><groupId>com.alibaba.cloud</groupId><artifactId>s…

利用Python进行大规模数据处理【第173篇—数据处理】

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 利用Python进行大规模数据处理&#xff1a;Hadoop与Spark的对比 随着数据量的不断增长&…

ros仿真启动小龟

1.启动RosMaster&#xff08;管理Ros中各个节点的“大管家”&#xff0c;每次启动Ros时需要首先启动RosMaster&#xff09; roscorefangfang-inspiron-5580:~/ros2/download/rosdistro$ roscore ... logging to /home/fang/.ros/log/6ec2d790-fe1d-11ee-aba8-1c1bb5cdec7c/ros…

java多线程-线程通信

简介 最常见的模式&#xff1a;生产者-消费者模式 线程通信模型 常见API 生产者抢到资源&#xff0c;生成资源&#xff0c;自己进入等待&#xff0c;唤醒消费者消费者抢到资源&#xff0c;消费数据&#xff0c;自己进入等待&#xff0c;唤醒生产者 定义容器 定义一个容器&#…

python/pygame 挑战魂斗罗 笔记(二)

一、建立地面碰撞体&#xff1a; 现在主角Bill能够站立在游戏地图的地面&#xff0c;是因为我们初始化的时候把Bill的位置固定了self.rect.y 250。而不是真正的站在地图的地面上。 背景地图是一个完整的地图&#xff0c;没有地面、台阶的概念&#xff0c;就无法通过碰撞检测来…

上位机图像处理和嵌入式模块部署(树莓派4b实现固件主流程)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 软件开发一般有软件需求、架构设计和详细设计、软件测试这四个部分。软件需求和软件测试都比较好理解&#xff0c;前者是说要实现哪些功能&#xf…

20240415,构造函数和析构函数,拷贝构造函数调用时机规则

目录 二&#xff0c;对象的初始化和清理 2.1 构造函数和析构函数 2.2 函数分类及调用 2.3 拷贝构造函数调用时机 2.4 构造函数调用规则 二&#xff0c;对象的初始化和清理 2.1 构造函数和析构函数 解决初始化和清理问题&#xff0c;编译器自动调用&#xff0c;如果不提…

C语言中的数据结构--双向链表

前言 上一节我们已经学习完了单链表&#xff08;单向不带头不循环链表&#xff09;的所有内容&#xff0c;我们在链表的分类里面知道了&#xff0c;链表分为单向的和双向的&#xff0c;那么本节我们就来进行双向链表&#xff08;带头双向循环链表&#xff09;的学习&#xff0c…

【CAD建模号】学习笔记(三):图形绘制区1

图形绘制区介绍 CAD建模号的图形绘制区可以绘制我们所需要的各种3D模型&#xff0c;绘制的图形即为模型对象&#xff0c;包括线、面、体等。 1. 二维图形绘制组 二维图形是建模的基础&#xff0c;大多数复杂的模型都是基于二维图形制作出来的&#xff0c;掌握二维图形的绘制等…

(六)Pandas文本数据 学习简要笔记 #Python #CDA学习打卡

一. 文本数据简介 1&#xff09;定义 指不能参与算术运算的任何字符&#xff0c;也称为字符型数据。如英文字母、汉字、不作为数值使用的数字(以单引号开头)和其他可输入的字符。 文本数据虽不能参加算术运算&#xff0c;但其具有纬度高、量大且语义复杂等特点&#xff0c;因…

r3live 使用前提 雷达-相机外参标定 livox_camera_lidar_calibration

标定的是相机到雷达的,R3live下面配置的雷达到相机的,所以要把得到外参旋转矩阵求逆,再填入,平移矩阵则取负 港科大livox_camera_calib虽然操作方便&#xff0c;但是使用mid360雷达会有视角问题&#xff08;投影三维点到相机&#xff09;&#xff0c;尝试了很多场景&#xff0c…

node-mysql数据库的下载与安装

01 mysql数据库的安装 网址&#xff1a;mysql.com/downloads/ 打开之后往下翻 点击 MySQL Community (GPL) Downloads 》 点击 MySRL Community Server 再点击 No thanks,just stant my download. 02 安装mysql 03 安装完成之后检查mysql服务是否开启 services.msc 04 启动…