diff --git a/main.go b/main.go index 6acc4c0..ccc543f 100644 --- a/main.go +++ b/main.go @@ -4,8 +4,16 @@ package main import ( + "crypto/sha256" + "encoding/base64" "flag" "fmt" + "go/ast" + "go/parser" + "go/printer" + "go/token" + "io" + "io/ioutil" "log" "os" "os/exec" @@ -29,7 +37,14 @@ Usage of garble: func main() { os.Exit(main1()) } -var workingDir string +var ( + workingDir string + deferred []func() error + fset = token.NewFileSet() + + b64 = base64.NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_z") + printerConfig = printer.Config{Mode: printer.RawFormat} +) func main1() int { if err := flagSet.Parse(os.Args[1:]); err != nil { @@ -57,6 +72,13 @@ func main1() int { return 1 } } + defer func() { + for _, fn := range deferred { + if err := fn(); err != nil { + fmt.Fprintln(os.Stderr, err) + } + } + }() cmd := exec.Command(args[0], transformed...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr @@ -83,9 +105,74 @@ func transformCompile(args []string) ([]string, error) { if !strings.Contains(trimpath, workingDir) { return nil, fmt.Errorf("-toolexec=garble should be used alongside -trimpath") } + std := flagValue(flags, "-std") == "true" + buildid := flagValue(flags, "-buildid") + for i, file := range files { + if std { + continue + } + var err error + files[i], err = transformGoFile(buildid, file) + if err != nil { + return nil, err + } + } return append(flags, files...), nil } +func hashWith(salt, value string) string { + const length = 8 + + d := sha256.New() + io.WriteString(d, salt) + io.WriteString(d, value) + sum := b64.EncodeToString(d.Sum(nil)) + + for i := 0; i < len(sum)-length; i++ { + if '0' <= sum[i] && sum[i] <= '9' { + continue + } + return sum[i : i+length] + } + return "_" + sum[:length-1] +} + +// transformGoFile creates a garbled copy of the Go file at path, and returns +// the path to the copy. +func transformGoFile(buildid, path string) (string, error) { + file, err := parser.ParseFile(fset, path, nil, 0) + if err != nil { + return "", err + } + ast.Inspect(file, func(node ast.Node) bool { + switch node := node.(type) { + case *ast.Ident: + switch { + case node.Obj == nil: + // a builtin name, or the package name + case node.Name == "main": + // possibly the main func + default: + node.Name = hashWith(buildid, node.Name) + } + } + return true + }) + f, err := ioutil.TempFile("", "garble") + if err != nil { + return "", err + } + defer f.Close() + printerConfig.Fprint(os.Stderr, fset, file) + if err := printerConfig.Fprint(f, fset, file); err != nil { + return "", err + } + if err := f.Close(); err != nil { + return "", err + } + return f.Name(), nil +} + func transformLink(args []string) ([]string, error) { flags, files := splitFlagsFromFiles(args, ".a") if len(files) == 0 { @@ -117,9 +204,15 @@ func flagValue(flags []string, name string) string { // -name=value return val } - if arg == name && i+1 < len(flags) { - // -name value - return flags[i+1] + if arg == name { + if i+1 < len(flags) { + if val := flags[i+1]; !strings.HasPrefix(val, "-") { + // -name value + return flags[i+1] + } + } + // -name, equivalent to -name=true + return "true" } } return "" diff --git a/testdata/scripts/basic.txt b/testdata/scripts/basic.txt index d9f505d..a63ac9f 100644 --- a/testdata/scripts/basic.txt +++ b/testdata/scripts/basic.txt @@ -40,9 +40,8 @@ exec readelf --section-details --symbols main ! grep $WORK main -# TODO -# ! grep 'globalVar' main -# ! grep 'globalFunc' main +! grep 'globalVar' main +! grep 'globalFunc' main -- main.go -- package main