昨天写了一篇关于压缩文件以及压缩文件解压问题,现在对于自己压缩的zip文件,从字节码的角度进行分析。
这里主要的知识是对于zip文件压缩格式的了解。
第一步:获取文件字节流,这部分代码在以前的程序中大量用到,呵呵,强悍的工具方法,虽然很简单。
final static public byte[] readfile(String name) {
FileInputStream in = null;
byte buffer[] = null;
try {
in = new FileInputStream(new File(name));
buffer = new byte[in.available()];
in.read(buffer);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return buffer;
}
现在我们已经获取到了zip压缩文件的字节码数组,下来就是对他的分析了。
在这里,我将文件内容首先简单的分为两个部分,跟格式定义相同:数据区以及目录区。
第二步:读取数据区的数据。
final static public Map<String, String> parsedata(byte[] b, int pos) {
Map<String, String> map = new HashMap<String, String>();
int sig = data(b, pos, 4);
pos += 4;
map.put("sig", i2s(sig));
int zpkware = data(b, pos, 2);
pos += 2;
map.put("zpkware", i2s(zpkware));
int flag = data(b, pos, 2);
pos += 2;
map.put("flag", i2s(flag));
int type = data(b, pos, 2);
pos += 2;
map.put("type", i2s(type));
int time = data(b, pos, 2);
pos += 2;
map.put("time", i2s(time));
int date = data(b, pos, 2);
pos += 2;
map.put("date", i2s(date));
int crc = data(b, pos, 4);
pos += 4;
map.put("crc", i2s(crc));
int zsize = data(b, pos, 4);
pos += 4;
map.put("zsize", i2s(zsize));
int usize = data(b, pos, 4);
pos += 4;
map.put("usize", i2s(usize));
int lname = data(b, pos, 2);
pos += 2;
map.put("lname", i2s(lname));
int laname = data(b, pos, 2);
pos += 2;
map.put("laname", i2s(laname));
byte[] name = new byte[lname];
for (int i = 0; i < lname; i++) {
name[i] = b[pos++];
}
map.put("name", new String(name));
map.put("position", i2s(pos));
map.put("data", "data");
return map;
}
第三步:读取目录区数据。
final static public Map<String, String> parsedir(byte[] b, int pos) {
Map<String, String> map = new HashMap<String, String>();
int sig = data(b, pos, 4);
pos += 4;
map.put("sig", i2s(sig));
int zpkware = data(b, pos, 2);
pos += 2;
map.put("zpkware", i2s(zpkware));
int upkware = data(b, pos, 2);
pos += 2;
map.put("upkware", i2s(upkware));
int flag = data(b, pos, 2);
pos += 2;
map.put("flag", i2s(flag));
int type = data(b, pos, 2);
pos += 2;
map.put("type", i2s(type));
int time = data(b, pos, 2);
pos += 2;
map.put("time", i2s(time));
int date = data(b, pos, 2);
pos += 2;
map.put("date", i2s(date));
int crc = data(b, pos, 4);
pos += 4;
map.put("crc", i2s(crc));
int zsize = data(b, pos, 4);
pos += 4;
map.put("zsize", i2s(zsize));
int usize = data(b, pos, 4);
pos += 4;
map.put("usize", i2s(usize));
int lname = data(b, pos, 2);
pos += 2;
map.put("lname", i2s(lname));
int laname = data(b, pos, 2);
pos += 2;
map.put("laname", i2s(laname));
int notes = data(b, pos, 2);
pos += 2;
map.put("notes", i2s(notes));
int disk = data(b, pos, 2);
pos += 2;
map.put("disk", i2s(disk));
int ifile = data(b, pos, 2);
pos += 2;
map.put("ifile", i2s(ifile));
int ofile = data(b, pos, 4);
pos += 4;
map.put("ofile", i2s(ofile));
int phead = data(b, pos, 4);
pos += 4;
map.put("phead", i2s(phead));
byte[] name = new byte[lname];
for (int i = 0; i < lname; i++) {
name[i] = b[pos++];
}
map.put("name", new String(name));
map.put("position", i2s(pos));
map.put("data", "dir");
return map;
}
现在都只是头部信息,对于文件内容没有处理。
第四步:分析整个文件包含的数据区以及目录区数据。
final static public Vector<Map<String, String>> parse(byte[] b) {
Vector<Map<String, String>> v = new Vector<Map<String, String>>();
Map<String, String> elem;
int pos = 0;
while (pos != -1) {
elem = parsedata(b, pos);
v.add(elem);
pos = new Integer((String) elem.get("position")).intValue();
pos = searchdata(b, pos);
}
pos = searchdir(b, 0);// OK
while (pos != -1) {
elem = parsedir(b, pos);
v.add(elem);
pos = new Integer((String) elem.get("position")).intValue();
pos = searchdir(b, pos);
}
return v;
}
第五步:用到的工具方法。
1、将int转换为字符串的。
final static public String i2s(int i) {
return String.valueOf(i);
}
2、寻找数据以及读取字节定位的。
final static public int data(byte[] b, int pos, int times) {
int record = 0;
for (int i = 0; i < times; i++) {
record += b[pos++] << (8 * i);
}
return record;
}
3、全字节数组寻找数据记录的。
final static public int searchdata(byte[] b, int pos) {
for (int i = pos; i < b.length; i++) {
if (i + 4 < b.length) {
if (data(b, i, 4) == 0x04034B50) {
return i;
}
}
}
return -1;
4、全字节数组寻找目录记录的。
final static public int searchdir(byte[] b, int pos) {
for (int i = pos; i < b.length; i++) {
if (i + 4 < b.length) {
if (data(b, i, 4) == 0x02014B50) {
return i;
}
}
}
return -1;
}
5、验证过程中的输出方法。
final static public void toString(byte[] b) {
for (int i = 0; i < b.length; i++) {
System.out.print(b[i] + " ");
}
}
final static public void toString(Map<String, String> map) {
Set<String> set = map.keySet();
for (String string : set) {
System.out.println(string + ":" + map.get(string));
}
}
final static public void toString(Vector<Map<String, String>> v) {
Map<String, String> map = null;
for (int i = 0; i < v.size(); i++) {
map = v.elementAt(i);
toString(map);
System.out.println("--------------------------------------------");
}
}
第六步:测试代码。
public static void main(String[] args) {
byte[] b = readfile("D://zipunzip//testzip.zip");
toString(parse(b));
}
OK,程序完成。
但是还有很大的缺陷,而此种缺陷则是由压缩文件模式引起的。
例如:
缺陷1:分析的压缩文件中对于压缩文件大小以及为压缩文件大小了个字段均为0,顾而无法获取文件内容的具体东西,并且文件内容进行了压缩,难以分析。顾而在程序中自己来检测文件位置来确定,也就出现了多余的position的记录项。
缺陷2:压缩过程由于没有进行属性控制,压缩文件的组织方式是按照文件的存放位置的,没有自己的过滤设置,即按照自己想存放的方式压缩。
因此,对于昨天的压缩文件还是要进一步的分析,以压缩成质量高的,可以自己明确分析的压缩文件,而不是压缩仅仅是成功的压缩文件。
对于这个问题,关注点集中于ZipEntry类中,没有太多的研究这个类,有谁知道这个里面参数的具体含义,以及如何组织自己的ZipEntry实例,给点指示,谢谢。