From 5f8bae06b7b2c959c9ea01956b2765cc5d03cfad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Sun, 20 Jun 2021 11:00:36 +0100 Subject: [PATCH] small improvements towards obfuscating the runtime I spent a couple of days trying to obfuscate all of std. Ultimately I failed at making it fully work, especially when it comes to the runtime package, but I did fix a few problems along the way, as seen here. First, fix the TODO to allow handleDirectives and transformGo to run on runtime packages as well, if they are considered private. Note that this is never true right now, but it matters once we remove runtimeRelated. Second, modify parsedebugvars in a way that doesn't break typechecking. We can remove AST nodes or even modify them in simple ways, but if we add new AST nodes after typechecking, those will lack type information. We were replacing the entire body, running into that problem. Instead, carefully cut the body to set some defaults, but remove everything from the point GODEBUG is read. Finally, add commented-out debug prints of transformed asm files. For #193. --- main.go | 18 +++++++++------ runtime_strip.go | 58 +++++++++++++++++++----------------------------- 2 files changed, 34 insertions(+), 42 deletions(-) diff --git a/main.go b/main.go index 7d7d7d3..ede2c12 100644 --- a/main.go +++ b/main.go @@ -583,6 +583,11 @@ func transformAsm(args []string) ([]string, error) { buf.WriteString(newName) } + // Uncomment for some quick debugging. Do not delete. + // if curPkg.Private { + // fmt.Fprintf(os.Stderr, "\n-- %s --\n%s", path, buf.Bytes()) + // } + name := filepath.Base(path) if path, err := writeTemp(name, buf.Bytes()); err != nil { return nil, err @@ -681,13 +686,11 @@ func transformCompile(args []string) ([]string, error) { for i, file := range files { name := filepath.Base(paths[i]) - // TODO(mvdan): allow running handleDirectives and transformGo - // on runtime too, by splitting the conditionals here. - switch { - case curPkg.ImportPath == "runtime": + switch curPkg.ImportPath { + case "runtime": // strip unneeded runtime code stripRuntime(name, file) - case curPkg.ImportPath == "runtime/internal/sys": + case "runtime/internal/sys": // The first declaration in zversion.go contains the Go // version as follows. Replace it here, since the // linker's -X does not work with constants. @@ -706,9 +709,10 @@ func transformCompile(args []string) ([]string, error) { } lit := spec.Values[0].(*ast.BasicLit) lit.Value = "`unknown`" - case strings.HasPrefix(name, "_cgo_"): + } + if strings.HasPrefix(name, "_cgo_") { // Don't obfuscate cgo code, since it's generated and it gets messy. - default: + } else { tf.handleDirectives(file.Comments) file = tf.transformGo(file) } diff --git a/runtime_strip.go b/runtime_strip.go index 1448070..cfc5a28 100644 --- a/runtime_strip.go +++ b/runtime_strip.go @@ -75,12 +75,30 @@ func stripRuntime(filename string, file *ast.File) { break } case "runtime1.go": + usesEnv := func(node ast.Node) bool { + seen := false + ast.Inspect(node, func(node ast.Node) bool { + ident, ok := node.(*ast.Ident) + if ok && ident.Name == "gogetenv" { + seen = true + return false + } + return true + }) + return seen + } + filenames: switch x.Name.Name { case "parsedebugvars": - // set defaults for GODEBUG cgocheck and - // invalidptr, remove code that reads in - // GODEBUG - x.Body = parsedebugvarsStmts + // keep defaults for GODEBUG cgocheck and invalidptr, + // remove code that reads GODEBUG via gogetenv + for i, stmt := range x.Body.List { + if usesEnv(stmt) { + x.Body.List = x.Body.List[:i] + break filenames + } + } + panic("did not see any gogetenv call in parsedebugvars") case "setTraceback": // tracebacks are completely hidden, no // sense keeping this function @@ -127,7 +145,7 @@ func stripRuntime(filename string, file *ast.File) { // generic solution like x/tools/imports. for _, imp := range file.Imports { if imp.Path.Value == `"internal/bytealg"` { - file.Decls = append(file.Decls, markUsedBytealg) + imp.Name = &ast.Ident{Name: "_"} break } } @@ -154,17 +172,6 @@ func removeImport(importPath string, specs []ast.Spec) []ast.Spec { return specs } -var markUsedBytealg = &ast.GenDecl{ - Tok: token.VAR, - Specs: []ast.Spec{&ast.ValueSpec{ - Names: []*ast.Ident{{Name: "_"}}, - Values: []ast.Expr{&ast.SelectorExpr{ - X: &ast.Ident{Name: "bytealg"}, - Sel: &ast.Ident{Name: "IndexByteString"}, - }}, - }}, -} - var hidePrintDecl = &ast.FuncDecl{ Name: ast.NewIdent("hidePrint"), Type: &ast.FuncType{Params: &ast.FieldList{ @@ -177,22 +184,3 @@ var hidePrintDecl = &ast.FuncDecl{ }}, Body: &ast.BlockStmt{}, } - -var parsedebugvarsStmts = ah.BlockStmt( - &ast.AssignStmt{ - Lhs: []ast.Expr{&ast.SelectorExpr{ - X: ast.NewIdent("debug"), - Sel: ast.NewIdent("cgocheck"), - }}, - Tok: token.ASSIGN, - Rhs: []ast.Expr{ah.IntLit(1)}, - }, - &ast.AssignStmt{ - Lhs: []ast.Expr{&ast.SelectorExpr{ - X: ast.NewIdent("debug"), - Sel: ast.NewIdent("invalidptr"), - }}, - Tok: token.ASSIGN, - Rhs: []ast.Expr{ah.IntLit(1)}, - }, -)