直接上代码,可能有些粗糙,不喜勿喷哈各位大神:
两个类,没细化处理,一个主类,一个配置类,本人IDEA+JDK1.8运行
1、主类HexConvertString.java
public class HexConvertString {
private static volatile LinkedHashMap constant_pool = new LinkedHashMap<>(10);//保存常量池解析出来的数据
public static void main(String[] args) {
FileInputStream fin = null;//用来读取class文件的输入流
StringWriter sw = new StringWriter();//保存转化后的十六进制的String流
try {
//本人本地的测试路径
fin = new FileInputStream(new File("X:\\Users\\csfxy\\Desktop\\MagnumMain.class"));
int len = 1;
byte[] temp = new byte[len];
//进行16进制的读取转化
for (; (fin.read(temp, 0, len)) != -1; ) {
if (temp[0] > 0xf && temp[0] <= 0xff) {//如果是在16到272之间,证明有两位数了,不需要前面加0
sw.write(Integer.toHexString(temp[0]));
} else if (temp[0] >= 0 && temp[0] <= 0xf) {//对于只有1位的16进制数前边补“0”
sw.write("0" + Integer.toHexString(temp[0]));
} else { //对于int<0的位转化为16进制的特殊处理,因为Java没有Unsigned int,所以这个int可能为负数
sw.write(Integer.toHexString(temp[0]).substring(6));
}
}
String readResult = sw.toString();//输出转化后的十六进制
//解析class 文件16进制
String access_flag = "";//类的访问修饰符
if (readResult.startsWith("cafebabe")) {
StringBuffer sb = new StringBuffer(readResult.substring(8));
//解析版本
long code = hexParseLong(sb.substring(0, ConstantParam.ITEM * 2)) + hexParseLong(sb.substring(ConstantParam.ITEM * 2, ConstantParam.ITEM * 4));
System.out.println("该class文件字节码由" + ConstantParam.JdkVersion.getName(code) + "编译");
sb = new StringBuffer(sb.substring(ConstantParam.ITEM * 4));
// System.out.println(sb.toString());
//解析常量池个数
int CONSTANT_POOL_COUNT = (int) (hexParseLong(sb.substring(0, ConstantParam.ITEM * 2)) - 1);
System.out.println("constant_pool_count:" + CONSTANT_POOL_COUNT);
sb = new StringBuffer(sb.substring(ConstantParam.ITEM * 2));
//解析常量
for (int i = 1; i <= CONSTANT_POOL_COUNT; i++) {
String desc = ConstantParam.ConstantPoolContrast.getDesc(hexParseLong(sb.substring(0, ConstantParam.ITEM)));//获取配置的描述信息
//常量池已经读取完毕了
if ("".equals(desc)) {
System.out.println("常量池读取完毕了");
break;
}
sb = new StringBuffer(sb.substring(ConstantParam.ITEM));
String[] subCount = desc.split(",");
int cacheLength = Integer.parseInt(subCount[1]);
if (desc.contains("Utf8")) {
long length = hexParseLong(sb.substring(0, ConstantParam.ITEM * cacheLength));//需要读取随后的多少个byte字节
sb = new StringBuffer(sb.substring(ConstantParam.ITEM * cacheLength));
String value = hexParseString(sb.substring(0, (int) (ConstantParam.ITEM * length)));
constant_pool.put("#" + i + " = " + subCount[0], value);
sb = new StringBuffer(sb.substring((int) (ConstantParam.ITEM * length)));
}
if (desc.contains("Integer") || desc.contains("Long")) {
long value = hexParseLong(sb.substring(0, ConstantParam.ITEM * cacheLength));
constant_pool.put("#" + i + " = " + subCount[0], value + "");
sb = new StringBuffer(sb.substring(ConstantParam.ITEM * cacheLength));
}
if (desc.contains("Float") || desc.contains("Double")) {
long ca_i = hexParseLong(sb.substring(0, ConstantParam.ITEM * cacheLength));
float value = Double.doubleToLongBits(ca_i);
constant_pool.put("#" + i + " = " + subCount[0], value + "");
sb = new StringBuffer(sb.substring(ConstantParam.ITEM * cacheLength));
}
if (desc.contains("Class") || desc.contains("String")) {
long ca_i = hexParseLong(sb.substring(0, ConstantParam.ITEM * cacheLength));
constant_pool.put("#" + i + " = " + subCount[0], "#" + ca_i);
sb = new StringBuffer(sb.substring(ConstantParam.ITEM * cacheLength));
}
if (desc.startsWith("Field") || desc.startsWith("Method") || desc.startsWith("Interface") || desc.startsWith("Name")) {
long index_1 = hexParseLong(sb.substring(0, ConstantParam.ITEM * cacheLength));
sb = new StringBuffer(sb.substring(ConstantParam.ITEM * cacheLength));
long index_2 = hexParseLong(sb.substring(0, ConstantParam.ITEM * Integer.parseInt(subCount[2])));
constant_pool.put("#" + i + " = " + subCount[0], "#" + index_1 + ",#" + index_2);
sb = new StringBuffer(sb.substring(ConstantParam.ITEM * Integer.valueOf(subCount[2])));
}
}
//解析类的访问修饰符,紧跟着常量池后四位标示该类的修饰符
char[] access_flags = sb.substring(0, 4).toCharArray();
char a_1 = access_flags[0];
char a_2 = access_flags[1];
char a_3 = access_flags[2];
char a_4 = access_flags[3];
switch (a_1) {
case '1':
access_flag += "ACC_SYNTHETIC ";
break;
case '2':
access_flag += "ACC_ANNOTATION ";
break;
case '4':
access_flag += "ACC_ENUM ";
break;
}
switch (a_2) {
case '2':
access_flag += "ACC_INTERFACE ";
break;
case '4':
access_flag += "ACC_ABSTRACT ";
break;
}
switch (a_3) {
case '1':
access_flag += "ACC_FINAL ";
break;
case '2':
access_flag += "ACC_SUPER ";
break;
}
switch (a_4) {
case '1':
access_flag += "ACC_PUBLIC ";
break;
case '2':
access_flag += "ACC_PRIVATE ";
break;
}
System.out.println("该类的访问修饰符是:" + access_flag);
//读取类名
} else {
System.out.println("错误!该class文件被篡改");
}
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File("D:/test.txt")), "utf-8"));
bw.write("该类的修饰符是:" + access_flag);
bw.newLine();
constant_pool.forEach((key, value) ->
{
try {
bw.write(key + " ");
bw.write(value);
bw.newLine();
} catch (IOException e) {
e.printStackTrace();
}
});
bw.flush();
bw.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (null != fin) {
try {
fin.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 将传入的String字符串和该字符串对应的二进制表现形式,转为long输出
*
* @param hex
* @return
*/
private static long hexParseLong(String hex) {
char[] chars = hex.toCharArray();
long number = 0;
for (int i = chars.length - 1; i >= 0; i--) {
number = number + Long.parseLong(String.valueOf(chars[i]), ConstantParam.RADIS) * (int) Math.pow(ConstantParam.RADIS, chars.length - 1 - i);
}
return number;
}
/**
* 将传入的String字符串和该字符串对应的二进制表现形式,转为String输出
*
* @param hex
* @return
*/
private static String hexParseString(String hex) throws UnsupportedEncodingException {
byte[] bytes = new byte[hex.length() / 2];
for (int i = 0; i < bytes.length; i++) {
bytes[i] = (byte) (0xff & Integer.parseInt(hex.substring(2 * i, 2 * (i + 1)), ConstantParam.RADIS));
}
return new String(bytes, "UTF-8");
}
}
2、配置类ConstantParam.java
public class ConstantParam {
public static final int ITEM = 2;//一个字节占2位,用来进行截取识别
public static final int RADIS = 16;//进制数
//JDK版本比照
public enum JdkVersion{
JDK1_1("JDK1.1",45),JDK1_2("JDK1.2",46),JDK1_3("JDK1.3",47),JDK1_4("JDK1.4",48),JDK5("JDK5",49),JDK6("JDK6",50),JDK7("JDK7",51),JDK8("JDK8",52);
private String name;
private long code;
public static String getName( long code){
for (JdkVersion value : JdkVersion.values()) {
if (code == value.code ){
return value.name;
}
}
return null;
}
private JdkVersion(String name,int code){
this.name = name;
this.code = code;
}
}
//常量池数据类型结构对照
public enum ConstantPoolContrast{
U1(1,"Utf8,2"),//名称,需要读取多少个byte,随后在需要读取多少个byte
U3(3,"Integer,4"),
U4(4,"Float,4"),
U5(5,"Long,8"),
U6(6,"Double,8"),
U7(7,"Class,2"),
U8(8,"String,2"),
U9(9,"Fieldref,2,2"),
U10(10,"Methodref,2,2"),
U11(11,"InterfaceMethodref,2,2"),
U12(12,"NameAndType,2,2");
private int index;
private String desc;
private ConstantPoolContrast( int index,String desc ){
this.desc = desc;
this.index = index;
}
public static String getDesc( long index ){
for (ConstantPoolContrast value : ConstantPoolContrast.values()) {
if (value.index == index ){
return value.desc;
}
}
return "";
}
}
}
可使用java命令解析 javap -verbose ./Test.class 进行结果对照,当然,可能超大类会有误差,粗版本,学习交流,记录下来
至于代码中为什么要这样处理,请看我的另外一篇文章解释https://blog.csdn.net/qq_22597325/article/details/92133375