Author Topic: [Java] Creating and extracting ZIP files  (Read 3452 times)

0 Members and 1 Guest are viewing this topic.

Offline Deque

  • P.I.N.N.
  • Global Moderator
  • Overlord
  • *
  • Posts: 1203
  • Cookies: 518
  • Programmer, Malware Analyst
    • View Profile
[Java] Creating and extracting ZIP files
« on: August 16, 2012, 01:31:04 pm »
Hello EZ.

Since I needed a similar class for my current project, I thought that might come in handy for others too.
This code demonstrates how to zip or unzip files with Java. I wrote some comments for the public methods, so I hope it is understandable (if not, you know where to ask).
I didn't really handle exceptions, because it is not meant as a tool, but as example.

Usage:

Compile: javac ZipCompression.java
Zip: java ZipCompression -a <directory>
Unzip: java ZipCompression -x <zipfile>

Code: (Java) [Select]
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.DirectoryIteratorException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashSet;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

public class ZipCompression {

    private final static String SEP = System.getProperty("file.separator");

    public static void main(String[] args) throws FileNotFoundException,
            IOException {
        if (args.length != 2
                || (!args[0].equals("-x") && !args[0].equals("-a"))) {

            printUsage();

        } else {
            if (args[0].equals("-a")) {
                zip(args[1]);
            } else {
                unzip(args[1]);
            }
        }
    }

    /**
     * Creates a zip archive with inFiles as entries.
     *
     * @param inFiles
     *            files that shall be put to the archive
     * @param outFile
     *            zip archive that shall be created
     * @throws FileNotFoundException
     * @throws IOException
     */
    public static void zip(Set<Path> inFiles, Path outFile)
            throws FileNotFoundException, IOException {
        System.out.println("creating zip file " + outFile);
        try (ZipOutputStream zout = new ZipOutputStream(new FileOutputStream(
                outFile.toString())) ;) {
            zip(inFiles, "", zout);
        }
    }

    /**
     * Extracts all files in zipFile to the destination directory.
     *
     * @param zipFile
     *            archive to extract
     * @param destinationDir
     *            directory, where the files shall be written to
     * @throws FileNotFoundException
     * @throws IOException
     */
    public static void unzip(Path zipFile, Path destinationDir)
            throws FileNotFoundException, IOException {
        System.out.println("extracting zip file " + zipFile);
        try (ZipInputStream zin = new ZipInputStream(new FileInputStream(
                zipFile.toString()))) {

            ZipEntry entry;
            while ((entry = zin.getNextEntry()) != null) {
                extractEntry(destinationDir, zin, entry);
            }
        }
    }

    /**
     * Creates a zip archive for all files in the given directory. The name of
     * the archive will be the directory name with the ending '.zip' and is
     * created in the parent directory.
     *
     * @param directoryName
     *            directory that shall be zipped
     * @throws FileNotFoundException
     * @throws IOException
     */
    public static void zip(String directoryName) throws FileNotFoundException,
            IOException {
        Path dir = Paths.get(directoryName);
        Set<Path> inFiles = getDirectoryFiles(dir);
        Path outFile = Paths.get(dir.toString() + ".zip");
        zip(inFiles, outFile);
    }

    private static void zip(Set<Path> inFiles, String base, ZipOutputStream zout)
            throws IOException, FileNotFoundException {
        for (Path file : inFiles) {
            if (Files.isDirectory(file)) {
                String filename = base + file.getFileName().toString();
                zip(getDirectoryFiles(file), filename + SEP, zout);
            } else {
                writeZipEntry(zout, file, base);
            }
        }
    }

    /**
     * Extracts all files of a given zip archive to the parent folder of the
     * archive.
     *
     * @param zipfileName
     *            archive to be extracted
     * @throws FileNotFoundException
     * @throws IOException
     */
    public static void unzip(String zipfileName) throws FileNotFoundException,
            IOException {
        Path zipFile = Paths.get(zipfileName);
        Path destinationDir = zipFile.getParent();
        unzip(zipFile, destinationDir);
    }

