func main()

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)
	}
}