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 == "" { continue } return symbols.DemangleSymbolName(name) } } return "" }