From 5cbbac56f337d481622f62d6189b12b5e8e50716 Mon Sep 17 00:00:00 2001 From: lu4p Date: Wed, 29 Jul 2020 02:25:51 +0200 Subject: [PATCH] move asthelper functions to separate package (#78) --- internal/asthelper/asthelper.go | 98 ++++++++++++++++++++++++++++++++ internal/literals/asthelpers.go | 71 ----------------------- internal/literals/literals.go | 29 +++++----- internal/literals/numbers.go | 29 +++++----- internal/literals/obfuscators.go | 15 ----- internal/literals/swap.go | 68 +++++++++++----------- internal/literals/xor.go | 22 +++---- runtime_api.go | 60 +++++++++---------- 8 files changed, 201 insertions(+), 191 deletions(-) create mode 100644 internal/asthelper/asthelper.go delete mode 100644 internal/literals/asthelpers.go diff --git a/internal/asthelper/asthelper.go b/internal/asthelper/asthelper.go new file mode 100644 index 0000000..c7e4b7b --- /dev/null +++ b/internal/asthelper/asthelper.go @@ -0,0 +1,98 @@ +package asthelper + +import ( + "fmt" + "go/ast" + "go/token" + "strconv" +) + +// Ident an identifier +func Ident(name string) *ast.Ident { + return &ast.Ident{Name: name} +} + +// StringLit returns an ast.BasicLit of kind STRING +func StringLit(value string) *ast.BasicLit { + return &ast.BasicLit{ + Kind: token.STRING, + Value: fmt.Sprintf("%q", value), + } +} + +// IntLit returns an ast.BasicLit of kind INT +func IntLit(value int) *ast.BasicLit { + return &ast.BasicLit{ + Kind: token.INT, + Value: strconv.Itoa(value), + } +} + +// IndexExpr "name[index]" +func IndexExpr(name string, index ast.Expr) *ast.IndexExpr { + return &ast.IndexExpr{ + X: Ident(name), + Index: index, + } +} + +// CallExpr "fun(arg)" +func CallExpr(fun ast.Expr, args ...ast.Expr) *ast.CallExpr { + return &ast.CallExpr{ + Fun: fun, + Args: args, + } +} + +// LambdaCall "func() resultType {block}()" +func LambdaCall(resultType ast.Expr, block *ast.BlockStmt) *ast.CallExpr { + funcLit := &ast.FuncLit{ + Type: &ast.FuncType{ + Params: &ast.FieldList{}, + Results: &ast.FieldList{ + List: []*ast.Field{ + {Type: resultType}, + }, + }, + }, + Body: block, + } + return CallExpr(funcLit) +} + +// ReturnStmt "return result" +func ReturnStmt(results ...ast.Expr) *ast.ReturnStmt { + return &ast.ReturnStmt{ + Results: results, + } +} + +// BoundsCheck "_ = name[pos]" +func BoundsCheck(name string, pos int) *ast.AssignStmt { + return &ast.AssignStmt{ + Lhs: []ast.Expr{Ident("_")}, + Tok: token.ASSIGN, + Rhs: []ast.Expr{IndexExpr("data", IntLit(pos))}, + } +} + +// BlockStmt a block of multiple statments e.g. a function body +func BlockStmt(stmts ...ast.Stmt) *ast.BlockStmt { + return &ast.BlockStmt{List: stmts} +} + +// ExprStmt convert an ast.Expr to an ast.Stmt +func ExprStmt(expr ast.Expr) *ast.ExprStmt { + return &ast.ExprStmt{X: expr} +} + +// DataToByteSlice turns a byte slice like []byte{1, 2, 3} into an AST +// expression +func DataToByteSlice(data []byte) *ast.CallExpr { + return &ast.CallExpr{ + Fun: &ast.ArrayType{ + Elt: &ast.Ident{Name: "byte"}, + }, + Args: []ast.Expr{StringLit(string(data))}, + } +} diff --git a/internal/literals/asthelpers.go b/internal/literals/asthelpers.go deleted file mode 100644 index 466e859..0000000 --- a/internal/literals/asthelpers.go +++ /dev/null @@ -1,71 +0,0 @@ -package literals - -import ( - "go/ast" - "go/token" - "strconv" -) - -func ident(name string) *ast.Ident { - return &ast.Ident{Name: name} -} - -func intLiteral(value int) *ast.BasicLit { - return &ast.BasicLit{ - Kind: token.INT, - Value: strconv.Itoa(value), - } -} - -// name[index] -func indexExpr(name string, index ast.Expr) *ast.IndexExpr { - return &ast.IndexExpr{ - X: ident(name), - Index: index, - } -} - -// fun(arg) -func callExpr(fun ast.Expr, arg ast.Expr) *ast.CallExpr { - var args []ast.Expr - if arg != nil { - args = []ast.Expr{arg} - } - - return &ast.CallExpr{ - Fun: fun, - Args: args, - } -} - -// func() resultType {block}() -func lambdaCall(resultType ast.Expr, block *ast.BlockStmt) *ast.CallExpr { - funcLit := &ast.FuncLit{ - Type: &ast.FuncType{ - Params: &ast.FieldList{}, - Results: &ast.FieldList{ - List: []*ast.Field{ - {Type: resultType}, - }, - }, - }, - Body: block, - } - return callExpr(funcLit, nil) -} - -// return result -func returnStmt(results ...ast.Expr) *ast.ReturnStmt { - return &ast.ReturnStmt{ - Results: results, - } -} - -// _ = data[pos] -func boundsCheckData(pos int) *ast.AssignStmt { - return &ast.AssignStmt{ - Lhs: []ast.Expr{ident("_")}, - Tok: token.ASSIGN, - Rhs: []ast.Expr{indexExpr("data", intLiteral(pos))}, - } -} diff --git a/internal/literals/literals.go b/internal/literals/literals.go index 35edffd..1c0cd83 100644 --- a/internal/literals/literals.go +++ b/internal/literals/literals.go @@ -9,6 +9,7 @@ import ( "strconv" "golang.org/x/tools/go/ast/astutil" + ah "mvdan.cc/garble/internal/asthelper" ) var ( @@ -182,16 +183,16 @@ func obfuscateString(data string) *ast.CallExpr { obfuscator := randObfuscator() block := obfuscator.obfuscate([]byte(data)) - block.List = append(block.List, returnStmt(callExpr(ident("string"), ident("data")))) + block.List = append(block.List, ah.ReturnStmt(ah.CallExpr(ah.Ident("string"), ah.Ident("data")))) - return lambdaCall(ident("string"), block) + return ah.LambdaCall(ah.Ident("string"), block) } func obfuscateByteSlice(data []byte) *ast.CallExpr { obfuscator := randObfuscator() block := obfuscator.obfuscate(data) - block.List = append(block.List, returnStmt(ident("data"))) - return lambdaCall(&ast.ArrayType{Elt: ident("byte")}, block) + block.List = append(block.List, ah.ReturnStmt(ah.Ident("data"))) + return ah.LambdaCall(&ast.ArrayType{Elt: ah.Ident("byte")}, block) } func obfuscateByteArray(data []byte, length int64) *ast.CallExpr { @@ -199,8 +200,8 @@ func obfuscateByteArray(data []byte, length int64) *ast.CallExpr { block := obfuscator.obfuscate(data) arrayType := &ast.ArrayType{ - Len: intLiteral(int(length)), - Elt: ident("byte"), + Len: ah.IntLit(int(length)), + Elt: ah.Ident("byte"), } sliceToArray := []ast.Stmt{ @@ -208,29 +209,29 @@ func obfuscateByteArray(data []byte, length int64) *ast.CallExpr { Decl: &ast.GenDecl{ Tok: token.VAR, Specs: []ast.Spec{&ast.ValueSpec{ - Names: []*ast.Ident{ident("newdata")}, + Names: []*ast.Ident{ah.Ident("newdata")}, Type: arrayType, }}, }, }, &ast.RangeStmt{ - Key: ident("i"), + Key: ah.Ident("i"), Tok: token.DEFINE, - X: ident("newdata"), + X: ah.Ident("newdata"), Body: &ast.BlockStmt{List: []ast.Stmt{ &ast.AssignStmt{ - Lhs: []ast.Expr{indexExpr("newdata", ident("i"))}, + Lhs: []ast.Expr{ah.IndexExpr("newdata", ah.Ident("i"))}, Tok: token.ASSIGN, - Rhs: []ast.Expr{indexExpr("data", ident("i"))}, + Rhs: []ast.Expr{ah.IndexExpr("data", ah.Ident("i"))}, }, }}, }, - returnStmt(ident("newdata")), + ah.ReturnStmt(ah.Ident("newdata")), } block.List = append(block.List, sliceToArray...) - return lambdaCall(arrayType, block) + return ah.LambdaCall(arrayType, block) } func obfuscateBool(data bool) *ast.BinaryExpr { @@ -244,7 +245,7 @@ func obfuscateBool(data bool) *ast.BinaryExpr { return &ast.BinaryExpr{ X: genObfuscateInt(dataUint64, intType), Op: token.EQL, - Y: intLiteral(1), + Y: ah.IntLit(1), } } diff --git a/internal/literals/numbers.go b/internal/literals/numbers.go index 6f84697..2da35d7 100644 --- a/internal/literals/numbers.go +++ b/internal/literals/numbers.go @@ -11,6 +11,7 @@ import ( "strconv" "golang.org/x/tools/go/ast/astutil" + ah "mvdan.cc/garble/internal/asthelper" ) var intTypes = map[types.Type]reflect.Type{ @@ -123,7 +124,7 @@ func bytesToUint(bits int) ast.Expr { var expr ast.Expr for i := 0; i < bytes; i++ { if i == 0 { - expr = callExpr(ident("uint"+bitsStr), indexExpr("data", intLiteral(i))) + expr = ah.CallExpr(ah.Ident("uint"+bitsStr), ah.IndexExpr("data", ah.IntLit(i))) continue } @@ -132,9 +133,9 @@ func bytesToUint(bits int) ast.Expr { X: expr, Op: token.OR, Y: &ast.BinaryExpr{ - X: callExpr(ident("uint"+bitsStr), indexExpr("data", intLiteral(i))), + X: ah.CallExpr(ah.Ident("uint"+bitsStr), ah.IndexExpr("data", ah.IntLit(i))), Op: token.SHL, - Y: intLiteral(shiftValue), + Y: ah.IntLit(shiftValue), }, } } @@ -166,26 +167,26 @@ func genObfuscateInt(data uint64, typeInfo reflect.Type) *ast.CallExpr { block := obfuscator.obfuscate(b) convertExpr := bytesToUint(bitsize) - block.List = append(block.List, boundsCheckData(byteSize-1), returnStmt(callExpr(ident(typeInfo.Name()), convertExpr))) + block.List = append(block.List, ah.BoundsCheck("data", byteSize-1), ah.ReturnStmt(ah.CallExpr(ah.Ident(typeInfo.Name()), convertExpr))) - return lambdaCall(ident(typeInfo.Name()), block) + return ah.LambdaCall(ah.Ident(typeInfo.Name()), block) } func uintToFloat(uintExpr *ast.CallExpr, typeStr string) *ast.CallExpr { usesUnsafe = true convert := &ast.StarExpr{ - X: callExpr( + X: ah.CallExpr( &ast.ParenExpr{ - X: &ast.StarExpr{X: ident(typeStr)}, + X: &ast.StarExpr{X: ah.Ident(typeStr)}, }, - callExpr( + ah.CallExpr( &ast.SelectorExpr{ - X: ident("unsafe"), - Sel: ident("Pointer"), + X: ah.Ident("unsafe"), + Sel: ah.Ident("Pointer"), }, &ast.UnaryExpr{ Op: token.AND, - X: ident("result"), + X: ah.Ident("result"), }, ), ), @@ -193,14 +194,14 @@ func uintToFloat(uintExpr *ast.CallExpr, typeStr string) *ast.CallExpr { block := &ast.BlockStmt{List: []ast.Stmt{ &ast.AssignStmt{ - Lhs: []ast.Expr{&ast.Ident{Name: "result"}}, + Lhs: []ast.Expr{ah.Ident("result")}, Tok: token.DEFINE, Rhs: []ast.Expr{uintExpr}, }, - returnStmt(convert), + ah.ReturnStmt(convert), }} - return lambdaCall(ident(typeStr), block) + return ah.LambdaCall(ah.Ident(typeStr), block) } func genObfuscateFloat(data interface{}) *ast.CallExpr { diff --git a/internal/literals/obfuscators.go b/internal/literals/obfuscators.go index 7b3cd49..3a82587 100644 --- a/internal/literals/obfuscators.go +++ b/internal/literals/obfuscators.go @@ -4,7 +4,6 @@ import ( cryptrand "crypto/rand" "fmt" "go/ast" - "go/token" mathrand "math/rand" "os" "strings" @@ -24,20 +23,6 @@ var ( envGarbleSeed = os.Getenv("GARBLE_SEED") ) -// dataToByteSlice turns a byte slice like []byte{1, 2, 3} into an AST -// expression -func dataToByteSlice(data []byte) *ast.CallExpr { - return &ast.CallExpr{ - Fun: &ast.ArrayType{ - Elt: &ast.Ident{Name: "byte"}, - }, - Args: []ast.Expr{&ast.BasicLit{ - Kind: token.STRING, - Value: fmt.Sprintf("%q", data), - }}, - } -} - // If math/rand.Seed() is not called, the generator behaves as if seeded by rand.Seed(1), // so the generator is deterministic. diff --git a/internal/literals/swap.go b/internal/literals/swap.go index 402bd3d..44869cd 100644 --- a/internal/literals/swap.go +++ b/internal/literals/swap.go @@ -5,6 +5,8 @@ import ( "go/token" "math" mathrand "math/rand" + + ah "mvdan.cc/garble/internal/asthelper" ) type swap struct{} @@ -29,12 +31,12 @@ func positionsToSlice(data []int) *ast.CompositeLit { arr := &ast.CompositeLit{ Type: &ast.ArrayType{ Len: &ast.Ellipsis{}, // Performance optimization - Elt: ident(getIndexType(len(data))), + Elt: ah.Ident(getIndexType(len(data))), }, Elts: []ast.Expr{}, } for _, data := range data { - arr.Elts = append(arr.Elts, intLiteral(data)) + arr.Elts = append(arr.Elts, ah.IntLit(data)) } return arr } @@ -65,83 +67,83 @@ func (x swap) obfuscate(data []byte) *ast.BlockStmt { data[positions[i]], data[positions[i+1]] = data[positions[i+1]]^localKey, data[positions[i]]^localKey } - return &ast.BlockStmt{List: []ast.Stmt{ + return ah.BlockStmt( &ast.AssignStmt{ - Lhs: []ast.Expr{ident("data")}, + Lhs: []ast.Expr{ah.Ident("data")}, Tok: token.DEFINE, - Rhs: []ast.Expr{dataToByteSlice(data)}, + Rhs: []ast.Expr{ah.DataToByteSlice(data)}, }, &ast.AssignStmt{ - Lhs: []ast.Expr{ident("positions")}, + Lhs: []ast.Expr{ah.Ident("positions")}, Tok: token.DEFINE, Rhs: []ast.Expr{positionsToSlice(positions)}, }, &ast.ForStmt{ Init: &ast.AssignStmt{ - Lhs: []ast.Expr{ident("i")}, + Lhs: []ast.Expr{ah.Ident("i")}, Tok: token.DEFINE, - Rhs: []ast.Expr{intLiteral(0)}, + Rhs: []ast.Expr{ah.IntLit(0)}, }, Cond: &ast.BinaryExpr{ - X: ident("i"), + X: ah.Ident("i"), Op: token.LSS, - Y: intLiteral(len(positions)), + Y: ah.IntLit(len(positions)), }, Post: &ast.AssignStmt{ - Lhs: []ast.Expr{ident("i")}, + Lhs: []ast.Expr{ah.Ident("i")}, Tok: token.ADD_ASSIGN, - Rhs: []ast.Expr{intLiteral(2)}, + Rhs: []ast.Expr{ah.IntLit(2)}, }, - Body: &ast.BlockStmt{List: []ast.Stmt{ + Body: ah.BlockStmt( &ast.AssignStmt{ - Lhs: []ast.Expr{ident("localKey")}, + Lhs: []ast.Expr{ah.Ident("localKey")}, Tok: token.DEFINE, Rhs: []ast.Expr{&ast.BinaryExpr{ X: &ast.BinaryExpr{ - X: callExpr(ident("byte"), ident("i")), + X: ah.CallExpr(ah.Ident("byte"), ah.Ident("i")), Op: token.ADD, - Y: callExpr(ident("byte"), &ast.BinaryExpr{ - X: indexExpr("positions", ident("i")), + Y: ah.CallExpr(ah.Ident("byte"), &ast.BinaryExpr{ + X: ah.IndexExpr("positions", ah.Ident("i")), Op: token.XOR, - Y: indexExpr("positions", &ast.BinaryExpr{ - X: ident("i"), + Y: ah.IndexExpr("positions", &ast.BinaryExpr{ + X: ah.Ident("i"), Op: token.ADD, - Y: intLiteral(1), + Y: ah.IntLit(1), }), }), }, Op: token.ADD, - Y: intLiteral(int(shiftKey)), + Y: ah.IntLit(int(shiftKey)), }}, }, &ast.AssignStmt{ Lhs: []ast.Expr{ - indexExpr("data", indexExpr("positions", ident("i"))), - indexExpr("data", indexExpr("positions", &ast.BinaryExpr{ - X: ident("i"), + ah.IndexExpr("data", ah.IndexExpr("positions", ah.Ident("i"))), + ah.IndexExpr("data", ah.IndexExpr("positions", &ast.BinaryExpr{ + X: ah.Ident("i"), Op: token.ADD, - Y: intLiteral(1), + Y: ah.IntLit(1), })), }, Tok: token.ASSIGN, Rhs: []ast.Expr{ &ast.BinaryExpr{ - X: indexExpr("data", indexExpr("positions", &ast.BinaryExpr{ - X: ident("i"), + X: ah.IndexExpr("data", ah.IndexExpr("positions", &ast.BinaryExpr{ + X: ah.Ident("i"), Op: token.ADD, - Y: intLiteral(1), + Y: ah.IntLit(1), })), Op: token.XOR, - Y: ident("localKey"), + Y: ah.Ident("localKey"), }, &ast.BinaryExpr{ - X: indexExpr("data", indexExpr("positions", ident("i"))), + X: ah.IndexExpr("data", ah.IndexExpr("positions", ah.Ident("i"))), Op: token.XOR, - Y: ident("localKey"), + Y: ah.Ident("localKey"), }, }, }, - }}, + ), }, - }} + ) } diff --git a/internal/literals/xor.go b/internal/literals/xor.go index e52054c..793dd73 100644 --- a/internal/literals/xor.go +++ b/internal/literals/xor.go @@ -3,6 +3,8 @@ package literals import ( "go/ast" "go/token" + + ah "mvdan.cc/garble/internal/asthelper" ) type xor struct{} @@ -20,28 +22,28 @@ func (x xor) obfuscate(data []byte) *ast.BlockStmt { return &ast.BlockStmt{List: []ast.Stmt{ &ast.AssignStmt{ - Lhs: []ast.Expr{ident("key")}, + Lhs: []ast.Expr{ah.Ident("key")}, Tok: token.DEFINE, - Rhs: []ast.Expr{dataToByteSlice(key)}, + Rhs: []ast.Expr{ah.DataToByteSlice(key)}, }, &ast.AssignStmt{ - Lhs: []ast.Expr{ident("data")}, + Lhs: []ast.Expr{ah.Ident("data")}, Tok: token.DEFINE, - Rhs: []ast.Expr{dataToByteSlice(data)}, + Rhs: []ast.Expr{ah.DataToByteSlice(data)}, }, &ast.RangeStmt{ - Key: ident("i"), - Value: ident("b"), + Key: ah.Ident("i"), + Value: ah.Ident("b"), Tok: token.DEFINE, - X: ident("key"), + X: ah.Ident("key"), Body: &ast.BlockStmt{List: []ast.Stmt{ &ast.AssignStmt{ - Lhs: []ast.Expr{indexExpr("data", ident("i"))}, + Lhs: []ast.Expr{ah.IndexExpr("data", ah.Ident("i"))}, Tok: token.ASSIGN, Rhs: []ast.Expr{&ast.BinaryExpr{ - X: indexExpr("data", ident("i")), + X: ah.IndexExpr("data", ah.Ident("i")), Op: token.XOR, - Y: ident("b"), + Y: ah.Ident("b"), }}, }, }}, diff --git a/runtime_api.go b/runtime_api.go index 819d15c..32bea8d 100644 --- a/runtime_api.go +++ b/runtime_api.go @@ -3,6 +3,8 @@ package main import ( "go/ast" "go/token" + + ah "mvdan.cc/garble/internal/asthelper" ) // addRuntimeAPI exposes additional functions in the runtime @@ -71,50 +73,45 @@ func addRuntimeAPI(filename string, file *ast.File) { } var fatalErrorsHiddenCheckStmt = &ast.IfStmt{ - Cond: &ast.Ident{Name: "fatalErrorsHidden"}, - Body: &ast.BlockStmt{List: []ast.Stmt{ - &ast.ReturnStmt{}, - }}, + Cond: ah.Ident("fatalErrorsHidden"), + Body: ah.BlockStmt(ah.ReturnStmt()), } var hideFatalErrorsDecls = []ast.Decl{ &ast.GenDecl{ Tok: token.VAR, Specs: []ast.Spec{&ast.ValueSpec{ - Names: []*ast.Ident{{Name: "fatalErrorsHidden"}}, - Type: &ast.Ident{Name: "bool"}, + Names: []*ast.Ident{ah.Ident("fatalErrorsHidden")}, + Type: ah.Ident("bool"), }}, }, &ast.FuncDecl{ - Name: &ast.Ident{Name: "hideFatalErrors"}, + Name: ah.Ident("hideFatalErrors"), Type: &ast.FuncType{Params: &ast.FieldList{ List: []*ast.Field{{ - Names: []*ast.Ident{{Name: "hide"}}, - Type: &ast.Ident{Name: "bool"}, + Names: []*ast.Ident{ah.Ident("hide")}, + Type: ah.Ident("bool"), }}, }}, - Body: &ast.BlockStmt{List: []ast.Stmt{ + Body: ah.BlockStmt( &ast.AssignStmt{ - Lhs: []ast.Expr{&ast.Ident{Name: "fatalErrorsHidden"}}, + Lhs: []ast.Expr{ah.Ident("fatalErrorsHidden")}, Tok: token.ASSIGN, - Rhs: []ast.Expr{&ast.Ident{Name: "hide"}}, + Rhs: []ast.Expr{ah.Ident("hide")}, }, - }}, + ), }, } var printanyHexCase = &ast.CaseClause{ - List: []ast.Expr{&ast.Ident{Name: "hex"}}, + List: []ast.Expr{ah.Ident("hex")}, Body: []ast.Stmt{ - &ast.ExprStmt{X: &ast.CallExpr{ - Fun: &ast.Ident{Name: "print"}, - Args: []ast.Expr{&ast.Ident{Name: "v"}}, - }}, + ah.ExprStmt(ah.CallExpr(ah.Ident("print"), ah.Ident("v"))), }, } var panicprintDecl = &ast.FuncDecl{ - Name: &ast.Ident{Name: "panicprint"}, + Name: ah.Ident("panicprint"), Type: &ast.FuncType{Params: &ast.FieldList{ List: []*ast.Field{{ Names: []*ast.Ident{{Name: "args"}}, @@ -123,24 +120,19 @@ var panicprintDecl = &ast.FuncDecl{ }}, }}, }}, - Body: &ast.BlockStmt{List: []ast.Stmt{ + Body: ah.BlockStmt( &ast.IfStmt{ - Cond: &ast.Ident{Name: "fatalErrorsHidden"}, - Body: &ast.BlockStmt{List: []ast.Stmt{ - &ast.ReturnStmt{}, - }}, + Cond: ah.Ident("fatalErrorsHidden"), + Body: ah.BlockStmt(ah.ReturnStmt()), }, &ast.RangeStmt{ - Key: &ast.Ident{Name: "_"}, - Value: &ast.Ident{Name: "arg"}, + Key: ah.Ident("_"), + Value: ah.Ident("arg"), Tok: token.DEFINE, - X: &ast.Ident{Name: "args"}, - Body: &ast.BlockStmt{List: []ast.Stmt{ - &ast.ExprStmt{X: &ast.CallExpr{ - Fun: &ast.Ident{Name: "printany"}, - Args: []ast.Expr{&ast.Ident{Name: "arg"}}, - }}, - }}, + X: ah.Ident("args"), + Body: ah.BlockStmt( + ah.ExprStmt(ah.CallExpr(ah.Ident("printany"), ah.Ident("arg"))), + ), }, - }}, + ), }