From 13e273c9b3377b1128da3ac627b275ade2221153 Mon Sep 17 00:00:00 2001 From: Andrew LeFevre Date: Sun, 16 Aug 2020 11:25:48 -0400 Subject: [PATCH] use bitsets to detirmine what literals to obfuscate, handle errors from obfuscateNumberLiteral. Found an logic bug in numerical obfuscation by handling these errors --- internal/literals/literals.go | 74 ++++++++++++++++++----------------- internal/literals/numbers.go | 42 ++++++++++++-------- 2 files changed, 64 insertions(+), 52 deletions(-) diff --git a/internal/literals/literals.go b/internal/literals/literals.go index 97674a5..d659d55 100644 --- a/internal/literals/literals.go +++ b/internal/literals/literals.go @@ -5,15 +5,26 @@ import ( "go/ast" "go/token" "go/types" + "math" mathrand "math/rand" "strconv" "strings" - _ "unsafe" "golang.org/x/tools/go/ast/astutil" ah "mvdan.cc/garble/internal/asthelper" ) +type obfuscateLiterals uint16 + +const ( + obfuscateStrings obfuscateLiterals = 1 << iota + obfuscateBytes + obfuscateIntegers + obfuscateFloats + obfuscateBooleans + obfuscateAll obfuscateLiterals = math.MaxUint16 +) + var ( usesUnsafe bool universalTrue = types.Universe.Lookup("true") @@ -27,27 +38,25 @@ func randObfuscator() obfuscator { // Obfuscate replace literals with obfuscated lambda functions func Obfuscate(files []*ast.File, info *types.Info, fset *token.FileSet, garbleLiterals string, blacklist map[types.Object]struct{}) []*ast.File { - var obfStrings, obfBytes, obfInts, obfFloats, obfBools bool + var obfLits obfuscateLiterals litOptions := strings.Split(garbleLiterals, ",") - for _, op := range litOptions { - switch op { - case "all": - obfStrings = true - obfBytes = true - obfInts = true - obfFloats = true - obfBools = true + for _, opt := range litOptions { + if opt == "all" { + obfLits = obfuscateAll break + } + + switch opt { case "strings": - obfStrings = true + obfLits |= obfuscateStrings case "bytes": - obfBytes = true + obfLits |= obfuscateBytes case "ints": - obfInts = true + obfLits |= obfuscateIntegers case "floats": - obfFloats = true + obfLits |= obfuscateFloats case "bools": - obfBools = true + obfLits |= obfuscateBooleans } } @@ -121,7 +130,7 @@ func Obfuscate(files []*ast.File, info *types.Info, fset *token.FileSet, garbleL data[i] = byte(value) } - if obfBytes { + if obfLits&obfuscateBytes != 0 { cursor.Replace(obfuscateByteArray(data, y.Len())) } case *types.Slice: @@ -144,7 +153,7 @@ func Obfuscate(files []*ast.File, info *types.Info, fset *token.FileSet, garbleL data = append(data, byte(value)) } - if obfBytes { + if obfLits&obfuscateBytes != 0 { cursor.Replace(obfuscateByteSlice(data)) } } @@ -156,16 +165,15 @@ func Obfuscate(files []*ast.File, info *types.Info, fset *token.FileSet, garbleL } switch x.Kind { - case token.INT: - if obfInts { - obfuscateNumberLiteral(cursor, info) - } - case token.FLOAT: - if obfFloats { - obfuscateNumberLiteral(cursor, info) + case token.INT, token.FLOAT: + cont, err := obfuscateNumberLiteral(cursor, info, obfLits) + if err != nil { + panic(fmt.Sprintf("obfuscating numerical literal failed: %v", err)) } + + return cont case token.STRING: - if !obfStrings { + if obfLits&obfuscateStrings == 0 { return true } @@ -191,17 +199,19 @@ func Obfuscate(files []*ast.File, info *types.Info, fset *token.FileSet, garbleL return true // we don't want to obfuscate imports etc. } - typeInfo := info.TypeOf(x) - if (isInteger(typeInfo) && obfInts) || (isFloat(typeInfo) && obfFloats) { - obfuscateNumberLiteral(cursor, info) + cont, err := obfuscateNumberLiteral(cursor, info, obfLits) + if err != nil { + panic(fmt.Sprintf("obfuscating numerical literal failed: %v", err)) } + + return cont case *ast.Ident: obj := info.ObjectOf(x) if obj == nil { return true } - if obfBools && (obj == universalTrue || obj == universalFalse) { + if obfLits&obfuscateBooleans != 0 && (obj == universalTrue || obj == universalFalse) { cursor.Replace(obfuscateBool(x.Name == "true")) } } @@ -219,12 +229,6 @@ func Obfuscate(files []*ast.File, info *types.Info, fset *token.FileSet, garbleL return files } -//go:linkname isInteger go/types.isInteger -func isInteger(typ types.Type) bool - -//go:linkname isFloat go/types.isFloat -func isFloat(typ types.Type) bool - func obfuscateString(data string) *ast.CallExpr { obfuscator := randObfuscator() block := obfuscator.obfuscate([]byte(data)) diff --git a/internal/literals/numbers.go b/internal/literals/numbers.go index 66517b5..6a97fdf 100644 --- a/internal/literals/numbers.go +++ b/internal/literals/numbers.go @@ -29,7 +29,7 @@ var intTypes = map[types.Type]reflect.Type{ types.Typ[types.Uintptr]: reflect.TypeOf(uintptr(0)), } -func obfuscateNumberLiteral(cursor *astutil.Cursor, info *types.Info) error { +func obfuscateNumberLiteral(cursor *astutil.Cursor, info *types.Info, obfLits obfuscateLiterals) (bool, error) { var ( call *ast.CallExpr basic *ast.BasicLit @@ -44,12 +44,12 @@ func obfuscateNumberLiteral(cursor *astutil.Cursor, info *types.Info) error { case *ast.UnaryExpr: basic, ok = x.X.(*ast.BasicLit) if !ok { - return errors.New("UnaryExpr doesn't contain basic literal") + return false, errors.New("UnaryExpr doesn't contain basic literal") } typeInfo = info.TypeOf(x) if x.Op != token.SUB { - return errors.New("UnaryExpr has a non SUB token") + return false, errors.New("UnaryExpr has a non SUB token") } sign = "-" @@ -71,50 +71,58 @@ func obfuscateNumberLiteral(cursor *astutil.Cursor, info *types.Info) error { // this guards against the case where the ast.BasicLit is inside an ast.UnaryExpr // and the BasicLit gets evaluated before the UnaryExpr if _, ok := cursor.Parent().(*ast.UnaryExpr); ok { - return nil + return true, nil } } default: - return errors.New("wrong node Type") + return false, errors.New("wrong node Type") } strValue := sign + basic.Value switch typeInfo { case types.Typ[types.Float32]: - fV, err := strconv.ParseFloat(strValue, 32) - if err != nil { - return err + if obfLits&obfuscateFloats != 0 { + fV, err := strconv.ParseFloat(strValue, 32) + if err != nil { + return false, err + } + call = genObfuscateFloat(float32(fV)) } - call = genObfuscateFloat(float32(fV)) case types.Typ[types.Float64], types.Typ[types.UntypedFloat]: - fV, err := strconv.ParseFloat(strValue, 64) - if err != nil { - return err + if obfLits&obfuscateFloats != 0 { + fV, err := strconv.ParseFloat(strValue, 64) + if err != nil { + return false, err + } + call = genObfuscateFloat(fV) } - call = genObfuscateFloat(fV) } if call != nil { cursor.Replace(call) - return nil + return true, nil + } + + if obfLits&obfuscateIntegers == 0 { + return false, nil } intValue, err := strconv.ParseInt(strValue, 0, 64) if err != nil { - return err + return false, err } intType, ok := intTypes[typeInfo] if !ok { - return errors.New("wrong type") + return false, errors.New("wrong type") } call = genObfuscateInt(uint64(intValue), intType) cursor.Replace(call) - return nil + return true, nil } func bytesToUint(bits int) ast.Expr {