Skip to content

Instantly share code, notes, and snippets.

@jpap

jpap/codesign.go Secret

Last active November 21, 2020 23:47
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jpap/6a32150449da137fee397f330ff6c35a to your computer and use it in GitHub Desktop.
Save jpap/6a32150449da137fee397f330ff6c35a to your computer and use it in GitHub Desktop.
Test program for adhoc codesign on Apple M1
// 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)
}
}
// 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)
}
}
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))
}
}
}
module main
go 1.13
require github.com/google/uuid v1.1.2
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