为了能够更加深入的学习class结构,本章节将写一个ClassByteCodeParser类(有极小部分数据结构较复杂没解析)来实现简单的class文件解析。
首先我们创建一个用于测试的TestHelloWorld.java文件,源码如下:
package com.anbai.sec.bytecode;import java.io.Serializable;/*** Creator: yz* Date: 2019/12/17*/@Deprecatedpublic class TestHelloWorld implements Serializable {private static final long serialVersionUID = -7366591802115333975L;private long id = 1l;private String username;private String password;public String hello(String content) {String str = "Hello:";return str + content;}public static void main(String[] args) {TestHelloWorld test = new TestHelloWorld();String str = test.hello("Hello World~");System.out.println(str);}public long getId() {return id;}public void setId(long id) {this.id = id;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}@Overridepublic String toString() {return "TestHelloWorld{" +"id=" + id +", username='" + username + '\'' +", password='" + password + '\'' +'}';}}
然后使用javac将TestHelloWorld.java编译成TestHelloWorld.class文件,或者使用maven构建javaweb-sec/javaweb-sec-source/javase/项目,构建成功后在javaweb-sec/javaweb-sec-source/javase/target/classes/com/anbai/sec/bytecode/目录下可以找到TestHelloWorld.class文件。
最后编写一个ClassByteCodeParser类,严格按照JVM规范中的类文件格式文档规定,依次解析class文件的各种数据类型就可以实现字节码解析了。
ClassByteCodeParser代码片段(省略了getter/setter和解析逻辑):
package com.anbai.sec.bytecode;/*** Java类字节码解析,参考:https://docs.oracle.com/javase/specs/jvms/se15/jvms15.pdf和https://github.com/ingokegel/jclasslib*/public class ClassByteCodeParser {/*** 转换为数据输入流*/private DataInputStream dis;/*** Class文件魔数*/private int magic;/*** Class小版本号*/private int minor;/*** Class大版本号*/private int major;/*** 常量池中的对象数量*/private int poolCount;/*** 创建常量池Map*/private final Map<Integer, Map<String, Object>> constantPoolMap = new LinkedHashMap<>();/*** 类访问修饰符*/private int accessFlags;/*** thisClass*/private String thisClass;/*** superClass*/private String superClass;/*** 接口数*/private int interfacesCount;/*** 接口Index数组*/private String[] interfaces;/*** 成员变量数量*/private int fieldsCount;/*** 成员变量数组*/private final Set<Map<String, Object>> fieldList = new HashSet<>();/*** 方法数*/private int methodsCount;/*** 方法数组*/private final Set<Map<String, Object>> methodList = new HashSet<>();/*** 属性数*/private int attributesCount;/*** 属性*/private Map<String, Object> attributes;/*** 解析Class字节码** @param in 类字节码输入流* @throws IOException 解析IO异常*/private void parseByteCode(InputStream in) throws IOException {// 将输入流转换成DataInputStreamthis.dis = new DataInputStream(in);// 解析字节码逻辑代码}public static void main(String[] args) throws IOException {// 解析单个class文件File classFile = new File(System.getProperty("user.dir"), "javaweb-sec-source/javase/target/classes/com/anbai/sec/bytecode/TestHelloWorld.class");ClassByteCodeParser codeParser = new ClassByteCodeParser();codeParser.parseByteCode(new FileInputStream(classFile));System.out.println(JSON.toJSONString(codeParser));}}
解析完TestHelloWorld.class后将会生成一个json字符串,省略掉复杂的constantPoolMap、fieldList、methodList、attributes属性后格式如下:
{"accessFlags": 33,"attributes": {},"attributesCount": 3,"constantPoolMap": {},"fieldList": [],"fieldsCount": 4,"interfaces": ["java/io/Serializable"],"interfacesCount": 1,"magic": -889275714,"major": 51,"methodList": [],"methodsCount": 10,"minor": 0,"poolCount": 95,"superClass": "java/lang/Object","thisClass": "com/anbai/sec/bytecode/TestHelloWorld"}
