afl-transmit/logistic/packer.go

113 lines
2.7 KiB
Go

package logistic
import (
"archive/tar"
"bytes"
"compress/flate"
"encoding/base64"
"fmt"
"io/ioutil"
"log"
"os"
)
// Packs a whole fuzzer directory - at least queue/, fuzz_bitmap, fuzzer_stats
func PackFuzzer(fuzzerName string, directory string) ([]byte, error) {
// Gather contents
contentArray := [][]byte{
[]byte(fuzzerName),
packSingleFile(directory, "fuzz_bitmap"),
packSingleFile(directory, "fuzzer_stats"),
packQueueFiles(directory),
}
// Prepare FLATE compression
var flateBuffer bytes.Buffer
flateWrite, flateErr := flate.NewWriter(&flateBuffer, flate.BestCompression)
if flateErr != nil {
return nil, fmt.Errorf("unable to prepare flate compressor: %s", flateErr)
}
// Convert all parts to base64, and concat them to the packet
firstRun := true
for _, a := range contentArray {
b64Buf := make([]byte, base64.StdEncoding.EncodedLen(len(a)))
base64.StdEncoding.Encode(b64Buf, a)
// Add newline char as separator, avoiding it on the first run
if firstRun {
firstRun = false
} else {
flateWrite.Write([]byte("\n"))
}
// Append base64 encoded content
flateWrite.Write(b64Buf)
}
flateWrite.Close()
// Return result: a big byte array, representing concatted base64-encoded files
return flateBuffer.Bytes(), nil
}
// Reads a single file and returns it
func packSingleFile(directory string, fileName string) []byte {
path := fmt.Sprintf("%s%c%s", directory, os.PathSeparator, fileName)
contents, readErr := ioutil.ReadFile(path)
if readErr != nil {
log.Printf("Failed to read file %s: %s", path, readErr)
return nil
}
return contents
}
// Packs the files in the given directory into a tar archive
func packQueueFiles(directory string) []byte {
var tarBuffer bytes.Buffer
tarWriter := tar.NewWriter(&tarBuffer)
// Get list of queue files
queuePath := fmt.Sprintf("%s%cqueue", directory, os.PathSeparator)
filesInDir, readErr := ioutil.ReadDir(queuePath)
if readErr != nil {
log.Printf("Failed to list directory content of %s: %s", directory, readErr)
return nil
}
// Walk over each file and add it to our archive
for _, f := range filesInDir {
// Check if we hit a directory (e.g. '.state')
if f.IsDir() {
// Ignore directories altogether
continue
}
// Create header for this file
header := &tar.Header{
Name: f.Name(),
Mode: 0600,
Size: f.Size(),
}
// Read file
path := fmt.Sprintf("%s%c%s", queuePath, os.PathSeparator, f.Name())
contents, readErr := ioutil.ReadFile(path)
if readErr != nil {
log.Printf("Failed to read file %s: %s", path, readErr)
continue
}
// Add header and contents to archive
tarWriter.WriteHeader(header)
tarWriter.Write(contents)
}
// Close constructed tar archive
tarWriter.Close()
// And return it
return tarBuffer.Bytes()
}