in cherry/codesign.go [163:356]
func main() {
if len(os.Args) != 2 {
fmt.Println("usage: codesign <binary>")
os.Exit(1)
}
fname := os.Args[1]
f, err := os.OpenFile(fname, os.O_RDWR, 0)
if err != nil {
panic(err)
}
defer f.Close()
mf, err := macho.NewFile(f)
if err != nil {
panic(err)
}
if mf.Magic != macho.Magic64 {
panic("not 64-bit")
}
if mf.ByteOrder != binary.LittleEndian {
panic("not little endian")
}
// find existing LC_CODE_SIGNATURE and __LINKEDIT segment
var sigOff, sigSz, linkeditOff int
var linkeditSeg, textSeg *macho.Segment
loadOff := fileHeaderSize64
for _, l := range mf.Loads {
data := l.Raw()
cmd, sz := get32le(data), get32le(data[4:])
if cmd == LC_CODE_SIGNATURE {
sigOff = int(get32le(data[8:]))
sigSz = int(get32le(data[12:]))
}
if seg, ok := l.(*macho.Segment); ok {
switch seg.Name {
case "__LINKEDIT":
linkeditSeg = seg
linkeditOff = loadOff
case "__TEXT":
textSeg = seg
}
}
loadOff += int(sz)
}
if sigOff == 0 {
st, err := f.Stat()
if err != nil {
panic(err)
}
sigOff = int(st.Size())
sigOff = roundUp(sigOff, 16) // round up to 16 bytes ???
err = f.Truncate(int64(sigOff))
if err != nil {
panic(err)
}
}
// compute sizes
id := "a.out\000"
nhashes := (sigOff + pageSize - 1) / pageSize
idOff := int(unsafe.Sizeof(CodeDirectory{}))
hashOff := idOff + len(id)
cdirSz := hashOff + nhashes*sha256.Size
sz := int(unsafe.Sizeof(SuperBlob{})+unsafe.Sizeof(Blob{})) + cdirSz
if sigSz != 0 && sz != sigSz {
println(sz, sigSz)
panic("LC_CODE_SIGNATURE exists but with a different size. already signed?")
}
if sigSz == 0 { // LC_CODE_SIGNATURE does not exist. Add one.
csCmdSz := int(unsafe.Sizeof(linkeditDataCmd{}))
csCmd := linkeditDataCmd{
cmd: LC_CODE_SIGNATURE,
cmdsize: uint32(csCmdSz),
dataoff: uint32(sigOff),
datasize: uint32(sz),
}
if loadOff+csCmdSz > int(mf.Sections[0].Offset) {
panic("no space for adding LC_CODE_SIGNATURE")
}
out := make([]byte, csCmdSz)
csCmd.put(out)
_, err = f.WriteAt(out, int64(loadOff))
if err != nil {
panic(err)
}
// fix up header: update Ncmd and Cmdsz
var tmp [8]byte
put32le(tmp[:4], mf.FileHeader.Ncmd+1)
_, err = f.WriteAt(tmp[:4], int64(unsafe.Offsetof(mf.FileHeader.Ncmd)))
if err != nil {
panic(err)
}
put32le(tmp[:4], mf.FileHeader.Cmdsz+uint32(csCmdSz))
_, err = f.WriteAt(tmp[:4], int64(unsafe.Offsetof(mf.FileHeader.Cmdsz)))
if err != nil {
panic(err)
}
// fix up LINKEDIT segment: update Memsz and Filesz
segSz := sigOff + sz - int(linkeditSeg.Offset)
put64le(tmp[:8], uint64(roundUp(segSz, 0x4000))) // round up to physical page size
_, err = f.WriteAt(tmp[:8], int64(linkeditOff)+int64(unsafe.Offsetof(macho.Segment64{}.Memsz)))
if err != nil {
panic(err)
}
put64le(tmp[:8], uint64(segSz))
_, err = f.WriteAt(tmp[:8], int64(linkeditOff)+int64(unsafe.Offsetof(macho.Segment64{}.Filesz)))
if err != nil {
panic(err)
}
}
// emit blob headers
sb := SuperBlob{
magic: CSMAGIC_EMBEDDED_SIGNATURE,
length: uint32(sz),
count: 1,
}
blob := Blob{
typ: CSSLOT_CODEDIRECTORY,
offset: uint32(unsafe.Sizeof(SuperBlob{}) + unsafe.Sizeof(Blob{})),
}
cdir := CodeDirectory{
magic: CSMAGIC_CODEDIRECTORY,
length: uint32(sz) - uint32(unsafe.Sizeof(SuperBlob{})+unsafe.Sizeof(Blob{})),
version: 0x20400,
flags: 0x20002, // adhoc | linkerSigned
hashOffset: uint32(hashOff),
identOffset: uint32(idOff),
nCodeSlots: uint32(nhashes),
codeLimit: uint32(sigOff),
hashSize: sha256.Size,
hashType: kSecCodeSignatureHashSHA256,
pageSize: uint8(pageSizeBits),
execSegBase: textSeg.Offset,
execSegLimit: textSeg.Filesz,
}
if mf.Type == macho.TypeExec {
cdir.execSegFlags = CS_EXECSEG_MAIN_BINARY
}
out := make([]byte, sz)
outp := out
outp = sb.put(outp)
outp = blob.put(outp)
outp = cdir.put(outp)
outp = puts(outp, []byte(id))
// emit hashes
_, err = f.Seek(0, os.SEEK_SET)
if err != nil {
panic(err)
}
var buf [pageSize]byte
fileOff := 0
for fileOff < sigOff {
n, err := io.ReadFull(f, buf[:])
if err == io.EOF {
break
}
if err != nil && err != io.ErrUnexpectedEOF {
panic(err)
}
if fileOff+n > sigOff {
n = sigOff - fileOff
}
h := sha256.New()
h.Write(buf[:n])
b := h.Sum(nil)
outp = puts(outp, b[:])
fileOff += n
}
if verbose {
for i := 0; i < len(out); i += 16 {
end := i + 16
if end > len(out) {
end = len(out)
}
fmt.Printf("% x\n", out[i:end])
}
}
_, err = f.WriteAt(out, int64(sigOff))
if err != nil {
panic(err)
}
}