热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

破解class文件的第一步:深入理解JAVAClass文件

破解class文件的第一步:深入理解JAVAClass文件-本文分享自华为云社区《java之深入class文件》,原文作者:技术火炬手。java语言是跨平台的,所谓一次编写,到处

本文分享自华为云社区《java之深入class文件》,原文作者:技术火炬手。

java语言是跨平台的,所谓一次编写,到处运行。之所以是跨平台的,就是java定义了一套与操作系统,硬件无关的字节码格式,这个字节码就是用java class文件来表示的,java class文件内部定义了虚拟机可以识别的字节码格式,这个格式是平台无关性的,在linux系统或者在windows系统上都是一致的。这个就好比html文件,我们定义好规范,这个系统只要去按照规范显示出来里面的内容就好了。

一.JVM的语言无关性

JVM是干什么用的?

运行java的啊,难不成是运行python的?

这句话是对的,但不完整,JVM并不是只能运行java程序。

事实上,JVM上运行的本身也不是java文件,而是class文件。

而能够编译转化为class文件的,并不只有java一种。

这就是JVM的语言无关性。

至于能不能运行python,取决于是否有一个能将python转成class文件的工具。

当然这样做没有太多的意义,毕竟python也有其运行环境,且在某种意义上,比java更强大,核心类库更完善。

各种语言也有各自的平台,所以没有必要强制编译。

但掌握class文件还是很有意义的。

作为一个程序员,你是否有过或者曾经有过创建一门语言的奢望?最好还是用汉语开发。

但现实,或者大学里的某个导师,却给你兜头一盆冷水。

先花个三五年研究汇编,再考虑实现这些。

三五年,黄花菜都凉了。

现在,有了JVM,似乎看到了一点希望的曙光。

二.class文件的本质

要实现之前的设想,或者说,想开发一个编译工具。首先要做的,就是要解构class文件本身。

无论如何得来,class文件的本质都是一组以 8 位字节为基础单位的2进制流。

记住,是2进制。

为了证明这一点,我们还是要用到一些工具。比如,Sublime。

它并不是一个直接查看2进制的工具,而是16 进制的编辑器(2进制和16进制可以无缝切换)。

这里面似乎还有python的事情哦。使用时,直接点击sublime_text.exe文件即可。

然后选择class文件,打开,如下图的样子。

看的人眼花对不对?这都什么玩意!

前文说了,2进制,不,这就是16进制啊。

如果你不想去看16进制,也可以使用javap,直接去查看字节码指令(详细内容见前文《一段java代码是如何执行的》)。

如果你也不想打开命令行,还有一个叫jclasslib的工具,可提供图形化界面,它还有适用于idea的插件。

但它不是重点,暂且忽略。

三.class文件结构揭秘

class文件格式中只有两种数据类型,无符号数和表。

其中,无符号数包含所有的基础数据类型和字符串,索引引用等,根据字节长度又可以分为u1,u2,u4,u8,分别代表无符号数的长度为1,2,4,8。

而表,即对象类型。

接下来,以sublime文件解析的内容为蓝本,按顺序说说的class文件的构成。

(1)class文件的头四个字节被称为魔数,它的作用是确定这个文件是否为一个能被虚拟机接受的 Class 文件。

如,上文中魔数的值为:

它代表该文件是一个class类型的文件,不信,你可以多打开几个class文件看看。

(2)接下来的四个字节代表jdk的版本

如上的内容代表jdk的版本为1.8。

PS:jdk1.1的版本数字为45,以后每跨一个大版本,数字+1,所以jdk1.8的版本数字为51(十进制),转化为16进制即为34。

(3)下面一个概念是常量池

以上内容是常量池的计数器,通过该数字,我们计算出常量的个数为15个(计算出的数字减1,因为该计数器的起始数不是0,而是1)

我们用javap命令打开常量池,证明常量的确是15个。

(4)常量池后面就是访问标志,访问标志主要分为如下类别

我们回头去看看这段class的源码(居然如此简单)

Java 代码

public class ByteCode {
    public ByteCode(){
    }
}

该类非接口,非抽象类,非枚举,非系统代码,非final,有pulbic,且编译器在jdk1.2之后,所以,满足条件的标志为:

ACC_PUBLIC和 ACC_SUPER,对应标志数为0001和0020,合并起来就是0021。如下图位置:

