Skip to content

Commit

Permalink
add LC_ENCRYPTION_INFO_64, LC_DYLD_EXPORTS_TRIE, LC_DYLD_CHAINED_FIXUPS
Browse files Browse the repository at this point in the history
  • Loading branch information
blacktop committed Jun 25, 2020
1 parent 41d616a commit 028be2c
Show file tree
Hide file tree
Showing 8 changed files with 515 additions and 6 deletions.
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@
---

## Why 🤔

This package goes beyond the Go's `debug/macho` to:

- Cover ALL load commands and architectures
- Provide nice summary string output
- Allow for creating custom macho

## Install

```bash
Expand All @@ -20,7 +28,17 @@ package main
import "github.com/blacktop/go-macho"

func main() {
f, err := os.Open("/path/to/macho")
if err != nil {
panic(err)
}

m, err := macho.NewFile(f)
if err != nil {
panic(err)
}

fmt.Println(m.FileTOC)
}
```

Expand Down
72 changes: 70 additions & 2 deletions cmds.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"unsafe"

"github.com/blacktop/go-macho/types"
"github.com/blacktop/go-macho/types/trie"
)

// A Load represents any Mach-O load command.
Expand Down Expand Up @@ -738,7 +739,43 @@ func (s *SourceVersion) String() string {
}

// TODO: LC_DYLIB_CODE_SIGN_DRS 0x2B /* Code signing DRs copied from linked dylibs */
// TODO: LC_ENCRYPTION_INFO_64 0x2C /* 64-bit encrypted segment information */

/*******************************************************************************
* LC_ENCRYPTION_INFO_64
*******************************************************************************/

// A EncryptionInfo64 represents a Mach-O 64-bit encrypted segment information
type EncryptionInfo64 struct {
LoadBytes
types.EncryptionInfo64Cmd
Offset uint32 // file offset of encrypted range
Size uint32 // file size of encrypted range
CryptID types.EncryptionSystem // which enryption system, 0 means not-encrypted yet
}

func (e *EncryptionInfo64) String() string {
if e.CryptID == 0 {
return fmt.Sprintf("Offset: 0x%x, Size: 0x%x (not-encrypted yet)", e.Offset, e.Size)
}
return fmt.Sprintf("Offset: 0x%x, Size: 0x%x, CryptID: 0x%x", e.Offset, e.Size, e.CryptID)
}
func (e *EncryptionInfo64) Copy() *EncryptionInfo64 {
return &EncryptionInfo64{EncryptionInfo64Cmd: e.EncryptionInfo64Cmd}
}
func (e *EncryptionInfo64) LoadSize(t *FileTOC) uint32 {
return uint32(unsafe.Sizeof(types.EncryptionInfo64Cmd{}))
}
func (e *EncryptionInfo64) Put(b []byte, o binary.ByteOrder) int {
o.PutUint32(b[0*4:], uint32(e.LoadCmd))
o.PutUint32(b[1*4:], e.Len)
o.PutUint32(b[2*4:], e.Offset)
o.PutUint32(b[3*4:], e.Size)
o.PutUint32(b[3*4:], uint32(e.CryptID))
o.PutUint32(b[3*4:], e.Pad)

return int(e.Len)
}

// TODO: LC_LINKER_OPTION 0x2D /* linker options in MH_OBJECT files */
// TODO: LC_LINKER_OPTIMIZATION_HINT 0x2E /* optimization hints in MH_OBJECT files */
// TODO: LC_VERSION_MIN_TVOS 0x2F /* build for AppleTV min OS version */
Expand Down Expand Up @@ -769,7 +806,38 @@ func (b *BuildVersion) String() string {
b.ToolVersion)
}

// TODO: LC_DYLD_EXPORTS_TRIE (0x33 | LC_REQ_DYLD) /* used with linkedit_data_command, payload is trie */
/*******************************************************************************
* LC_DYLD_EXPORTS_TRIE
*******************************************************************************/

