## see also https://0x41414141.de/blog/2017-03-30-trojan-ransom.win32.foreign-hides-payload-exe-in-gif-file/ # package main import ( "bufio" "bytes" "container/ring" "encoding/hex" "flag" "fmt" "io/ioutil" "log" "os" ) var inFile string func init() { flag.StringVar(&inFile, "in", "", "path to input file") } func check(err error) { if err != nil { log.Fatal(err) } } func main() { // parse flags flag.Parse() // check if inputFile is given if inFile == "" { err := fmt.Errorf("no input file given") check(err) } // determine fileSize of inputFile var fileSize int64 file, err := os.Open(inFile) check(err) fi, err := file.Stat() check(err) fileSize = fi.Size() var offset int64 var bytesRead int // extract XOR key keyBuf := make([]byte, 256) offset, err = file.Seek(10, 0) check(err) bytesRead, err = file.Read(keyBuf) check(err) log.Printf("read %d at offset %d into keyBuf\n", bytesRead, offset) log.Println("Key extracted is:") d := hex.Dumper(os.Stdout) defer d.Close() d.Write(keyBuf) err = ioutil.WriteFile("key", keyBuf, 0755) check(err) log.Printf("key was written to the file 'key'\n") // extract encrypted EXE from inFile encBlockSize := fileSize - 10 - 256 - 266 encBlockBuf := make([]byte, encBlockSize) offset, err = file.Seek(266, 0) check(err) bytesRead, err = file.Read(encBlockBuf) check(err) log.Printf("read %d at offset %d into encBlockBuf\n", bytesRead, offset) // extract last block from inFile lastBlockBuf := make([]byte, 266) offset, err = file.Seek(-532, 2) check(err) bytesRead, err = file.Read(lastBlockBuf) check(err) log.Printf("read %d at offset %d into lastBlock\n", bytesRead, offset) // xor encBlockBuf with XOR key decBlock := XOR(encBlockBuf, keyBuf) // decBlock = append(decBlock, lastBlockBuf) var exeBuf bytes.Buffer w := bufio.NewWriter(&exeBuf) w.Write(decBlock) w.Write(lastBlockBuf) w.Flush() // write exe file out err = ioutil.WriteFile("exe", exeBuf.Bytes(), 0755) check(err) log.Printf("wrote output to file 'exe'\n") } // XOR implements a rolling xor where the key size is allowed not to be the same size as the data func XOR(data, key []byte) []byte { // setup ring buffer with key data r := ring.New(len(key)) for i := 0; i < r.Len(); i++ { r.Value = key[i] r = r.Next() } // do the actual xor operation for i := 0; i < len(data); i++ { // foreach byte of data data[i] = r.Value.(byte) ^ data[i] // xor data byte with current ring element r = r.Next() // move ring forward } return data }