macho.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. // Copyright 2018 Google Inc. All rights reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package symbol_inject
  15. import (
  16. "debug/macho"
  17. "encoding/binary"
  18. "fmt"
  19. "io"
  20. "os"
  21. "os/exec"
  22. "path/filepath"
  23. "sort"
  24. "strings"
  25. )
  26. func machoSymbolsFromFile(r io.ReaderAt) (*File, error) {
  27. machoFile, err := macho.NewFile(r)
  28. if err != nil {
  29. return nil, cantParseError{err}
  30. }
  31. return extractMachoSymbols(machoFile)
  32. }
  33. func extractMachoSymbols(machoFile *macho.File) (*File, error) {
  34. symbols := machoFile.Symtab.Syms
  35. sort.SliceStable(symbols, func(i, j int) bool {
  36. if symbols[i].Sect != symbols[j].Sect {
  37. return symbols[i].Sect < symbols[j].Sect
  38. }
  39. return symbols[i].Value < symbols[j].Value
  40. })
  41. file := &File{IsMachoFile: true}
  42. for _, section := range machoFile.Sections {
  43. file.Sections = append(file.Sections, &Section{
  44. Name: section.Name,
  45. Addr: section.Addr,
  46. Offset: uint64(section.Offset),
  47. Size: section.Size,
  48. })
  49. }
  50. for _, symbol := range symbols {
  51. if symbol.Sect > 0 {
  52. section := file.Sections[symbol.Sect-1]
  53. file.Symbols = append(file.Symbols, &Symbol{
  54. // symbols in macho files seem to be prefixed with an underscore
  55. Name: strings.TrimPrefix(symbol.Name, "_"),
  56. // MachO symbol value is virtual address of the symbol, convert it to offset into the section.
  57. Addr: symbol.Value - section.Addr,
  58. // MachO symbols don't have size information.
  59. Size: 0,
  60. Section: section,
  61. })
  62. }
  63. }
  64. return file, nil
  65. }
  66. func dumpMachoSymbols(r io.ReaderAt) error {
  67. machoFile, err := macho.NewFile(r)
  68. if err != nil {
  69. return cantParseError{err}
  70. }
  71. fmt.Println("&macho.File{")
  72. fmt.Println("\tSections: []*macho.Section{")
  73. for _, section := range machoFile.Sections {
  74. fmt.Printf("\t\t&macho.Section{SectionHeader: %#v},\n", section.SectionHeader)
  75. }
  76. fmt.Println("\t},")
  77. fmt.Println("\tSymtab: &macho.Symtab{")
  78. fmt.Println("\t\tSyms: []macho.Symbol{")
  79. for _, symbol := range machoFile.Symtab.Syms {
  80. fmt.Printf("\t\t\t%#v,\n", symbol)
  81. }
  82. fmt.Println("\t\t},")
  83. fmt.Println("\t},")
  84. fmt.Println("}")
  85. return nil
  86. }
  87. func CodeSignMachoFile(path string) error {
  88. filename := filepath.Base(path)
  89. cmd := exec.Command("/usr/bin/codesign", "--force", "-s", "-", "-i", filename, path)
  90. if err := cmd.Run(); err != nil {
  91. return err
  92. }
  93. return modifyCodeSignFlags(path)
  94. }
  95. const LC_CODE_SIGNATURE = 0x1d
  96. const CSSLOT_CODEDIRECTORY = 0
  97. // To make codesign not invalidated by stripping, modify codesign flags to 0x20002
  98. // (adhoc | linkerSigned).
  99. func modifyCodeSignFlags(path string) error {
  100. f, err := os.OpenFile(path, os.O_RDWR, 0)
  101. if err != nil {
  102. return err
  103. }
  104. defer f.Close()
  105. // Step 1: find code signature section.
  106. machoFile, err := macho.NewFile(f)
  107. if err != nil {
  108. return err
  109. }
  110. var codeSignSectionOffset uint32 = 0
  111. var codeSignSectionSize uint32 = 0
  112. for _, l := range machoFile.Loads {
  113. data := l.Raw()
  114. cmd := machoFile.ByteOrder.Uint32(data)
  115. if cmd == LC_CODE_SIGNATURE {
  116. codeSignSectionOffset = machoFile.ByteOrder.Uint32(data[8:])
  117. codeSignSectionSize = machoFile.ByteOrder.Uint32(data[12:])
  118. }
  119. }
  120. if codeSignSectionOffset == 0 {
  121. return fmt.Errorf("code signature section not found")
  122. }
  123. data := make([]byte, codeSignSectionSize)
  124. _, err = f.ReadAt(data, int64(codeSignSectionOffset))
  125. if err != nil {
  126. return err
  127. }
  128. // Step 2: get flags offset.
  129. blobCount := binary.BigEndian.Uint32(data[8:])
  130. off := 12
  131. var codeDirectoryOff uint32 = 0
  132. for blobCount > 0 {
  133. blobType := binary.BigEndian.Uint32(data[off:])
  134. if blobType == CSSLOT_CODEDIRECTORY {
  135. codeDirectoryOff = binary.BigEndian.Uint32(data[off+4:])
  136. break
  137. }
  138. blobCount--
  139. off += 8
  140. }
  141. if codeDirectoryOff == 0 {
  142. return fmt.Errorf("no code directory in code signature section")
  143. }
  144. flagsOff := codeSignSectionOffset + codeDirectoryOff + 12
  145. // Step 3: modify flags.
  146. flagsData := make([]byte, 4)
  147. _, err = f.ReadAt(flagsData, int64(flagsOff))
  148. if err != nil {
  149. return err
  150. }
  151. oldFlags := binary.BigEndian.Uint32(flagsData)
  152. if oldFlags != 0x2 {
  153. return fmt.Errorf("unexpected flags in code signature section: 0x%x", oldFlags)
  154. }
  155. binary.BigEndian.PutUint32(flagsData, 0x20002)
  156. _, err = f.WriteAt(flagsData, int64(flagsOff))
  157. return err
  158. }