我們都知道,Java編譯器負(fù)責(zé)將.java文件編譯成.class文件,class文件存儲的是java字節(jié)碼,與.java文件無關(guān)(只要你愿意寫一個編譯器,也可以將別的語言寫的源代碼編譯成.class文件),本文準(zhǔn)備詳細(xì)解剖class文件的內(nèi)部結(jié)構(gòu),并且把class文件結(jié)構(gòu)讀取并顯示出來。
Class文件的格式由JVM規(guī)范規(guī)定,一共有以下部分:
1. magic number,必須是0xCAFEBABE,用于快速識別是否是一個class文件。 2. version,包括major和minor,如果版本號超過了JVM的識別范圍,JVM將拒絕執(zhí)行。 3. constant pool,常量池,存放所有用到的常量。 4. access flag,定義類的訪問權(quán)限。 5. this class和super class,指示如何找到this class和super class。 6. interfaces,存放所有interfaces。 7. fields,存放所有fields。 8. methods,存放所有methods。 9. attributes,存放所有attributes。 先寫一個Test.java:
package example.test; 然后編譯,放在C:\Documents and Settings\Administrator\桌面\解析class文件\TestClass.java。
我們用Java來讀取和分析class,ClassAnalyzer的功能便是讀取Test.class,分析結(jié)構(gòu),然后顯示出來,DOS下顯示的內(nèi)容比較少,我們通過StringBuffer最后輸出到txt文件,實現(xiàn)由文件到文件的轉(zhuǎn)變:
import java.io.*; public class ClassAnalyzer
{ public static StringBuffer sb = new StringBuffer(); public static byte[] analyzeConstant_tag; public static String[] analyzeConstant_utf8; public static DataOutputStream dataout; public static void main(String[] args)
{ DataInputStream input = null; try { FileOutputStream out = new FileOutputStream("C:\\Documents and Settings\\Administrator\\桌面\\解析class文件\\ReadClass.tmp"); BufferedOutputStream buffout = new BufferedOutputStream(out); dataout = new DataOutputStream(buffout); input = new DataInputStream(new BufferedInputStream(new FileInputStream( "C:\\Documents and Settings\\Administrator\\桌面\\解析class文件\\TestClass.class" ))); //獲取文件大小
FileSize(new DataInputStream(new BufferedInputStream(new FileInputStream( "C:\\Documents and Settings\\Administrator\\桌面\\解析class文件\\TestClass.class" )))); //解析數(shù)據(jù) analyze(input); } catch (Exception e) { System.out.println("解析失敗!"); } finally { try { input.close(); dataout.close();} catch (Exception e) {} } } public static void FileSize(DataInputStream input) throws IOException {
int length = 0; //長度,字節(jié),B double kblength = 0.0D; //長度,千字節(jié),KB while (input.read() != -1) ++length; kblength = length / 1024.0D; System.out.println("文件大?。ㄗ止?jié)):" + length + "\n文件大小(KB):" + kblength); input.close(); } public static void analyze(DataInputStream input) throws IOException
{ // 讀取幻數(shù) int magic = u4(input); if (magic == 0xCAFEBABE) //-889275714 System.out.println("magic number = 0xCAFEBABE -----標(biāo)準(zhǔn)class文件"); else { throw new RuntimeException("無效的幻數(shù)!"); } // 讀取副、主版本號
int minor_ver = u2(input); int major_ver = u2(input); System.out.println("Version = " + major_ver + "." + minor_ver); add(" 常規(guī)信息 "); add("Minor version: " + minor_ver); add("Major version: " + major_ver); // 讀取常量池表中表項的個數(shù)
short const_pool_count = u2(input); System.out.println("constant pool size = " + const_pool_count); add("Constant pool count: " + const_pool_count); // 讀取每個常量
analyzeConstant_tag = new byte[const_pool_count]; //存儲常量數(shù)據(jù)類型 analyzeConstant_tag[0] = 0; //系統(tǒng)保留,class中不存在 for (int i = 1; i < const_pool_count; ++i) analyzeConstant(input, i); //分析常數(shù) jiexitmp(); //解析tmp文件
// 讀取Class的聲明中使用的修飾符掩碼并解析
short access_flags = u2(input); System.out.print("access_flags = " + access_flags); String access_flags_16= "0x"+Integer.toHexString(access_flags); int[] access = {0x0001,0x0010, 0x0020,0x0200,0x0400}; String[] access_str = {"public","final","","interface","abstract"}; //NO.2--super String access_tmp = ""; for(int i=0;i<access.length;i++) { if((access_flags & access[i]) == access[i]) { if(i == 0)access_tmp += access_str[i]; else if(i != 2)access_tmp += " " + access_str[i]; } } System.out.println(" [" + access_tmp + " ]"); add("Access flags: " + access_flags_16 + " [" + access_tmp + " ]"); //讀取類或者接口的全限定名稱
short this_class_index = u2(input); short super_class_index = u2(input); System.out.println("This class = " + this_class_index); System.out.println("Super class = " + super_class_index); add("This class: " + this_class_index); add("Super class: " + super_class_index); // read interfaces count: short interfaces_count = u2(input); System.out.println("超接口個數(shù) = " + interfaces_count); add("Interfaces count: " + interfaces_count); // read each interface: for(int i=1; i<=interfaces_count; i++) { short interface_index = u2(input); System.out.println("No. " + i + " interface index = " + interface_index); } } public static byte u1(DataInputStream input) throws IOException
{ return input.readByte(); } public static Short u2(DataInputStream input) throws IOException
{ return input.readShort(); } public static int u4(DataInputStream input) throws IOException
{ return input.readInt(); } public static long u8(DataInputStream input) throws IOException
{ return input.readLong(); } public static void add(String str) throws IOException
{ sb.append(str+"\n"); } public static void analyzeConstant(DataInputStream input, int index) throws IOException {
// 用于讀: byte n8; short n16; int n32; long n64; float f; double d; byte[] buffer; byte tag = input.readByte(); //讀取數(shù)據(jù)類型標(biāo)簽 analyzeConstant_tag[index] = tag; //存儲常量數(shù)據(jù)類型 System.out.println("\n常量索引 = " + index + ", 數(shù)據(jù)類型標(biāo)簽 = " + (int)tag); switch(tag) { case 1: // utf-8 string System.out.println(" 常量類型 = Utf8 //Utf8存儲格式"); //Utf8存儲格式 n16 = u2(input); System.out.println(" length = " + n16); dataout.writeShort(n16); buffer = new byte[n16]; input.readFully(buffer); //數(shù)組讀滿才返回 System.out.println(" value = " + new String(buffer)); dataout.writeUTF(new String(buffer)); break; case 3: // integer System.out.println(" 常量類型 = Integer //Integer存儲格式"); //Integer存儲格式 n32 = u4(input); System.out.println(" value = " + n32); dataout.writeInt(n32); break; case 4: // float System.out.println(" 常量類型 = Float //Float存儲格式"); //Float存儲格式 f = u4(input);//input.readFloat(); System.out.println(" value = " + f); dataout.writeFloat(f); break; case 5: // long System.out.println(" 常量類型 = Long //Long存儲格式"); //Long存儲格式 n64 = u8(input); System.out.println(" value = " + n64); dataout.writeLong(n64); break; case 6: // double System.out.println(" 常量類型 = Double //Double存儲格式"); //Double存儲格式 d = u8(input); System.out.println(" value = " + d); dataout.writeDouble(d); break; case 7: // class or interface reference System.out.println(" 常量類型 = Class //類索引"); //類索引 n16 = u2(input); System.out.println(" index = " + n16 + " (在哪里可以找到類名)"); dataout.writeShort(n16); break; case 8: // string System.out.println(" 常量類型 = String //字符串索引"); //字符串索引 n16 = u2(input); System.out.println(" index = " + n16); dataout.writeShort(n16); break; case 9: // field reference System.out.println(" 常量類型 = Fieldref //領(lǐng)域參數(shù)"); //領(lǐng)域參數(shù) n16 = u2(input); System.out.println("class index = " + n16 + " (在哪里可以找到類名)"); dataout.writeShort(n16); n16 = u2(input); System.out.println("nameAndType = " + n16 + " (在哪里可以找到的名稱和類型)"); dataout.writeShort(n16); break; case 10: // method reference System.out.println(" 常量類型 = Methodref //方法參考"); //方法參考 n16 = u2(input); System.out.println("class index = " + n16 + " (在哪里可以找到類)"); dataout.writeShort(n16); n16 = u2(input); System.out.println("nameAndType = " + n16 + " (在哪里可以找到名稱和類型)"); dataout.writeShort(n16); break; case 11: // interface method reference System.out.println(" 常量類型 = InterfaceMethodref //接口方法參考值"); //接口方法參考值 n16 = u2(input); System.out.println("class index = " + n16 + " (在哪里可以找到接口)"); dataout.writeShort(n16); n16 = u2(input); System.out.println("nameAndType = " + n16 + " (在哪里可以找到名稱和類型)"); dataout.writeShort(n16); break; case 12: // name and type reference System.out.println(" 常量類型 = NameAndType //名稱和類型"); //名稱和類型 n16 = u2(input); System.out.println(" name index = " + n16 + " (在哪里可以找到這個名字)"); dataout.writeShort(n16); n16 = u2(input); System.out.println(" descripter = " + n16 + " (在哪里可以找到描述符)"); dataout.writeShort(n16); break; default: throw new RuntimeException("Invalid constant pool flag: " + tag); } //end switch } public static void jiexitmp()
{ DataInputStream input = null; try { input = new DataInputStream(new BufferedInputStream(new FileInputStream( "C:\\Documents and Settings\\Administrator\\桌面\\解析class文件\\ReadClass.tmp" ))); /**********analyzeConstant_tag[]存儲常量池表中常量數(shù)據(jù)類型(共11種)**********/
int[] tagSpecies = new int[11]; //存儲常量池表中常量各種數(shù)據(jù)類型的計數(shù) int const_pool_count = analyzeConstant_tag.length; // 讀取常量池表中表項的個數(shù) for(int i=0;i<const_pool_count;i++) //對數(shù)據(jù)類型的計數(shù)數(shù)組進(jìn)行賦值
{ System.out.println(analyzeConstant_tag[i]); //按順序輸出各種數(shù)據(jù)的類型 switch (analyzeConstant_tag[i]) { case 1: ++tagSpecies[0]; break; case 3: ++tagSpecies[1]; break; case 4: ++tagSpecies[2]; break; case 5: ++tagSpecies[3]; break; case 6: ++tagSpecies[4]; break; case 7: ++tagSpecies[5]; break; case 8: ++tagSpecies[6]; break; case 9: ++tagSpecies[7]; break; case 10: ++tagSpecies[8]; break; case 11: ++tagSpecies[9]; break; case 12: ++tagSpecies[10]; break; } } for(int i=0;i<tagSpecies.length;i++) System.out.println("數(shù)量"+tagSpecies[i]);/* analyzeConstant_utf8 = new String[analyzeConstant_utf8_num]; int j = 0; for(int i=0;i<const_pool_count;i++) if(analyzeConstant_tag[i]==1)analyzeConstant_utf8[j++] = "r"; for(int i=0;i<analyzeConstant_utf8.length;i++) System.out.println("NO."+i+" "+analyzeConstant_utf8[i]); */ } catch (Exception e) { System.out.println("解析失敗!"); } finally { try { input.close(); } catch (Exception e) {} } }
} //End /***** byte u1 Short u2 int u4 ClassFile表結(jié)構(gòu)
ClassFile { u4 magic; //幻數(shù) u2 minor_version; //副版本號
u2 major_version; //主版本號
u2 constant_pool_count; //常量池表中表項的個數(shù)
cp_info constant_pool[constant_pool_count-1]; //每個常量的信息
u2 access_flags; //類修飾符掩碼
u2 this_Class; //類或者接口的全限定名稱
u2 super_Class; //父類全限定名稱
u2 interfaces_count;
u2 interfaces[interfaces_count];
u2 fields_count;
field_info fields[fields_count];
u2 methods_count;
method_info methods[methods_count];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
1//Utf8存儲格式
dataout.writeShort(n16); //length dataout.writeUTF(new String(buffer)); //字符串 3//Integer存儲格式
dataout.writeInt(n32); 4//Float存儲格式
dataout.writeFloat(f); 5//Long存儲格式
dataout.writeLong(n64); 6//Double存儲格式
dataout.writeDouble(d); 7//類索引
dataout.writeShort(n16); 8//字符串索引
dataout.writeShort(n16); 9//領(lǐng)域參數(shù)
dataout.writeShort(n16); //類名 dataout.writeShort(n16); //名稱和類型 10//方法參考
dataout.writeShort(n16); //類名 dataout.writeShort(n16); //名稱和類型 11//接口方法參考值
dataout.writeShort(n16); //接口 dataout.writeShort(n16); //名稱和類型 12//名稱和類型
dataout.writeShort(n16); //名字索引 dataout.writeShort(n16); //描述符索引 *****/ 代碼還有很多不足,還有很多沒有解析,下次繼續(xù)吧!
作者:qq379264347
論壇:http://zcb.
轉(zhuǎn)載請保留本段版權(quán)信息,謝謝!
|
|