    private static void printUsage() {
        System.out.println("usage:");
        System.out.println("\tjava ZipCompression -x <zipfile>");
        System.out.println("\tjava ZipCompression -a <directory>");
    }

    private static Set<Path> getDirectoryFiles(Path dir) {
        Set<Path> dirFiles = new HashSet<>();
        try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
            for (Path file : stream) {
                dirFiles.add(file);
            }
        } catch (IOException | DirectoryIteratorException e) {
            e.printStackTrace();
        }
        return dirFiles;
    }

    private static void writeZipEntry(ZipOutputStream out, Path inFile,
            String base) throws IOException, FileNotFoundException {
        System.out.println("write zip entry " + inFile);
        try (FileInputStream in = new FileInputStream(inFile.toString())) {
            out.putNextEntry(new ZipEntry(base
                    + inFile.getFileName().toString()));
            byte[] buf = new byte[1024];
            int len;

            while ((len = in.read(buf)) != -1) {
                out.write(buf, 0, len);
            }
            out.closeEntry();
        }
    }

    private static void extractEntry(Path destinationDir, ZipInputStream zin,
            ZipEntry entry) throws IOException, FileNotFoundException {

        Path currentFile = Paths
                .get(destinationDir.toString(), entry.getName());
        System.out.println("extracting file " + currentFile);
        Files.createDirectories(currentFile.getParent());

        try (FileOutputStream out = new FileOutputStream(currentFile.toString())) {
            byte[] buf = new byte[1024];
            int len;
            while ((len = zin.read(buf)) != -1) {
                out.write(buf, 0, len);
            }
        }
        zin.closeEntry();
    }

}

Edit: Slight changes to the code: Program keeps directory structure.
« Last Edit: August 17, 2012, 05:11:19 pm by Deque »

Offline The Alchemist

  • Peasant
  • *
  • Posts: 100
  • Cookies: 18
  • Cult Of Personality
    • View Profile
    • Scriptings - Paste Tool
Re: [Java] Creating and extracting ZIP files
« Reply #1 on: August 16, 2012, 05:07:25 pm »
That is cool... Can you make a java brute force program to open password protected zipped files?
Defeat the best... To be the best...

Offline Deque

  • P.I.N.N.
  • Global Moderator
  • Overlord
  • *
  • Posts: 1203
  • Cookies: 518
  • Programmer, Malware Analyst
    • View Profile
Re: [Java] Creating and extracting ZIP files
« Reply #2 on: August 16, 2012, 05:29:05 pm »
A few weeks ago I did some research about how this can be done. Yes, I am able to write such a program.

Offline The Alchemist

  • Peasant
  • *
  • Posts: 100
  • Cookies: 18
  • Cult Of Personality
    • View Profile
    • Scriptings - Paste Tool
Re: [Java] Creating and extracting ZIP files
« Reply #3 on: August 16, 2012, 06:02:02 pm »
Cool... Make one and share IF YOU GET TIME... I'm looking forward to it...
Defeat the best... To be the best...

Offline Ragehottie

  • Knight
  • **
  • Posts: 313
  • Cookies: -9
  • Hack to learn, not learn to hack.
    • View Profile
Re: [Java] Creating and extracting ZIP files
« Reply #4 on: August 16, 2012, 10:41:42 pm »
If you did that would be awesome. I wanted to do something like that in Python and if I had some code to glance at that would be wayy easier.
Blog: rexmckinnon.tumblr.com

Offline techb

  • Soy Sauce Feeler
  • Global Moderator
  • King
  • *
  • Posts: 2350
  • Cookies: 345
  • Aliens do in fact wear hats.
    • View Profile
    • github
Re: [Java] Creating and extracting ZIP files
« Reply #5 on: August 16, 2012, 10:54:00 pm »
If you did that would be awesome. I wanted to do something like that in Python and if I had some code to glance at that would be wayy easier.