(5)类索引,父类索引和接口索引

  • 上文访问标志后面就是类索引,索引值为0002,对应常量池第二位。
  • 类索引后面就是夫类索引,索引值为0003,对应常量池第三位。
  • 父类索引后面就是接口索引,索引值为0000,代表该类没有实现任何接口。

(6)字段表,方法表,属性表

三大索引之后就是字段表

字段表为0000,代表无字段。

如上图,方法表分为四部分

  • 方法表计数器的结果为1,代表有一个字段
  • 方法表访问标志为0001,代表public
  • 方法表名称索引为0004,对应常量池第4个
  • 方法表描述索引为0005,对应常量池第5个

属性表以此类推。

四.字节码指令

单独开一个章节讲讲字节码指令,它存在于方法表中,如下分类:

(1)加载和存储指令

此部分内容,见前文《一段java代码是如何执行的》)

(2)运算或算术指令

源码:

Java 代码

public class Test {
    public void add(int a,int b){
        System.out.println(a+b);
        System.out.println(a-b);
        System.out.println(a*b);
        System.out.println(a/b);
    }
}

字节码指令如下:

(3)类型转换指令

源码:

Java 代码

public class Test {
    public void add(int a,int b){
        int c = 1;
        long d = c;
    }
}

字节码指令:

(4)创建实例指令

这个不用多讲,就是new

(5)创建数组指令

源码:

Java 代码

public class Test {
    public void add(int a,int b){
        int[] c = new int[4];
        String[] d = new String[5];
    }
}

字节码指令:

(6)访问字段指令

源码:

Java 代码

public class Test {
    private static String name = "1";
    private String age = "2";
    public static void main(String[] args) {
        Test test = new Test();
        String a = test.age;
        String b = Test.name;
    }
}

字节码指令:

(7)数组存取指令

源码:

Java 代码

public static void main(String[] args) {
    String[] a = new String[5];
    a[1] = "2";
    String b = a[1];
}

字节码指令:

(8)检查实例类型指令

就是instanceof,演示略

(9)方法返回指令

就是return,演示略

五.异常操作

直接看一段代码:

Java 代码

