Files
ipsw/pkg/dyld/utils.go

201 lines
7.5 KiB
Go

package dyld
import (
"encoding/binary"
"fmt"
"strings"
"github.com/blacktop/go-macho/types"
)
func output(show bool, fmtStr string, args ...any) {
if show {
fmt.Printf(fmtStr, args...)
}
}
// Is64bit returns if dyld is 64bit or not
func (f *File) Is64bit() bool {
return strings.Contains(f.Headers[f.UUID].Magic.String(), "64")
}
// IsArm64 returns if dyld is arm64 or not (meaning I can disassemble it)
func (f *File) IsArm64() bool {
return strings.Contains(f.Headers[f.UUID].Magic.String(), "arm64")
}
// GetOffset returns the offset for a given virtual address and the cache's UUID that contains it
func (f *File) GetOffset(address uint64) (types.UUID, uint64, error) {
for uuid, cacheMaps := range f.Mappings {
for _, mapping := range cacheMaps {
if mapping.Address <= address && address < mapping.Address+mapping.Size {
return uuid, (address - mapping.Address) + mapping.FileOffset, nil
}
}
}
var badUUID types.UUID // will create a NULL uuid
return badUUID, 0, fmt.Errorf("address %#x not within any mappings address range", address)
}
// GetOffsetForUUID returns the offset for a given virtual address for a given cache UUID
func (f *File) GetOffsetForUUID(uuid types.UUID, address uint64) (uint64, error) {
for _, mapping := range f.Mappings[uuid] {
if mapping.Address <= address && address < mapping.Address+mapping.Size {
return (address - mapping.Address) + mapping.FileOffset, nil
}
}
return 0, fmt.Errorf("address %#x not within any mappings address range", address)
}
// IsAddressInCache returns if the virtual address is in the cache's mappings
func (f *File) IsAddressInCache(uuid types.UUID, address uint64) bool {
for _, mapping := range f.Mappings[uuid] {
if mapping.Address <= address && address < mapping.Address+mapping.Size {
return true
}
}
return false
}
// GetVMAddress returns a map of uuids to virtual address for a given offset
func (f *File) GetVMAddress(offset uint64) (map[types.UUID]uint64, error) {
uuid2addr := make(map[types.UUID]uint64)
for uuid, cacheMaps := range f.Mappings {
for _, mapping := range cacheMaps {
if mapping.FileOffset <= offset && offset < mapping.FileOffset+mapping.Size {
uuid2addr[uuid] = (offset - mapping.FileOffset) + mapping.Address
}
}
}
if len(uuid2addr) == 0 {
return nil, fmt.Errorf("offset %#x not within any mappings file offset range", offset)
}
return uuid2addr, nil
}
// GetVMAddressForUUID returns the virtual address for a given offset for a given cache UUID
func (f *File) GetVMAddressForUUID(uuid types.UUID, offset uint64) (uint64, error) {
for _, mapping := range f.MappingsWithSlideInfo[uuid] {
if mapping.FileOffset <= offset && offset < mapping.FileOffset+mapping.Size {
return (offset - mapping.FileOffset) + mapping.Address, nil
}
}
return 0, fmt.Errorf("offset %#x not within any mappings file offset range", offset)
}
func (f *File) GetCacheOffset(vmoffset uint64) (types.UUID, uint64, error) {
if vmoffset < f.SubCacheInfo[0].CacheVMOffset {
return f.UUID, vmoffset, nil // vm offset in primary subcache
}
for idx, scinfo := range f.SubCacheInfo { // check the sub subcaches
if idx < len(f.SubCacheInfo)-1 {
if scinfo.CacheVMOffset <= vmoffset && vmoffset < f.SubCacheInfo[idx+1].CacheVMOffset {
return scinfo.UUID, vmoffset - scinfo.CacheVMOffset, nil
}
} else {
if scinfo.CacheVMOffset <= vmoffset {
return scinfo.UUID, vmoffset - scinfo.CacheVMOffset, nil
}
}
}
// NOTE: via the dyld src comments; the .symbols subcache is unmmapped
return types.UUID{}, 0, fmt.Errorf("offset %#x not within any sub cache VM offset range", vmoffset)
}
func (f *File) GetCacheOffsetFromAddress(addr uint64) (types.UUID, uint64, error) {
vmstart := f.Headers[f.UUID].SharedRegionStart
for idx, scinfo := range f.SubCacheInfo { // check the sub subcaches
if idx < len(f.SubCacheInfo)-1 {
if vmstart+scinfo.CacheVMOffset <= addr && addr < vmstart+f.SubCacheInfo[idx+1].CacheVMOffset {
return scinfo.UUID, addr - (vmstart + scinfo.CacheVMOffset) + scinfo.CacheVMOffset, nil
}
} else {
if vmstart+scinfo.CacheVMOffset <= addr {
return scinfo.UUID, addr - (vmstart + scinfo.CacheVMOffset) + scinfo.CacheVMOffset, nil
}
}
}
// NOTE: via the dyld src comments; the .symbols subcache is unmmapped
return types.UUID{}, 0, fmt.Errorf("address %#x not within any sub cache VM offset range", addr)
}
func (f *File) GetCacheVMAddress(offset uint64) (types.UUID, uint64, error) {
if offset < f.SubCacheInfo[0].CacheVMOffset {
return f.UUID, f.MappingsWithSlideInfo[f.UUID][0].Address + offset, nil // vm addr in primary subcache
}
for idx, scinfo := range f.SubCacheInfo { // check the sub subcaches
if idx < len(f.SubCacheInfo)-1 {
if scinfo.CacheVMOffset <= offset && offset < f.SubCacheInfo[idx+1].CacheVMOffset {
return scinfo.UUID, f.MappingsWithSlideInfo[scinfo.UUID][0].Address + (offset - scinfo.CacheVMOffset), nil
}
} else {
if scinfo.CacheVMOffset <= offset {
return scinfo.UUID, f.MappingsWithSlideInfo[scinfo.UUID][0].Address + (offset - scinfo.CacheVMOffset), nil
}
}
}
// NOTE: via the dyld src comments; the .symbols subcache is unmmapped
return types.UUID{}, 0, fmt.Errorf("offset %#x not within any sub cache VM offset range", offset)
}
// GetMappingForOffsetForUUID returns the mapping containing a given file offset for a given cache UUID
func (f *File) GetMappingForOffsetForUUID(uuid types.UUID, offset uint64) (*CacheMapping, error) {
for _, mapping := range f.Mappings[uuid] {
if mapping.FileOffset <= offset && offset < mapping.FileOffset+mapping.Size {
return mapping, nil
}
}
return nil, fmt.Errorf("offset %#x not within any mappings file offset range", offset)
}
// GetMappingForVMAddress returns the mapping containing a given virtual address
func (f *File) GetMappingForVMAddress(address uint64) (types.UUID, *CacheMappingWithSlideInfo, error) {
for uuid := range f.MappingsWithSlideInfo {
for _, mapping := range f.MappingsWithSlideInfo[uuid] {
if mapping.Address <= address && address < mapping.Address+mapping.Size {
return uuid, mapping, nil
}
}
}
return types.UUID{}, nil, fmt.Errorf("address %#x not within any mapping's address range", address)
}
// ReadBytesForUUID returns bytes at a given offset for a given cache UUID
func (f *File) ReadBytesForUUID(uuid types.UUID, offset int64, size uint64) ([]byte, error) {
data := make([]byte, size)
if _, err := f.r[uuid].ReadAt(data, offset); err != nil {
return nil, fmt.Errorf("failed to read bytes at offset %#x: %v", offset, err)
}
return data, nil
}
// ReadPointerForUUID returns pointer at a given offset for a given cache UUID
func (f *File) ReadPointerForUUID(uuid types.UUID, offset uint64) (uint64, error) {
u64 := make([]byte, 8)
if _, err := f.r[uuid].ReadAt(u64, int64(offset)); err != nil {
return 0, fmt.Errorf("failed to read pointer at offset %#x: %v", offset, err)
}
return binary.LittleEndian.Uint64(u64), nil
}
// ReadPointerAtAddress returns pointer at a given virtual address
func (f *File) ReadPointerAtAddress(address uint64) (uint64, error) {
uuid, offset, err := f.GetOffset(address)
if err != nil {
return 0, fmt.Errorf("failed to get offset for address %#x: %v", address, err)
}
return f.ReadPointerForUUID(uuid, offset)
}
func (f *File) GetSubCacheExtensionFromUUID(uuid types.UUID) (string, error) {
for idx, sc := range f.SubCacheInfo {
if sc.UUID == uuid {
if len(sc.Extention) == 0 {
return fmt.Sprintf(".%d", idx), nil
}
return sc.Extention, nil
}
}
return "", fmt.Errorf("failed to find subcache extension for uuid %s", uuid.String())
}