为了能够更加深入的学习class结构,本章节将写一个ClassByteCodeParser类(有极小部分数据结构较复杂没解析)来实现简单的class文件解析。
首先我们创建一个用于测试的TestHelloWorld.java
文件,源码如下:
package com.anbai.sec.bytecode;
import java.io.Serializable;
/**
* Creator: yz
* Date: 2019/12/17
*/
@Deprecated
public 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;
}
@Override
public 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 {
// 将输入流转换成DataInputStream
this.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"
}