public class Test {
    public void test() {
        InputStream in = null;
        try {
            in = new FileInputStream("i.txt");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }finally {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

代码是一段典型的文件流操作,与其他代码不同的是,它捕获了两个异常。

那么,字节码指令又是如何处理该异常的呢

我们可以看到,最底下出现了一个exception table,即异常表,它记录了所有的异常数据

以异常表第一行举例,from,to分别代表,如果第12行,到第16行间发生异常,则直接跳到第19行(target)。

六.装箱拆箱

这是绕不过去的一个话题。

但凡有一点java基础的人都知道,java有八大基础数据类型,每一种类型都对应一种包装类。如int之于Integer,long之于Long。

一般来讲,基础数据类型和包装类都可以相互赋值。但这其中的逻辑如何呢?

Java 代码

public class Test {
    public static void main(String[] args) {
       Integer i = 1;
       int a = 2;
       int b = 3;
       i = a;
       b = i;
    }
}

我们来看看字节码指令


从字节码指令中,我们可以看到,有三次拆装操作

  • 第一次,调用Integer的valueOf方法,讲常量1转为Integer类型;
  • 第二次,调用Integer的valueOf方法,讲栈顶值2转为Integer类型;
  • 第三次,调用intValue方法,讲Integer转为int,然后赋值给b。

前两部为装箱,后一步为拆箱。

这就是拆装箱的底层实现逻辑了。

点击关注,第一时间了解华为云新鲜技术~


推荐阅读
  • 在《Cocos2d-x学习笔记:基础概念解析与内存管理机制深入探讨》中,详细介绍了Cocos2d-x的基础概念,并深入分析了其内存管理机制。特别是针对Boost库引入的智能指针管理方法进行了详细的讲解,例如在处理鱼的运动过程中,可以通过编写自定义函数来动态计算角度变化,利用CallFunc回调机制实现高效的游戏逻辑控制。此外,文章还探讨了如何通过智能指针优化资源管理和避免内存泄漏,为开发者提供了实用的编程技巧和最佳实践。 ... [详细]
  • Web开发框架概览:Java与JavaScript技术及框架综述
    Web开发涉及服务器端和客户端的协同工作。在服务器端,Java是一种优秀的编程语言,适用于构建各种功能模块,如通过Servlet实现特定服务。客户端则主要依赖HTML进行内容展示,同时借助JavaScript增强交互性和动态效果。此外,现代Web开发还广泛使用各种框架和库,如Spring Boot、React和Vue.js,以提高开发效率和应用性能。 ... [详细]
  • 线程能否先以安全方式获取对象,再进行非安全发布? ... [详细]
  • 数字图书馆近期展出了一批精选的Linux经典著作,这些书籍虽然部分较为陈旧,但依然具有重要的参考价值。如需转载相关内容,请务必注明来源:小文论坛(http://www.xiaowenbbs.com)。 ... [详细]
  • C++ 开发实战:实用技巧与经验分享
    C++ 开发实战:实用技巧与经验分享 ... [详细]
  • 本指南从零开始介绍Scala编程语言的基础知识,重点讲解了Scala解释器REPL(读取-求值-打印-循环)的使用方法。REPL是Scala开发中的重要工具,能够帮助初学者快速理解和实践Scala的基本语法和特性。通过详细的示例和练习,读者将能够熟练掌握Scala的基础概念和编程技巧。 ... [详细]
  • 如何利用Java 5 Executor框架高效构建和管理线程池
    Java 5 引入了 Executor 框架,为开发人员提供了一种高效管理和构建线程池的方法。该框架通过将任务提交与任务执行分离,简化了多线程编程的复杂性。利用 Executor 框架,开发人员可以更灵活地控制线程的创建、分配和管理,从而提高服务器端应用的性能和响应能力。此外,该框架还提供了多种线程池实现,如固定线程池、缓存线程池和单线程池,以适应不同的应用场景和需求。 ... [详细]
  • 您的数据库配置是否安全?DBSAT工具助您一臂之力!
    本文探讨了Oracle提供的免费工具DBSAT,该工具能够有效协助用户检测和优化数据库配置的安全性。通过全面的分析和报告,DBSAT帮助用户识别潜在的安全漏洞,并提供针对性的改进建议,确保数据库系统的稳定性和安全性。 ... [详细]
  • Linux基础知识:Vi与Vim编辑器详解
    Linux基础知识:Vi与Vim编辑器详解 ... [详细]
  • Java中不同类型的常量池(字符串常量池、Class常量池和运行时常量池)的对比与关联分析
    在研究Java虚拟机的过程中,笔者发现存在多种类型的常量池,包括字符串常量池、Class常量池和运行时常量池。通过查阅CSDN、博客园等相关资料,对这些常量池的特性、用途及其相互关系进行了详细探讨。本文将深入分析这三种常量池的差异与联系,帮助读者更好地理解Java虚拟机的内部机制。 ... [详细]
  • 七款高效编辑器与笔记工具推荐:KindEditor自动换行功能解析
    本文推荐了七款高效的编辑器与笔记工具,并详细解析了KindEditor的自动换行功能。其中,轻笔记QingBiJi是一款完全免费的记事本软件,用户可以通过其简洁的界面和强大的功能轻松记录和管理日常事务。此外,该软件还支持多平台同步,确保用户在不同设备间无缝切换。 ... [详细]
  • 本文详细介绍了如何安全地手动卸载Exchange Server 2003,以确保系统的稳定性和数据的完整性。根据微软官方支持文档(https://support.microsoft.com/kb833396/zh-cn),在进行卸载操作前,需要特别注意备份重要数据,并遵循一系列严格的步骤,以避免对现有网络环境造成不利影响。此外,文章还提供了详细的故障排除指南,帮助管理员在遇到问题时能够迅速解决,确保整个卸载过程顺利进行。 ... [详细]
  • 在 openSUSE Tumbleweed 系统上搭建 51 单片机开发环境并进行编程实践。首先,通过 `sudo zypper in emacs` 命令安装文本编辑器 Emacs。接着,使用 `sudo zypper in sdcc` 安装 SDCC 编译器。最后,利用 `wget` 下载 sdcflash Python 脚本,以便于单片机的烧录和调试。此外,还介绍了如何配置开发环境,确保各组件协同工作,提高开发效率。 ... [详细]
  • Hadoop 2.6 主要由 HDFS 和 YARN 两大部分组成,其中 YARN 包含了运行在 ResourceManager 的 JVM 中的组件以及在 NodeManager 中运行的部分。本文深入探讨了 Hadoop 2.6 日志文件的解析方法,并详细介绍了 MapReduce 日志管理的最佳实践,旨在帮助用户更好地理解和优化日志处理流程,提高系统运维效率。 ... [详细]
  • 如何撰写PHP电商项目的实战经验? ... [详细]
author-avatar
castellaniygw_623
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有