@ -7,6 +7,7 @@ import (
"bytes"
"bytes"
"encoding/base64"
"encoding/base64"
"encoding/binary"
"encoding/binary"
"encoding/gob"
"encoding/json"
"encoding/json"
"flag"
"flag"
"fmt"
"fmt"
@ -629,16 +630,6 @@ func transformCompile(args []string) ([]string, error) {
// generating it.
// generating it.
flags = append ( flags , "-dwarf=false" )
flags = append ( flags , "-dwarf=false" )
if ( curPkg . ImportPath == "runtime" && opts . Tiny ) || curPkg . ImportPath == "runtime/internal/sys" {
// Even though these packages aren't private, we will still process
// them later to remove build information and strip code from the
// runtime. However, we only want flags to work on private packages.
opts . GarbleLiterals = false
opts . DebugDir = ""
} else if ! curPkg . Private {
return append ( flags , paths ... ) , nil
}
for i , path := range paths {
for i , path := range paths {
if filepath . Base ( path ) == "_gomod_.go" {
if filepath . Base ( path ) == "_gomod_.go" {
// never include module info
// never include module info
@ -647,13 +638,6 @@ func transformCompile(args []string) ([]string, error) {
}
}
}
}
flags = alterTrimpath ( flags )
newImportCfg , err := processImportCfg ( flags )
if err != nil {
return nil , err
}
var files [ ] * ast . File
var files [ ] * ast . File
for _ , path := range paths {
for _ , path := range paths {
file , err := parser . ParseFile ( fset , path , nil , parser . ParseComments )
file , err := parser . ParseFile ( fset , path , nil , parser . ParseComments )
@ -662,6 +646,35 @@ func transformCompile(args []string) ([]string, error) {
}
}
files = append ( files , file )
files = append ( files , file )
}
}
tf := newTransformer ( )
if err := tf . typecheck ( files ) ; err != nil {
return nil , err
}
flags = alterTrimpath ( flags )
newImportCfg , err := processImportCfg ( flags )
if err != nil {
return nil , err
}
if err = loadKnownReflectAPIs ( curPkg ) ; err != nil {
return nil , err
}
tf . findReflectFunctions ( files )
if err := saveKnownReflectAPIs ( curPkg ) ; err != nil {
return nil , err
}
if ( curPkg . ImportPath == "runtime" && opts . Tiny ) || curPkg . ImportPath == "runtime/internal/sys" {
// Even though these packages aren't private, we will still process
// them later to remove build information and strip code from the
// runtime. However, we only want flags to work on private packages.
opts . GarbleLiterals = false
opts . DebugDir = ""
} else if ! curPkg . Private {
return append ( flags , paths ... ) , nil
}
// Literal obfuscation uses math/rand, so seed it deterministically.
// Literal obfuscation uses math/rand, so seed it deterministically.
randSeed := opts . Seed
randSeed := opts . Seed
@ -671,11 +684,6 @@ func transformCompile(args []string) ([]string, error) {
// log.Printf("seeding math/rand with %x\n", randSeed)
// log.Printf("seeding math/rand with %x\n", randSeed)
mathrand . Seed ( int64 ( binary . BigEndian . Uint64 ( randSeed ) ) )
mathrand . Seed ( int64 ( binary . BigEndian . Uint64 ( randSeed ) ) )
tf := newTransformer ( )
if err := tf . typecheck ( files ) ; err != nil {
return nil , err
}
tf . prefillIgnoreObjects ( files )
tf . prefillIgnoreObjects ( files )
// log.Println(flags)
// log.Println(flags)
@ -1015,58 +1023,132 @@ func processImportCfg(flags []string) (newImportCfg string, _ error) {
return newCfg . Name ( ) , nil
return newCfg . Name ( ) , nil
}
}
type knownReflect struct {
pkgPath string
name string
}
type funcFullName = string
type funcFullName = string
type reflectParameterPosition = int
// knownReflectAPIs is a static record of what std APIs use reflection on their
// knownReflectAPIs is a static record of what std APIs use reflection on their
// parameters, so we can avoid obfuscating types used with them.
// parameters, so we can avoid obfuscating types used with them.
//
//
// For now, this table is manually maintained, until we automatically detect and
// propagate the uses of reflect in std and third party APIs.
// If we end up maintaining this table for a long time, we should probably
// improve the process via either code generation or sanity checks.
//
// TODO: record which parameters get used with reflection, as it's often not all
// of them.
//
// TODO: we're not including fmt.Printf, as it would have many false positives,
// TODO: we're not including fmt.Printf, as it would have many false positives,
// unless we were smart enough to detect which arguments get used as %#v or %T.
// unless we were smart enough to detect which arguments get used as %#v or %T.
//
var knownReflectAPIs = map [ funcFullName ] [ ] reflectParameterPosition {
// TODO: this should also support third party APIs; see
"reflect.TypeOf" : { 0 } ,
// https://github.com/burrowers/garble/issues/162
"reflect.ValueOf" : { 0 } ,
var knownReflectAPIs = map [ funcFullName ] bool {
}
"reflect.TypeOf" : true ,
"reflect.ValueOf" : true ,
func loadKnownReflectAPIs ( currPkg * listedPackage ) error {
for path := range importCfgEntries {
"encoding/json.Marshal" : true ,
pkg , err := listPackage ( path )
"encoding/json.MarshalIndent" : true ,
if err != nil {
"encoding/json.Unmarshal" : true ,
return err
"(*encoding/json.Decoder).Decode" : true ,
}
"(*encoding/json.Encoder).Encode" : true ,
// this function literal is used for the deferred close
"encoding/gob.Register" : true ,
err = func ( ) error {
"encoding/gob.RegisterName" : true ,
filename := strings . TrimSuffix ( pkg . Export , "-d" ) + "-garble-d"
"(*encoding/gob.Decoder).Decode" : true ,
f , err := os . Open ( filename )
"(*encoding/gob.Encoder).Encode" : true ,
if err != nil {
return err
"encoding/xml.Marshal" : true ,
}
"encoding/xml.MarshalIndent" : true ,
defer f . Close ( )
"encoding/xml.Unmarshal" : true ,
"(*encoding/xml.Decoder).Decode" : true ,
// Decode appends new entries to the existing map
"(*encoding/xml.Encoder).Encode" : true ,
return gob . NewDecoder ( f ) . Decode ( & knownReflectAPIs )
"(*encoding/xml.Decoder).DecodeElement" : true ,
} ( )
"(*encoding/xml.Encoder).EncodeElement" : true ,
if err != nil {
return err
"(*text/template.Template).Execute" : true ,
}
}
"(*html/template.Template).Execute" : true ,
return nil
"net/rpc.Register" : true ,
}
"net/rpc.RegisterName" : true ,
func saveKnownReflectAPIs ( currPkg * listedPackage ) error {
filename := strings . TrimSuffix ( currPkg . Export , "-d" ) + "-garble-d"
f , err := os . Create ( filename )
if err != nil {
return err
}
defer f . Close ( )
return gob . NewEncoder ( f ) . Encode ( knownReflectAPIs )
}
func ( tf * transformer ) findReflectFunctions ( files [ ] * ast . File ) {
visitReflect := func ( node ast . Node ) {
funcDecl , ok := node . ( * ast . FuncDecl )
if ! ok {
return
}
funcObj := tf . info . ObjectOf ( funcDecl . Name ) . ( * types . Func )
var paramNames [ ] string
for _ , param := range funcDecl . Type . Params . List {
for _ , name := range param . Names {
paramNames = append ( paramNames , name . Name )
}
}
ast . Inspect ( funcDecl , func ( node ast . Node ) bool {
call , ok := node . ( * ast . CallExpr )
if ! ok {
return true
}
sel , ok := call . Fun . ( * ast . SelectorExpr )
if ! ok {
return true
}
fnType , _ := tf . info . ObjectOf ( sel . Sel ) . ( * types . Func )
if fnType == nil || fnType . Pkg ( ) == nil {
return true
}
fullName := fnType . FullName ( )
var identifiers [ ] string
for _ , argPos := range knownReflectAPIs [ fullName ] {
arg := call . Args [ argPos ]
ident , ok := arg . ( * ast . Ident )
if ! ok {
continue
}
obj := tf . info . ObjectOf ( ident )
if obj . Parent ( ) == funcObj . Scope ( ) {
identifiers = append ( identifiers , ident . Name )
}
}
if identifiers == nil {
return true
}
var argumentPosReflect [ ] int
for _ , ident := range identifiers {
for paramPos , paramName := range paramNames {
if ident == paramName {
argumentPosReflect = append ( argumentPosReflect , paramPos )
}
}
}
knownReflectAPIs [ funcObj . FullName ( ) ] = argumentPosReflect
return true
} )
}
lenPrevKnownReflectAPIs := len ( knownReflectAPIs )
for _ , file := range files {
for _ , decl := range file . Decls {
visitReflect ( decl )
}
}
// if a new reflectAPI is found we need to Re-evaluate all functions which might be using that API
if len ( knownReflectAPIs ) > lenPrevKnownReflectAPIs {
tf . findReflectFunctions ( files )
}
}
}
// prefillIgnoreObjects collects objects which should not be obfuscated,
// prefillIgnoreObjects collects objects which should not be obfuscated,
@ -1085,23 +1167,30 @@ func (tf *transformer) prefillIgnoreObjects(files []*ast.File) {
if ! ok {
if ! ok {
return true
return true
}
}
sel , ok := call . Fun . ( * ast . SelectorExpr )
ident , ok := call . Fun . ( * ast . Ident )
if ! ok {
if ! ok {
return true
sel , ok := call . Fun . ( * ast . SelectorExpr )
if ! ok {
return true
}
ident = sel . Sel
}
}
fnType , _ := tf . info . ObjectOf ( sel . Sel ) . ( * types . Func )
fnType , _ := tf . info . ObjectOf ( ident ) . ( * types . Func )
if fnType == nil || fnType . Pkg ( ) == nil {
if fnType == nil || fnType . Pkg ( ) == nil {
return true
return true
}
}
fullName := fnType . FullName ( )
fullName := fnType . FullName ( )
// log.Printf("%s: %s", fset.Position(node.Pos()), fullName)
// log.Printf("%s: %s", fset.Position(node.Pos()), fullName)
if knownReflectAPIs [ fullName ] {
for _ , argPos := range knownReflectAPIs [ fullName ] {
for _ , arg := range call . Args {
arg := call . Args [ argPos ]
argType := tf . info . TypeOf ( arg )
argType := tf . info . TypeOf ( arg )
tf . recordIgnore ( argType , tf . pkg . Path ( ) )
tf . recordIgnore ( argType , tf . pkg . Path ( ) )
}
}
}
return true
return true
}
}
for _ , file := range files {
for _ , file := range files {