Files
ipsw/pkg/kernelcache/kext.go
T
2023-04-12 20:00:05 -06:00

207 lines
7.9 KiB
Go

package kernelcache
import (
"bytes"
"encoding/binary"
"fmt"
"sort"
"github.com/apex/log"
"github.com/blacktop/go-macho"
"github.com/blacktop/go-macho/pkg/fixupchains"
"github.com/blacktop/go-plist"
)
const tagPtrMask = 0xffff000000000000
type PrelinkInfo struct {
PrelinkInfoDictionary []CFBundle `plist:"_PrelinkInfoDictionary,omitempty" json:"prelink_info_dictionary,omitempty"`
}
type CFBundle struct {
ID string `plist:"CFBundleIdentifier,omitempty" json:"id,omitempty"`
Name string `plist:"CFBundleName,omitempty" json:"name,omitempty"`
SDK string `plist:"DTSDKName,omitempty" json:"sdk,omitempty"`
SDKBuild string `plist:"DTSDKBuild,omitempty" json:"sdk_build,omitempty"`
Xcode string `plist:"DTXcode,omitempty" json:"xcode,omitempty"`
XcodeBuild string `plist:"DTXcodeBuild,omitempty" json:"xcode_build,omitempty"`
Copyright string `plist:"NSHumanReadableCopyright,omitempty" json:"copyright,omitempty"`
BuildMachineOSBuild string `plist:"BuildMachineOSBuild,omitempty" json:"build_machine_os_build,omitempty"`
DevelopmentRegion string `plist:"CFBundleDevelopmentRegion,omitempty" json:"development_region,omitempty"`
PlatformName string `plist:"DTPlatformName,omitempty" json:"platform_name,omitempty"`
PlatformVersion string `plist:"DTPlatformVersion,omitempty" json:"platform_version,omitempty"`
PlatformBuild string `plist:"DTPlatformBuild,omitempty" json:"platform_build,omitempty"`
PackageType string `plist:"CFBundlePackageType,omitempty" json:"package_type,omitempty"`
Version string `plist:"CFBundleVersion,omitempty" json:"version,omitempty"`
ShortVersionString string `plist:"CFBundleShortVersionString,omitempty" json:"short_version_string,omitempty"`
CompatibleVersion string `plist:"OSBundleCompatibleVersion,omitempty" json:"compatible_version,omitempty"`
MinimumOSVersion string `plist:"MinimumOSVersion,omitempty" json:"minimum_os_version,omitempty"`
SupportedPlatforms []string `plist:"CFBundleSupportedPlatforms,omitempty" json:"supported_platforms,omitempty"`
Signature string `plist:"CFBundleSignature,omitempty" json:"signature,omitempty"`
IOKitPersonalities map[string]interface{} `plist:"IOKitPersonalities,omitempty" json:"io_kit_personalities,omitempty"`
OSBundleLibraries map[string]string `plist:"OSBundleLibraries,omitempty" json:"os_bundle_libraries,omitempty"`
UIDeviceFamily []int `plist:"UIDeviceFamily,omitempty" json:"ui_device_family,omitempty"`
OSBundleRequired string `plist:"OSBundleRequired,omitempty" json:"os_bundle_required,omitempty"`
UIRequiredDeviceCapabilities []string `plist:"UIRequiredDeviceCapabilities,omitempty" json:"ui_required_device_capabilities,omitempty"`
AppleSecurityExtension bool `plist:"AppleSecurityExtension,omitempty" json:"apple_security_extension,omitempty"`
InfoDictionaryVersion string `plist:"CFBundleInfoDictionaryVersion,omitempty" json:"info_dictionary_version,omitempty"`
OSKernelResource bool `plist:"OSKernelResource,omitempty" json:"os_kernel_resource,omitempty"`
GetInfoString string `plist:"CFBundleGetInfoString,omitempty" json:"get_info_string,omitempty"`
AllowUserLoad bool `plist:"OSBundleAllowUserLoad,omitempty" json:"allow_user_load,omitempty"`
ExecutableLoadAddr uint64 `plist:"_PrelinkExecutableLoadAddr,omitempty" json:"executable_load_addr,omitempty"`
ModuleIndex uint64 `plist:"ModuleIndex,omitempty" json:"module_index,omitempty"`
Executable string `plist:"CFBundleExecutable,omitempty" json:"executable,omitempty"`
BundlePath string `plist:"_PrelinkBundlePath,omitempty" json:"bundle_path,omitempty"`
RelativePath string `plist:"_PrelinkExecutableRelativePath,omitempty" json:"relative_path,omitempty"`
}
type KmodInfoT struct {
NextAddr uint64
InfoVersion int32
ID uint32
Name [64]byte
Version [64]byte
ReferenceCount int32 // # linkage refs to this
ReferenceListAddr uint64 // who this refs (links on)
Address uint64 // starting address
Size uint64 // total size
HeaderSize uint64 // unwired hdr size
StartAddr uint64
StopAddr uint64
}
func (i KmodInfoT) String() string {
return fmt.Sprintf("id: %#x, name: %s, version: %s, ref_cnt: %d, ref_list: %#x, addr: %#x, size: %#x, header_size: %#x, start: %#x, stop: %#x, next: %#x, info_ver: %d",
i.ID,
string(i.Name[:]),
string(i.Version[:]),
i.ReferenceCount,
i.ReferenceListAddr,
i.Address,
i.Size,
i.HeaderSize,
i.StartAddr,
i.StopAddr,
i.NextAddr,
i.InfoVersion,
)
}
func GetKextStartVMAddrs(m *macho.File) ([]uint64, error) {
if kmodStart := m.Section("__PRELINK_INFO", "__kmod_start"); kmodStart != nil {
data, err := kmodStart.Data()
if err != nil {
return nil, err
}
ptrs := make([]uint64, kmodStart.Size/8)
if err := binary.Read(bytes.NewReader(data), binary.LittleEndian, &ptrs); err != nil {
return nil, err
}
return ptrs, nil
}
return nil, fmt.Errorf("section __PRELINK_INFO.__kmod_start not found")
}
func GetKextInfos(m *macho.File) ([]KmodInfoT, error) {
var infos []KmodInfoT
if kmodStart := m.Section("__PRELINK_INFO", "__kmod_info"); kmodStart != nil {
data, err := kmodStart.Data()
if err != nil {
return nil, err
}
ptrs := make([]uint64, kmodStart.Size/8)
if err := binary.Read(bytes.NewReader(data), binary.LittleEndian, &ptrs); err != nil {
return nil, err
}
for _, ptr := range ptrs {
// fmt.Printf("ptr: %#x, untagged: %#x\n", ptr, unTag(ptr))
off, err := m.GetOffset(ptr | tagPtrMask)
if err != nil {
return nil, err
}
info := KmodInfoT{}
infoBytes := make([]byte, binary.Size(info))
_, err = m.ReadAt(infoBytes, int64(off))
if err != nil {
return nil, err
}
if err := binary.Read(bytes.NewReader(infoBytes), binary.LittleEndian, &info); err != nil {
return nil, fmt.Errorf("failed to read KmodInfoT at %#x: %v", off, err)
}
// fixups
info.StartAddr = fixupchains.DyldChainedPtr64KernelCacheRebase{Pointer: info.StartAddr}.Target() + m.GetBaseAddress()
info.StopAddr = fixupchains.DyldChainedPtr64KernelCacheRebase{Pointer: info.StopAddr}.Target() + m.GetBaseAddress()
infos = append(infos, info)
}
return infos, nil
}
return nil, fmt.Errorf("section __PRELINK_INFO.__kmod_start not found")
}
func GetKexts(kernel *macho.File) ([]CFBundle, error) {
if infoSec := kernel.Section("__PRELINK_INFO", "__info"); infoSec != nil {
data, err := infoSec.Data()
if err != nil {
return nil, fmt.Errorf("failed to read __PRELINK_INFO.__info section: %v", err)
}
var prelink PrelinkInfo
decoder := plist.NewDecoder(bytes.NewReader(bytes.Trim([]byte(data), "\x00")))
err = decoder.Decode(&prelink)
if err != nil {
return nil, fmt.Errorf("failed to decode __PRELINK_INFO.__info section: %v", err)
}
return prelink.PrelinkInfoDictionary, nil
}
return nil, fmt.Errorf("section __PRELINK_INFO.__info not found")
}
// KextList lists all the kernel extensions in the kernelcache
func KextList(kernelPath string, diffable bool) ([]string, error) {
var out []string
m, err := macho.Open(kernelPath)
if err != nil {
return nil, err
}
defer m.Close()
bundles, err := GetKexts(m)
if err != nil {
return nil, err
}
kextStartAdddrs, err := GetKextStartVMAddrs(m)
if err != nil {
log.Debugf("failed to get kext start addresses: %v", err)
}
if diffable {
for _, bundle := range bundles {
out = append(out, fmt.Sprintf("%s (%s)", bundle.ID, bundle.Version))
}
} else {
for _, bundle := range bundles {
if !bundle.OSKernelResource && len(kextStartAdddrs) > 0 {
out = append(out, fmt.Sprintf("%#x: %s (%s)", kextStartAdddrs[bundle.ModuleIndex]|tagPtrMask, bundle.ID, bundle.Version))
} else {
out = append(out, fmt.Sprintf("%#x: %s (%s)", bundle.ExecutableLoadAddr, bundle.ID, bundle.Version))
}
}
}
sort.Strings(out)
return out, nil
}