mirror of
https://github.com/blacktop/ipsw.git
synced 2026-06-07 12:27:36 +00:00
183 lines
4.9 KiB
Go
183 lines
4.9 KiB
Go
package cpp
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/blacktop/go-macho"
|
|
"github.com/blacktop/go-macho/types"
|
|
"github.com/blacktop/ipsw/pkg/symbols"
|
|
)
|
|
|
|
// VtableEntry is a decoded function pointer from a recovered C++ vtable.
|
|
type VtableEntry struct {
|
|
Index int
|
|
SlotAddress uint64
|
|
Address uint64
|
|
Symbol string
|
|
}
|
|
|
|
// FunctionBody is the bounded analysis input for a recovered function.
|
|
type FunctionBody struct {
|
|
Function types.Function
|
|
Owner *macho.File
|
|
Data []byte
|
|
}
|
|
|
|
// OwnerForVMAddr returns the fileset entry that owns addr, if known.
|
|
func (s *Scanner) OwnerForVMAddr(addr uint64) *macho.File {
|
|
return s.fileForVMAddr(addr)
|
|
}
|
|
|
|
// ClassOwner returns the fileset entry that owns the strongest address
|
|
// associated with class.
|
|
func (s *Scanner) ClassOwner(class Class) *macho.File {
|
|
for _, addr := range []uint64{class.MetaPtr, class.VtableAddr, class.Ctor, class.MetaVtableAddr} {
|
|
if addr == 0 {
|
|
continue
|
|
}
|
|
if owner := s.fileForVMAddr(addr); owner != nil {
|
|
return owner
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// EntryForOwner returns the bundle/fileset identifier associated with owner.
|
|
func (s *Scanner) EntryForOwner(owner *macho.File) string {
|
|
return s.entryForFile(owner)
|
|
}
|
|
|
|
// VtableEntries returns up to max decoded vtable entries for class.
|
|
func (s *Scanner) VtableEntries(class Class, max int) []VtableEntry {
|
|
if max <= 0 || class.VtableAddr == 0 {
|
|
return nil
|
|
}
|
|
owner := s.ClassOwner(class)
|
|
if owner == nil {
|
|
return nil
|
|
}
|
|
out := make([]VtableEntry, 0, max)
|
|
for idx := range max {
|
|
addr := class.VtableAddr + uint64(idx*8)
|
|
ptr, ok := s.fallbackPointerAt(owner, addr)
|
|
if !ok || !validKernelPointer(ptr) {
|
|
break
|
|
}
|
|
out = append(out, VtableEntry{
|
|
Index: idx,
|
|
SlotAddress: addr,
|
|
Address: ptr,
|
|
Symbol: s.SymbolName(ptr),
|
|
})
|
|
}
|
|
return out
|
|
}
|
|
|
|
// VtableEntry returns a single decoded vtable entry for class.
|
|
func (s *Scanner) VtableEntry(class Class, index int) (VtableEntry, bool) {
|
|
if index < 0 || class.VtableAddr == 0 {
|
|
return VtableEntry{}, false
|
|
}
|
|
owner := s.ClassOwner(class)
|
|
if owner == nil {
|
|
return VtableEntry{}, false
|
|
}
|
|
addr := class.VtableAddr + uint64(index*8)
|
|
ptr, ok := s.fallbackPointerAt(owner, addr)
|
|
if !ok || !validKernelPointer(ptr) {
|
|
return VtableEntry{}, false
|
|
}
|
|
return VtableEntry{
|
|
Index: index,
|
|
SlotAddress: addr,
|
|
Address: ptr,
|
|
Symbol: s.SymbolName(ptr),
|
|
}, true
|
|
}
|
|
|
|
// VtableSlotAddress returns the VM address of class' vtable slot.
|
|
func (s *Scanner) VtableSlotAddress(class Class, index int) (uint64, bool) {
|
|
if index < 0 || class.VtableAddr == 0 {
|
|
return 0, false
|
|
}
|
|
return class.VtableAddr + uint64(index*8), true
|
|
}
|
|
|
|
// FunctionBodyAt returns function bounds, owner, and bytes for addr.
|
|
func (s *Scanner) FunctionBodyAt(addr uint64) (FunctionBody, error) {
|
|
owner := s.fileForVMAddr(addr)
|
|
fn, fnOwner, err := s.functionForAddrInAnyFile(owner, addr)
|
|
if err != nil {
|
|
return FunctionBody{}, err
|
|
}
|
|
data, err := s.functionDataFor(fnOwner, fn)
|
|
if err != nil {
|
|
return FunctionBody{}, err
|
|
}
|
|
return FunctionBody{Function: fn, Owner: fnOwner, Data: data}, nil
|
|
}
|
|
|
|
// ReadPointerAt resolves a kernel pointer from addr using the scanner's
|
|
// warmed fixup and section pointer caches.
|
|
func (s *Scanner) ReadPointerAt(owner *macho.File, addr uint64) (uint64, bool) {
|
|
return s.fallbackPointerAt(owner, addr)
|
|
}
|
|
|
|
// ReadUint32At reads a little-endian uint32 from addr.
|
|
func (s *Scanner) ReadUint32At(owner *macho.File, addr uint64) (uint32, error) {
|
|
lookupOwner, _ := s.pointerLookupOwner(owner, addr)
|
|
if lookupOwner == nil {
|
|
return 0, fmt.Errorf("no owner for %#x", addr)
|
|
}
|
|
var buf [4]byte
|
|
if _, err := lookupOwner.ReadAtAddr(buf[:], addr); err != nil {
|
|
return 0, err
|
|
}
|
|
return lookupOwner.ByteOrder.Uint32(buf[:]), nil
|
|
}
|
|
|
|
// ReadUint64At reads a little-endian uint64 from addr.
|
|
func (s *Scanner) ReadUint64At(owner *macho.File, addr uint64) (uint64, error) {
|
|
lookupOwner, _ := s.pointerLookupOwner(owner, addr)
|
|
if lookupOwner == nil {
|
|
return 0, fmt.Errorf("no owner for %#x", addr)
|
|
}
|
|
var buf [8]byte
|
|
if _, err := lookupOwner.ReadAtAddr(buf[:], addr); err != nil {
|
|
return 0, err
|
|
}
|
|
return lookupOwner.ByteOrder.Uint64(buf[:]), nil
|
|
}
|
|
|
|
// ReadCStringAt reads a C string from addr from the owning fileset entry.
|
|
func (s *Scanner) ReadCStringAt(owner *macho.File, addr uint64) (string, error) {
|
|
return getCStringFromAny(s.root, owner, addr)
|
|
}
|
|
|
|
// SymbolName returns a demangled function symbol at addr when the Mach-O has
|
|
// one. It returns an empty string for stripped addresses.
|
|
func (s *Scanner) SymbolName(addr uint64) string {
|
|
if addr == 0 {
|
|
return ""
|
|
}
|
|
owner := s.fileForVMAddr(addr)
|
|
for _, file := range []*macho.File{owner, s.root} {
|
|
if file == nil {
|
|
continue
|
|
}
|
|
syms, err := file.FindAddressSymbols(addr)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
for _, sym := range syms {
|
|
name := strings.TrimSpace(sym.Name)
|
|
if name == "" || name == "<redacted>" {
|
|
continue
|
|
}
|
|
return symbols.DemangleSymbolName(name)
|
|
}
|
|
}
|
|
return ""
|
|
}
|