mirror of
https://github.com/blacktop/ipsw.git
synced 2026-05-08 12:22:26 +00:00
196d77de6f
Add API endpoints and CLI support for discovering C++ classes and symbolication of kernelcaches, refactor Mach-O handling, and improve symbol collection. - API: add /kernel/cpp and /kernel/symbolicate routes, request param structs, response types, and openKernel helper. Use cpp scanner and signature parsing to return classes and symbol maps. - CLI: wire scanner LogStats flag, refactor kernel symbolicate command (schema writer helper, improved signature parsing, and symbol matching logic). Add tests for symbolicator schema and kernel symbol matching. - Signature pkg: add kernel C++ symbol extraction (pkg/signature/kernel_cpp.go) and SymbolicateMachO to symbolicate already-open Mach-Os; integrate C++ symbols into symbol map and update signature matching/logging behavior. - Internal: refactor in-memory DB lookups (findMachOByUUID, findSymbolByAddr) to reduce duplication. Improve symbols collection for kernel Mach-Os (collectKernelMachoSymbols, extra kernel symbols from signature/C++), add helpers to append symbols. - Kernelcache CPP: add LogStats option and conditional logging of scan stats. - Crashlog/ips: update wording to reflect kernel symbols are from kernel analysis and store KernelSymbols earlier in processing; parse signatures only when configured. Also add unit tests for new symbolication helpers and kernel C++ signature handling. Overall this consolidates kernel symbol discovery, improves reuse, and surfaces C++-derived symbols in symbol maps.
217 lines
4.8 KiB
Go
217 lines
4.8 KiB
Go
package db
|
|
|
|
import (
|
|
"encoding/gob"
|
|
"fmt"
|
|
"os"
|
|
"slices"
|
|
|
|
"github.com/blacktop/ipsw/internal/model"
|
|
"github.com/pkg/errors"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// Memory is a database that stores data in memory.
|
|
type Memory struct {
|
|
IPSWs map[string]*model.Ipsw
|
|
Path string
|
|
}
|
|
|
|
// NewInMemory creates a new in-memory database.
|
|
func NewInMemory(path string) (Database, error) {
|
|
if path == "" {
|
|
return nil, errors.New("'path' is required")
|
|
}
|
|
return &Memory{
|
|
IPSWs: make(map[string]*model.Ipsw),
|
|
Path: path,
|
|
}, nil
|
|
}
|
|
|
|
// Connect connects to the database.
|
|
func (m *Memory) Connect() error {
|
|
f, err := os.Open(m.Path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer f.Close()
|
|
return gob.NewDecoder(f).Decode(&m.IPSWs)
|
|
}
|
|
|
|
// Create creates a new entry in the database.
|
|
// It returns ErrAlreadyExists if the key already exists.
|
|
func (m *Memory) Create(value any) error {
|
|
if ipsw, ok := value.(*model.Ipsw); ok {
|
|
if _, exists := m.IPSWs[ipsw.ID]; exists {
|
|
return gorm.ErrDuplicatedKey
|
|
}
|
|
m.IPSWs[ipsw.ID] = ipsw
|
|
return nil
|
|
}
|
|
return fmt.Errorf("invalid type: %T", value)
|
|
}
|
|
|
|
// Get returns the IPSW for the given key.
|
|
// It returns ErrNotFound if the key does not exist.
|
|
func (m *Memory) Get(id string) (*model.Ipsw, error) {
|
|
ipsw, exists := m.IPSWs[id]
|
|
if !exists {
|
|
return nil, errors.Errorf("no IPSW found with id: %s", id)
|
|
}
|
|
return ipsw, nil
|
|
}
|
|
|
|
// GetIpswByName returns the IPSW for the given name.
|
|
// It returns ErrNotFound if the key does not exist.
|
|
func (m *Memory) GetIpswByName(name string) (*model.Ipsw, error) {
|
|
for _, ipsw := range m.IPSWs {
|
|
if ipsw.Name == name {
|
|
return ipsw, nil
|
|
}
|
|
}
|
|
return nil, model.ErrNotFound
|
|
}
|
|
|
|
// GetIPSW returns the IPSW for the given version, build, and device.
|
|
// It returns ErrNotFound if the IPSW does not exist.
|
|
func (m *Memory) GetIPSW(version, build, device string) (*model.Ipsw, error) {
|
|
for _, ipsw := range m.IPSWs {
|
|
if ipsw.Version == version && ipsw.BuildID == build {
|
|
var devs []string
|
|
for _, dev := range ipsw.Devices {
|
|
devs = append(devs, dev.Name)
|
|
}
|
|
if slices.Contains(devs, device) {
|
|
return ipsw, nil
|
|
}
|
|
}
|
|
}
|
|
return nil, model.ErrNotFound
|
|
}
|
|
|
|
func (m *Memory) GetDSC(uuid string) (*model.DyldSharedCache, error) {
|
|
for _, ipsw := range m.IPSWs {
|
|
for _, dyld := range ipsw.DSCs {
|
|
if dyld.UUID == uuid {
|
|
return dyld, nil
|
|
}
|
|
}
|
|
}
|
|
return nil, model.ErrNotFound
|
|
}
|
|
|
|
func (m *Memory) GetDSCImage(uuid string, addr uint64) (*model.Macho, error) {
|
|
for _, ipsw := range m.IPSWs {
|
|
for _, dyld := range ipsw.DSCs {
|
|
if dyld.UUID == uuid {
|
|
for _, img := range dyld.Images {
|
|
if addr >= img.TextStart && addr < img.TextEnd {
|
|
return img, nil
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nil, model.ErrNotFound
|
|
}
|
|
|
|
func (m *Memory) GetMachO(uuid string) (*model.Macho, error) {
|
|
for _, ipsw := range m.IPSWs {
|
|
if macho := findMachOByUUID(ipsw, uuid); macho != nil {
|
|
return macho, nil
|
|
}
|
|
}
|
|
return nil, model.ErrNotFound
|
|
}
|
|
|
|
func (m *Memory) GetSymbol(uuid string, addr uint64) (*model.Symbol, error) {
|
|
for _, ipsw := range m.IPSWs {
|
|
if macho := findMachOByUUID(ipsw, uuid); macho != nil {
|
|
if sym := findSymbolByAddr(macho.Symbols, addr); sym != nil {
|
|
return sym, nil
|
|
}
|
|
}
|
|
}
|
|
return nil, model.ErrNotFound
|
|
}
|
|
|
|
func (m *Memory) GetSymbols(uuid string) ([]*model.Symbol, error) {
|
|
for _, ipsw := range m.IPSWs {
|
|
if macho := findMachOByUUID(ipsw, uuid); macho != nil {
|
|
return macho.Symbols, nil
|
|
}
|
|
}
|
|
return nil, model.ErrNotFound
|
|
}
|
|
|
|
func findMachOByUUID(ipsw *model.Ipsw, uuid string) *model.Macho {
|
|
for _, dyld := range ipsw.DSCs {
|
|
for _, img := range dyld.Images {
|
|
if img.UUID == uuid {
|
|
return img
|
|
}
|
|
}
|
|
}
|
|
for _, fs := range ipsw.FileSystem {
|
|
if fs.UUID == uuid {
|
|
return fs
|
|
}
|
|
}
|
|
for _, kc := range ipsw.Kernels {
|
|
for _, kext := range kc.Kexts {
|
|
if kext.UUID == uuid {
|
|
return kext
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func findSymbolByAddr(symbols []*model.Symbol, addr uint64) *model.Symbol {
|
|
for _, sym := range symbols {
|
|
if addr >= sym.Start && addr < sym.End {
|
|
return sym
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Set sets the value for the given key.
|
|
// It overwrites any previous value for that key.
|
|
func (m *Memory) Save(value any) error {
|
|
if ipsw, ok := value.(*model.Ipsw); ok {
|
|
m.IPSWs[ipsw.ID] = ipsw
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (m *Memory) List(version string) ([]*model.Ipsw, error) {
|
|
ipsws := []*model.Ipsw{}
|
|
for _, p := range m.IPSWs {
|
|
if p.Version == version {
|
|
ipsws = append(ipsws, p)
|
|
}
|
|
}
|
|
return ipsws, nil
|
|
}
|
|
|
|
// Delete removes the given key.
|
|
// It returns ErrNotFound if the key does not exist.
|
|
func (m *Memory) Delete(id string) error {
|
|
delete(m.IPSWs, id)
|
|
return nil
|
|
}
|
|
|
|
// Close closes the database.
|
|
// It returns ErrClosed if the database is already closed.
|
|
func (m *Memory) Close() error {
|
|
f, err := os.Open(m.Path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer f.Close()
|
|
gob.Register([]any{})
|
|
gob.Register(map[string]any{})
|
|
return gob.NewEncoder(f).Encode(m.IPSWs)
|
|
}
|