Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Invalid Entry Compressed Size

I'm using a bytecode library known as ASM to alter classfiles, then I want to write each classfile back into a jar file rather than a folder filled with class files. I do this by running this code:

My problem occurs when a ZipException is throw for not being the expected size, i.e.

java.util.zip.ZipException: invalid entry compressed size (expected 695 but got 693 bytes)
    at java.util.zip.ZipOutputStream.closeEntry(Unknown Source)
    at org.steinburg.client.accessor.Accessor.accessJar(Accessor.java:64)
    at org.steinburg.client.accessor.Accessor.<init>(Accessor.java:41)
    at Loader.main(Loader.java:5)


package org.steinburg.client.accessor;

import java.io.DataInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;

public class Accessor {

private String input;
private File inFile;
private JarEntry jarEntry;
private JarFile jarFile;
private Enumeration<JarEntry> entries;
private InputStream is;

private String out;
private File outFile;
private FileOutputStream fos;
private JarOutputStream jos;

private byte[] bytes;

public Accessor(){
    try{
        input = new String("Input Jar.jar");
        inFile = new File(input);
        jarFile = new JarFile(inFile);
        entries = jarFile.entries();
        out = new String("Output Jar.jar");
        outFile = new File(out);
        fos = new FileOutputStream(outFile);
        jos = new JarOutputStream(fos);
        accessJar();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

protected final void accessJar(){
    try {
        while (entries.hasMoreElements()){
            jarEntry = entries.nextElement();
            is = jarFile.getInputStream(jarEntry);
            if (jarEntry.getName().endsWith(".class")){
                ClassReader cr = new ClassReader(is);
                ClassWriter cw = new ClassWriter(cr, 0);
                FieldAdapter fa = new FieldAdapter(cw);
                cr.accept(fa, 0);
                bytes = cw.toByteArray();
            } else {
                bytes = readBytes(is);
            }
            JarEntry je = new JarEntry(jarEntry);
            jos.putNextEntry(je);
            jos.write(bytes);
            jos.closeEntry();
        }
        jos.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

protected byte[] readBytes(InputStream inputStream){
    try{
        DataInputStream reader = new DataInputStream(inputStream);
        byte[] toReturn = new byte[reader.available()];
        reader.readFully(toReturn);
        reader.close();
        return toReturn;
    } catch (Exception e){
        e.printStackTrace();
        return null;
    }
}

}

The ClassReader, ClassWriter (each part of library), and FieldAdapter (made by myself) just altar the code, and to get the bytes of the entire class I use cw.toByteArray(). There is no problem as for the bytecode manipulation itself, it's just the writing to a new jar file through a JarOutputStream. Anyone known how to solve this problem?

like image 649
Steinburg Schglokentini Avatar asked Nov 17 '12 23:11

Steinburg Schglokentini


2 Answers

The problematic line is this:

JarEntry je = new JarEntry(jarEntry);

as it will also copy the compressed size.

Unless you care about preserving other fields than name, you should use this constructor and provide only the file name, as this:

JarEntry je = new JarEntry(jarEntry.getName ());

The compressed size will be then automatically computed.

like image 148
ShyJ Avatar answered Nov 17 '22 23:11

ShyJ


doing in this way it will not copy Jar Attributes and other meta information. The issue is caused when the jar/zip file is modified after it is initially packaged.

You can do this:

JarEntry je = new JarEntry(jarEntry);
je.setCompressedSize(-1);

This will cause the size of entry to be recalculated and you'll get copied all other attributes from the original jarEntry

like image 6
petod Avatar answered Nov 18 '22 00:11

petod