Skip to content

Commit

Permalink
feat: add non-darwin version of ipsw dyld split cmd
Browse files Browse the repository at this point in the history
  • Loading branch information
blacktop committed Nov 22, 2021
1 parent 69d87f5 commit dd43705
Show file tree
Hide file tree
Showing 6 changed files with 299 additions and 87 deletions.
76 changes: 0 additions & 76 deletions cmd/ipsw/cmd/dyld_split.go

This file was deleted.

137 changes: 137 additions & 0 deletions cmd/ipsw/cmd/dyld_split_darwin.go
@@ -0,0 +1,137 @@
//go:build darwin && cgo

package cmd

import (
"fmt"
"os"
"path/filepath"
"runtime"

"github.com/apex/log"
"github.com/blacktop/ipsw/pkg/dyld"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)

func init() {
dyldCmd.AddCommand(splitCmd)

splitCmd.Flags().StringP("xcode", "x", "", "Path to Xcode.app")
viper.BindPFlag("dyld.split.xcode", ipswCmd.Flags().Lookup("xcode"))

splitCmd.MarkZshCompPositionalArgumentFile(1, "dyld_shared_cache*")
}

// splitCmd represents the split command
var splitCmd = &cobra.Command{
Use: "split <dyld_shared_cache> <optional_output_path>",
Short: "Extracts all the dyld_shared_cache libraries",
Args: cobra.MinimumNArgs(1),
SilenceUsage: false,
SilenceErrors: true,
RunE: func(cmd *cobra.Command, args []string) error {

if Verbose {
log.SetLevel(log.DebugLevel)
}

xcodePath := viper.GetString("dyld.split.xcode")
dscPath := filepath.Clean(args[0])

fileInfo, err := os.Lstat(dscPath)
if err != nil {
return fmt.Errorf("file %s does not exist", dscPath)
}

// Check if file is a symlink
if fileInfo.Mode()&os.ModeSymlink != 0 {
symlinkPath, err := os.Readlink(dscPath)
if err != nil {
return errors.Wrapf(err, "failed to read symlink %s", dscPath)
}
// TODO: this seems like it would break
linkParent := filepath.Dir(dscPath)
linkRoot := filepath.Dir(linkParent)

dscPath = filepath.Join(linkRoot, symlinkPath)
}

if runtime.GOOS != "darwin" {
log.Fatal("dyld_shared_cache splitting only works on macOS")
}

if len(args) > 1 {
outputPath, _ := filepath.Abs(filepath.Clean(args[1]))
if _, err := os.Stat(outputPath); os.IsNotExist(err) {
return fmt.Errorf("path %s does not exist", outputPath)
}
log.Infof("Splitting dyld_shared_cache to %s\n", outputPath)
return dyld.Split(dscPath, outputPath, xcodePath)
}

log.Info("Splitting dyld_shared_cache")
return dyld.Split(dscPath, filepath.Dir(dscPath), xcodePath)

// dscPath := filepath.Clean(args[0])

// fileInfo, err := os.Lstat(dscPath)
// if err != nil {
// return fmt.Errorf("file %s does not exist", dscPath)
// }

// // Check if file is a symlink
// if fileInfo.Mode()&os.ModeSymlink != 0 {
// symlinkPath, err := os.Readlink(dscPath)
// if err != nil {
// return errors.Wrapf(err, "failed to read symlink %s", dscPath)
// }
// // TODO: this seems like it would break
// linkParent := filepath.Dir(dscPath)
// linkRoot := filepath.Dir(linkParent)

// dscPath = filepath.Join(linkRoot, symlinkPath)
// }

// f, err := dyld.Open(dscPath)
// if err != nil {
// return err
// }
// defer f.Close()

// var wg sync.WaitGroup

// for _, i := range f.Images {
// wg.Add(1)

// go func(i *dyld.CacheImage) {
// defer wg.Done()

// m, err := i.GetMacho()
// if err != nil {
// // return err
// }
// defer m.Close()

// // f.GetLocalSymbolsForImage(i)

// folder := filepath.Dir(dscPath) // default to folder of shared cache
// fname := filepath.Join(folder, i.Name) // default to full dylib path

// if err := m.Export(fname, nil, m.GetBaseAddress(), i.GetLocalSymbols()); err != nil {
// // return fmt.Errorf("failed to export entry MachO %s; %v", i.Name, err)
// }

// if err := rebaseMachO(f, fname); err != nil {
// // return fmt.Errorf("failed to rebase macho via cache slide info: %v", err)
// }
// }(i)

// }

// wg.Wait()

// return nil
},
}
151 changes: 151 additions & 0 deletions cmd/ipsw/cmd/dyld_split_other.go
@@ -0,0 +1,151 @@
//go:build !(darwin && cgo)

package cmd

import (
"fmt"
"os"
"path/filepath"

"github.com/apex/log"
"github.com/blacktop/ipsw/pkg/dyld"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/vbauerster/mpb/v7"
"github.com/vbauerster/mpb/v7/decor"
)

