@ -584,6 +584,8 @@ var transformFuncs = map[string]func([]string) ([]string, error){
"link" : transformLink ,
"link" : transformLink ,
}
}
var rxIncludeHeader = regexp . MustCompile ( ` #include\s+"([^"]+)" ` )
func transformAsm ( args [ ] string ) ( [ ] string , error ) {
func transformAsm ( args [ ] string ) ( [ ] string , error ) {
if ! curPkg . ToObfuscate {
if ! curPkg . ToObfuscate {
return args , nil // we're not obfuscating this package
return args , nil // we're not obfuscating this package
@ -612,15 +614,8 @@ func transformAsm(args []string) ([]string, error) {
return append ( flags , newPaths ... ) , nil
return append ( flags , newPaths ... ) , nil
}
}
// We need to replace all function references with their obfuscated name
const missingHeader = "missing header path"
// counterparts.
newHeaderPaths := make ( map [ string ] string )
// Luckily, all func names in Go assembly files are immediately followed
// by the unicode "middle dot", like:
//
// TEXT ·privateAdd(SB),$0-24
const middleDot = '·'
middleDotLen := utf8 . RuneLen ( middleDot )
var buf bytes . Buffer
var buf bytes . Buffer
for _ , path := range paths {
for _ , path := range paths {
// Read the entire file into memory.
// Read the entire file into memory.
@ -629,10 +624,79 @@ func transformAsm(args []string) ([]string, error) {
if err != nil {
if err != nil {
return nil , err
return nil , err
}
}
offset := 0
for _ , match := range rxIncludeHeader . FindAllSubmatchIndex ( content , - 1 ) {
start , end := offset + match [ 2 ] , offset + match [ 3 ]
path := string ( content [ start : end ] )
if strings . ContainsAny ( path , "\n\"" ) {
// If we failed to keep track of offsets, we could see a header
// path that contains quotes or newlines, which should not happen.
return nil , fmt . Errorf ( "bad offset tracking? %q" , path )
}
newPath := newHeaderPaths [ path ]
switch newPath {
case missingHeader : // no need to try again
continue
case "" : // first time we see this header
buf . Reset ( )
buf . Reset ( )
content , err := os . ReadFile ( path )
if errors . Is ( err , fs . ErrNotExist ) {
newHeaderPaths [ path ] = missingHeader
continue // a header file provided by Go or the system
} else if err != nil {
return nil , err
}
replaceAsmNames ( & buf , content )
// For now, we replace `foo.h` or `dir/foo.h` with `garbled_foo.h`.
// The different name ensures we don't use the unobfuscated file.
// This is far from perfect, but does the job for the time being.
// In the future, use a randomized name.
newPath = "garbled_" + filepath . Base ( path )
// Uncomment for some quick debugging. Do not delete.
// fmt.Fprintf(os.Stderr, "\n-- %s --\n%s", path, buf.Bytes())
if _ , err := writeTemp ( newPath , buf . Bytes ( ) ) ; err != nil {
return nil , err
}
newHeaderPaths [ path ] = newPath
}
offset += len ( newPath ) - len ( path )
// TODO: copying the bytes in a loop like this is far from optimal.
var newContent [ ] byte
newContent = append ( newContent , content [ : start ] ... )
newContent = append ( newContent , newPath ... )
newContent = append ( newContent , content [ end : ] ... )
content = newContent
}
buf . Reset ( )
replaceAsmNames ( & buf , content )
// Uncomment for some quick debugging. Do not delete.
// fmt.Fprintf(os.Stderr, "\n-- %s --\n%s", path, buf.Bytes())
name := filepath . Base ( path )
if path , err := writeTemp ( name , buf . Bytes ( ) ) ; err != nil {
return nil , err
} else {
newPaths = append ( newPaths , path )
}
}
return append ( flags , newPaths ... ) , nil
}
func replaceAsmNames ( buf * bytes . Buffer , remaining [ ] byte ) {
// We need to replace all function references with their obfuscated name
// counterparts.
// Luckily, all func names in Go assembly files are immediately followed
// by the unicode "middle dot", like:
//
// TEXT ·privateAdd(SB),$0-24
const middleDot = '·'
middleDotLen := utf8 . RuneLen ( middleDot )
// Find all middle-dot names, and replace them.
remaining := content
for {
for {
i := bytes . IndexRune ( remaining , middleDot )
i := bytes . IndexRune ( remaining , middleDot )
if i < 0 {
if i < 0 {
@ -652,7 +716,7 @@ func transformAsm(args []string) ([]string, error) {
localName := false
localName := false
if i >= 0 {
if i >= 0 {
switch remaining [ i - 1 ] {
switch remaining [ i - 1 ] {
case ' ' , '\t' , '$ ':
case ' ' , '\t' , '$ ', ',' , '( ':
localName = true
localName = true
}
}
}
}
@ -685,21 +749,6 @@ func transformAsm(args []string) ([]string, error) {
}
}
buf . WriteString ( newName )
buf . WriteString ( newName )
}
}
// Uncomment for some quick debugging. Do not delete.
// if curPkg.ToObfuscate {
// fmt.Fprintf(os.Stderr, "\n-- %s --\n%s", path, buf.Bytes())
// }
name := filepath . Base ( path )
if path , err := writeTemp ( name , buf . Bytes ( ) ) ; err != nil {
return nil , err
} else {
newPaths = append ( newPaths , path )
}
}
return append ( flags , newPaths ... ) , nil
}
}
// writeTemp is a mix between os.CreateTemp and os.WriteFile, as it writes a
// writeTemp is a mix between os.CreateTemp and os.WriteFile, as it writes a