Search code examples
gojarzipunzip

How to extract a .jar file


I like to extract a .jar file in Go. I tried different approaches with the build in libraries but with no success. In buff is a little part of the JAR I try to analyze. The errors also occur on the full byte array.

flate:

buff := []byte{80, 75, 3, 4, 10, 0, 8, 8, 8, 0, 239, 77, 77, 78,  
147,
98, 6, 159, 116, 0, 0, 0, 132, 0, 0, 0, 20, 0, 0, 0, 77, 69, 84, 65, 
45, 73, 78, 70, 47, 77, 65, 78, 73, 70, 69, 83, 84, 46, 77, 70, 37, 
140, 49, 14, 194, 48, 12, 0, 247, 72, 249, 131, 63, 16, 43, 128, 88, 
178, 181, 221, 144, 178, 178, 91, 196, 64, 32, 184, 81, 28, 42, 248, 
61,5, 214, 187, 211, 69, 146, 124, 102, 237}  

b := bytes.NewReader(buff)
zr := flate.NewReader(b)
if _, err := io.Copy(os.Stdout, zr); err != nil {
    log.Fatal(err)
}
if err := zr.Close(); err != nil {
    log.Fatal(err)
}

fmt.Println()

Error: flate: corrupt input before offset 5

zlib:

b := bytes.NewReader(buff)

r, err := zlib.NewReader(b)
if err != nil {
    panic(err)
}
io.Copy(os.Stdout, r)

r.Close()

Error: zlib: invalid header

Wikipedia says JAR is a extension of zip and normal compression programs can unzip them. Any ideas how to get this working? Like adding a working header or is my code wrong?


Solution

  • JAR files are zip archives, not just zlib or flate compressed data.

    So use the archive/zip package to properly process them. For example, to list the files inside the JAR file:

    r, err := zip.NewReader(bytes.NewReader(buff), int64(len(buff)))
    if err != nil {
        panic(err)
    }
    
    for _, f := range r.File {
        fmt.Println("Found in jar:", f.Name)
    }
    

    Of course you have to provide the full file content, else you will most likely get an error when obtaining the zip.Reader.

    If you also want to print the contents of the files inside the archive, this is how you could do it:

    for _, f := range r.File {
        fmt.Printf("Found in jar: %s, contents:\n", f.Name)
        rc, err := f.Open()
        if err != nil {
            log.Fatal(err)
        }
        _, err = io.CopyN(os.Stdout, rc, int64(f.UncompressedSize64))
        if err != nil {
            log.Fatal(err)
        }
        rc.Close()
        fmt.Println()
    }
    

    Here's a JAR file which contains a single file named a.txt, with contents "Hello Gopher":

    buff := []byte{80, 75, 3, 4, 10, 0, 0, 0, 0, 0, 91, 109, 103, 78, 132, 225, 60, 127, 13, 0, 0, 0, 13, 0, 0, 0, 5, 0, 28, 0, 97, 46, 116, 120, 116, 85, 84, 9, 0, 3, 206, 17, 129, 92, 219, 17, 129, 92, 117, 120, 11, 0, 1, 4, 232, 3, 0, 0, 4, 232, 3, 0, 0, 72, 101, 108, 108, 111, 32, 71, 111, 112, 104, 101, 114, 10, 80, 75, 1, 2, 30, 3, 10, 0, 0, 0, 0, 0, 91, 109, 103, 78, 132, 225, 60, 127, 13, 0, 0, 0, 13, 0, 0, 0, 5, 0, 24, 0, 0, 0, 0, 0, 1, 0, 0, 0, 164, 129, 0, 0, 0, 0, 97, 46, 116, 120, 116, 85, 84, 5, 0, 3, 206, 17, 129, 92, 117, 120, 11, 0, 1, 4, 232, 3, 0, 0, 4, 232, 3, 0, 0, 80, 75, 5, 6, 0, 0, 0, 0, 1, 0, 1, 0, 75, 0, 0, 0, 76, 0, 0, 0, 0, 0}
    

    Running the above code on this buffer, the output is (try it on the Go Playground):

    Found in jar: a.txt, contents:
    Hello Gopher