// A DyldExportsTrie used with linkedit_data_command, payload is trie
type DyldExportsTrie struct {
LoadBytes
types.DyldExportsTrieCmd
Tries []trie.TrieEntry
}

func (t *DyldExportsTrie) String() string {
return fmt.Sprintf("Count: %d", len(t.Tries))
}

/*******************************************************************************
* LC_DYLD_CHAINED_FIXUPS
*******************************************************************************/

// A DyldChainedFixups used with linkedit_data_command
type DyldChainedFixups struct {
LoadBytes
types.DyldChainedFixupsCmd
Offset uint32
Size uint32
ImportsCount uint32
}

func (cf *DyldChainedFixups) String() string {
return fmt.Sprintf("Offset: 0x%x, Size: 0x%x, Imports: %d", cf.Offset, cf.Size, cf.ImportsCount)
}

// TODO: LC_DYLD_CHAINED_FIXUPS (0x34 | LC_REQ_DYLD) /* used with linkedit_data_command */

/*******************************************************************************
Expand Down
77 changes: 74 additions & 3 deletions file.go
Original file line number Diff line number Diff line change
Expand Up @@ -808,7 +808,19 @@ func NewFile(r io.ReaderAt, loads ...types.LoadCmd) (*File, error) {
l.LoadBytes = LoadBytes(cmddat)
f.Loads[i] = l
// TODO: case types.LcDylibCodeSignDrs:
// TODO: case types.LcEncryptionInfo64:
case types.LC_ENCRYPTION_INFO_64:
var ei types.EncryptionInfo64Cmd
b := bytes.NewReader(cmddat)
if err := binary.Read(b, bo, &ei); err != nil {
return nil, err
}
l := new(EncryptionInfo64)
l.LoadCmd = cmd
l.Offset = ei.Offset
l.Size = ei.Size
l.CryptID = ei.CryptID
l.LoadBytes = LoadBytes(cmddat)
f.Loads[i] = l
// TODO: case types.LcLinkerOption:
// TODO: case types.LcLinkerOptimizationHint:
// TODO: case types.LcVersionMinTvos:
Expand All @@ -827,6 +839,7 @@ func NewFile(r io.ReaderAt, loads ...types.LoadCmd) (*File, error) {
l.Minos = build.Minos.String()
l.Sdk = build.Sdk.String()
l.NumTools = build.NumTools
// TODO: handle more than one tool case
if build.NumTools > 0 {
if err := binary.Read(b, bo, &buildTool); err != nil {
return nil, err
Expand All @@ -836,8 +849,66 @@ func NewFile(r io.ReaderAt, loads ...types.LoadCmd) (*File, error) {
}
l.LoadBytes = LoadBytes(cmddat)
f.Loads[i] = l
// TODO: case types.LcDyldExportsTrie:
// TODO: case types.LcDyldChainedFixups:
case types.LC_DYLD_EXPORTS_TRIE:
var led types.LinkEditDataCmd
var err error
b := bytes.NewReader(cmddat)
if err := binary.Read(b, bo, &led); err != nil {
return nil, err
}
ldat := make([]byte, led.Size)
if _, err := r.ReadAt(ldat, int64(led.Offset)); err != nil {
return nil, err
}
l := new(DyldExportsTrie)
l.LoadCmd = cmd
l.LoadBytes = LoadBytes(cmddat)
l.Tries, err = trie.ParseTrie(ldat, 0)
if err != nil {
return nil, err
}
f.Loads[i] = l
case types.LC_DYLD_CHAINED_FIXUPS:
var led types.LinkEditDataCmd
b := bytes.NewReader(cmddat)
if err := binary.Read(b, bo, &led); err != nil {
return nil, err
}
l := new(DyldChainedFixups)
l.LoadCmd = cmd
l.Offset = led.Offset
l.Size = led.Size
l.LoadBytes = LoadBytes(cmddat)

var dcf types.DyldChainedFixups
// var dcsis types.DyldChainedStartsInSegment
ldat := make([]byte, led.Size)
if _, err := r.ReadAt(ldat, int64(led.Offset)); err != nil {
return nil, err
}
fsr := bytes.NewReader(ldat)
if err := binary.Read(fsr, bo, &dcf); err != nil {
return nil, err
}
l.ImportsCount = dcf.ImportsCount
// fmt.Printf("%#v\n", dcf)

// fsr.Seek(int64(dcf.StartsOffset), io.SeekStart)
// var segCount uint32
// if err := binary.Read(fsr, bo, &segCount); err != nil {
// return nil, err
// }
// segInfoOffset := make([]uint32, segCount)
// if err := binary.Read(fsr, bo, &segInfoOffset); err != nil {
// return nil, err
// }
// fmt.Println(segInfoOffset)

// if err := binary.Read(fsr, bo, &dcsis); err != nil {
// return nil, err
// }
// fmt.Println(dcsis)
f.Loads[i] = l
}
if s != nil {
s.sr = io.NewSectionReader(r, int64(s.Offset), int64(s.Filesz))
Expand Down
Empty file removed go.sum
Empty file.
9 changes: 8 additions & 1 deletion types/cpu.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,14 @@ func (st CPUSubtype) GoString(cpu CPU) string {
case CPUArm:
return stringName(uint32(st&CpuSubtypeMask), cpuSubtypeArmStrings, true)
case CPUArm64:
return stringName(uint32(st&CpuSubtypeMask), cpuSubtypeArm64Strings, true)
var feature string
caps := st & CpuSubtypeFeatureMask
if caps&CpuSubtypePtrauthAbiUser == 0 {
feature = fmt.Sprintf(" caps: PAC%02d", (caps&CpuSubtypeArm64PtrAuthMask)>>24)
} else {
feature = fmt.Sprintf(" caps: PAK%02d", (caps&CpuSubtypeArm64PtrAuthMask)>>24)
}
return stringName(uint32(st&CpuSubtypeMask), cpuSubtypeArm64Strings, true) + feature
}
return "UNKNOWN"
}
50 changes: 50 additions & 0 deletions types/trie/flags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package trie

import "strings"

type CacheExportFlag int

const (
exportSymbolFlagsKindMask CacheExportFlag = 0x03
exportSymbolFlagsKindRegular CacheExportFlag = 0x00
exportSymbolFlagsKindThreadLocal CacheExportFlag = 0x01
exportSymbolFlagsKindAbsolute CacheExportFlag = 0x02
exportSymbolFlagsWeakDefinition CacheExportFlag = 0x04
exportSymbolFlagsReexport CacheExportFlag = 0x08
exportSymbolFlagsStubAndResolver CacheExportFlag = 0x10
)

func (f CacheExportFlag) Regular() bool {
return (f & exportSymbolFlagsKindMask) == exportSymbolFlagsKindRegular
}
func (f CacheExportFlag) ThreadLocal() bool {
return (f & exportSymbolFlagsKindMask) == exportSymbolFlagsKindThreadLocal
}
func (f CacheExportFlag) Absolute() bool {
return (f & exportSymbolFlagsKindMask) == exportSymbolFlagsKindAbsolute
}
func (f CacheExportFlag) WeakDefinition() bool {
return f == exportSymbolFlagsWeakDefinition
}
func (f CacheExportFlag) ReExport() bool {
return f == exportSymbolFlagsReexport
}
func (f CacheExportFlag) StubAndResolver() bool {
return f == exportSymbolFlagsStubAndResolver
}
func (f CacheExportFlag) String() string {
var fStr string
if f.Regular() {
fStr += "Regular "
if f.StubAndResolver() {
fStr += "(Has Resolver Function)"
} else if f.WeakDefinition() {
fStr += "(Weak Definition)"
}
} else if f.ThreadLocal() {
fStr += "Thread Local"
} else if f.Absolute() {
fStr += "Absolute"
}
return strings.TrimSpace(fStr)
}
Loading

0 comments on commit 028be2c

Please sign in to comment.