diff --git a/internal/literals/literals.go b/internal/literals/literals.go index fc6c9b9..6c13622 100644 --- a/internal/literals/literals.go +++ b/internal/literals/literals.go @@ -54,15 +54,20 @@ func Obfuscate(files []*ast.File, info *types.Info, fset *token.FileSet, blackli for _, name := range spec.Names { obj := info.ObjectOf(name) - // The object itself is blacklisted, e.g. a value that needs to be constant - if _, ok := blacklist[obj]; ok { + basic, ok := obj.Type().(*types.Basic) + if !ok { + // skip the block if it contains non basic types + return false + } + + if basic.Info()&types.IsUntyped != 0 { + // skip the block if it contains untyped constants return false } - } - for _, val := range spec.Values { - if _, ok := val.(*ast.BasicLit); !ok { - return false // skip the block if it contains non basic literals + // The object itself is blacklisted, e.g. a value that needs to be constant + if _, ok := blacklist[obj]; ok { + return false } } } @@ -79,35 +84,53 @@ func Obfuscate(files []*ast.File, info *types.Info, fset *token.FileSet, blackli case *ast.CompositeLit: byteType := types.Universe.Lookup("byte").Type() - switch x := info.TypeOf(x.Type).(type) { + switch y := info.TypeOf(x.Type).(type) { case *types.Array: - if x.Elem() != byteType { + if y.Elem() != byteType { return true } - case *types.Slice: - if x.Elem() != byteType { - return true + + data := make([]byte, y.Len()) + + for i, el := range x.Elts { + lit, ok := el.(*ast.BasicLit) + if !ok { + return true + } + + value, err := strconv.Atoi(lit.Value) + if err != nil { + return true + } + + data[i] = byte(value) } - default: - return true - } + cursor.Replace(obfuscateByteArray(data, y.Len())) - var data []byte - for _, el := range x.Elts { - lit, ok := el.(*ast.BasicLit) - if !ok { + case *types.Slice: + if y.Elem() != byteType { return true } - value, err := strconv.Atoi(lit.Value) - if err != nil { - return true + var data []byte + + for _, el := range x.Elts { + lit, ok := el.(*ast.BasicLit) + if !ok { + return true + } + + value, err := strconv.Atoi(lit.Value) + if err != nil { + return true + } + + data = append(data, byte(value)) } + cursor.Replace(obfuscateByteSlice(data)) - data = append(data, byte(value)) } - cursor.Replace(obfuscateBytes(data)) case *ast.BasicLit: switch cursor.Name() { case "Values", "Rhs", "Value", "Args", "X", "Y", "Results": @@ -152,7 +175,7 @@ func obfuscateString(data string) *ast.CallExpr { return callExpr(&ast.Ident{Name: "string"}, block) } -func obfuscateBytes(data []byte) *ast.CallExpr { +func obfuscateByteSlice(data []byte) *ast.CallExpr { obfuscator := randObfuscator() block := obfuscator.obfuscate(data) block.List = append(block.List, &ast.ReturnStmt{ @@ -161,6 +184,56 @@ func obfuscateBytes(data []byte) *ast.CallExpr { return callExpr(&ast.ArrayType{Elt: &ast.Ident{Name: "byte"}}, block) } +func obfuscateByteArray(data []byte, length int64) *ast.CallExpr { + obfuscator := randObfuscator() + block := obfuscator.obfuscate(data) + + arrayType := &ast.ArrayType{ + Len: &ast.BasicLit{ + Kind: token.INT, + Value: strconv.Itoa(int(length)), + }, + Elt: &ast.Ident{Name: "byte"}, + } + + sliceToArray := []ast.Stmt{ + &ast.DeclStmt{ + Decl: &ast.GenDecl{ + Tok: token.VAR, + Specs: []ast.Spec{&ast.ValueSpec{ + Names: []*ast.Ident{{Name: "newdata"}}, + Type: arrayType, + }}, + }, + }, + &ast.RangeStmt{ + Key: &ast.Ident{Name: "i"}, + Tok: token.DEFINE, + X: &ast.Ident{Name: "newdata"}, + Body: &ast.BlockStmt{List: []ast.Stmt{ + &ast.AssignStmt{ + Lhs: []ast.Expr{&ast.IndexExpr{ + X: &ast.Ident{Name: "newdata"}, + Index: &ast.Ident{Name: "i"}, + }}, + Tok: token.ASSIGN, + Rhs: []ast.Expr{&ast.IndexExpr{ + X: &ast.Ident{Name: "data"}, + Index: &ast.Ident{Name: "i"}, + }}, + }, + }}, + }, + &ast.ReturnStmt{Results: []ast.Expr{ + &ast.Ident{Name: "newdata"}, + }}, + } + + block.List = append(block.List, sliceToArray...) + + return callExpr(arrayType, block) +} + // ConstBlacklist blacklist identifieres used in constant expressions func ConstBlacklist(node ast.Node, info *types.Info, blacklist map[types.Object]struct{}) { blacklistObjects := func(node ast.Node) bool { diff --git a/testdata/scripts/literals.txt b/testdata/scripts/literals.txt index 8d93eee..3b4d2a7 100644 --- a/testdata/scripts/literals.txt +++ b/testdata/scripts/literals.txt @@ -7,7 +7,7 @@ cmp stderr main.stderr cmp stderr normal.stderr ! binsubstr main$exe 'garbleDecrypt' 'Lorem' 'ipsum' 'dolor' 'first assign' 'second assign' 'First Line' 'Second Line' 'map value' 'to obfuscate' 'also obfuscate' 'stringTypeField String' 'stringTypeStruct' -binsubstr main$exe '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' 'testMap3 value' 'testMap1 new value' 'testMap3 new value' 'stringType func param' 'stringType return' +binsubstr main$exe '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' 'testMap3 value' 'testMap1 new value' 'testMap3 new value' 'stringType func param' 'stringType return' 'skip untyped const' [short] stop # checking that the build is reproducible is slow # Also check that the binary is reproducible. @@ -28,7 +28,7 @@ type strucTest struct { const ( cnst string = "Lorem" - multiline = `First Line + multiline string = `First Line Second Line` ) @@ -70,7 +70,7 @@ func main() { field: "to obfuscate", anotherfield: "also obfuscate", } - + lambda := func() string { return "😅 😅" }() @@ -98,6 +98,9 @@ type stringTypeStruct struct { // typedTest types defined from string broke previously func typedTest() { + const skipUntypedConst = "skip untyped const" + stringTypeFunc(skipUntypedConst) + const skipTypedConst stringType = "skip typed const" // skip var skipTypedVar stringType = "skip typed var" // skip @@ -158,6 +161,7 @@ func constantTest() { const f = "foo" // skip const i = length + len(f) + println(length, i) } func byteTest() { @@ -177,6 +181,14 @@ func byteTest() { print(elm, ", ") } println() + + d := func() [4]byte { + return [4]byte{12, 13} + }() + for _, elm := range d { + print(elm, ", ") + } + println() } func stringTypeFunc(s stringType) stringType { @@ -197,6 +209,7 @@ new value another literal also skip this 1 0 1 +skip untyped const skip typed const skip typed var skip typed var assign stringTypeField String stringTypeField strType stringType lambda func return @@ -204,6 +217,8 @@ stringType func param stringType return foo foo +3 6 12, 13, 12, 13, 12, 13, +12, 13, 0, 0,