-
-
Save jpap/6a32150449da137fee397f330ff6c35a to your computer and use it in GitHub Desktop.
Test program for adhoc codesign on Apple M1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright 2020 The Go Authors. All rights reserved. | |
// Use of this source code is governed by a BSD-style | |
// license that can be found in the LICENSE file. | |
// This programs does ad-hoc code signing fo Mach-O files. | |
// It tries to do what darwin linker does. | |
package main | |
import ( | |
"crypto/sha256" | |
"debug/macho" | |
"encoding/binary" | |
"fmt" | |
"io" | |
"os" | |
"unsafe" | |
) | |
const ( | |
pageSizeBits = 12 | |
pageSize = 1 << pageSizeBits | |
) | |
const LC_CODE_SIGNATURE = 0x1d | |
const fileHeaderSize64 = 8 * 4 | |
const ( | |
CSMAGIC_REQUIREMENT = 0xfade0c00 // single Requirement blob | |
CSMAGIC_REQUIREMENTS = 0xfade0c01 // Requirements vector (internal requirements) | |
CSMAGIC_CODEDIRECTORY = 0xfade0c02 // CodeDirectory blob | |
CSMAGIC_EMBEDDED_SIGNATURE = 0xfade0cc0 // embedded form of signature data | |
CSMAGIC_DETACHED_SIGNATURE = 0xfade0cc1 // multi-arch collection of embedded signatures | |
CSSLOT_CODEDIRECTORY = 0 // slot index for CodeDirectory | |
) | |
const ( | |
kSecCodeSignatureNoHash = 0 // null value | |
kSecCodeSignatureHashSHA1 = 1 // SHA-1 | |
kSecCodeSignatureHashSHA256 = 2 // SHA-256 | |
kSecCodeSignatureHashSHA256Truncated = 3 // SHA-256 truncated to first 20 bytes | |
kSecCodeSignatureHashSHA384 = 4 // SHA-384 | |
kSecCodeSignatureHashSHA512 = 5 // SHA-512 | |
) | |
type Blob struct { | |
typ uint32 // type of entry | |
offset uint32 // offset of entry | |
// data follows | |
} | |
func (b *Blob) put(out []byte) []byte { | |
out = put32be(out, b.typ) | |
out = put32be(out, b.offset) | |
return out | |
} | |
type SuperBlob struct { | |
magic uint32 // magic number | |
length uint32 // total length of SuperBlob | |
count uint32 // number of index entries following | |
// blobs []Blob | |
} | |
func (s *SuperBlob) put(out []byte) []byte { | |
out = put32be(out, s.magic) | |
out = put32be(out, s.length) | |
out = put32be(out, s.count) | |
return out | |
} | |
type CodeDirectory struct { | |
magic uint32 // magic number (CSMAGIC_CODEDIRECTORY) | |
length uint32 // total length of CodeDirectory blob | |
version uint32 // compatibility version | |
// | |
flags uint32 // setup and mode flags | |
hashOffset uint32 // offset of hash slot element at index zero | |
identOffset uint32 // offset of identifier string | |
nSpecialSlots uint32 // number of special hash slots | |
nCodeSlots uint32 // number of ordinary (code) hash slots | |
codeLimit uint32 // limit to main image signature range | |
hashSize uint8 // size of each hash in bytes | |
hashType uint8 // type of hash (cdHashType* constants) | |
_pad1 uint8 // unused (must be zero) | |
pageSize uint8 // log2(page size in bytes); 0 => infinite | |
_pad2 uint32 // unused (must be zero) | |
// data follows | |
} | |
func (c *CodeDirectory) put(out []byte) []byte { | |
out = put32be(out, c.magic) | |
out = put32be(out, c.length) | |
out = put32be(out, c.version) | |
out = put32be(out, c.flags) | |
out = put32be(out, c.hashOffset) | |
out = put32be(out, c.identOffset) | |
out = put32be(out, c.nSpecialSlots) | |
out = put32be(out, c.nCodeSlots) | |
out = put32be(out, c.codeLimit) | |
out = put8(out, c.hashSize) | |
out = put8(out, c.hashType) | |
out = put8(out, c._pad1) | |
out = put8(out, c.pageSize) | |
out = put32be(out, c._pad2) | |
return out | |
} | |
type linkeditDataCmd struct { | |
cmd uint32 | |
cmdsize uint32 // sizeof(struct linkedit_data_command) | |
dataoff uint32 // file offset of data in __LINKEDIT segment | |
datasize uint32 // file size of data in __LINKEDIT segment | |
} | |
func (l *linkeditDataCmd) put(out []byte) []byte { | |
// load command is little endian | |
out = put32le(out, l.cmd) | |
out = put32le(out, l.cmdsize) | |
out = put32le(out, l.dataoff) | |
out = put32le(out, l.datasize) | |
return out | |
} | |
func get32le(b []byte) uint32 { return binary.LittleEndian.Uint32(b) } | |
func put32le(b []byte, x uint32) []byte { binary.LittleEndian.PutUint32(b, x); return b[4:] } | |
func put32be(b []byte, x uint32) []byte { binary.BigEndian.PutUint32(b, x); return b[4:] } | |
func put64be(b []byte, x uint64) []byte { binary.BigEndian.PutUint64(b, x); return b[8:] } | |
func put64le(b []byte, x uint64) []byte { binary.LittleEndian.PutUint64(b, x); return b[8:] } | |
func put8(b []byte, x uint8) []byte { b[0] = x; return b[1:] } | |
func puts(b, s []byte) []byte { n := copy(b, s); return b[n:] } | |
const verbose = false | |
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 *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 linkeditOff == 0 { | |
if seg, ok := l.(*macho.Segment); ok && seg.Name == "__LINKEDIT" { | |
linkeditSeg = seg | |
linkeditOff = loadOff | |
} | |
} | |
loadOff += int(sz) | |
} | |
if sigOff == 0 { | |
st, err := f.Stat() | |
if err != nil { | |
panic(err) | |
} | |
sigOff = int(st.Size()) | |
sigOff = (sigOff + 15) &^ 15 // round up to 16 bytes ??? | |
err = f.Truncate(int64(sigOff)) | |
if err != nil { | |
panic(err) | |
} | |
} | |
// compute sizes | |
nhashes := (sigOff + pageSize - 1) / pageSize | |
idOff := int(unsafe.Sizeof(CodeDirectory{})) | |
hashOff := idOff + len(fname) + 1 | |
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(segSz)) | |
_, err = f.WriteAt(tmp[:8], int64(linkeditOff)+int64(unsafe.Offsetof(macho.Segment64{}.Memsz))) | |
if err != nil { | |
panic(err) | |
} | |
_, 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: 0x20001, | |
flags: 0x20002, // adhoc | linkerSigned | |
hashOffset: uint32(hashOff), | |
identOffset: uint32(idOff), | |
nCodeSlots: uint32(nhashes), | |
codeLimit: uint32(sigOff), | |
hashSize: sha256.Size, | |
hashType: kSecCodeSignatureHashSHA256, | |
pageSize: uint8(pageSizeBits), | |
} | |
out := make([]byte, sz) | |
outp := out | |
outp = sb.put(outp) | |
outp = blob.put(outp) | |
outp = cdir.put(outp) | |
outp = puts(outp, []byte(fname+"\000")) | |
// 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) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright 2020 The Go Authors. All rights reserved. | |
// Use of this source code is governed by a BSD-style | |
// license that can be found in the LICENSE file. | |
// This programs does ad-hoc code signing fo Mach-O files. | |
// It tries to do what darwin linker does. | |
package main | |
import ( | |
"crypto/sha256" | |
"debug/macho" | |
"encoding/binary" | |
"fmt" | |
"io" | |
"os" | |
"unsafe" | |
"github.com/google/uuid" | |
) | |
const ( | |
pageSizeBits = 12 | |
pageSize = 1 << pageSizeBits | |
) | |
const ( | |
LC_CODE_SIGNATURE = 0x1d | |
LC_CODE_UUID = 0x1b | |
) | |
const fileHeaderSize64 = 8 * 4 | |
const ( | |
CSMAGIC_REQUIREMENT = 0xfade0c00 // single Requirement blob | |
CSMAGIC_REQUIREMENTS = 0xfade0c01 // Requirements vector (internal requirements) | |
CSMAGIC_CODEDIRECTORY = 0xfade0c02 // CodeDirectory blob | |
CSMAGIC_EMBEDDED_SIGNATURE = 0xfade0cc0 // embedded form of signature data | |
CSMAGIC_DETACHED_SIGNATURE = 0xfade0cc1 // multi-arch collection of embedded signatures | |
CSSLOT_CODEDIRECTORY = 0 // slot index for CodeDirectory | |
) | |
const ( | |
kSecCodeSignatureNoHash = 0 // null value | |
kSecCodeSignatureHashSHA1 = 1 // SHA-1 | |
kSecCodeSignatureHashSHA256 = 2 // SHA-256 | |
kSecCodeSignatureHashSHA256Truncated = 3 // SHA-256 truncated to first 20 bytes | |
kSecCodeSignatureHashSHA384 = 4 // SHA-384 | |
kSecCodeSignatureHashSHA512 = 5 // SHA-512 | |
) | |
type Blob struct { | |
typ uint32 // type of entry | |
offset uint32 // offset of entry | |
// data follows | |
} | |
func (b *Blob) put(out []byte) []byte { | |
out = put32be(out, b.typ) | |
out = put32be(out, b.offset) | |
return out | |
} | |
type SuperBlob struct { | |
magic uint32 // magic number | |
length uint32 // total length of SuperBlob | |
count uint32 // number of index entries following | |
// blobs []Blob | |
} | |
func (s *SuperBlob) put(out []byte) []byte { | |
out = put32be(out, s.magic) | |
out = put32be(out, s.length) | |
out = put32be(out, s.count) | |
return out | |
} | |
type CodeDirectory struct { | |
magic uint32 // magic number (CSMAGIC_CODEDIRECTORY) | |
length uint32 // total length of CodeDirectory blob | |
version uint32 // compatibility version | |
// | |
flags uint32 // setup and mode flags | |
hashOffset uint32 // offset of hash slot element at index zero | |
identOffset uint32 // offset of identifier string | |
nSpecialSlots uint32 // number of special hash slots | |
nCodeSlots uint32 // number of ordinary (code) hash slots | |
codeLimit uint32 // limit to main image signature range | |
hashSize uint8 // size of each hash in bytes | |
hashType uint8 // type of hash (cdHashType* constants) | |
_pad1 uint8 // unused (must be zero) | |
pageSize uint8 // log2(page size in bytes); 0 => infinite | |
_pad2 uint32 // unused (must be zero) | |
// data follows | |
} | |
func (c *CodeDirectory) put(out []byte) []byte { | |
out = put32be(out, c.magic) | |
out = put32be(out, c.length) | |
out = put32be(out, c.version) | |
out = put32be(out, c.flags) | |
out = put32be(out, c.hashOffset) | |
out = put32be(out, c.identOffset) | |
out = put32be(out, c.nSpecialSlots) | |
out = put32be(out, c.nCodeSlots) | |
out = put32be(out, c.codeLimit) | |
out = put8(out, c.hashSize) | |
out = put8(out, c.hashType) | |
out = put8(out, c._pad1) | |
out = put8(out, c.pageSize) | |
out = put32be(out, c._pad2) | |
return out | |
} | |
type linkeditDataCmd struct { | |
cmd uint32 | |
cmdsize uint32 // sizeof(struct linkedit_data_command) | |
dataoff uint32 // file offset of data in __LINKEDIT segment | |
datasize uint32 // file size of data in __LINKEDIT segment | |
} | |
func (l *linkeditDataCmd) put(out []byte) []byte { | |
// load command is little endian | |
out = put32le(out, l.cmd) | |
out = put32le(out, l.cmdsize) | |
out = put32le(out, l.dataoff) | |
out = put32le(out, l.datasize) | |
return out | |
} | |
type uuidCmd struct { | |
cmd uint32 | |
cmdsize uint32 // sizeof(struct uuid_command) | |
uuid [16]byte // the 128-bit uuid | |
} | |
func (l *uuidCmd) Raw() []byte { | |
b := make([]byte, unsafe.Sizeof(uuidCmd{})) | |
l.put(b) | |
return b | |
} | |
func (l *uuidCmd) put(out []byte) []byte { | |
// load command is little endian | |
out = put32le(out, l.cmd) | |
out = put32le(out, l.cmdsize) | |
out = puts(out, l.uuid[:]) | |
return out | |
} | |
func get32le(b []byte) uint32 { return binary.LittleEndian.Uint32(b) } | |
func put32le(b []byte, x uint32) []byte { binary.LittleEndian.PutUint32(b, x); return b[4:] } | |
func put32be(b []byte, x uint32) []byte { binary.BigEndian.PutUint32(b, x); return b[4:] } | |
func put64be(b []byte, x uint64) []byte { binary.BigEndian.PutUint64(b, x); return b[8:] } | |
func put64le(b []byte, x uint64) []byte { binary.LittleEndian.PutUint64(b, x); return b[8:] } | |
func put8(b []byte, x uint8) []byte { b[0] = x; return b[1:] } | |
func puts(b, s []byte) []byte { n := copy(b, s); return b[n:] } | |
const verbose = false | |
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_UUID | |
hasUUID := false | |
loadOff := fileHeaderSize64 | |
for _, l := range mf.Loads { | |
data := l.Raw() | |
cmd, sz := get32le(data), get32le(data[4:]) | |
if cmd == LC_CODE_UUID { | |
hasUUID = true | |
} | |
loadOff += int(sz) | |
} | |
if !hasUUID { // LC_CODE_UUID does not exist. Add one. | |
uuidCmdSz := int(unsafe.Sizeof(uuidCmd{})) | |
uuidCmd := &uuidCmd{ | |
cmd: LC_CODE_UUID, | |
cmdsize: uint32(uuidCmdSz), | |
} | |
// Create the random UUID and copy into load command | |
UUID, err := uuid.NewRandom() | |
if err != nil { | |
panic(err) | |
} | |
{ | |
b, err := UUID.MarshalBinary() | |
if err != nil { | |
panic(err) | |
} | |
copy(uuidCmd.uuid[:], b) | |
} | |
if loadOff+uuidCmdSz > int(mf.Sections[0].Offset) { | |
panic("no space for adding LC_CODE_UUID") | |
} | |
_, err = f.WriteAt(uuidCmd.Raw(), int64(loadOff)) | |
if err != nil { | |
panic(err) | |
} | |
// fix up header: update Ncmd and Cmdsz | |
mf.Loads = append(mf.Loads, uuidCmd) | |
mf.FileHeader.Ncmd++ | |
mf.FileHeader.Cmdsz += uint32(uuidCmdSz) | |
var tmp [8]byte | |
put32le(tmp[:4], mf.FileHeader.Ncmd) | |
_, err = f.WriteAt(tmp[:4], int64(unsafe.Offsetof(mf.FileHeader.Ncmd))) | |
if err != nil { | |
panic(err) | |
} | |
put32le(tmp[:4], mf.FileHeader.Cmdsz) | |
_, err = f.WriteAt(tmp[:4], int64(unsafe.Offsetof(mf.FileHeader.Cmdsz))) | |
if err != nil { | |
panic(err) | |
} | |
} | |
// find existing LC_CODE_SIGNATURE and __LINKEDIT segment | |
var sigOff, sigSz, linkeditOff int | |
var linkeditSeg *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 linkeditOff == 0 { | |
if seg, ok := l.(*macho.Segment); ok && seg.Name == "__LINKEDIT" { | |
linkeditSeg = seg | |
linkeditOff = loadOff | |
} | |
} | |
loadOff += int(sz) | |
} | |
if sigOff == 0 { | |
st, err := f.Stat() | |
if err != nil { | |
panic(err) | |
} | |
sigOff = int(st.Size()) | |
sigOff = (sigOff + 15) &^ 15 // round up to 16 bytes ??? | |
err = f.Truncate(int64(sigOff)) | |
if err != nil { | |
panic(err) | |
} | |
} | |
// compute sizes | |
nhashes := (sigOff + pageSize - 1) / pageSize | |
idOff := int(unsafe.Sizeof(CodeDirectory{})) | |
hashOff := idOff + len(fname) + 1 | |
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(segSz)) | |
_, err = f.WriteAt(tmp[:8], int64(linkeditOff)+int64(unsafe.Offsetof(macho.Segment64{}.Memsz))) | |
if err != nil { | |
panic(err) | |
} | |
_, 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: 0x20001, | |
flags: 0x20002, // adhoc | linkerSigned | |
hashOffset: uint32(hashOff), | |
identOffset: uint32(idOff), | |
nCodeSlots: uint32(nhashes), | |
codeLimit: uint32(sigOff), | |
hashSize: sha256.Size, | |
hashType: kSecCodeSignatureHashSHA256, | |
pageSize: uint8(pageSizeBits), | |
} | |
out := make([]byte, sz) | |
outp := out | |
outp = sb.put(outp) | |
outp = blob.put(outp) | |
outp = cdir.put(outp) | |
outp = puts(outp, []byte(fname+"\000")) | |
// 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) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"flag" | |
"fmt" | |
"io/ioutil" | |
"math/rand" | |
"os" | |
"os/exec" | |
"path" | |
"strings" | |
"time" | |
) | |
var goSrc = `package main | |
import "fmt" | |
func main() { | |
fmt.Println("hello $NUM") | |
} | |
` | |
var cSrc = `#include <stdio.h> | |
int main() { | |
printf("hello $NUM\n"); | |
return 0; | |
} | |
` | |
func mustLookPath(path string) string { | |
p, err := exec.LookPath(path) | |
if err != nil { | |
panic(fmt.Errorf("can't find %s: %w", path, err)) | |
} | |
return p | |
} | |
func main() { | |
flagWork := flag.Bool("work", false, "keep working copy of program") | |
flagRun := flag.Bool("run", true, "run the program after signing") | |
flagC := flag.Bool("c", false, "build a C program instead of a Go program") | |
flagShowArch := flag.Bool("showarch", false, "run `arch` before build") | |
flagCSTool := flag.String("cstool", "codesign.go", "which codesign tool to use: none, codesign.go, codesign.uuid.go, apple, or a path to a binary") | |
flagSleep := flag.Int("sleep", 0, "seconds to sleep before execution") | |
flag.Parse() | |
goBinPath := path.Join( | |
os.Getenv("HOME"), | |
"/Development/go/src/github.com/golang/go/bin/go", | |
) | |
goEnv := append(os.Environ(), | |
[]string{ | |
"GOOS=darwin", | |
"GOARCH=arm64", | |
}..., | |
) | |
fmt.Printf("Using gc: %v\n", goBinPath) | |
// Generate unique program | |
rng := rand.New(rand.NewSource(time.Now().UnixNano())) | |
n := fmt.Sprintf("%d", rng.Intn(1000_000_000)) | |
cSrc = strings.Replace(cSrc, "$NUM", n, 1) | |
if err := ioutil.WriteFile("program.c", []byte(cSrc), 0644); err != nil { | |
panic(fmt.Errorf("generate: %w", err)) | |
} | |
goSrc = strings.Replace(goSrc, "$NUM", n, 1) | |
if err := ioutil.WriteFile("program.go", []byte(goSrc), 0644); err != nil { | |
panic(fmt.Errorf("generate: %w", err)) | |
} | |
// Generate unique binary name | |
name := fmt.Sprintf("test-%s", n) | |
fmt.Printf("Executable: %v\n", name) | |
var cmd exec.Cmd | |
if !*flagWork { | |
defer os.Remove(name) | |
} | |
// Show current arch | |
if *flagShowArch { | |
fmt.Printf("Current arch: ") | |
cmd = exec.Cmd{ | |
Path: mustLookPath("arch"), | |
Args: []string{"arch"}, | |
Stdout: os.Stdout, | |
Stderr: os.Stderr, | |
} | |
if err := cmd.Run(); err != nil { | |
panic(fmt.Errorf("run: %w", err)) | |
} | |
fmt.Println() | |
} | |
// Build program | |
if *flagC { | |
fmt.Println("Building C program...") | |
cmd = exec.Cmd{ | |
Path: mustLookPath("clang"), | |
Args: []string{"clang", "-Wl,-no_adhoc_codesign", "-arch", "arm64", "-o", name, "program.c"}, | |
Stdout: os.Stdout, | |
Stderr: os.Stderr, | |
} | |
} else { | |
fmt.Println("Building Go program...") | |
cmd = exec.Cmd{ | |
Path: goBinPath, | |
Args: []string{"go", "build", "-o", name, "program.go"}, | |
Env: goEnv, | |
Stdout: os.Stdout, | |
Stderr: os.Stderr, | |
} | |
} | |
if err := cmd.Run(); err != nil { | |
panic(fmt.Errorf("build: %w", err)) | |
} | |
// Codesign program | |
doCodesign := true | |
switch *flagCSTool { | |
case "none": | |
fmt.Println("Skipping codesign...") | |
doCodesign = false | |
case "codesign.go": | |
fmt.Println("Codesign with codesign.go...") | |
cmd = exec.Cmd{ | |
Path: goBinPath, | |
Args: []string{"go", "run", "codesign.go", name}, | |
Env: goEnv, | |
Stdout: os.Stdout, | |
Stderr: os.Stderr, | |
} | |
case "codesign.uuid.go": | |
fmt.Println("Codesign with codesign.uuid.go...") | |
cmd = exec.Cmd{ | |
Path: goBinPath, | |
Args: []string{"go", "run", "codesign.uuid.go", name}, | |
Env: goEnv, | |
Stdout: os.Stdout, | |
Stderr: os.Stderr, | |
} | |
case "apple": | |
fmt.Println("Codesign with Apple's tool...") | |
cmd = exec.Cmd{ | |
Path: "/usr/bin/codesign", | |
Args: []string{"codesign", "-s-", name}, | |
Env: goEnv, | |
Stdout: os.Stdout, | |
Stderr: os.Stderr, | |
} | |
default: | |
fmt.Printf("Codesign with %s...\n", *flagCSTool) | |
cmd = exec.Cmd{ | |
Path: mustLookPath(*flagCSTool), | |
Args: []string{"codesign", name}, | |
Env: goEnv, | |
Stdout: os.Stdout, | |
Stderr: os.Stderr, | |
} | |
} | |
if doCodesign { | |
if err := cmd.Run(); err != nil { | |
panic(fmt.Errorf("codesign: %w", err)) | |
} | |
} | |
if t := *flagSleep; t > 0 { | |
fmt.Printf("Sleeping for %d sec...\n", t) | |
time.Sleep(time.Duration(t) * time.Second) | |
} | |
// Run adhoc signed program | |
if *flagRun { | |
fmt.Println("Run...") | |
cmd = exec.Cmd{ | |
Path: "./" + name, | |
Args: []string{name}, | |
Stdout: os.Stdout, | |
Stderr: os.Stderr, | |
} | |
if err := cmd.Run(); err != nil { | |
panic(fmt.Errorf("run: %w", err)) | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module main | |
go 1.13 | |
require github.com/google/uuid v1.1.2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= | |
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment