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:
blacktop
2022-09-12 14:30:29 -06:00
parent 74a9a5f4e4
commit ebed875d64
2 changed files with 152 additions and 125 deletions
+151 -124
View File
@@ -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
View File
@@ -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()