java字节码操作

Wesley13
• 阅读 713

你知道如何操作JAVA字节码文件吗,这里将介绍与操作Java字节码有关的基本知识和操作Java字节码的方法及Demo,首先我们来看一下AOP的概念,AOP是OOP的延续,是AspectOrientedProgramming的缩写,意思是面向方面编程。

如何操作JAVA字节码文件

  本文将介绍与操作Java字节码有关的基本知识和操作Java字节码的方法及Demo,谈到操作Java字节码,不能不谈到AOP(AspectOrientedProgramming),下面来简单介绍一下:

AOP简介

  AOP是OOP的延续,是AspectOrientedProgramming的缩写,意思是面向方面编程。AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,AOP可以说也是这种目标的一种实现。

   AOP的一个典型应用就是J2EE。J2EE应用系统只有部署在J2EE容器中才能运行,那么为什么划分为J2EE容器和J2EE应用系统?通过对 J2EE容器运行机制的分析,可以发现:实际上J2EE容器分离了一般应用系统的一些通用功能,例如事务机制、安全机制以及对象池或线程池等性能优化机 制。

  这些功能机制是每个应用系统几乎都需要的,因此可以从具体应用系统中分离出来,形成一个通用的框架平台,而且,这些功能机制的设计 开发有一定难度,同时运行的稳定性和快速性都非常重要,必须经过长时间调试和运行经验积累而成,因此,形成了专门的J2EE容器服务器产品,如 TomcatJBoss。

  简单了解AOP后,再来了解一下AOP底层技术:

AOP(AspectOrientedProgramming)底层技术比较

java字节码操作

  从上面的图表中分析可以看到,对于一般的操作Java字节码要求(实际上是能够满足笔者100%的要求),综合考虑功能,性能,可用性,易用性,使用Java字节码框架来操作Java字节码是最佳的选择。

  下面来了解一下都有哪些开源操作JavaJava字节码的框架:

  Javassist;

  cglib;

  SERP;

  Packagegnu.bytecode;

  Cojen;

  Jdec;

  BCEL;

  ObjectWebASM;

  JClassLib;

  TroveClassFileAPI;

  Jiapi;

  ClassfileReader&Writer;

  JBET;

  Retroweaver;

  Jen;

  Soot

这里重点介绍一下ASM,因为下面将使用ASM框架进行Java字节码修改。

  ASM这个Java字节码操控框架能被用来动态生成类或者增强既有类的功能。ASM可以直接产生二进制class文件,也可以在类被加载入 Java虚拟机之前动态改变类行为。Javaclass被存储在严格格式定义的.class文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类 名称、方法、属性以及Java字节码(指令)。ASM从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。下图对当前接触 常用的操作Java字节码框架进行了一个比较:

java字节码操作

ASM的几个特性:

  1.JAVABased.

  ASM是基于JAVA的,即用JAVA实现的。

  2.Visitor模式.

  对于ASM来说,Javaclass被描述为一棵树;使用“Visitor”模式遍历整个二进制结构。

  3.复杂性低.易学易用.

  ASM提供了更为现代的编程模型,降低了操作Java字节码的复杂性,使用事件驱动的处理方式使得用户只需要关注于对其编程有意义的部分,而不必了解Java类文件格式的所有细节:ASM框架提供了默认的“responsetaker”处理这一切。

  4.较高的性能

  对Java字节码进行操作的同时尽量减小的性能的损失(性能的损失是不可避免)。

  这里来介绍一下ASM组成及顺序图:

java字节码操作

  Corepackage提供了一个读写、修改Javabytecode的API,并且为其它的package定义了依据。这个package对于生成Javabytecode、实现大多数的bytecode变换而言意义重大。

  Treepackage提供了Javabytecode的内存表示法。

  Analysispackage提供了基本的数据流分析和类型检查算法,它们将用于在treeoackage中存储Java方法bytecode。

  Commonspackage(包含在ASM2.0中)提供了一些常用的bytecode转换和用于简化bytecode生成的适配器。

  Utilpackage包含了一些帮助类和简单的bytecode验证器,它们将有助于开发或者测试。

  XMLpackage提供了一个用于在bytecode和XML之间进行转换的适配器,和一些允许使用XSLT定义bytecode转换的兼容SAX的适配器。

  顺序图:

java字节码操作

***************************************************************************************

