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.
pull/371/head
Daniel Martí 4 years ago committed by lu4p
parent 691a44cecb
commit 5f8bae06b7

@ -583,6 +583,11 @@ func transformAsm(args []string) ([]string, error) {
buf.WriteString(newName) 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) name := filepath.Base(path)
if path, err := writeTemp(name, buf.Bytes()); err != nil { if path, err := writeTemp(name, buf.Bytes()); err != nil {
return nil, err return nil, err
@ -681,13 +686,11 @@ func transformCompile(args []string) ([]string, error) {
for i, file := range files { for i, file := range files {
name := filepath.Base(paths[i]) name := filepath.Base(paths[i])
// TODO(mvdan): allow running handleDirectives and transformGo switch curPkg.ImportPath {
// on runtime too, by splitting the conditionals here. case "runtime":
switch {
case curPkg.ImportPath == "runtime":
// strip unneeded runtime code // strip unneeded runtime code
stripRuntime(name, file) stripRuntime(name, file)
case curPkg.ImportPath == "runtime/internal/sys": case "runtime/internal/sys":
// The first declaration in zversion.go contains the Go // The first declaration in zversion.go contains the Go
// version as follows. Replace it here, since the // version as follows. Replace it here, since the
// linker's -X does not work with constants. // 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 := spec.Values[0].(*ast.BasicLit)
lit.Value = "`unknown`" 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. // Don't obfuscate cgo code, since it's generated and it gets messy.
default: } else {
tf.handleDirectives(file.Comments) tf.handleDirectives(file.Comments)
file = tf.transformGo(file) file = tf.transformGo(file)
} }

@ -75,12 +75,30 @@ func stripRuntime(filename string, file *ast.File) {
break break
} }
case "runtime1.go": 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 { switch x.Name.Name {
case "parsedebugvars": case "parsedebugvars":
// set defaults for GODEBUG cgocheck and // keep defaults for GODEBUG cgocheck and invalidptr,
// invalidptr, remove code that reads in // remove code that reads GODEBUG via gogetenv
// GODEBUG for i, stmt := range x.Body.List {
x.Body = parsedebugvarsStmts if usesEnv(stmt) {
x.Body.List = x.Body.List[:i]
break filenames
}
}
panic("did not see any gogetenv call in parsedebugvars")
case "setTraceback": case "setTraceback":
// tracebacks are completely hidden, no // tracebacks are completely hidden, no
// sense keeping this function // sense keeping this function
@ -127,7 +145,7 @@ func stripRuntime(filename string, file *ast.File) {
// generic solution like x/tools/imports. // generic solution like x/tools/imports.
for _, imp := range file.Imports { for _, imp := range file.Imports {
if imp.Path.Value == `"internal/bytealg"` { if imp.Path.Value == `"internal/bytealg"` {
file.Decls = append(file.Decls, markUsedBytealg) imp.Name = &ast.Ident{Name: "_"}
break break
} }
} }
@ -154,17 +172,6 @@ func removeImport(importPath string, specs []ast.Spec) []ast.Spec {
return specs 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{ var hidePrintDecl = &ast.FuncDecl{
Name: ast.NewIdent("hidePrint"), Name: ast.NewIdent("hidePrint"),
Type: &ast.FuncType{Params: &ast.FieldList{ Type: &ast.FuncType{Params: &ast.FieldList{
@ -177,22 +184,3 @@ var hidePrintDecl = &ast.FuncDecl{
}}, }},
Body: &ast.BlockStmt{}, 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)},
},
)

Loading…
Cancel
Save