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