1
2
3
4
5 package buildid
6
7 import (
8 "bytes"
9 "cmd/internal/codesign"
10 "crypto/sha256"
11 "debug/macho"
12 "fmt"
13 "io"
14 )
15
16
17
18
19
20
21 func FindAndHash(r io.Reader, id string, bufSize int) (matches []int64, hash [32]byte, err error) {
22 if bufSize == 0 {
23 bufSize = 31 * 1024
24 }
25 if len(id) > bufSize {
26 return nil, [32]byte{}, fmt.Errorf("buildid.FindAndHash: buffer too small")
27 }
28 zeros := make([]byte, len(id))
29 idBytes := []byte(id)
30
31
32
33
34 r = excludeMachoCodeSignature(r)
35
36
37
38
39
40
41
42
43 tiny := (len(id) + 127) &^ 127
44 buf := make([]byte, tiny+bufSize)
45 h := sha256.New()
46 start := tiny
47 for offset := int64(0); ; {
48
49
50
51 n, err := io.ReadFull(r, buf[tiny:])
52 if err != io.ErrUnexpectedEOF && err != io.EOF && err != nil {
53 return nil, [32]byte{}, err
54 }
55
56
57 for {
58 i := bytes.Index(buf[start:tiny+n], idBytes)
59 if i < 0 {
60 break
61 }
62 matches = append(matches, offset+int64(start+i-tiny))
63 h.Write(buf[start : start+i])
64 h.Write(zeros)
65 start += i + len(id)
66 }
67 if n < bufSize {
68
69 h.Write(buf[start : tiny+n])
70 break
71 }
72
73
74
75
76 if start < len(buf)-tiny {
77 h.Write(buf[start : len(buf)-tiny])
78 start = len(buf) - tiny
79 }
80
81
82 copy(buf[0:], buf[bufSize:])
83 start -= bufSize
84 offset += int64(bufSize)
85 }
86 h.Sum(hash[:0])
87 return matches, hash, nil
88 }
89
90 func Rewrite(w io.WriterAt, pos []int64, id string) error {
91 b := []byte(id)
92 for _, p := range pos {
93 if _, err := w.WriteAt(b, p); err != nil {
94 return err
95 }
96 }
97
98
99 if f, cmd, ok := findMachoCodeSignature(w); ok {
100 if codesign.Size(int64(cmd.Dataoff), "a.out") == int64(cmd.Datasize) {
101
102
103
104 text := f.Segment("__TEXT")
105 cs := make([]byte, cmd.Datasize)
106 codesign.Sign(cs, w.(io.Reader), "a.out", int64(cmd.Dataoff), int64(text.Offset), int64(text.Filesz), f.Type == macho.TypeExec)
107 if _, err := w.WriteAt(cs, int64(cmd.Dataoff)); err != nil {
108 return err
109 }
110 }
111 }
112
113 return nil
114 }
115
116 func excludeMachoCodeSignature(r io.Reader) io.Reader {
117 _, cmd, ok := findMachoCodeSignature(r)
118 if !ok {
119 return r
120 }
121 return &excludedReader{r, 0, int64(cmd.Dataoff), int64(cmd.Dataoff + cmd.Datasize)}
122 }
123
124
125
126
127 type excludedReader struct {
128 r io.Reader
129 off int64
130 start, end int64
131 }
132
133 func (r *excludedReader) Read(p []byte) (int, error) {
134 n, err := r.r.Read(p)
135 if n > 0 && r.off+int64(n) > r.start && r.off < r.end {
136 cstart := r.start - r.off
137 if cstart < 0 {
138 cstart = 0
139 }
140 cend := r.end - r.off
141 if cend > int64(n) {
142 cend = int64(n)
143 }
144 zeros := make([]byte, cend-cstart)
145 copy(p[cstart:cend], zeros)
146 }
147 r.off += int64(n)
148 return n, err
149 }
150
151 func findMachoCodeSignature(r interface{}) (*macho.File, codesign.CodeSigCmd, bool) {
152 ra, ok := r.(io.ReaderAt)
153 if !ok {
154 return nil, codesign.CodeSigCmd{}, false
155 }
156 f, err := macho.NewFile(ra)
157 if err != nil {
158 return nil, codesign.CodeSigCmd{}, false
159 }
160 cmd, ok := codesign.FindCodeSigCmd(f)
161 return f, cmd, ok
162 }
163
View as plain text