I have a brute force script on my blog, adding the zip lib would be easy enough.
>>>import this
-----------------------------

Offline Deque

  • P.I.N.N.
  • Global Moderator
  • Overlord
  • *
  • Posts: 1203
  • Cookies: 518
  • Programmer, Malware Analyst
    • View Profile
Re: [Java] Creating and extracting ZIP files
« Reply #6 on: August 17, 2012, 09:41:24 am »
If it is only for the bruteforcing: I have such code too and I also have a self written tutorial that explains the bruteforce code. If you want me to publish it here, just tell me.

However, I didn't intend to use a third party library for dealing with password protected zip files in Java. That wouldn't be challenging and I like to understand how the file formats are constructed.
My research led me to this:
http://www.pkware.com/documents/casestudies/APPNOTE.TXT

There is the info about how it can be done manually:

Quote
Decryption
----------

PKWARE is grateful to Mr. Roger Schlafly for his expert contribution
towards the development of PKWARE's traditional encryption.

PKZIP encrypts the compressed data stream.  Encrypted files must
be decrypted before they can be extracted.

Each encrypted file has an extra 12 bytes stored at the start of
the data area defining the encryption header for that file.  The
encryption header is originally set to random values, and then
itself encrypted, using three, 32-bit keys.  The key values are
initialized using the supplied encryption password.  After each byte
is encrypted, the keys are then updated using pseudo-random number
generation techniques in combination with the same CRC-32 algorithm
used in PKZIP and described elsewhere in this document.

The following is the basic steps required to decrypt a file:

1) Initialize the three 32-bit keys with the password.
2) Read and decrypt the 12-byte encryption header, further
   initializing the encryption keys.
3) Read and decrypt the compressed data stream using the
   encryption keys.

Step 1 - Initializing the encryption keys
-----------------------------------------

Key(0) <- 305419896
Key(1) <- 591751049
Key(2) <- 878082192

loop for i <- 0 to length(password)-1
    update_keys(password(i))
end loop

Where update_keys() is defined as:

update_keys(char):
  Key(0) <- crc32(key(0),char)
  Key(1) <- Key(1) + (Key(0) & 000000ffH)
  Key(1) <- Key(1) * 134775813 + 1
  Key(2) <- crc32(key(2),key(1) >> 24)
end update_keys

Where crc32(old_crc,char) is a routine that given a CRC value and a
character, returns an updated CRC value after applying the CRC-32
algorithm described elsewhere in this document.

Step 2 - Decrypting the encryption header
-----------------------------------------

The purpose of this step is to further initialize the encryption
keys, based on random data, to render a plaintext attack on the
data ineffective.

Read the 12-byte encryption header into Buffer, in locations
Buffer(0) thru Buffer(11).

loop for i <- 0 to 11
    C <- buffer(i) ^ decrypt_byte()
    update_keys(C)
    buffer(i) <- C
end loop

Where decrypt_byte() is defined as:

unsigned char decrypt_byte()
    local unsigned short temp
    temp <- Key(2) | 2
    decrypt_byte <- (temp * (temp ^ 1)) >> 8
end decrypt_byte

After the header is decrypted,  the last 1 or 2 bytes in Buffer
should be the high-order word/byte of the CRC for the file being
decrypted, stored in Intel low-byte/high-byte order.  Versions of
PKZIP prior to 2.0 used a 2 byte CRC check; a 1 byte CRC check is
used on versions after 2.0.  This can be used to test if the password
supplied is correct or not.

Step 3 - Decrypting the compressed data stream
----------------------------------------------

The compressed data stream can be decrypted as follows:

loop until done
    read a character into C
    Temp <- C ^ decrypt_byte()
    update_keys(temp)
    output Temp
end loop


That is not quite hard. You/I just have to translate this to code.
« Last Edit: August 17, 2012, 09:43:21 am by Deque »