func init() {
dyldCmd.AddCommand(splitCmd)
splitCmd.Flags().BoolP("all", "a", false, "Split ALL dylibs")
splitCmd.Flags().Bool("force", false, "Overwrite existing extracted dylib(s)")
splitCmd.Flags().String("output", "o", "Directory to extract the dylib(s)")
splitCmd.MarkZshCompPositionalArgumentFile(1, "dyld_shared_cache*")
}

// splitCmd represents the split command
var splitCmd = &cobra.Command{
Use: "split <dyld_shared_cache> <optional_output_path>",
Short: "Extracts all the dyld_shared_cache libraries",
Args: cobra.MinimumNArgs(1),
SilenceUsage: false,
SilenceErrors: true,
RunE: func(cmd *cobra.Command, args []string) error {

var bar *mpb.Bar

if Verbose {
log.SetLevel(log.DebugLevel)
}

dumpALL, _ := cmd.Flags().GetBool("all")
forceExtract, _ := cmd.Flags().GetBool("force")
extractPath, _ := cmd.Flags().GetString("output")

dscPath := filepath.Clean(args[0])

fileInfo, err := os.Lstat(dscPath)
if err != nil {
return fmt.Errorf("file %s does not exist", dscPath)
}

// Check if file is a symlink
if fileInfo.Mode()&os.ModeSymlink != 0 {
symlinkPath, err := os.Readlink(dscPath)
if err != nil {
return errors.Wrapf(err, "failed to read symlink %s", dscPath)
}
// TODO: this seems like it would break
linkParent := filepath.Dir(dscPath)
linkRoot := filepath.Dir(linkParent)

dscPath = filepath.Join(linkRoot, symlinkPath)
}

f, err := dyld.Open(dscPath)
if err != nil {
return err
}
defer f.Close()

if len(args) > 1 || dumpALL {

var images []*dyld.CacheImage

if dumpALL {
images = f.Images
// initialize progress bar
p := mpb.New(mpb.WithWidth(80))
// adding a single bar, which will inherit container's width
bar = p.Add(int64(len(images)),
// progress bar filler with customized style
mpb.NewBarFiller(mpb.BarStyle().Lbound("[").Filler("=").Tip(">").Padding("-").Rbound("|")),
mpb.PrependDecorators(
decor.Name(" ", decor.WC{W: len(" ") + 1, C: decor.DidentRight}),
// replace ETA decorator with "done" message, OnComplete event
decor.OnComplete(
decor.AverageETA(decor.ET_STYLE_GO, decor.WC{W: 4}), "✅ ",
),
),
mpb.AppendDecorators(
decor.Percentage(),
// decor.OnComplete(decor.EwmaETA(decor.ET_STYLE_GO, float64(len(images))/2048), "✅ "),
decor.Name(" ] "),
),
)
} else {
if img := f.Image(args[1]); img != nil {
images = append(images, img)
} else {
log.Errorf("dylib %s not found in %s", args[1], dscPath)
return nil
}
}

for _, i := range images {
m, err := i.GetMacho()
if err != nil {
return err
}

folder := filepath.Dir(dscPath) // default to folder of shared cache
if len(extractPath) > 0 {
folder = extractPath
}

fname := filepath.Join(folder, filepath.Base(i.Name)) // default to NOT full dylib path
if dumpALL {
fname = filepath.Join(folder, i.Name)
}

if _, err := os.Stat(fname); os.IsNotExist(err) || forceExtract {

f.GetLocalSymbolsForImage(i)

if err := m.Export(fname, nil, m.GetBaseAddress(), i.GetLocalSymbols()); err != nil {
return fmt.Errorf("failed to export entry MachO %s; %v", i.Name, err)
}

if err := rebaseMachO(f, fname); err != nil {
return fmt.Errorf("failed to rebase macho via cache slide info: %v", err)
}

if !dumpALL {
log.Infof("Created %s", fname)
} else {
bar.Increment()
}
} else {
if !dumpALL {
log.Warnf("dylib already exists: %s", fname)
} else {
bar.Increment()
}
}
m.Close()
}
}

return nil
},
}
4 changes: 2 additions & 2 deletions go.mod
Expand Up @@ -8,7 +8,7 @@ require (
github.com/PuerkitoBio/goquery v1.8.0
github.com/apex/log v1.9.0
github.com/blacktop/go-arm64 v0.6.18
github.com/blacktop/go-macho v1.1.72
github.com/blacktop/go-macho v1.1.73
github.com/blacktop/go-plist v1.0.1
github.com/blacktop/lzss v0.1.1
github.com/blacktop/ranger v1.0.3
Expand All @@ -27,7 +27,7 @@ require (
github.com/spf13/cobra v1.2.1
github.com/spf13/viper v1.9.0
github.com/ulikunitz/xz v0.5.10
github.com/vbauerster/mpb/v5 v5.4.0
github.com/vbauerster/mpb/v7 v7.1.5
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871
golang.org/x/net v0.0.0-20211118161319-6a13c67c3ce4
golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1
Expand Down

0 comments on commit dd43705

Please sign in to comment.