Demo

  这里我们来实现这样一个功能:在不能改变原代码功能的前提下,对于一个特定类的特定方法有没有被测试过,以HelloTaobao类中方法helloHeyun为例。

  类HelloTaobao:

  • publicclassHelloTaobao
  • {
  • publicvoidhelloHeyun()
  • {
  • System.out.println(“Hello,ThisisHeyun’sinvestigationaboutcodecoverage!”);
  • }
  • }

  主方法类:

  • publicclassMain
  • {
  • publicstaticvoidmain(String[]args)
  • {
  • HelloTaobaoht=newHelloTaobao();
  • ht.heyunHeyun();
  • }
  • }

  到这里,我们运行一下程序,会在Console输出字符串:“Hello,ThisisHeyun’sinvestigationaboutcodecoverage!”。

  下面我们来操作一下Java字节码文件HelloTaobao.class:

  1.想操作Java字节码的某一方法,需要继承ASM中的ClassAdapter和MethodAdapter

  2.定义类Generator来读入Java字节码文件HellTaobao,改造Java字节码文件,生成改造后的同名Java字节码文件HellTaobao,代码如下:

  • publicclassGenerator

  • {

  • publicstaticvoidmain(String[]args)throwsException

  • {

  • ClassReadercr=newClassReader(“HellTaobao”);

  • ClassWritercw=newClassWriter(ClassWriter.COMPUTE_MAXS);

  • ClassAdapterclassAdapter=newByteCodeClassHandler(cw);

  • cr.accept(classAdapter,ClassReader.SKIP_DEBUG);

  • byte[]data=cw.toByteArray();

  • Filefile=newFile(“HellTaobao.class”);

  • FileOutputStreamfout=newFileOutputStream(file);

  • fout.write(data);

  • fout.close();

  • }

  • }

3.ByteCodeClassHandler(自定义)类继承ClassAdapter(fromASM)

  4.ByteCodeClassHandler类中重写visitMethod,这个方法里去判断如果Java字节码文件HelloTaobao.class包含方法helloHeyun就调用ByteCodeMethodHandler类

  • publicclassByteCodeClassHandlerextendsClassAdapter

  • {

  • publicByteCodeClassHandler(ClassVisitorcv)

  • {

  • super(cv);

  • }

  • publicvoidvisit(intversion,intaccess,Stringname,Stringsignature,

  • StringsuperName,String[]interfaces)

  • {

  • super.visit(version,access,name,signature,superName,interfaces);

  • }

  • publicvoidvisitSource(Stringsource,Stringdebug)

  • {

  • super.visitSource(source,debug);

  • }

  • publicvoidvisitEnd()

  • {

  • }

  • @Override

  • publicMethodVisitorvisitMethod(intaccess,Stringname,Stringdesc,

  • Stringsignature,String[]exceptions)

  • {

  • MethodVisitormv=cv.visitMethod(access,name,desc,signature,

  • exceptions);

  • MethodVisitorwrappedMv=mv;

  • if(mv!=null)

  • {

  • //对于”helloHeyun”方法进行改造

  • if(name.equals(“helloHeyun”))

  • {

  • //使用自定义MethodVisitor,改写方法内容

  • wrappedMv=newByteCodeMethodHandler(mv);

  • }

  • }

  • returnwrappedMv;

  • }

  • }

  5.ByteCodeMethodHandler(自定义)继承MethodAdapter(fromASM),这里来做改造想要调用的自定义 方法,这里将调用类ControlByteCode(自定义)中的controlByteCodeByHeyun(自定义)方法

  • publicclassByteCodeMethodHandlerextendsMethodAdapter

  • {

  • publicByteCodeMethodHandler(MethodVisitormv)

  • {

  • super(mv);

  • }

  • publicvoidvisitCode()

  • {

  • visitMethodInsn(Opcodes.INVOKESTATIC,“ControlByteCode”,

  • “controlByteCodeByHeyun”,“()V”);

  • }

  • }

 6.ControlByteCode类的controlByteCodeByHeyun方法如下

  1. publicclassControlByteCode

  2. {

  3. publicstaticvoidcontrolByteCodeByHeyun()

  4. {

  5. System.out.println(“Thismethodhasalreadybeencovered.”);

  6. //TODOrealsecuritycheck

  7. }

  8. }

  7.这样,当运行完Generator类中main方法后,会生成一个和原Java字节码文件同名的文件(可以观察出,会比以前的文件大,当然也可以用MD5来确定是两个不同文件)。

  8.此时在运行主方法类Main,会发现在Console打印如下:

  1. Hello,ThisisHeyun’sinvestigationaboutcodecoverage!

  2. Thismethodhasalreadybeencovered.

  9.由此,可以看出,在原功能没有变化的前提下,通过改变Java字节码文件,我们实现了CodeCoverage的雏形。实际上,很多CodeCoverage工具(如Cobertura)都是运用此方法来实现Instrument(插装)的。

