mirror of
https://github.com/blacktop/ipsw.git
synced 2026-06-07 12:27:36 +00:00
1574 lines
48 KiB
Go
1574 lines
48 KiB
Go
package dyld
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/sha1"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"io"
|
|
"strings"
|
|
"unsafe"
|
|
|
|
"github.com/apex/log"
|
|
"github.com/blacktop/go-macho/pkg/trie"
|
|
"github.com/blacktop/go-macho/types"
|
|
"github.com/blacktop/ipsw/pkg/table"
|
|
)
|
|
|
|
//go:generate go tool stringer -type=closureType,linkKind -output closure_string.go
|
|
|
|
type closureType uint32
|
|
|
|
const (
|
|
// containers which have an overall length and TypedBytes inside ther content
|
|
launchClosure closureType = 1 // contains TypedBytes of closure attributes including imageArray
|
|
imageArray closureType = 2 // sizeof(ImageArray) + sizeof(uint32_t)*count + size of all images
|
|
image closureType = 3 // contains TypedBytes of image attributes
|
|
dlopenClosure closureType = 4 // contains TypedBytes of closure attributes including imageArray
|
|
// attributes for Images
|
|
imageFlags closureType = 7 // sizeof(Image::Flags)
|
|
pathWithHash closureType = 8 // len = uint32_t + length path + 1 use multiple entries for aliases
|
|
fileInodeAndTime closureType = 9 // sizeof(FileInfo)
|
|
cdHash closureType = 10 // 20 use multiple entries on watchOS for all hashes
|
|
uuid closureType = 11 // 16
|
|
mappingInfo closureType = 12 // sizeof(MappingInfo)
|
|
diskSegment closureType = 13 // sizeof(DiskSegment) * count
|
|
cacheSegment closureType = 14 // sizeof(DyldCacheSegment) * count
|
|
dependents closureType = 15 // sizeof(LinkedImage) * count
|
|
initOffsets closureType = 16 // sizeof(uint32_t) * count
|
|
dofOffsets closureType = 17 // sizeof(uint32_t) * count
|
|
codeSignLoc closureType = 18 // sizeof(CodeSignatureLocation)
|
|
farPlayLoc closureType = 19 // sizeof(FarPlayRange)
|
|
rebaseFixups closureType = 20 // sizeof(RebasePattern) * count
|
|
bindFixups closureType = 21 // sizeof(BindPattern) * count
|
|
cachePatchInfo closureType = 22 // deprecated
|
|
textFixups closureType = 23 // sizeof(TextFixupPattern) * count
|
|
imageOverride closureType = 24 // sizeof(ImageNum)
|
|
initBefores closureType = 25 // sizeof(ImageNum) * count
|
|
initsSection closureType = 26 // sizeof(InitializerSectionRange)
|
|
chainedFixupsTargets closureType = 27 // sizeof(ResolvedSymbolTarget) * count
|
|
termOffsets closureType = 28 // sizeof(uint32_t) * count
|
|
chainedStartsOffset closureType = 29 // sizeof(uint64_t)
|
|
objcFixups closureType = 30 // sizeof(ResolvedSymbolTarget) + (sizeof(uint32_t) * 2) + (sizeof(ProtocolISAFixup) * count) + (sizeof(SelectorReferenceFixup) * count)
|
|
// attributes for Closures (launch or dlopen)
|
|
closureFlags closureType = 32 // sizeof(Closure::Flags)
|
|
dyldCacheUUID closureType = 33 // 16
|
|
missingFiles closureType = 34
|
|
envVar closureType = 35 // "DYLD_BLAH=stuff"
|
|
topImage closureType = 36 // sizeof(ImageNum)
|
|
libDyldEntry closureType = 37 // sizeof(ResolvedSymbolTarget)
|
|
libSystemNum closureType = 38 // sizeof(ImageNum)
|
|
mainEntry closureType = 40 // sizeof(ResolvedSymbolTarget)
|
|
startEntry closureType = 41 // sizeof(ResolvedSymbolTarget) // used by programs built with crt1.o
|
|
cacheOverrides closureType = 42 // sizeof(PatchEntry) * count // used if process uses interposing or roots (cached dylib overrides)
|
|
interposeTuples closureType = 43 // sizeof(InterposingTuple) * count
|
|
existingFiles closureType = 44 // uint64_t + (SkippedFiles * count)
|
|
selectorTable closureType = 45 // uint32_t + (sizeof(ObjCSelectorImage) * count) + hashTable size
|
|
classTable closureType = 46 // (3 * uint32_t) + (sizeof(ObjCClassImage) * count) + classHashTable size + protocolHashTable size
|
|
warning closureType = 47 // len = uint32_t + length path + 1 use one entry per warning
|
|
duplicateClassesTable closureType = 48 // duplicateClassesHashTable
|
|
progVars closureType = 49 // sizeof(uint32_t)
|
|
)
|
|
|
|
const (
|
|
FirstDyldCacheImageNum = 0x00000001
|
|
LastDyldCacheImageNum = 0x00000FFF
|
|
FirstOtherOSImageNum = 0x00001001
|
|
LastOtherOSImageNum = 0x00001FFF
|
|
FirstLaunchClosureImageNum = 0x00002000
|
|
MissingWeakLinkedImage = 0x0FFFFFFF
|
|
)
|
|
|
|
type CImage struct {
|
|
ID uint32
|
|
Name string
|
|
Hash uint32
|
|
UUID types.UUID
|
|
Flags cFlags
|
|
Dependents []linkedImage
|
|
CacheSegments []cacheSegmentType
|
|
DiskSegments []diskSegmentType
|
|
InitOrder []uint32
|
|
DofOffsets []uint32
|
|
InitializerOffsets []uint32
|
|
TerminatorOffsets []uint32
|
|
InitializerSections []initializerSectionRangeType
|
|
MappingInfo mappingInfoType
|
|
CodeSignatureLocation codeSignatureLocationType
|
|
CDHash cdhash
|
|
Rebases []rebasePatternType
|
|
Binds []BindPattern
|
|
TextFixups []TextFixupPattern
|
|
ChainedFixupsTargets []resolvedSymbolTarget
|
|
ChainedStartsOffset uint64
|
|
ObjcFixups ObjcFixups
|
|
FarPlayLocation farPlayLocType
|
|
FileInfo fileInfoType
|
|
ImageOverride uint32
|
|
}
|
|
|
|
type LaunchClosure struct {
|
|
Flags lcFlags
|
|
Images []*CImage
|
|
DyldUUID types.UUID
|
|
LibSystemNum uint32
|
|
LibDyldEntry resolvedSymbolTarget
|
|
TopImage uint32
|
|
MainEntry resolvedSymbolTarget
|
|
StartEntry resolvedSymbolTarget
|
|
ProgVars uint32
|
|
Warnings []warningType
|
|
DyldCacheFixups []PatchEntry
|
|
MissingFiles []string
|
|
EnvVars []string
|
|
}
|
|
|
|
func (i CImage) GetID() uint32 {
|
|
return uint32(i.Flags.ImageNum())
|
|
}
|
|
func (i CImage) PageSize() uint32 {
|
|
if i.Flags.Has16KbPages() {
|
|
return 0x4000
|
|
}
|
|
return 0x1000
|
|
}
|
|
|
|
func (i CImage) String(d *File, verbose bool) string {
|
|
var mappings string
|
|
if i.MappingInfo.TotalVmPages > 0 {
|
|
var slice string
|
|
if i.MappingInfo.SliceOffsetIn4K > 0 {
|
|
slice = fmt.Sprintf("Slice Offset: %#x\n", i.MappingInfo.SliceOffsetIn4K*0x1000)
|
|
}
|
|
mappings = fmt.Sprintf(
|
|
"Total VM Size: %#x\n"+
|
|
"%s",
|
|
i.MappingInfo.TotalVmPages*i.PageSize(),
|
|
slice,
|
|
)
|
|
}
|
|
var cacheSegs string
|
|
if len(i.CacheSegments) > 0 {
|
|
cacheSegs = "Cache Segments:\n"
|
|
for _, cs := range i.CacheSegments {
|
|
cacheSegs += fmt.Sprintf("\t%s\n", cs)
|
|
}
|
|
}
|
|
var diskSegs string
|
|
if len(i.DiskSegments) > 0 {
|
|
diskSegs = "\nMappings:\n"
|
|
tableString := &strings.Builder{}
|
|
|
|
mdata := [][]string{}
|
|
prevFOff := uint32(0)
|
|
for _, ds := range i.DiskSegments {
|
|
mdata = append(mdata, []string{
|
|
fmt.Sprintf("%#08x", prevFOff),
|
|
fmt.Sprintf("%#08x", ds.FilePageCount()*i.PageSize()),
|
|
fmt.Sprintf("%#08x", ds.VMPageCount()*i.PageSize()),
|
|
ds.PermString(),
|
|
})
|
|
prevFOff += ds.FilePageCount() * i.PageSize()
|
|
}
|
|
tbl := table.NewStringBuilderTableWriter(tableString)
|
|
tbl.SetHeader([]string{"File Offset", "File Size", "VM Size", "Prot"})
|
|
tbl.SetBorders(nil)
|
|
tbl.SetCenterSeparator("|")
|
|
tbl.AppendBulk(mdata)
|
|
tbl.SetAlignment(1)
|
|
tbl.Render()
|
|
|
|
diskSegs += tableString.String()
|
|
}
|
|
var initOrder string
|
|
if len(i.InitOrder) > 0 {
|
|
initOrder = "\nInit Order:\n"
|
|
for _, io := range i.InitOrder {
|
|
if val, ok := d.ImageArray[io]; ok {
|
|
initOrder += fmt.Sprintf("\t%s\n", val.Name)
|
|
} else {
|
|
if io == FirstLaunchClosureImageNum {
|
|
initOrder += fmt.Sprintf("\t%s\n", i.Name)
|
|
} else {
|
|
log.Errorf("no image found for ID %d", i)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
var deps string
|
|
if len(i.Dependents) > 0 {
|
|
deps = "\nDependents:\n"
|
|
for _, dp := range i.Dependents {
|
|
if val, ok := d.ImageArray[dp.ImgNum()]; ok {
|
|
deps += fmt.Sprintf("\t%-8s) %s\n", dp.Link(), val.Name)
|
|
} else {
|
|
if dp.ImgNum() == FirstLaunchClosureImageNum {
|
|
deps += fmt.Sprintf("\t%-8s) %s\n", dp.Link(), i.Name)
|
|
} else if dp.ImgNum() == MissingWeakLinkedImage && dp.Link() == weak {
|
|
continue
|
|
} else {
|
|
log.Errorf("no image found for ID %d", dp.ImgNum())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
var inits string
|
|
var terms string
|
|
var dofoff string
|
|
if verbose {
|
|
if len(i.InitializerOffsets) > 0 {
|
|
inits = "\nInitializers:\n"
|
|
for _, i := range i.InitializerOffsets {
|
|
inits += fmt.Sprintf("\t%#x\n", i)
|
|
}
|
|
}
|
|
if len(i.TerminatorOffsets) > 0 {
|
|
terms = "\nTerminators:\n"
|
|
for _, t := range i.TerminatorOffsets {
|
|
terms += fmt.Sprintf("\t%#x\n", t)
|
|
}
|
|
}
|
|
if len(i.DofOffsets) > 0 {
|
|
dofoff = "\nDOF Offsets:\n"
|
|
for _, do := range i.DofOffsets {
|
|
dofoff += fmt.Sprintf("\t%#x\n", do)
|
|
}
|
|
}
|
|
}
|
|
var cslocation string
|
|
if i.CodeSignatureLocation.FileSize > 0 {
|
|
cslocation = fmt.Sprintf(
|
|
"Code Signature: offset: 0x%08x-0x%08x, size: %5d\n",
|
|
i.CodeSignatureLocation.FileOffset,
|
|
i.CodeSignatureLocation.FileOffset+i.CodeSignatureLocation.FileSize,
|
|
i.CodeSignatureLocation.FileSize,
|
|
)
|
|
}
|
|
var cdhash string
|
|
if len(i.CDHash) > 0 {
|
|
cdhash = fmt.Sprintf("CDHash: %s\n", i.CDHash)
|
|
}
|
|
var id string
|
|
if i.ID != FirstLaunchClosureImageNum {
|
|
id = fmt.Sprintf("ID: %d\n", i.ID)
|
|
}
|
|
return fmt.Sprintf(
|
|
"%s"+
|
|
"Name: %s\n"+
|
|
"Flags: %s\n"+
|
|
"UUID: %s\n"+
|
|
"%s%s%s%s%s%s%s%s%s%s",
|
|
id,
|
|
i.Name,
|
|
i.Flags,
|
|
i.UUID,
|
|
cdhash,
|
|
cslocation,
|
|
mappings,
|
|
cacheSegs,
|
|
diskSegs,
|
|
deps,
|
|
initOrder,
|
|
inits,
|
|
terms,
|
|
dofoff,
|
|
)
|
|
}
|
|
|
|
type cdhash []byte
|
|
|
|
func (cd cdhash) String() string {
|
|
h := sha1.New()
|
|
h.Write(cd)
|
|
return fmt.Sprintf("%x", h.Sum(nil))
|
|
}
|
|
|
|
type cacheSegmentType uint64
|
|
|
|
func (d cacheSegmentType) CacheOffset() uint32 {
|
|
return uint32(types.ExtractBits(uint64(d), 0, 32))
|
|
}
|
|
func (d cacheSegmentType) Size() uint32 {
|
|
return uint32(types.ExtractBits(uint64(d), 32, 28))
|
|
}
|
|
func (d cacheSegmentType) Permissions() uint32 {
|
|
return uint32(types.ExtractBits(uint64(d), 60, 4))
|
|
}
|
|
func (d cacheSegmentType) String() string {
|
|
return fmt.Sprintf("offset: %#08x, size: %#08x, perms: %s", d.CacheOffset(), d.Size(), types.VmProtection(d.Permissions()))
|
|
}
|
|
|
|
const ReadOnlyDataPermissions = 2
|
|
|
|
type diskSegmentType uint64
|
|
|
|
func (d diskSegmentType) FilePageCount() uint32 {
|
|
return uint32(types.ExtractBits(uint64(d), 0, 30))
|
|
}
|
|
func (d diskSegmentType) VMPageCount() uint32 {
|
|
return uint32(types.ExtractBits(uint64(d), 30, 30))
|
|
}
|
|
func (d diskSegmentType) Permissions() uint32 {
|
|
return uint32(types.ExtractBits(uint64(d), 60, 3))
|
|
}
|
|
func (d diskSegmentType) PermString() string {
|
|
perms := types.VmProtection(d.Permissions()).String()
|
|
if d.Permissions() == ReadOnlyDataPermissions {
|
|
perms = "-w-"
|
|
}
|
|
return perms
|
|
}
|
|
func (d diskSegmentType) PaddingNotSeg() bool {
|
|
return uint32(types.ExtractBits(uint64(d), 63, 1)) != 0
|
|
}
|
|
func (d diskSegmentType) String() string {
|
|
|
|
return fmt.Sprintf("file_pages: %d, vm_pages: %d, perms: %s, noncontig_segs : %t",
|
|
d.FilePageCount(),
|
|
d.VMPageCount(),
|
|
d.PermString(),
|
|
d.PaddingNotSeg())
|
|
}
|
|
|
|
type rebasePatternType uint32
|
|
|
|
func (r rebasePatternType) RepeatCount() uint32 {
|
|
return uint32(types.ExtractBits(uint64(r), 0, 20))
|
|
}
|
|
func (r rebasePatternType) ContigCount() uint32 {
|
|
return uint32(types.ExtractBits(uint64(r), 20, 8))
|
|
}
|
|
func (r rebasePatternType) SkipCount() uint32 {
|
|
return uint32(types.ExtractBits(uint64(r), 28, 4))
|
|
}
|
|
|
|
func (r rebasePatternType) String() string {
|
|
return fmt.Sprintf("repeat_count: %d, contig_count: %d, skip_count : %d",
|
|
r.RepeatCount(),
|
|
r.ContigCount(),
|
|
r.SkipCount())
|
|
}
|
|
|
|
type targetKind uint8
|
|
|
|
const (
|
|
KindRebase targetKind = iota
|
|
KindSharedCache
|
|
KindImage
|
|
KindAbsolute
|
|
)
|
|
|
|
type resolvedSymbolTarget uint64
|
|
|
|
func (r resolvedSymbolTarget) Kind() targetKind {
|
|
return targetKind(types.ExtractBits(uint64(r), 0, 2))
|
|
}
|
|
func (r resolvedSymbolTarget) SharedCacheOffset() uint64 {
|
|
return types.ExtractBits(uint64(r), 2, 22)
|
|
}
|
|
func (r resolvedSymbolTarget) ImageNum() uint64 {
|
|
return types.ExtractBits(uint64(r), 2, 22)
|
|
}
|
|
func (r resolvedSymbolTarget) ImageOffset() uint64 {
|
|
return types.ExtractBits(uint64(r), 24, 40)
|
|
}
|
|
func (r resolvedSymbolTarget) AbsoluteValue() int64 { // sign extended
|
|
return int64(types.ExtractBits(uint64(r), 2, 62))
|
|
}
|
|
func (r resolvedSymbolTarget) Raw() uint64 {
|
|
return uint64(r)
|
|
}
|
|
func (r resolvedSymbolTarget) String() string {
|
|
switch r.Kind() {
|
|
case KindRebase:
|
|
return fmt.Sprintf("kind: rebase, raw: %#x", r.Raw())
|
|
case KindSharedCache:
|
|
return fmt.Sprintf("kind: shared_cache, offset: %#x", r.SharedCacheOffset())
|
|
case KindImage:
|
|
return fmt.Sprintf("kind: image, index: %d, offset: %#x", r.ImageNum(), r.ImageOffset())
|
|
case KindAbsolute:
|
|
return fmt.Sprintf("kind: absolute, value: %#x", r.AbsoluteValue())
|
|
}
|
|
return fmt.Sprintf("error: unknown kind %d", r.Kind())
|
|
}
|
|
|
|
type BindPattern struct {
|
|
Target resolvedSymbolTarget
|
|
Detail bindPatternType
|
|
}
|
|
|
|
func (b BindPattern) String() string {
|
|
return fmt.Sprintf("%s, %s", b.Target, b.Detail)
|
|
}
|
|
|
|
type bindPatternType uint64
|
|
|
|
func (b bindPatternType) StartVMOffset() uint64 { // max 1TB offset
|
|
return types.ExtractBits(uint64(b), 0, 40)
|
|
}
|
|
func (b bindPatternType) SkipCount() uint64 {
|
|
return types.ExtractBits(uint64(b), 40, 8)
|
|
}
|
|
func (b bindPatternType) RepeatCount() uint64 {
|
|
return types.ExtractBits(uint64(b), 48, 16)
|
|
}
|
|
|
|
func (b bindPatternType) String() string {
|
|
return fmt.Sprintf("start_vmoffset: %#x, skip_count: %d, repeat_count : %d",
|
|
b.StartVMOffset(),
|
|
b.SkipCount(),
|
|
b.RepeatCount())
|
|
}
|
|
|
|
type TextFixupPattern struct {
|
|
Target resolvedSymbolTarget
|
|
StartVmOffset uint32
|
|
RepeatCount uint16
|
|
SkipCount uint16
|
|
}
|
|
|
|
func (t TextFixupPattern) String() string {
|
|
return fmt.Sprintf("%s, start_vmoffset: %#x, skip_count: %d, repeat_count : %d",
|
|
t.Target, t.StartVmOffset, t.SkipCount, t.RepeatCount)
|
|
}
|
|
|
|
type ObjcFixups struct {
|
|
objcFixupsType
|
|
ProtocolISAFixups []protocolISAFixup
|
|
SelRefFixups []SelectorReferenceFixup
|
|
StableSwiftFixupCount uint32
|
|
MethodListFixupCount uint32
|
|
ClassStableSwiftFixups []classStableSwiftFixup
|
|
MethodListFixups []methodListFixup
|
|
}
|
|
type objcFixupsType struct {
|
|
ProtocolClassTarget resolvedSymbolTarget
|
|
ImageInfoVMOffset uint64
|
|
ProtocolFixupCount uint32
|
|
SelRefFixupCount uint32
|
|
}
|
|
|
|
type protocolISAFixup uint64
|
|
|
|
func (p protocolISAFixup) StartVMOffset() uint64 {
|
|
return types.ExtractBits(uint64(p), 0, 40)
|
|
}
|
|
func (p protocolISAFixup) SkipCount() uint64 {
|
|
return types.ExtractBits(uint64(p), 40, 8)
|
|
}
|
|
func (p protocolISAFixup) RepeatCount() uint64 {
|
|
return types.ExtractBits(uint64(p), 48, 16)
|
|
}
|
|
func (p protocolISAFixup) String() string {
|
|
return fmt.Sprintf("start_vmoffset: %#x, skip_count: %d, repeat_count : %d",
|
|
p.StartVMOffset(),
|
|
p.SkipCount(),
|
|
p.RepeatCount())
|
|
}
|
|
|
|
type SelectorReferenceFixup uint32
|
|
|
|
func (s SelectorReferenceFixup) String() string {
|
|
return fmt.Sprintf("offset: %#x, %s", uint32(s), chainEntry(s))
|
|
}
|
|
|
|
type chainEntry uint32
|
|
|
|
func (c chainEntry) Index() uint32 {
|
|
return uint32(types.ExtractBits(uint64(c), 0, 24))
|
|
}
|
|
func (c chainEntry) Next() uint32 {
|
|
return uint32(types.ExtractBits(uint64(c), 24, 7))
|
|
}
|
|
func (c chainEntry) InSharedCache() bool {
|
|
return types.ExtractBits(uint64(c), 31, 1) != 0
|
|
}
|
|
func (c chainEntry) String() string {
|
|
return fmt.Sprintf("index: %d, next: %d, in_cache : %t",
|
|
c.Index(), c.Next(), c.InSharedCache())
|
|
}
|
|
|
|
type classStableSwiftFixup uint64
|
|
|
|
func (c classStableSwiftFixup) StartVMOffset() uint64 {
|
|
return types.ExtractBits(uint64(c), 0, 40)
|
|
}
|
|
func (c classStableSwiftFixup) SkipCount() uint64 {
|
|
return types.ExtractBits(uint64(c), 40, 8)
|
|
}
|
|
func (c classStableSwiftFixup) RepeatCount() uint64 {
|
|
return types.ExtractBits(uint64(c), 48, 16)
|
|
}
|
|
func (c classStableSwiftFixup) String() string {
|
|
return fmt.Sprintf("start_vmoffset: %#x, skip_count: %d, repeat_count : %d",
|
|
c.StartVMOffset(),
|
|
c.SkipCount(),
|
|
c.RepeatCount())
|
|
}
|
|
|
|
type methodListFixup uint64
|
|
|
|
func (m methodListFixup) StartVMOffset() uint64 {
|
|
return types.ExtractBits(uint64(m), 0, 40)
|
|
}
|
|
func (m methodListFixup) SkipCount() uint64 {
|
|
return types.ExtractBits(uint64(m), 40, 8)
|
|
}
|
|
func (m methodListFixup) RepeatCount() uint64 {
|
|
return types.ExtractBits(uint64(m), 48, 16)
|
|
}
|
|
func (m methodListFixup) String() string {
|
|
return fmt.Sprintf("start_vmoffset: %#x, skip_count: %d, repeat_count : %d",
|
|
m.StartVMOffset(),
|
|
m.SkipCount(),
|
|
m.RepeatCount())
|
|
}
|
|
|
|
type linkKind uint32
|
|
|
|
const (
|
|
regular linkKind = 0
|
|
weak linkKind = 1
|
|
upward linkKind = 2
|
|
reExport linkKind = 3
|
|
)
|
|
|
|
type linkedImage uint32
|
|
|
|
func (l linkedImage) ImgNum() uint32 {
|
|
return uint32(types.ExtractBits(uint64(l), 0, 30))
|
|
}
|
|
func (l linkedImage) Link() linkKind {
|
|
return linkKind(types.ExtractBits(uint64(l), 30, 2))
|
|
}
|
|
func (l linkedImage) String() string {
|
|
return fmt.Sprintf("img_no: %d, link: %s", l.ImgNum(), l.Link())
|
|
}
|
|
|
|
type ImageArray struct {
|
|
imageArrayType
|
|
Offsets []uint32
|
|
}
|
|
|
|
func (i ImageArray) String() string {
|
|
return fmt.Sprintf("%s, frst_img: %d, %s", i.T, i.FrstImageNum, i.CR)
|
|
}
|
|
|
|
type imageArrayType struct {
|
|
T typedBytes
|
|
FrstImageNum uint32
|
|
CR countAndHasRoots
|
|
}
|
|
|
|
type typedBytes uint32
|
|
|
|
func (t typedBytes) Type() closureType {
|
|
return closureType(types.ExtractBits(uint64(t), 0, 8))
|
|
}
|
|
func (t typedBytes) PayloadLength() uint32 {
|
|
return uint32(types.ExtractBits(uint64(t), 8, 24))
|
|
}
|
|
func (t typedBytes) String() string {
|
|
return fmt.Sprintf("type: %s, length: %#x, raw: %#x", t.Type(), t.PayloadLength(), uint32(t))
|
|
}
|
|
|
|
var sizeOfTypeBytes = uint32(binary.Size(typedBytes(0)))
|
|
|
|
type countAndHasRoots uint32
|
|
|
|
func (cr countAndHasRoots) Count() uint32 {
|
|
return uint32(types.ExtractBits(uint64(cr), 0, 31))
|
|
}
|
|
func (cr countAndHasRoots) HasRoots() bool {
|
|
return types.ExtractBits(uint64(cr), 31, 1) != 0
|
|
}
|
|
func (cr countAndHasRoots) String() string {
|
|
return fmt.Sprintf("count: %d, has_roots: %t", cr.Count(), cr.HasRoots())
|
|
}
|
|
|
|
type lcFlags uint32
|
|
|
|
func (f lcFlags) UsedAtPaths() bool {
|
|
return types.ExtractBits(uint64(f), 0, 16) != 0
|
|
}
|
|
func (f lcFlags) UsedFallbackPaths() bool {
|
|
return types.ExtractBits(uint64(f), 0, 16) != 0
|
|
}
|
|
func (f lcFlags) InitImageCount() uint32 {
|
|
return uint32(types.ExtractBits(uint64(f), 0, 16))
|
|
}
|
|
func (f lcFlags) HasInsertedLibraries() bool {
|
|
return types.ExtractBits(uint64(f), 0, 16) != 0
|
|
}
|
|
func (f lcFlags) HasProgVars() bool {
|
|
return types.ExtractBits(uint64(f), 0, 16) != 0
|
|
}
|
|
func (f lcFlags) UsedInterposing() bool {
|
|
return types.ExtractBits(uint64(f), 0, 16) != 0
|
|
}
|
|
func (f lcFlags) Padding() uint32 {
|
|
return uint32(types.ExtractBits(uint64(f), 0, 16))
|
|
}
|
|
func (f lcFlags) String() string {
|
|
var flags []string
|
|
if f.UsedAtPaths() {
|
|
flags = append(flags, "@paths")
|
|
}
|
|
if !f.UsedFallbackPaths() {
|
|
flags = append(flags, "fallback_paths")
|
|
}
|
|
if f.HasInsertedLibraries() {
|
|
flags = append(flags, "inserted_libs")
|
|
}
|
|
if f.HasProgVars() {
|
|
flags = append(flags, "prog_vars")
|
|
}
|
|
if f.UsedInterposing() {
|
|
flags = append(flags, "interposing")
|
|
}
|
|
return fmt.Sprintf("initial_image_count: %d, flags: [%s]",
|
|
f.InitImageCount(),
|
|
strings.Join(flags, "|"),
|
|
)
|
|
}
|
|
|
|
type cFlags uint64
|
|
|
|
func (f cFlags) ImageNum() uint16 {
|
|
return uint16(types.ExtractBits(uint64(f), 0, 16))
|
|
}
|
|
func (f cFlags) MaxLoadCount() uint16 {
|
|
return uint16(types.ExtractBits(uint64(f), 16, 12))
|
|
}
|
|
func (f cFlags) IsInvalid() bool { // IsInvalid an error occurred creating the info for this image
|
|
return types.ExtractBits(uint64(f), 28, 1) != 0
|
|
}
|
|
func (f cFlags) Has16KbPages() bool {
|
|
return types.ExtractBits(uint64(f), 29, 1) != 0
|
|
}
|
|
func (f cFlags) Is64() bool {
|
|
return types.ExtractBits(uint64(f), 30, 1) != 0
|
|
}
|
|
func (f cFlags) HasObjC() bool {
|
|
return types.ExtractBits(uint64(f), 31, 1) != 0
|
|
}
|
|
func (f cFlags) MayHavePlusLoads() bool {
|
|
return types.ExtractBits(uint64(f), 32, 1) != 0
|
|
}
|
|
func (f cFlags) IsEncrypted() bool { // IsEncrypted image is DSMOS or FarPlay encrypted
|
|
return types.ExtractBits(uint64(f), 33, 1) != 0
|
|
}
|
|
func (f cFlags) HasWeakDefs() bool {
|
|
return types.ExtractBits(uint64(f), 34, 1) != 0
|
|
}
|
|
func (f cFlags) NeverUnload() bool {
|
|
return types.ExtractBits(uint64(f), 35, 1) != 0
|
|
}
|
|
func (f cFlags) CwdSameAsThis() bool { // CwdSameAsThis dylibs use file system relative paths, cwd must be main's dr
|
|
return types.ExtractBits(uint64(f), 36, 1) != 0
|
|
}
|
|
func (f cFlags) IsPlatformBinary() bool { // IsPlatformBinary part of OS - can be loaded into LV process
|
|
return types.ExtractBits(uint64(f), 37, 1) != 0
|
|
}
|
|
func (f cFlags) IsBundle() bool {
|
|
return types.ExtractBits(uint64(f), 38, 1) != 0
|
|
}
|
|
func (f cFlags) IsDylib() bool {
|
|
return types.ExtractBits(uint64(f), 39, 1) != 0
|
|
}
|
|
func (f cFlags) IsExecutable() bool {
|
|
return types.ExtractBits(uint64(f), 40, 1) != 0
|
|
}
|
|
func (f cFlags) OverridableDylib() bool { // OverridableDylib only applicable to cached dylibs
|
|
return types.ExtractBits(uint64(f), 41, 1) != 0
|
|
}
|
|
func (f cFlags) InDyldCache() bool {
|
|
return types.ExtractBits(uint64(f), 42, 1) != 0
|
|
}
|
|
func (f cFlags) HasTerminators() bool {
|
|
return types.ExtractBits(uint64(f), 43, 1) != 0
|
|
}
|
|
func (f cFlags) HasReadOnlyData() bool {
|
|
return types.ExtractBits(uint64(f), 44, 1) != 0
|
|
}
|
|
func (f cFlags) HasChainedFixups() bool {
|
|
return types.ExtractBits(uint64(f), 45, 1) != 0
|
|
}
|
|
func (f cFlags) HasPrecomputedObjC() bool {
|
|
return types.ExtractBits(uint64(f), 46, 1) != 0
|
|
}
|
|
func (f cFlags) FixupsNotEncoded() bool {
|
|
return types.ExtractBits(uint64(f), 47, 1) != 0
|
|
}
|
|
func (f cFlags) RebasesNotEncoded() bool {
|
|
return types.ExtractBits(uint64(f), 48, 1) != 0
|
|
}
|
|
func (f cFlags) HasOverrideImageNum() bool {
|
|
return types.ExtractBits(uint64(f), 49, 1) != 0
|
|
}
|
|
func (f cFlags) String() string {
|
|
var flags []string
|
|
if f.IsInvalid() {
|
|
flags = append(flags, "invalid")
|
|
}
|
|
if !f.Has16KbPages() {
|
|
flags = append(flags, "4kb_pages")
|
|
}
|
|
if !f.Is64() {
|
|
flags = append(flags, "32bit")
|
|
}
|
|
if f.HasObjC() {
|
|
flags = append(flags, "objc")
|
|
}
|
|
if f.MayHavePlusLoads() {
|
|
flags = append(flags, "plus_loads")
|
|
}
|
|
if f.IsEncrypted() {
|
|
flags = append(flags, "encrypted")
|
|
}
|
|
if f.HasWeakDefs() {
|
|
flags = append(flags, "weak_defs")
|
|
}
|
|
if !f.NeverUnload() {
|
|
flags = append(flags, "can_unload")
|
|
}
|
|
if f.CwdSameAsThis() {
|
|
flags = append(flags, "cwd_same_as_this")
|
|
}
|
|
if f.IsPlatformBinary() {
|
|
flags = append(flags, "platform_bin")
|
|
}
|
|
if f.IsBundle() {
|
|
flags = append(flags, "bundle")
|
|
}
|
|
if f.IsDylib() {
|
|
flags = append(flags, "dylib")
|
|
}
|
|
if f.IsExecutable() {
|
|
flags = append(flags, "exe")
|
|
}
|
|
if f.OverridableDylib() {
|
|
flags = append(flags, "overridable")
|
|
}
|
|
if f.InDyldCache() {
|
|
flags = append(flags, "in_cache")
|
|
}
|
|
if f.HasTerminators() {
|
|
flags = append(flags, "terms")
|
|
}
|
|
if f.HasReadOnlyData() {
|
|
flags = append(flags, "readonly_data")
|
|
}
|
|
if f.HasChainedFixups() {
|
|
flags = append(flags, "fixups")
|
|
}
|
|
if f.HasPrecomputedObjC() {
|
|
flags = append(flags, "precomp_objc")
|
|
}
|
|
if f.FixupsNotEncoded() {
|
|
flags = append(flags, "fixups_not_enc")
|
|
}
|
|
if f.RebasesNotEncoded() {
|
|
flags = append(flags, "rebase_not_enc")
|
|
}
|
|
if f.HasOverrideImageNum() {
|
|
flags = append(flags, "override_img_no")
|
|
}
|
|
var max string
|
|
if f.MaxLoadCount() > 0 {
|
|
max = fmt.Sprintf("max_load: %d, ", f.MaxLoadCount())
|
|
}
|
|
return fmt.Sprintf("%s%s",
|
|
max,
|
|
strings.Join(flags, "|"),
|
|
)
|
|
}
|
|
|
|
type mappingInfoType struct {
|
|
TotalVmPages uint32
|
|
SliceOffsetIn4K uint32
|
|
}
|
|
type initializerSectionRangeType struct {
|
|
SectionOffset uint32
|
|
SectionSize uint32
|
|
}
|
|
type codeSignatureLocationType struct {
|
|
FileOffset uint32
|
|
FileSize uint32
|
|
}
|
|
type farPlayLocType struct {
|
|
RangeStart uint32
|
|
RangeLength uint32
|
|
}
|
|
type fileInfoType struct {
|
|
INode uint64
|
|
ModTime uint64
|
|
}
|
|
|
|
type warningType struct {
|
|
Type uint32
|
|
Message []byte
|
|
}
|
|
|
|
type PatchEntry struct {
|
|
OverriddenDylibInCache uint32
|
|
ExportCacheOffset uint32
|
|
Replacement resolvedSymbolTarget
|
|
}
|
|
|
|
type duplicateClassesHashTable struct {
|
|
// uint32_t capacity;
|
|
// uint32_t occupied;
|
|
// uint32_t shift;
|
|
// uint32_t mask;
|
|
// StringTarget sentinelTarget;
|
|
// uint32_t roundedTabSize;
|
|
// uint64_t salt;
|
|
}
|
|
|
|
// GetProgClosuresOffsets returns the closure trie data
|
|
// NOTE: this doesn't auto add the ProgClosuresAddr (and probably should)
|
|
func (f *File) GetProgClosuresOffsets() ([]trie.Node, error) {
|
|
var addr uint64
|
|
var size uint64
|
|
|
|
if f.Headers[f.UUID].ProgClosuresTrieAddr == 0 {
|
|
if f.Headers[f.UUID].ProgramTrieAddr == 0 {
|
|
return nil, fmt.Errorf("cache does not contain prog closures trie info")
|
|
} else {
|
|
addr = f.Headers[f.UUID].ProgramTrieAddr
|
|
size = uint64(f.Headers[f.UUID].ProgramTrieSize)
|
|
}
|
|
} else {
|
|
addr = f.Headers[f.UUID].ProgClosuresTrieAddr
|
|
size = f.Headers[f.UUID].ProgClosuresTrieSize
|
|
}
|
|
|
|
uuid, offset, err := f.GetOffset(addr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
sr := io.NewSectionReader(f.r[uuid], 0, 1<<63-1)
|
|
|
|
sr.Seek(int64(offset), io.SeekStart)
|
|
|
|
progClosuresTrie := make([]byte, size)
|
|
if err := binary.Read(sr, f.ByteOrder, &progClosuresTrie); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
r := bytes.NewReader(progClosuresTrie)
|
|
|
|
nodes, err := trie.ParseTrie(r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for idx, node := range nodes {
|
|
r.Seek(int64(node.Offset), io.SeekStart)
|
|
off, err := trie.ReadUleb128(r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
nodes[idx].Offset = off
|
|
}
|
|
|
|
return nodes, nil
|
|
}
|
|
|
|
// GetProgClosureAddress returns the closure pointer for a given path
|
|
func (f *File) GetProgClosureAddress(executablePath string) (uint64, error) {
|
|
|
|
var addr uint64
|
|
var size uint64
|
|
|
|
if f.Headers[f.UUID].ProgClosuresTrieAddr == 0 {
|
|
if f.Headers[f.UUID].ProgramTrieAddr == 0 {
|
|
return 0, fmt.Errorf("cache does not contain prog closures trie info")
|
|
} else {
|
|
addr = f.Headers[f.UUID].ProgramTrieAddr
|
|
size = uint64(f.Headers[f.UUID].ProgramTrieSize)
|
|
}
|
|
} else {
|
|
addr = f.Headers[f.UUID].ProgClosuresTrieAddr
|
|
size = f.Headers[f.UUID].ProgClosuresTrieSize
|
|
}
|
|
|
|
uuid, offset, err := f.GetOffset(addr)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
sr := io.NewSectionReader(f.r[uuid], 0, 1<<63-1)
|
|
|
|
sr.Seek(int64(offset), io.SeekStart)
|
|
|
|
progClosuresTrie := make([]byte, size)
|
|
if err := binary.Read(sr, f.ByteOrder, &progClosuresTrie); err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
r := bytes.NewReader(progClosuresTrie)
|
|
|
|
if _, err = trie.WalkTrie(r, executablePath); err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
closureOffset, err := trie.ReadUleb128(r)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return f.Headers[f.UUID].ProgClosuresAddr + closureOffset, nil
|
|
}
|
|
|
|
// GetDylibsImageArray returns the dylibs image array
|
|
func (f *File) GetProgClosureImageArray() error {
|
|
var addr uint64
|
|
var size uint64
|
|
|
|
if f.IsDyld4 {
|
|
return fmt.Errorf("dyld4 no longer uses prog closures")
|
|
}
|
|
|
|
if f.Headers[f.UUID].ProgClosuresAddr == 0 {
|
|
return fmt.Errorf("cache does not contain prog launch closure info")
|
|
} else {
|
|
addr = f.Headers[f.UUID].ProgClosuresAddr
|
|
size = f.Headers[f.UUID].ProgClosuresSize
|
|
}
|
|
|
|
u, offset, err := f.GetOffset(addr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
sr := io.NewSectionReader(f.r[u], 0, 1<<63-1)
|
|
|
|
sr.Seek(int64(offset), io.SeekStart)
|
|
|
|
imageArrayData := make([]byte, size)
|
|
if err := binary.Read(sr, f.ByteOrder, &imageArrayData); err != nil {
|
|
return err
|
|
}
|
|
|
|
return f.parseClosureContainers(bytes.NewReader(imageArrayData))
|
|
}
|
|
|
|
// GetDylibsImageArray returns the dylibs image array
|
|
func (f *File) GetDylibsImageArray() error {
|
|
var addr uint64
|
|
var size uint64
|
|
|
|
if f.Headers[f.UUID].DylibsImageArrayAddr == 0 {
|
|
return fmt.Errorf("cache does not contain dylibs image array info")
|
|
} else {
|
|
addr = f.Headers[f.UUID].DylibsImageArrayAddr
|
|
size = f.Headers[f.UUID].DylibsImageArraySize
|
|
}
|
|
|
|
u, offset, err := f.GetOffset(addr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
sr := io.NewSectionReader(f.r[u], 0, 1<<63-1)
|
|
|
|
sr.Seek(int64(offset), io.SeekStart)
|
|
|
|
imageArrayData := make([]byte, size)
|
|
if err := binary.Read(sr, f.ByteOrder, &imageArrayData); err != nil {
|
|
return err
|
|
}
|
|
|
|
return f.parseClosureContainers(bytes.NewReader(imageArrayData))
|
|
}
|
|
|
|
func (f *File) GetDylibsImageArrayIDs() ([]trie.Node, error) {
|
|
|
|
dylibsTrieAddr, dylibsTrieSize, err := f.dylibsTrieInfo()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
uuid, off, err := f.GetOffset(dylibsTrieAddr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
sr := io.NewSectionReader(f.r[uuid], 0, 1<<63-1)
|
|
|
|
sr.Seek(int64(off), io.SeekStart)
|
|
|
|
dylibTrie := make([]byte, dylibsTrieSize)
|
|
if err := binary.Read(sr, f.ByteOrder, &dylibTrie); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return trie.ParseTrie(bytes.NewReader(dylibTrie))
|
|
}
|
|
|
|
// GetDylibIndex returns the index of a given dylib
|
|
func (f *File) GetDylibIndex(path string) (uint64, error) {
|
|
|
|
dylibsTrieAddr, dylibsTrieSize, err := f.dylibsTrieInfo()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
uuid, off, err := f.GetOffset(dylibsTrieAddr)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
sr := io.NewSectionReader(f.r[uuid], 0, 1<<63-1)
|
|
|
|
sr.Seek(int64(off), io.SeekStart)
|
|
|
|
dylibTrie := make([]byte, dylibsTrieSize)
|
|
if err := binary.Read(sr, f.ByteOrder, &dylibTrie); err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
imageNode, err := trie.WalkTrie(bytes.NewReader(dylibTrie), path)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("dylib not found in dylibs trie")
|
|
}
|
|
|
|
imageIndex, _, err := trie.ReadUleb128FromBuffer(bytes.NewBuffer(dylibTrie[imageNode:]))
|
|
if err != nil {
|
|
return 0, fmt.Errorf("failed to read ULEB at image node in dyblibs trie")
|
|
}
|
|
|
|
return imageIndex, nil
|
|
}
|
|
|
|
func (f *File) dylibsTrieInfo() (uint64, uint64, error) {
|
|
header := f.Headers[f.UUID]
|
|
dylibsTrieFieldEnd := uint32(unsafe.Offsetof(CacheHeader{}.DylibsTrieSize) + unsafe.Sizeof(CacheHeader{}.DylibsTrieSize))
|
|
if header.MappingOffset < dylibsTrieFieldEnd {
|
|
return 0, 0, fmt.Errorf("cache does not contain dylibs trie info")
|
|
}
|
|
|
|
dylibsTrieAddr := header.DylibsTrieAddr
|
|
dylibsTrieSize := header.DylibsTrieSize
|
|
if dylibsTrieAddr == 0 || dylibsTrieSize == 0 {
|
|
return 0, 0, fmt.Errorf("cache does not contain dylibs trie info")
|
|
}
|
|
if f.size > 0 && dylibsTrieSize > uint64(f.size) {
|
|
return 0, 0, fmt.Errorf("dylibs trie size %#x exceeds cache size %#x", dylibsTrieSize, f.size)
|
|
}
|
|
|
|
return dylibsTrieAddr, dylibsTrieSize, nil
|
|
}
|
|
|
|
// GetDlopenOtherImageArray returns the other images array
|
|
func (f *File) GetDlopenOtherImageArray() error {
|
|
var addr uint64
|
|
var size uint64
|
|
|
|
if f.Headers[f.UUID].OtherImageArrayAddr == 0 {
|
|
return fmt.Errorf("cache does not contain other image array info")
|
|
} else {
|
|
addr = f.Headers[f.UUID].OtherImageArrayAddr
|
|
size = f.Headers[f.UUID].OtherImageArraySize
|
|
}
|
|
|
|
u, offset, err := f.GetOffset(addr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
sr := io.NewSectionReader(f.r[u], 0, 1<<63-1)
|
|
|
|
sr.Seek(int64(offset), io.SeekStart)
|
|
|
|
imageArrayData := make([]byte, size)
|
|
if err := binary.Read(sr, f.ByteOrder, &imageArrayData); err != nil {
|
|
return err
|
|
}
|
|
|
|
return f.parseClosureContainers(bytes.NewReader(imageArrayData))
|
|
}
|
|
|
|
// GetDlopenOtherImages returns the dlopen other images trie data
|
|
func (f *File) GetDlopenOtherImages() ([]trie.Node, error) {
|
|
|
|
if f.Headers[f.UUID].OtherTrieAddr == 0 {
|
|
return nil, fmt.Errorf("cache does not contain dlopen other image trie info")
|
|
}
|
|
|
|
uuid, offset, err := f.GetOffset(f.Headers[f.UUID].OtherTrieAddr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
sr := io.NewSectionReader(f.r[uuid], 0, 1<<63-1)
|
|
|
|
sr.Seek(int64(offset), io.SeekStart)
|
|
|
|
otherTrie := make([]byte, f.Headers[uuid].OtherTrieSize)
|
|
if err := binary.Read(sr, f.ByteOrder, &otherTrie); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
r := bytes.NewReader(otherTrie)
|
|
|
|
nodes, err := trie.ParseTrie(r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for idx, node := range nodes {
|
|
r.Seek(int64(node.Offset), io.SeekStart)
|
|
index, err := trie.ReadUleb128(r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
nodes[idx].Offset = index
|
|
}
|
|
|
|
return nodes, err
|
|
}
|
|
|
|
// GetDlopenOtherImageIndex returns the dlopen other image index for a given path
|
|
func (f *File) GetDlopenOtherImageIndex(path string) (uint64, error) {
|
|
sr := io.NewSectionReader(f.r[f.UUID], 0, 1<<63-1)
|
|
|
|
if f.Headers[f.UUID].OtherTrieAddr == 0 {
|
|
return 0, fmt.Errorf("cache does not contain dlopen other image trie info")
|
|
}
|
|
|
|
offset, err := f.GetOffsetForUUID(f.UUID, f.Headers[f.UUID].OtherTrieAddr)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
sr.Seek(int64(offset), io.SeekStart)
|
|
|
|
otherTrie := make([]byte, f.Headers[f.UUID].OtherTrieSize)
|
|
if err := binary.Read(sr, f.ByteOrder, &otherTrie); err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
r := bytes.NewReader(otherTrie)
|
|
|
|
if _, err = trie.WalkTrie(r, path); err != nil {
|
|
return 0, fmt.Errorf("failed to walk dlopen other image trie data: %v", err)
|
|
}
|
|
|
|
imageNum, err := trie.ReadUleb128(r)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("failed to read ULEB at image node in dlopen other image trie: %v", err)
|
|
}
|
|
|
|
return imageNum, nil
|
|
}
|
|
|
|
func (f *File) parseClosureContainers(r *bytes.Reader) error {
|
|
var container typedBytes
|
|
|
|
for {
|
|
err := binary.Read(r, binary.LittleEndian, &container)
|
|
|
|
if err == io.EOF {
|
|
break
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
switch container.Type() {
|
|
case launchClosure: // contains TypedBytes of closure attributes including imageArray
|
|
dat := make([]byte, container.PayloadLength())
|
|
if _, err := r.Read(dat); err != nil {
|
|
return fmt.Errorf("failed to read launchClosure data: %v", err)
|
|
}
|
|
if err := f.parseLaunchClosure(bytes.NewReader(dat)); err != nil {
|
|
return fmt.Errorf("failed to parse launch closure: %v", err)
|
|
}
|
|
case imageArray: // sizeof(ImageArray) + sizeof(uint32_t)*count + size of all images
|
|
if _, err := r.Seek(-int64(sizeOfTypeBytes), io.SeekCurrent); err != nil {
|
|
return fmt.Errorf("failed to rewind the reader: %v", err)
|
|
}
|
|
dat := make([]byte, container.PayloadLength()+sizeOfTypeBytes)
|
|
if _, err := r.Read(dat); err != nil {
|
|
return fmt.Errorf("failed to read imageArray data: %v", err)
|
|
}
|
|
cimages, err := f.parseClosureImageArray(bytes.NewReader(dat))
|
|
if err != nil {
|
|
return fmt.Errorf("failed to parse closure image array: %v", err)
|
|
}
|
|
for idx, cimg := range cimages {
|
|
if _, ok := f.ImageArray[cimg.ID]; ok {
|
|
return fmt.Errorf("image array map already contains image ID %d at index %d", cimg.ID, idx)
|
|
}
|
|
f.ImageArray[cimg.ID] = cimg
|
|
}
|
|
case image: // contains TypedBytes of image attributes
|
|
panic("closure container image not implimented")
|
|
case dlopenClosure: // contains TypedBytes of closure attributes including imageArray
|
|
panic("closure container dlopenClosure not implimented")
|
|
default:
|
|
return fmt.Errorf("got unexpected cointainer type %s", container.Type())
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (f *File) parseLaunchClosure(r *bytes.Reader) error {
|
|
lc := &LaunchClosure{}
|
|
|
|
var tbytes typedBytes
|
|
for {
|
|
err := binary.Read(r, binary.LittleEndian, &tbytes)
|
|
|
|
if err == io.EOF {
|
|
break
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
switch tbytes.Type() {
|
|
case imageArray:
|
|
if _, err := r.Seek(-int64(sizeOfTypeBytes), io.SeekCurrent); err != nil {
|
|
return fmt.Errorf("failed to rewind the reader: %v", err)
|
|
}
|
|
dat := make([]byte, tbytes.PayloadLength()+sizeOfTypeBytes)
|
|
if _, err := r.Read(dat); err != nil {
|
|
return fmt.Errorf("failed to read imageArray data: %v", err)
|
|
}
|
|
cimages, err := f.parseClosureImageArray(bytes.NewReader(dat))
|
|
if err != nil {
|
|
return fmt.Errorf("failed to parse %s: %v", tbytes.Type(), err)
|
|
}
|
|
lc.Images = append(lc.Images, cimages...)
|
|
case closureFlags: // sizeof(Closure::Flags)
|
|
if err := binary.Read(r, binary.LittleEndian, &lc.Flags); err != nil {
|
|
return fmt.Errorf("failed to parse %s: %v", tbytes.Type(), err)
|
|
}
|
|
case dyldCacheUUID: // 16
|
|
if err := binary.Read(r, binary.LittleEndian, &lc.DyldUUID); err != nil {
|
|
return fmt.Errorf("failed to parse %s: %v", tbytes.Type(), err)
|
|
}
|
|
case missingFiles:
|
|
if tbytes.PayloadLength() > 0 {
|
|
dat := make([]byte, tbytes.PayloadLength())
|
|
if _, err := r.Read(dat); err != nil {
|
|
return fmt.Errorf("failed to parse %s: %v", tbytes.Type(), err)
|
|
}
|
|
sr := bytes.NewBuffer(dat)
|
|
for {
|
|
s, err := sr.ReadString('\x00')
|
|
if err == io.EOF {
|
|
break
|
|
}
|
|
if err != nil {
|
|
return fmt.Errorf("failed to read string: %v", err)
|
|
}
|
|
s = strings.Trim(s, "\x00")
|
|
if len(s) > 0 {
|
|
lc.MissingFiles = append(lc.MissingFiles, s)
|
|
}
|
|
}
|
|
}
|
|
case envVar: // "DYLD_BLAH=stuff"
|
|
if tbytes.PayloadLength() > 0 {
|
|
dat := make([]byte, tbytes.PayloadLength())
|
|
if _, err := r.Read(dat); err != nil {
|
|
return fmt.Errorf("failed to parse %s: %v", tbytes.Type(), err)
|
|
}
|
|
lc.EnvVars = append(lc.EnvVars, string(dat))
|
|
}
|
|
case topImage: // sizeof(ImageNum)
|
|
if err := binary.Read(r, binary.LittleEndian, &lc.TopImage); err != nil {
|
|
return fmt.Errorf("failed to parse %s: %v", tbytes.Type(), err)
|
|
}
|
|
case libDyldEntry: // sizeof(ResolvedSymbolTarget)
|
|
if err := binary.Read(r, binary.LittleEndian, &lc.LibDyldEntry); err != nil {
|
|
return fmt.Errorf("failed to parse %s: %v", tbytes.Type(), err)
|
|
}
|
|
case libSystemNum: // sizeof(ImageNum)
|
|
if err := binary.Read(r, binary.LittleEndian, &lc.LibSystemNum); err != nil {
|
|
return fmt.Errorf("failed to parse %s: %v", tbytes.Type(), err)
|
|
}
|
|
case mainEntry: // sizeof(ResolvedSymbolTarget)
|
|
if err := binary.Read(r, binary.LittleEndian, &lc.MainEntry); err != nil {
|
|
return fmt.Errorf("failed to parse %s: %v", tbytes.Type(), err)
|
|
}
|
|
case startEntry: // sizeof(ResolvedSymbolTarget) // used by programs built with crt1.o
|
|
if err := binary.Read(r, binary.LittleEndian, &lc.StartEntry); err != nil {
|
|
return fmt.Errorf("failed to parse %s: %v", tbytes.Type(), err)
|
|
}
|
|
case cacheOverrides: // sizeof(PatchEntry) * count // used if process uses interposing or roots (cached dylib overrides)
|
|
lc.DyldCacheFixups = make([]PatchEntry, tbytes.PayloadLength()/uint32(binary.Size(PatchEntry{})))
|
|
if err := binary.Read(r, binary.LittleEndian, &lc.DyldCacheFixups); err != nil {
|
|
return fmt.Errorf("failed to parse %s: %v", tbytes.Type(), err)
|
|
}
|
|
case interposeTuples: // sizeof(InterposingTuple) * count
|
|
panic("launch closure type interposeTuples not implimented")
|
|
case existingFiles: // uint64_t + (SkippedFiles * count)
|
|
panic("launch closure type existingFiles not implimented")
|
|
case selectorTable: // uint32_t + (sizeof(ObjCSelectorImage) * count) + hashTable size
|
|
// panic("not implimented")
|
|
// utils.Indent(log.Debug, 2)("selectorTable - Skipping") // TODO: FINISH THESE !!!! 🤬
|
|
r.Seek(int64(tbytes.PayloadLength()), io.SeekCurrent)
|
|
case classTable: // (3 * uint32_t) + (sizeof(ObjCClassImage) * count) + classHashTable size + protocolHashTable size
|
|
// panic("not implimented")
|
|
// utils.Indent(log.Debug, 2)("classTable - Skipping")
|
|
r.Seek(int64(tbytes.PayloadLength()), io.SeekCurrent)
|
|
case warning: // len = uint32_t + length path + 1 use one entry per warning
|
|
var warn warningType
|
|
if err := binary.Read(r, binary.LittleEndian, &warn.Type); err != nil {
|
|
return fmt.Errorf("failed to parse %s: %v", tbytes.Type(), err)
|
|
}
|
|
warn.Message = make([]byte, tbytes.PayloadLength()-uint32(binary.Size(warn.Type)))
|
|
if err := binary.Read(r, binary.LittleEndian, &warn.Message); err != nil {
|
|
return fmt.Errorf("failed to parse %s: %v", tbytes.Type(), err)
|
|
}
|
|
lc.Warnings = append(lc.Warnings, warn)
|
|
case duplicateClassesTable: // duplicateClassesHashTable
|
|
// var s stringHash
|
|
// if err := binary.Read(r, binary.LittleEndian, &s); err != nil {
|
|
// return fmt.Errorf("failed to parse %s: %v", tbytes.Type(), err)
|
|
// }
|
|
// fmt.Printf("delta: %#x\n", tbytes.PayloadLength()-uint32(binary.Size(s)))
|
|
// fmt.Println(s)
|
|
// panic("not implimented")
|
|
// utils.Indent(log.Debug, 2)("duplicateClassesTable - Skipping")
|
|
r.Seek(int64(tbytes.PayloadLength()), io.SeekCurrent)
|
|
case progVars: // sizeof(uint32_t)
|
|
if err := binary.Read(r, binary.LittleEndian, &lc.ProgVars); err != nil {
|
|
return fmt.Errorf("failed to parse %s: %v", tbytes.Type(), err)
|
|
}
|
|
default:
|
|
return fmt.Errorf("found unsupported launch closure type %s", tbytes.Type())
|
|
}
|
|
}
|
|
|
|
f.Closures = append(f.Closures, lc)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (f *File) parseClosureImageArray(r *bytes.Reader) ([]*CImage, error) {
|
|
var itype typedBytes
|
|
var iarray ImageArray
|
|
var cimages []*CImage
|
|
|
|
if err := binary.Read(r, binary.LittleEndian, &iarray.imageArrayType); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
iarray.Offsets = make([]uint32, iarray.CR.Count())
|
|
if err := binary.Read(r, binary.LittleEndian, &iarray.Offsets); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for i := uint32(0); i < iarray.CR.Count(); i++ {
|
|
if err := binary.Read(r, binary.LittleEndian, &itype); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if itype.Type() != image {
|
|
return nil, fmt.Errorf("got unexpected type %s; expected image", itype.Type())
|
|
}
|
|
|
|
dat := make([]byte, itype.PayloadLength())
|
|
if _, err := r.Read(dat); err != nil {
|
|
return nil, fmt.Errorf("failed to read image data: %v", err)
|
|
}
|
|
|
|
ci, err := parseClosureImage(bytes.NewReader(dat))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to parse %s: %v", itype, err)
|
|
}
|
|
|
|
if ci.ID != 0 { // TODO: why do these zeroed out images exist?
|
|
cimages = append(cimages, ci)
|
|
}
|
|
}
|
|
|
|
return cimages, nil
|
|
}
|
|
|
|
func parseClosureImage(r *bytes.Reader) (*CImage, error) {
|
|
var tbytes typedBytes
|
|
|
|
ci := &CImage{}
|
|
|
|
for { // parse image
|
|
err := binary.Read(r, binary.LittleEndian, &tbytes)
|
|
|
|
if err == io.EOF {
|
|
break
|
|
}
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
switch tbytes.Type() {
|
|
case imageFlags:
|
|
if err := binary.Read(r, binary.LittleEndian, &ci.Flags); err != nil {
|
|
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
|
|
}
|
|
ci.ID = ci.GetID()
|
|
case pathWithHash:
|
|
if err := binary.Read(r, binary.LittleEndian, &ci.Hash); err != nil {
|
|
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
|
|
}
|
|
dat := make([]byte, tbytes.PayloadLength()-uint32(binary.Size(uint32(0))))
|
|
if err := binary.Read(r, binary.LittleEndian, &dat); err != nil {
|
|
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
|
|
}
|
|
ci.Name = strings.Trim(string(dat), "\x00")
|
|
case fileInodeAndTime:
|
|
if err := binary.Read(r, binary.LittleEndian, &ci.FileInfo); err != nil {
|
|
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
|
|
}
|
|
case cdHash:
|
|
if tbytes.PayloadLength() != 20 {
|
|
return nil, fmt.Errorf("found unexpected cdhash of length %d; expected 20", tbytes.PayloadLength())
|
|
}
|
|
ci.CDHash = make([]byte, tbytes.PayloadLength())
|
|
if err := binary.Read(r, binary.LittleEndian, &ci.CDHash); err != nil {
|
|
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
|
|
}
|
|
case uuid:
|
|
if err := binary.Read(r, binary.LittleEndian, &ci.UUID); err != nil {
|
|
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
|
|
}
|
|
case mappingInfo:
|
|
if err := binary.Read(r, binary.LittleEndian, &ci.MappingInfo); err != nil {
|
|
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
|
|
}
|
|
case diskSegment:
|
|
ci.DiskSegments = make([]diskSegmentType, tbytes.PayloadLength()/uint32(binary.Size(diskSegmentType(0))))
|
|
if err := binary.Read(r, binary.LittleEndian, &ci.DiskSegments); err != nil {
|
|
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
|
|
}
|
|
case cacheSegment:
|
|
ci.CacheSegments = make([]cacheSegmentType, tbytes.PayloadLength()/uint32(binary.Size(cacheSegmentType(0))))
|
|
if err := binary.Read(r, binary.LittleEndian, &ci.CacheSegments); err != nil {
|
|
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
|
|
}
|
|
case dependents:
|
|
ci.Dependents = make([]linkedImage, tbytes.PayloadLength()/uint32(binary.Size(linkedImage(0))))
|
|
if err := binary.Read(r, binary.LittleEndian, &ci.Dependents); err != nil {
|
|
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
|
|
}
|
|
case initOffsets:
|
|
ci.InitializerOffsets = make([]uint32, tbytes.PayloadLength()/uint32(binary.Size(uint32(0))))
|
|
if err := binary.Read(r, binary.LittleEndian, &ci.InitializerOffsets); err != nil {
|
|
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
|
|
}
|
|
case dofOffsets:
|
|
ci.DofOffsets = make([]uint32, tbytes.PayloadLength()/uint32(binary.Size(uint32(0))))
|
|
if err := binary.Read(r, binary.LittleEndian, &ci.DofOffsets); err != nil {
|
|
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
|
|
}
|
|
case codeSignLoc:
|
|
if err := binary.Read(r, binary.LittleEndian, &ci.CodeSignatureLocation); err != nil {
|
|
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
|
|
}
|
|
case farPlayLoc:
|
|
if err := binary.Read(r, binary.LittleEndian, &ci.FarPlayLocation); err != nil {
|
|
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
|
|
}
|
|
case rebaseFixups:
|
|
ci.Rebases = make([]rebasePatternType, tbytes.PayloadLength()/uint32(binary.Size(rebasePatternType(0))))
|
|
if err := binary.Read(r, binary.LittleEndian, &ci.Rebases); err != nil {
|
|
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
|
|
}
|
|
case bindFixups:
|
|
ci.Binds = make([]BindPattern, tbytes.PayloadLength()/uint32(binary.Size(BindPattern{})))
|
|
if err := binary.Read(r, binary.LittleEndian, &ci.Binds); err != nil {
|
|
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
|
|
}
|
|
case cachePatchInfo:
|
|
return nil, fmt.Errorf("found deprecated type %s", tbytes.Type())
|
|
case textFixups:
|
|
ci.TextFixups = make([]TextFixupPattern, tbytes.PayloadLength()/uint32(binary.Size(TextFixupPattern{})))
|
|
if err := binary.Read(r, binary.LittleEndian, &ci.TextFixups); err != nil {
|
|
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
|
|
}
|
|
case imageOverride:
|
|
if err := binary.Read(r, binary.LittleEndian, &ci.ImageOverride); err != nil {
|
|
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
|
|
}
|
|
case initBefores:
|
|
ci.InitOrder = make([]uint32, tbytes.PayloadLength()/uint32(binary.Size(uint32(0))))
|
|
if err := binary.Read(r, binary.LittleEndian, &ci.InitOrder); err != nil {
|
|
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
|
|
}
|
|
case initsSection:
|
|
ci.InitializerSections = make([]initializerSectionRangeType, tbytes.PayloadLength()/uint32(binary.Size(initializerSectionRangeType{})))
|
|
if err := binary.Read(r, binary.LittleEndian, &ci.InitializerSections); err != nil {
|
|
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
|
|
}
|
|
case chainedFixupsTargets:
|
|
ci.ChainedFixupsTargets = make([]resolvedSymbolTarget, tbytes.PayloadLength()/uint32(binary.Size(resolvedSymbolTarget(0))))
|
|
if err := binary.Read(r, binary.LittleEndian, &ci.ChainedFixupsTargets); err != nil {
|
|
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
|
|
}
|
|
case termOffsets:
|
|
ci.TerminatorOffsets = make([]uint32, tbytes.PayloadLength()/uint32(binary.Size(uint32(0))))
|
|
if err := binary.Read(r, binary.LittleEndian, &ci.TerminatorOffsets); err != nil {
|
|
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
|
|
}
|
|
case chainedStartsOffset:
|
|
if err := binary.Read(r, binary.LittleEndian, &ci.ChainedStartsOffset); err != nil {
|
|
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
|
|
}
|
|
case objcFixups:
|
|
contentSize := int(tbytes.PayloadLength())
|
|
if err := binary.Read(r, binary.LittleEndian, &ci.ObjcFixups.objcFixupsType); err != nil {
|
|
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
|
|
}
|
|
contentSize -= binary.Size(objcFixupsType{})
|
|
if ci.ObjcFixups.ProtocolFixupCount > 0 {
|
|
ci.ObjcFixups.ProtocolISAFixups = make([]protocolISAFixup, ci.ObjcFixups.ProtocolFixupCount)
|
|
if err := binary.Read(r, binary.LittleEndian, &ci.ObjcFixups.ProtocolISAFixups); err != nil {
|
|
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
|
|
}
|
|
contentSize -= binary.Size(ci.ObjcFixups.ProtocolISAFixups)
|
|
}
|
|
if ci.ObjcFixups.SelRefFixupCount > 0 {
|
|
ci.ObjcFixups.SelRefFixups = make([]SelectorReferenceFixup, ci.ObjcFixups.SelRefFixupCount)
|
|
if err := binary.Read(r, binary.LittleEndian, &ci.ObjcFixups.SelRefFixups); err != nil {
|
|
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
|
|
}
|
|
contentSize -= binary.Size(ci.ObjcFixups.SelRefFixups)
|
|
}
|
|
if contentSize > 0 {
|
|
if err := binary.Read(r, binary.LittleEndian, &ci.ObjcFixups.StableSwiftFixupCount); err != nil {
|
|
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
|
|
}
|
|
if err := binary.Read(r, binary.LittleEndian, &ci.ObjcFixups.MethodListFixupCount); err != nil {
|
|
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
|
|
}
|
|
if ci.ObjcFixups.StableSwiftFixupCount > 0 {
|
|
ci.ObjcFixups.ClassStableSwiftFixups = make([]classStableSwiftFixup, ci.ObjcFixups.StableSwiftFixupCount)
|
|
if err := binary.Read(r, binary.LittleEndian, &ci.ObjcFixups.ClassStableSwiftFixups); err != nil {
|
|
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
|
|
}
|
|
}
|
|
if ci.ObjcFixups.MethodListFixupCount > 0 {
|
|
ci.ObjcFixups.MethodListFixups = make([]methodListFixup, ci.ObjcFixups.MethodListFixupCount)
|
|
if err := binary.Read(r, binary.LittleEndian, &ci.ObjcFixups.MethodListFixups); err != nil {
|
|
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
|
|
}
|
|
}
|
|
}
|
|
default:
|
|
return nil, fmt.Errorf("found unsupported type %s", tbytes.Type())
|
|
}
|
|
}
|
|
|
|
return ci, nil
|
|
}
|