don't obfuscate some literals which might break typechecking

pull/41/head
lu4p 4 years ago committed by GitHub
parent 4c64b13506
commit 234174b418
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -371,10 +371,6 @@ func transformCompile(args []string) ([]string, error) {
mathrand.Seed(int64(binary.BigEndian.Uint64([]byte(buildInfo.buildID))))
}
if envGarbleLiterals {
files = obfuscateLiterals(files)
}
info := &types.Info{
Defs: make(map[*ast.Ident]types.Object),
Uses: make(map[*ast.Ident]types.Object),
@ -384,6 +380,15 @@ func transformCompile(args []string) ([]string, error) {
return nil, fmt.Errorf("typecheck error: %v", err)
}
if envGarbleLiterals {
files = obfuscateLiterals(files, info)
// ast changed so we need to typecheck again
pkg, err = origTypesConfig.Check(pkgPath, fset, files, info)
if err != nil {
return nil, fmt.Errorf("typecheck error: %v", err)
}
}
tempDir, err := ioutil.TempDir("", "garble-build")
if err != nil {
return nil, err

@ -5,30 +5,107 @@ import (
"fmt"
"go/ast"
"go/token"
"go/types"
"strconv"
"strings"
"golang.org/x/tools/go/ast/astutil"
)
func obfuscateLiterals(files []*ast.File) []*ast.File {
pre := func(cursor *astutil.Cursor) bool {
decl, ok := cursor.Node().(*ast.GenDecl)
if !ok || decl.Tok != token.CONST {
return true
func isTypeDefStr(typ types.Type) bool {
strType := types.Typ[types.String]
if named, ok := typ.(*types.Named); ok {
return types.Identical(named.Underlying(), strType)
}
return false
}
func containsTypeDefStr(expr ast.Expr, info *types.Info) bool {
typ := info.TypeOf(expr)
//log.Println(expr, typ, reflect.TypeOf(expr), reflect.TypeOf(typ))
if sig, ok := typ.(*types.Signature); ok {
for i := 0; i < sig.Params().Len(); i++ {
if isTypeDefStr(sig.Params().At(i).Type()) {
return true
}
}
}
for _, spec := range decl.Specs {
for _, val := range spec.(*ast.ValueSpec).Values {
if v, ok := val.(*ast.BasicLit); !ok || v.Kind != token.STRING {
return false // skip the block if it contains non basic literals
if mapT, ok := typ.(*types.Map); ok {
return isTypeDefStr(mapT.Elem()) || isTypeDefStr(mapT.Key())
}
if named, ok := typ.(*types.Named); ok {
return isTypeDefStr(named)
}
return false
}
func obfuscateLiterals(files []*ast.File, info *types.Info) []*ast.File {
pre := func(cursor *astutil.Cursor) bool {
switch x := cursor.Node().(type) {
case *ast.ValueSpec:
return !containsTypeDefStr(x.Type, info)
case *ast.AssignStmt:
for _, expr := range x.Lhs {
if index, ok := expr.(*ast.IndexExpr); ok {
return !containsTypeDefStr(index.X, info)
}
if ident, ok := expr.(*ast.Ident); ok {
return !containsTypeDefStr(ident, info)
}
}
}
case *ast.CallExpr:
return !containsTypeDefStr(x.Fun, info)
case *ast.CompositeLit:
if t, ok := x.Type.(*ast.MapType); ok {
return !(containsTypeDefStr(t.Key, info) || containsTypeDefStr(t.Value, info))
}
case *ast.FuncDecl:
if x.Type.Results == nil {
return true
}
for _, result := range x.Type.Results.List {
for _, name := range result.Names {
return !containsTypeDefStr(name, info)
}
}
case *ast.KeyValueExpr:
if ident, ok := x.Key.(*ast.Ident); ok {
return !containsTypeDefStr(ident, info)
}
case *ast.GenDecl:
if x.Tok != token.CONST {
return true
}
for _, spec := range x.Specs {
spec, ok := spec.(*ast.ValueSpec)
if !ok {
return false
}
for _, val := range spec.Values {
if v, ok := val.(*ast.BasicLit); !ok || v.Kind != token.STRING {
return false // skip the block if it contains non basic literals
}
}
// constants are not possible if we want to obfuscate literals, therefore
// move all constant blocks which only contain strings to variables
decl.Tok = token.VAR
}
x.Tok = token.VAR
// constants are not possible if we want to obfuscate literals, therefore
// move all constant blocks which only contain strings to variables
}
return true
}

@ -2,10 +2,9 @@
garble -literals build
exec ./main$exe
cmp stderr main.stderr
! binsubstr main$exe 'Lorem' 'ipsum' 'dolor' 'first assign' 'second assign' 'First Line' 'Second Line' 'map value' 'to obfuscate' 'also obfuscate'
binsubstr main$exe 'Skip this block,' 'also skip this'
! binsubstr main$exe 'Lorem' 'ipsum' 'dolor' 'first assign' 'second assign' 'First Line' 'Second Line' 'map value' 'to obfuscate' 'also obfuscate' 'stringTypeField String' 'stringTypeStruct'
binsubstr main$exe 'Skip this block,' 'also skip this' 'skip typed const' 'skip typed var' 'skip typed var assign' 'stringTypeField strType' 'stringType lambda func return' 'testMap1 key' 'testMap2 key' 'testMap3 key' 'testMap1 value' 'testMap2 value' 'testMap3 value' 'testMap1 new value' 'testMap2 new value' 'testMap3 new value' 'stringType func param' 'stringType return'
[short] stop # checking that the build is reproducible is slow
# Also check that the binary is reproducible.
@ -19,14 +18,17 @@ module test/main
-- main.go --
package main
// The lack of imports is an edge case, since we have to add imports like
// crypto/aes.
type strucTest struct {
field string
anotherfield string
}
const (
cnst = "Lorem"
multiline = `First Line
cnst string = "Lorem"
multiline = `First Line
Second Line`
)
@ -58,7 +60,6 @@ func main() {
println(cnst)
println(multiline)
println(variable)
println(localVar)
println(reassign)
println(empty)
@ -68,38 +69,76 @@ func main() {
anotherfield: "also obfuscate",
}
println(x.field)
println(x.anotherfield)
println(x.field, x.anotherfield)
testMap := map[string]string{"map key": "map value"}
testMap["map key"] = "new value"
println(testMap["map key"])
println("another literal")
println(skip1, skip2)
println(i, foo, bar)
typedTest()
}
-- decl_without_imports.go --
package main
type stringType string
// The lack of imports is an edge case, since we have to add imports like
// crypto/aes.
type stringTypeStruct struct {
str string
strType stringType
}
// typedTest types defined from string broke previously
func typedTest() {
const skipTypedConst stringType = "skip typed const" // skip
var skipTypedVar stringType = "skip typed var" // skip
var variable = "ipsum"
var skipTypedVarAssign stringType
skipTypedVarAssign = "skip typed var assign" // skip
println(skipTypedConst, skipTypedVar, skipTypedVarAssign)
y := stringTypeStruct{
str: "stringTypeField String", // obfuscate
strType: "stringTypeField strType", // skip
}
println(y.str, y.strType)
z := func(s stringType) stringType {
return "stringType lambda func return" // skip
}("lambda call") // skip
println(z)
testMap1 := map[string]stringType{"testMap1 key": "testMap1 value"} // skip
testMap1["testMap1 key"] = "testMap1 new value" // skip
testMap2 := map[stringType]string{"testMap2 key": "testMap2 value"} // skip
testMap2["testMap2 key"] = "testMap2 new value" // skip
testMap3 := map[stringType]stringType{"testMap3 key": "testMap3 value"} // skip
testMap3["testMap3 key"] = "testMap3 new value" // skip
println(stringTypeFunc("stringType func param")) // skip
}
func stringTypeFunc(s stringType) stringType {
println(s)
return "stringType return" // skip
}
-- main.stderr --
Lorem
First Line
Second Line
ipsum
dolor
second assign
to obfuscate
also obfuscate
map value
to obfuscate also obfuscate
new value
another literal
Skip this block, also skip this
1 0 1
skip typed const skip typed var skip typed var assign
stringTypeField String stringTypeField strType
stringType lambda func return
stringType func param
stringType return
Loading…
Cancel
Save