点赞
收藏
评论区
推荐文章
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
限时发布!纯手打“RocketMQ笔记”
1JVM的内存区域布局java代码的执行步骤有三点java源码文件编译器字节码文件字节码文件JVM机器码机器码系统CPU执行JVM执行的字节码需要用类加载来载入;字节码文件可以来自本地文件,可以在网络上获取,也可以实时生成。就是说你可以跳过写java代码阶段,直接生成字节码交由JVM执行其中Jav
Java层逆向分析方法和技巧
本公众号分享的所有技术仅用于学习交流,请勿用于其他非法活动,如有错漏,欢迎留言指正Java层逆向分析方法和技巧一、smali汇编1.Dalvik字节码java字节码、Dalvik字节码、机器码之间的关系?在Android上,Java代码首先经过编译器编译成
Stella981 Stella981
3年前
Python Challenge Level 18
初学Python,挑战一下流行的PythonChallenge,很不幸,卡在了18关~~被字符字节码之间的转换搞得焦头烂额,不过终于搞定了还是很happy的~~~主要的问题就是16进制形式的字符如何转成字节码(注意:不是encoding)如:\'89','50','4e','47','0d','0a','1a','0a','00
Wesley13 Wesley13
3年前
Java是如何实现跨平台的
一.Java是如何实现跨平台的1.我们编写的Java源码,编译后会生成一种.class文件,称为字节码文件2.Java虚拟机JVM就是负责将字节码文件翻译成特定平台下的机器码然后运行。也就是说,只要在不同平台上安装对应的JVM,就可以运行字节码文件,运行我们编写的Java程序。!(https:/
Wesley13 Wesley13
3年前
Java程序运行机制及开发环境
Java既是编译型语言,又是解释型语言java源文件首先需要通过javac编译生成后缀名为.class的字节码文件(与平台无关,只面向JVM),然后使用Java虚拟机将字节码解释成特定平台上的机器码运行。Java虚拟机JVM不同平台上的JVM不同,但是都提供了相同的接口。开发Java准备1.下
Wesley13 Wesley13
3年前
Java虚拟机(一):JVM简介
JVM简介Java虚拟机(JVM)是由Java虚拟机规范定义的,其上运行的是字节码指令集。这种字节码指令集包含一个字节的操作码(opcode),零至多个操作数(oprand),虚拟机规范明确定义了每种字节码指令完成的功能是什么以及需要多少个操作数。Java虚拟机上运行的class文件,这个文件中包含字节码指令流以及类定义的信息,所以Java虚
Wesley13 Wesley13
3年前
Java字节码增强探秘
1.字节码1.1什么是字节码?Java之所以可以“一次编译,到处运行”,一是因为JVM针对各种操作系统、平台都进行了定制,二是因为无论在什么平台,都可以编译生成固定格式的字节码(.class文件)供JVM使用。因此,也可以看出字节码对于Java生态的重要性。之所以被称之为字节码,是因为字节码文件由十六进制值组成,
Stella981 Stella981
3年前
JVM基础命令
介绍java虚拟机的指令功能,至少能阅读java代码生成的字节码指令含义一、概述Java虚拟机采用基于栈的架构,其指令由操作码和操作数组成。操作码:一个字节长度(0~255),意味着指令集的操作码个数不能操作256条。操作数:一条指令可以有零或者多个操作数,且操作数可以是1个或者多个字节。编译后的代码没有采用操作数
京东云开发者 京东云开发者
10个月前
打开java语言世界通往字节码世界的大门——ASM字节码操作类库
一、ASM介绍1、ASM是什么ASM是一个通用的Java字节码操作和分析框架。它可以用于修改现有类或直接以二进制形式动态生成类。ASM提供了一些常见的字节码转换和分析算法,可以从中构建定制的复杂转换和代码分析工具。ASM提供了与其他Java字节码框架类似的