mirror of
https://github.com/blacktop/ipsw.git
synced 2026-06-07 12:27:36 +00:00
feat: add NEW --all-fileset-entries flag to ipsw macho disass to allow for disass-ing ALL kernelcache file-set entries at once
This commit is contained in:
+151
-124
@@ -33,6 +33,7 @@ import (
|
||||
"github.com/blacktop/go-macho/types"
|
||||
"github.com/blacktop/ipsw/pkg/disass"
|
||||
"github.com/fatih/color"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
@@ -48,6 +49,7 @@ func init() {
|
||||
machoDisassCmd.Flags().BoolP("quiet", "q", false, "Do NOT markup analysis (Faster)")
|
||||
// machoDisassCmd.Flags().StringP("input", "i", "", "Input function JSON file")
|
||||
machoDisassCmd.Flags().StringP("fileset-entry", "t", "", "Which fileset entry to analyze")
|
||||
machoDisassCmd.Flags().BoolP("all-fileset-entries", "z", false, "Parse all fileset entries")
|
||||
machoDisassCmd.Flags().StringP("section", "x", "", "Disassemble an entire segment/section (i.e. __TEXT_EXEC.__text)")
|
||||
machoDisassCmd.Flags().String("cache", "", "Path to .a2s addr to sym cache file (speeds up analysis)")
|
||||
|
||||
@@ -59,6 +61,7 @@ func init() {
|
||||
viper.BindPFlag("macho.disass.quiet", machoDisassCmd.Flags().Lookup("quiet"))
|
||||
// viper.BindPFlag("macho.disass.input", machoDisassCmd.Flags().Lookup("input"))
|
||||
viper.BindPFlag("macho.disass.fileset-entry", machoDisassCmd.Flags().Lookup("fileset-entry"))
|
||||
viper.BindPFlag("macho.disass.all-fileset-entries", machoDisassCmd.Flags().Lookup("all-fileset-entries"))
|
||||
viper.BindPFlag("macho.disass.section", machoDisassCmd.Flags().Lookup("section"))
|
||||
viper.BindPFlag("macho.disass.cache", machoDisassCmd.Flags().Lookup("cache"))
|
||||
|
||||
@@ -74,6 +77,7 @@ var machoDisassCmd = &cobra.Command{
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
var m *macho.File
|
||||
var ms []*macho.File
|
||||
var middleAddr uint64
|
||||
var symbolMap map[uint64]string
|
||||
|
||||
@@ -96,7 +100,6 @@ var machoDisassCmd = &cobra.Command{
|
||||
filesetEntry := viper.GetString("macho.disass.fileset-entry")
|
||||
cacheFile := viper.GetString("macho.disass.cache")
|
||||
|
||||
// funcFile, _ := cmd.Flags().GetString("input")
|
||||
allFuncs := false
|
||||
|
||||
if len(symbolName) > 0 && startAddr != 0 {
|
||||
@@ -105,6 +108,11 @@ var machoDisassCmd = &cobra.Command{
|
||||
allFuncs = true
|
||||
// return fmt.Errorf("you must supply a --symbol OR --vaddr to disassemble")
|
||||
}
|
||||
if len(filesetEntry) > 0 && viper.GetBool("macho.disass.all-fileset-entries") {
|
||||
return fmt.Errorf("you can only use --fileset-entry OR --all-fileset-entries (not both)")
|
||||
} else if viper.GetBool("macho.disass.all-fileset-entries") && len(segmentSection) == 0 {
|
||||
log.Warn("you probably want to add --section '__TEXT_EXEC.__text'; as the NEW MH_FILESET entries don't ALL have LC_FUNCTION_STARTS")
|
||||
}
|
||||
|
||||
machoPath := filepath.Clean(args[0])
|
||||
|
||||
@@ -126,6 +134,11 @@ var machoDisassCmd = &cobra.Command{
|
||||
}
|
||||
}
|
||||
|
||||
if !strings.Contains(strings.ToLower(m.FileHeader.SubCPU.String(m.CPU)), "arm64") {
|
||||
log.Errorf("can only disassemble arm64 binaries")
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(filesetEntry) > 0 {
|
||||
if m.FileTOC.FileHeader.Type == types.MH_FILESET {
|
||||
m, err = m.GetFileSetFileByName(filesetEntry)
|
||||
@@ -135,42 +148,153 @@ var machoDisassCmd = &cobra.Command{
|
||||
} else {
|
||||
log.Error("MachO type is not MH_FILESET (cannot use --fileset-entry)")
|
||||
}
|
||||
}
|
||||
|
||||
if !strings.Contains(strings.ToLower(m.FileHeader.SubCPU.String(m.CPU)), "arm64") {
|
||||
log.Errorf("can only disassemble arm64 binaries")
|
||||
return nil
|
||||
ms = append(ms, m)
|
||||
} else if viper.GetBool("macho.disass.all-fileset-entries") {
|
||||
for _, fe := range m.FileSets() {
|
||||
mfe, err := m.GetFileSetFileByName(fe.EntryID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse entry %s: %v", filesetEntry, err)
|
||||
}
|
||||
ms = append(ms, mfe)
|
||||
}
|
||||
} else {
|
||||
ms = append(ms, m)
|
||||
}
|
||||
|
||||
symbolMap = make(map[uint64]string)
|
||||
|
||||
if !quiet {
|
||||
if len(cacheFile) == 0 {
|
||||
cacheFile = machoPath + ".a2s"
|
||||
for _, m := range ms {
|
||||
if !quiet {
|
||||
if len(cacheFile) == 0 {
|
||||
cacheFile = machoPath + ".a2s"
|
||||
}
|
||||
if _, err := os.Stat(cacheFile); errors.Is(err, os.ErrNotExist) {
|
||||
if _, err := os.Create(cacheFile); err != nil {
|
||||
return fmt.Errorf("failed to create address-to-symbol cache file %s: %v", cacheFile, err)
|
||||
}
|
||||
} else {
|
||||
log.Infof("Loading symbol cache file...")
|
||||
if f, err := os.Open(cacheFile); err != nil {
|
||||
return fmt.Errorf("failed to open address-to-symbol cache file %s: %v", cacheFile, err)
|
||||
} else {
|
||||
if err := gob.NewDecoder(f).Decode(&symbolMap); err != nil {
|
||||
return fmt.Errorf("failed to decode address-to-symbol cache file: %v", err)
|
||||
}
|
||||
f.Close()
|
||||
}
|
||||
}
|
||||
}
|
||||
if a2sFile, err := os.Create(cacheFile); err == nil {
|
||||
// Decoding the serialized data
|
||||
log.Infof("Loading symbol cache file...")
|
||||
if err := gob.NewDecoder(a2sFile).Decode(&symbolMap); err != nil {
|
||||
return fmt.Errorf("failed to decode cache file: %v", err)
|
||||
|
||||
if allFuncs && len(segmentSection) == 0 {
|
||||
for _, fn := range m.GetFunctions() {
|
||||
data, err := m.GetFunctionData(fn)
|
||||
if err != nil {
|
||||
log.Errorf("failed to get data for function: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
engine := disass.NewMachoDisass(m, &symbolMap, &disass.Config{
|
||||
Data: data,
|
||||
StartAddress: fn.StartAddr,
|
||||
Middle: 0,
|
||||
AsJSON: asJSON,
|
||||
Demangle: demangleFlag,
|
||||
Quite: quiet,
|
||||
Color: viper.GetBool("color"),
|
||||
})
|
||||
|
||||
//***********************
|
||||
//* First pass ANALYSIS *
|
||||
//***********************
|
||||
if !quiet {
|
||||
if err := engine.Triage(); err != nil {
|
||||
return fmt.Errorf("first pass triage failed: %v", err)
|
||||
}
|
||||
if len(symbolMap) == 0 {
|
||||
if err := engine.Analyze(); err != nil {
|
||||
return fmt.Errorf("MachO analysis failed: %v", err)
|
||||
}
|
||||
}
|
||||
if err := engine.SaveAddrToSymMap(cacheFile); err != nil {
|
||||
log.Errorf("failed to save symbol map: %v", err)
|
||||
}
|
||||
}
|
||||
//***************
|
||||
//* DISASSEMBLE *
|
||||
//***************
|
||||
disass.Disassemble(engine)
|
||||
}
|
||||
} else {
|
||||
log.Errorf("failed to open cache file %s: %v", cacheFile, err)
|
||||
}
|
||||
}
|
||||
if len(symbolName) > 0 {
|
||||
startAddr, err = m.FindSymbolAddress(symbolName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if len(segmentSection) > 0 {
|
||||
parts := strings.Split(segmentSection, ".")
|
||||
if len(parts) != 2 {
|
||||
return fmt.Errorf("invalid --section format, must be segment.section")
|
||||
}
|
||||
if sec := m.Section(parts[0], parts[1]); sec != nil {
|
||||
startAddr = sec.Addr
|
||||
instructions = sec.Size / 4
|
||||
} else {
|
||||
return fmt.Errorf("failed to find section %s", segmentSection)
|
||||
}
|
||||
}
|
||||
// startAddr > 0 TODO: support slides
|
||||
// if slide > 0 {
|
||||
// startAddr = startAddr - slide
|
||||
// }
|
||||
|
||||
if allFuncs && len(segmentSection) == 0 {
|
||||
for _, fn := range m.GetFunctions() {
|
||||
data, err := m.GetFunctionData(fn)
|
||||
if err != nil {
|
||||
log.Errorf("failed to get data for function: %v", err)
|
||||
continue
|
||||
/*
|
||||
* Read in data to disassemble
|
||||
*/
|
||||
var data []byte
|
||||
if instructions > 0 {
|
||||
off, err := m.GetOffset(startAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data = make([]byte, instructions*4)
|
||||
if _, err := m.ReadAt(data, int64(off)); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if fn, err := m.GetFunctionForVMAddr(startAddr); err == nil {
|
||||
soff, err := m.GetOffset(fn.StartAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data = make([]byte, uint64(fn.EndAddr-fn.StartAddr))
|
||||
if _, err := m.ReadAt(data, int64(soff)); err != nil {
|
||||
return err
|
||||
}
|
||||
if startAddr != fn.StartAddr {
|
||||
middleAddr = startAddr
|
||||
startAddr = fn.StartAddr
|
||||
}
|
||||
} else {
|
||||
log.Warnf("disassembling 100 instructions at %#x", startAddr)
|
||||
instructions = 100
|
||||
off, err := m.GetOffset(startAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data = make([]byte, instructions*4)
|
||||
if _, err := m.ReadAt(data, int64(off)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(data) == 0 {
|
||||
log.Fatal("failed to disassemble")
|
||||
}
|
||||
|
||||
engine := disass.NewMachoDisass(m, &symbolMap, &disass.Config{
|
||||
Data: data,
|
||||
StartAddress: fn.StartAddr,
|
||||
Middle: 0,
|
||||
StartAddress: startAddr,
|
||||
Middle: middleAddr,
|
||||
AsJSON: asJSON,
|
||||
Demangle: demangleFlag,
|
||||
Quite: quiet,
|
||||
@@ -184,10 +308,8 @@ var machoDisassCmd = &cobra.Command{
|
||||
if err := engine.Triage(); err != nil {
|
||||
return fmt.Errorf("first pass triage failed: %v", err)
|
||||
}
|
||||
if len(symbolMap) == 0 {
|
||||
if err := engine.Analyze(); err != nil {
|
||||
return fmt.Errorf("MachO analysis failed: %v", err)
|
||||
}
|
||||
if err := engine.Analyze(); err != nil {
|
||||
return fmt.Errorf("MachO analysis failed: %v", err)
|
||||
}
|
||||
if err := engine.SaveAddrToSymMap(cacheFile); err != nil {
|
||||
log.Errorf("failed to save symbol map: %v", err)
|
||||
@@ -198,101 +320,6 @@ var machoDisassCmd = &cobra.Command{
|
||||
//***************
|
||||
disass.Disassemble(engine)
|
||||
}
|
||||
} else {
|
||||
if len(symbolName) > 0 {
|
||||
startAddr, err = m.FindSymbolAddress(symbolName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if len(segmentSection) > 0 {
|
||||
parts := strings.Split(segmentSection, ".")
|
||||
if len(parts) != 2 {
|
||||
return fmt.Errorf("invalid --section format, must be segment.section")
|
||||
}
|
||||
if sec := m.Section(parts[0], parts[1]); sec != nil {
|
||||
startAddr = sec.Addr
|
||||
instructions = sec.Size / 4
|
||||
} else {
|
||||
return fmt.Errorf("failed to find section %s", segmentSection)
|
||||
}
|
||||
}
|
||||
// startAddr > 0 TODO: support slides
|
||||
// if slide > 0 {
|
||||
// startAddr = startAddr - slide
|
||||
// }
|
||||
|
||||
/*
|
||||
* Read in data to disassemble
|
||||
*/
|
||||
var data []byte
|
||||
if instructions > 0 {
|
||||
off, err := m.GetOffset(startAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data = make([]byte, instructions*4)
|
||||
if _, err := m.ReadAt(data, int64(off)); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if fn, err := m.GetFunctionForVMAddr(startAddr); err == nil {
|
||||
soff, err := m.GetOffset(fn.StartAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data = make([]byte, uint64(fn.EndAddr-fn.StartAddr))
|
||||
if _, err := m.ReadAt(data, int64(soff)); err != nil {
|
||||
return err
|
||||
}
|
||||
if startAddr != fn.StartAddr {
|
||||
middleAddr = startAddr
|
||||
startAddr = fn.StartAddr
|
||||
}
|
||||
} else {
|
||||
log.Warnf("disassembling 100 instructions at %#x", startAddr)
|
||||
instructions = 100
|
||||
off, err := m.GetOffset(startAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data = make([]byte, instructions*4)
|
||||
if _, err := m.ReadAt(data, int64(off)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(data) == 0 {
|
||||
log.Fatal("failed to disassemble")
|
||||
}
|
||||
|
||||
engine := disass.NewMachoDisass(m, &symbolMap, &disass.Config{
|
||||
Data: data,
|
||||
StartAddress: startAddr,
|
||||
Middle: middleAddr,
|
||||
AsJSON: asJSON,
|
||||
Demangle: demangleFlag,
|
||||
Quite: quiet,
|
||||
Color: viper.GetBool("color"),
|
||||
})
|
||||
|
||||
//***********************
|
||||
//* First pass ANALYSIS *
|
||||
//***********************
|
||||
if !quiet {
|
||||
if err := engine.Triage(); err != nil {
|
||||
return fmt.Errorf("first pass triage failed: %v", err)
|
||||
}
|
||||
if err := engine.Analyze(); err != nil {
|
||||
return fmt.Errorf("MachO analysis failed: %v", err)
|
||||
}
|
||||
if err := engine.SaveAddrToSymMap(cacheFile); err != nil {
|
||||
log.Errorf("failed to save symbol map: %v", err)
|
||||
}
|
||||
}
|
||||
//***************
|
||||
//* DISASSEMBLE *
|
||||
//***************
|
||||
disass.Disassemble(engine)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
+1
-1
@@ -456,7 +456,7 @@ func (d *MachoDisass) SaveAddrToSymMap(dest string) error {
|
||||
utils.Indent(log.Warn, 2)("creating in the temp folder")
|
||||
utils.Indent(log.Warn, 3)(fmt.Sprintf("to use in the future you must supply the flag: --cache %s ", tempa2sfile))
|
||||
} else if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("failed to open address to symbol cache file %s: %v", dest, err)
|
||||
}
|
||||
defer of.Close()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user