dyld

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 7, 2023 License: MIT, MIT Imports: 41 Imported by: 0

Documentation

Overview

Copyright © 2018-2023 blacktop

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Index

Constants

This section is empty.

Variables

View Source
var AddrToFuncCmd = &cobra.Command{
	Use:           "a2f <dyld_shared_cache> <vaddr>",
	Short:         "Lookup function containing unslid address",
	Args:          cobra.MinimumNArgs(1),
	SilenceUsage:  false,
	SilenceErrors: true,
	RunE: func(cmd *cobra.Command, args []string) error {

		if viper.GetBool("verbose") {
			log.SetLevel(log.DebugLevel)
		}

		slide := viper.GetUint64("dyld.a2f.slide")
		ptrFile := viper.GetString("dyld.a2f.in")
		jsonFile := viper.GetString("dyld.a2f.out")
		asJSON := viper.GetBool("dyld.a2f.json")
		cacheFile := viper.GetString("dyld.a2f.cache")

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

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

		if fileInfo.Mode()&os.ModeSymlink != 0 {
			symlinkPath, err := os.Readlink(dscPath)
			if err != nil {
				return errors.Wrapf(err, "failed to read symlink %s", dscPath)
			}

			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(ptrFile) > 0 {
			var fs []Func
			var enc *json.Encoder

			imap := make(map[*dyld.CacheImage][]uint64)

			pfile, err := os.Open(ptrFile)
			if err != nil {
				return err
			}
			defer pfile.Close()

			scanner := bufio.NewScanner(pfile)

			log.Infof("Parsing functions for pointers in %s", ptrFile)
			for scanner.Scan() {
				addr, err := utils.ConvertStrToInt(scanner.Text())
				if err != nil {
					return err
				}

				var unslidAddr uint64 = addr
				if slide > 0 {
					unslidAddr = addr - slide
				}

				image, err := f.GetImageContainingVMAddr(unslidAddr)
				if err != nil {
					return err
				}

				imap[image] = append(imap[image], unslidAddr)
			}

			if err := scanner.Err(); err != nil {
				return err
			}

			if len(jsonFile) > 0 {
				jFile, err := os.Create(jsonFile)
				if err != nil {
					return err
				}
				defer jFile.Close()
				enc = json.NewEncoder(jFile)
			} else {
				enc = json.NewEncoder(os.Stdout)
			}

			if len(cacheFile) == 0 {
				cacheFile = dscPath + ".a2s"
			}
			if err := f.OpenOrCreateA2SCache(cacheFile); err != nil {
				return err
			}

			for img, ptrs := range imap {
				m, err := img.GetMacho()
				if err != nil {
					return err
				}
				defer m.Close()

				for _, ptr := range ptrs {
					if fn, err := m.GetFunctionForVMAddr(ptr); err == nil {
						if symName, ok := f.AddressToSymbol[fn.StartAddr]; ok {
							fn.Name = symName
						}
						fs = append(fs, Func{
							Addr:  ptr,
							Start: fn.StartAddr,
							End:   fn.EndAddr,
							Size:  fn.EndAddr - fn.StartAddr,
							Name:  fn.Name,
							Image: filepath.Base(img.Name),
						})
					}
				}
			}

			if err := enc.Encode(fs); err != nil {
				return err
			}
		} else {
			if len(args) < 2 {
				return fmt.Errorf("you must supply an virtual address")
			}
			addr, err := utils.ConvertStrToInt(args[1])
			if err != nil {
				return err
			}

			var unslidAddr uint64 = addr
			if slide > 0 {
				unslidAddr = addr - slide
			}

			image, err := f.GetImageContainingVMAddr(unslidAddr)
			if err != nil {
				return err
			}

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

			if err := image.Analyze(); err != nil {
				return err
			}

			if fn, err := m.GetFunctionForVMAddr(unslidAddr); err == nil {
				if asJSON {
					if symName, ok := f.AddressToSymbol[fn.StartAddr]; ok {
						fn.Name = symName
					}
					if err := json.NewEncoder(os.Stdout).Encode(Func{
						Addr:  addr,
						Start: fn.StartAddr,
						End:   fn.EndAddr,
						Size:  fn.EndAddr - fn.StartAddr,
						Name:  fn.Name,
						Image: filepath.Base(image.Name),
					}); err != nil {
						return err
					}
				} else {
					if symName, ok := f.AddressToSymbol[fn.StartAddr]; ok {
						if unslidAddr-fn.StartAddr == 0 {
							fmt.Printf("\n%#x: %s (start: %#x, end: %#x)\n", addr, symName, fn.StartAddr, fn.EndAddr)
						} else {
							fmt.Printf("\n%#x: %s + %d (start: %#x, end: %#x)\n", addr, symName, unslidAddr-fn.StartAddr, fn.StartAddr, fn.EndAddr)
						}
						return nil
					}
					fmt.Printf("\n%#x: func_%x (start: %#x, end: %#x)\n", addr, addr, fn.StartAddr, fn.EndAddr)
				}
			} else {
				log.Errorf("%#x is not in any known function", unslidAddr)
			}
		}

		return nil
	},
}

AddrToFuncCmd represents the a2f command

View Source
var AddrToOffsetCmd = &cobra.Command{
	Use:   "a2o <dyld_shared_cache> <vaddr>",
	Short: "Convert dyld_shared_cache address to offset",
	Args:  cobra.MinimumNArgs(2),
	RunE: func(cmd *cobra.Command, args []string) error {

		if viper.GetBool("verbose") {
			log.SetLevel(log.DebugLevel)
		}

		inDec, _ := cmd.Flags().GetBool("dec")
		inHex, _ := cmd.Flags().GetBool("hex")

		if inDec && inHex {
			return fmt.Errorf("you can only use --dec OR --hex")
		}

		addr, err := utils.ConvertStrToInt(args[1])
		if err != nil {
			return err
		}

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

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

		if fileInfo.Mode()&os.ModeSymlink != 0 {
			symlinkPath, err := os.Readlink(dscPath)
			if err != nil {
				return errors.Wrapf(err, "failed to read symlink %s", dscPath)
			}

			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 f.Headers[f.UUID].CacheType == dyld.CacheTypeUniversal {
			utils.Indent(log.Warn, 2)("dyld4 cache with stub islands detected (will search within dyld_subcache_entry's cacheVMOffsets)")
		} else if f.IsDyld4 {
			utils.Indent(log.Warn, 2)("dyld4 cache detected (will search for offset in each subcache)")
		}

		off, err := dsc.ConvertAddressToOffset(f, addr)
		if err != nil {
			return err
		}

		if inDec {
			fmt.Printf("%d\n", off.File.Offset)
		} else if inHex {
			fmt.Printf("%#x\n", off.File.Offset)
		} else {
			if f.IsDyld4 {
				var stubs string
				if off.File.SubCache.InStubs {
					stubs = "STUB Island "
				}
				log.WithFields(log.Fields{
					"hex": fmt.Sprintf("%#x", off.File.Offset),
					"dec": fmt.Sprintf("%d", off.File.Offset),
					"sub_cache": fmt.Sprintf("%sdsc%s, mapping: %s, UUID: %s",
						stubs,
						off.File.SubCache.Extension,
						off.File.SubCache.Mapping,
						off.File.SubCache.UUID,
					),
				}).Info("Offset")
			} else {
				log.WithFields(log.Fields{
					"hex":     fmt.Sprintf("%#x", off.File.Offset),
					"dec":     fmt.Sprintf("%d", off.File.Offset),
					"mapping": off.File.SubCache.Mapping,
				}).Info("Offset")
			}
		}

		if off.Cache != nil {
			var stubs string
			if off.Cache.SubCache.InStubs {
				stubs = "STUB Island "
			}
			log.WithFields(log.Fields{
				"hex": fmt.Sprintf("%#x", off.Cache.Offset),
				"dec": fmt.Sprintf("%d", off.Cache.Offset),
				"sub_cache": fmt.Sprintf("%sdsc%s, mapping: %s, UUID: %s",
					stubs,
					off.Cache.SubCache.Extension,
					off.Cache.SubCache.Mapping,
					off.Cache.SubCache.UUID,
				),
			}).Info("CACHE offset")
		}

		return nil
	},
}

AddrToOffsetCmd represents the a2o command

View Source
var AddrToSymCmd = &cobra.Command{
	Use:           "a2s <dyld_shared_cache> <vaddr>",
	Short:         "Lookup symbol at unslid address",
	SilenceUsage:  false,
	SilenceErrors: true,
	Args:          cobra.MinimumNArgs(2),
	RunE: func(cmd *cobra.Command, args []string) error {

		if viper.GetBool("verbose") {
			log.SetLevel(log.DebugLevel)
		}

		slide := viper.GetUint64("dyld.a2s.slide")
		showImage := viper.GetBool("dyld.a2s.image")
		showMapping := viper.GetBool("dyld.a2s.mapping")
		cacheFile := viper.GetString("dyld.a2s.cache")

		addr, err := utils.ConvertStrToInt(args[1])
		if err != nil {
			return err
		}

		var unslidAddr uint64 = addr
		if slide > 0 {
			unslidAddr = addr - slide
		}

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

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

		if fileInfo.Mode()&os.ModeSymlink != 0 {
			symlinkPath, err := os.Readlink(dscPath)
			if err != nil {
				return fmt.Errorf("failed to read symlink %s", dscPath)
			}

			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(cacheFile) == 0 {
			cacheFile = dscPath + ".a2s"
		}
		if err := f.OpenOrCreateA2SCache(cacheFile); err != nil {
			return err
		}

		sym, err := dsc.LookupSymbol(f, unslidAddr)
		if err != nil {
			return err
		}

		if showMapping {
			var stubs string
			if sym.StubIsland {
				stubs = "STUB Island "
			}
			fmt.Printf("\nMAPPING\n")
			fmt.Printf("=======\n")
			fmt.Printf("  > %s(dsc%s) UUID: %s, %s\n\n", stubs, sym.Extension, sym.UUID, sym.Mapping)
		}

		if showImage {
			fmt.Println("IMAGE")
			fmt.Println("-----")
			fmt.Printf(" > %s (%s.%s)\n\n", sym.Image, sym.Segment, sym.Section)
		} else {
			log.WithFields(log.Fields{
				"dylib":   sym.Image,
				"section": fmt.Sprintf("%s.%s", sym.Segment, sym.Section),
			}).Info("Address location")
		}

		fmt.Printf("%#x: %s\n", unslidAddr, sym.Symbol)

		return nil
	},
}

AddrToSymCmd represents the a2s command

View Source
var DisassCmd = &cobra.Command{
	Use:           "disass <dyld_shared_cache>",
	Aliases:       []string{"dis"},
	Short:         "Disassemble dyld_shared_cache at symbol/vaddr",
	Args:          cobra.MinimumNArgs(1),
	SilenceUsage:  true,
	SilenceErrors: true,
	RunE: func(cmd *cobra.Command, args []string) error {

		var data []byte
		var middleAddr uint64
		var image *dyld.CacheImage

		if viper.GetBool("verbose") {
			log.SetLevel(log.DebugLevel)
		}
		color.NoColor = !viper.GetBool("color")

		symbolName := viper.GetString("dyld.disass.symbol")
		startAddr := viper.GetUint64("dyld.disass.vaddr")
		imageName := viper.GetString("dyld.disass.image")
		instructions := viper.GetUint64("dyld.disass.count")

		demangleFlag := viper.GetBool("dyld.disass.demangle")
		asJSON := viper.GetBool("dyld.disass.json")
		quiet := viper.GetBool("dyld.disass.quiet")

		funcFile := viper.GetString("dyld.disass.input")
		cacheFile := viper.GetString("dyld.disass.cache")

		if len(symbolName) > 0 && startAddr != 0 {
			return fmt.Errorf("you can only use --symbol OR --vaddr (not both)")
		} else if len(funcFile) > 0 && (len(symbolName) > 0 || startAddr != 0 || len(imageName) > 0) {
			return fmt.Errorf("you can NOT combine the --input flag with other filter flags (--symbol|--vaddr|--image)")
		} else if len(symbolName) == 0 && startAddr == 0 {
			if len(imageName) == 0 && len(funcFile) == 0 {
				return fmt.Errorf("if you don't supply a --image or --input flag you MUST supply a --symbol OR --vaddr to disassemble")
			}
		}

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

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

		if fileInfo.Mode()&os.ModeSymlink != 0 {
			symlinkPath, err := os.Readlink(dscPath)
			if err != nil {
				return errors.Wrapf(err, "failed to read symlink %s", dscPath)
			}

			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 !f.IsArm64() {
			log.Errorf("can only disassemble arm64 caches")
			return nil
		}

		if !quiet || len(symbolName) > 0 {
			if len(cacheFile) == 0 {
				cacheFile = dscPath + ".a2s"
			}
			if err := f.OpenOrCreateA2SCache(cacheFile); err != nil {
				return err
			}
		}

		if len(imageName) > 0 {
			image, err = f.Image(imageName)
			if err != nil {
				return err
			}
		}

		if len(symbolName) > 0 {
			log.Info("Locating symbol: " + symbolName)
			if len(imageName) > 0 {
				sym, err := image.GetSymbol(symbolName)
				if err != nil {
					return err
				}
				startAddr = sym.Address
			} else {
				startAddr, image, err = f.GetSymbolAddress(symbolName)
				if err != nil {
					return err
				}
			}
			log.Infof("Found symbol in %s", filepath.Base(image.Name))
		} else {
			if len(imageName) > 0 {
				m, err := image.GetMacho()
				if err != nil {
					return err
				}
				defer m.Close()

				for idx, fn := range m.GetFunctions() {
					data, err := m.GetFunctionData(fn)
					if err != nil {
						log.Errorf("failed to get data for function: %v", err)
						continue
					}

					engine := dyld.NewDyldDisass(f, &disass.Config{
						Image:        image.Name,
						Data:         data,
						StartAddress: fn.StartAddr,
						Middle:       0,
						AsJSON:       asJSON,
						Demangle:     demangleFlag,
						Quite:        quiet,
						Color:        viper.GetBool("color"),
					})

					if !quiet {

						if err := image.Analyze(); err != nil {
							return err
						}
						if err := engine.Triage(); err != nil {
							return fmt.Errorf("first pass triage failed: %v", err)
						}
						for _, img := range engine.Dylibs() {
							if err := img.Analyze(); err != nil {
								return err
							}
						}
					} else {
						if !asJSON {
							if idx == 0 {
								fmt.Printf("sub_%x:\n", fn.StartAddr)
							} else {
								fmt.Printf("\nsub_%x:\n", fn.StartAddr)
							}
						}
					}

					disass.Disassemble(engine)
				}

				return nil
			}
		}

		if len(funcFile) > 0 {
			funcFile = filepath.Clean(funcFile)
			fdata, _ := os.ReadFile(funcFile)

			var funcs []Func
			if err := json.Unmarshal(fdata, &funcs); err != nil {
				return fmt.Errorf("failed to parse function JSON file %s: %v", funcFile, err)
			}

			for _, fn := range funcs {
				uuid, off, err := f.GetOffset(fn.Start)
				if err != nil {
					return err
				}

				data, err := f.ReadBytesForUUID(uuid, int64(off), fn.Size)
				if err != nil {
					return err
				}

				engine := dyld.NewDyldDisass(f, &disass.Config{
					Image:        fn.Image,
					Data:         data,
					StartAddress: fn.Start,
					Middle:       0,
					AsJSON:       asJSON,
					Demangle:     demangleFlag,
					Quite:        quiet,
					Color:        viper.GetBool("color"),
				})

				if !quiet {

					image, err = f.Image(fn.Image)
					if err != nil {
						return err
					}
					if err := image.Analyze(); err != nil {
						return err
					}
					if err := engine.Triage(); err != nil {
						return fmt.Errorf("first pass triage failed: %v", err)
					}
					for _, img := range engine.Dylibs() {
						if err := img.Analyze(); err != nil {
							return err
						}
					}
				} else {
					if !asJSON {
						if len(fn.Name) > 0 {
							fmt.Printf("\n%s:\n", fn.Name)
						} else {
							fmt.Printf("\nsub_%x:\n", fn.Start)
						}
					}
				}

				disass.Disassemble(engine)
			}
		} else {

			if instructions > 0 {
				uuid, off, err := f.GetOffset(startAddr)
				if err != nil {
					return err
				}
				data, err = f.ReadBytesForUUID(uuid, int64(off), instructions*4)
				if err != nil {
					return err
				}
			} else {
				image, err = f.GetImageContainingVMAddr(startAddr)
				if err != nil {
					return err
				}
				if err := image.Analyze(); err != nil {
					return err
				}
				m, err := image.GetMacho()
				if err != nil {
					return err
				}
				defer m.Close()
				if fn, err := m.GetFunctionForVMAddr(startAddr); err == nil {
					uuid, soff, err := f.GetOffset(fn.StartAddr)
					if err != nil {
						return err
					}
					data, err = f.ReadBytesForUUID(uuid, int64(soff), uint64(fn.EndAddr-fn.StartAddr))
					if err != nil {
						return err
					}
					if startAddr != fn.StartAddr {
						middleAddr = startAddr
						startAddr = fn.StartAddr
					}
				} else {
					log.Warnf("disassembling 100 instructions at %#x", startAddr)
					instructions = 100
					uuid, off, err := f.GetOffset(startAddr)
					if err != nil {
						return err
					}
					data, err = f.ReadBytesForUUID(uuid, int64(off), instructions*4)
					if err != nil {
						return err
					}
				}
			}
			if data == nil {
				log.Fatal("failed to disassemble")
			}

			// // Apply slide
			// if slide > 0 {
			// 	startAddr = startAddr - slide
			// }
			var imageName string
			if image != nil {
				imageName = image.Name
			}
			engine := dyld.NewDyldDisass(f, &disass.Config{
				Image:        imageName,
				Data:         data,
				StartAddress: startAddr,
				Middle:       middleAddr,
				AsJSON:       asJSON,
				Demangle:     demangleFlag,
				Quite:        quiet,
				Color:        viper.GetBool("color"),
			})

			if !quiet {

				if err := engine.Triage(); err != nil {
					return fmt.Errorf("first pass triage failed: %v", err)
				}
				for _, img := range engine.Dylibs() {
					if err := img.Analyze(); err != nil {
						return err
					}
				}
			} else {
				if !asJSON {
					if len(symbolName) > 0 {
						fmt.Printf("%s:\n", symbolName)
					} else {
						fmt.Printf("sub_%x:\n", startAddr)
					}
				}
			}

			disass.Disassemble(engine)
		}

		return nil
	},
}

disassCmd represents the disass command

View Source
var DumpCmd = &cobra.Command{
	Use:   "dump <dyld_shared_cache> <address>",
	Short: "Dump dyld_shared_cache data at given virtual address",
	Args:  cobra.MaximumNArgs(2),
	RunE: func(cmd *cobra.Command, args []string) error {

		if viper.GetBool("verbose") {
			log.SetLevel(log.DebugLevel)
		}
		color.NoColor = !viper.GetBool("color")

		size := viper.GetUint64("dyld.dump.size")
		count := viper.GetUint64("dyld.dump.count")
		asAddrs := viper.GetBool("dyld.dump.addr")
		asBytes := viper.GetBool("dyld.dump.bytes")
		fromImage := viper.GetString("dyld.dump.image")
		segmentSection := viper.GetString("dyld.dump.section")
		outFile := viper.GetString("dyld.dump.output")

		if size > 0 && count > 0 {
			return fmt.Errorf("you can only use --size OR --count")
		} else if asAddrs && asBytes {
			return fmt.Errorf("you can only use --addr OR --bytes")
		} else if asAddrs && size > 0 {
			return fmt.Errorf("you can only use --addr with --count")
		} else if asBytes && count > 0 {
			return fmt.Errorf("you can only use --bytes with --size")
		} else if fromImage != "" && segmentSection == "" {
			return fmt.Errorf("you must specify a --section when using --image")
		} else if fromImage == "" && segmentSection != "" {
			return fmt.Errorf("you must specify an --image when using --section")
		} else if len(segmentSection) > 0 && len(args) != 1 {
			return fmt.Errorf("you can only use <address> OR (--image AND --section)")
		}

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

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

		if fileInfo.Mode()&os.ModeSymlink != 0 {
			symlinkPath, err := os.Readlink(dscPath)
			if err != nil {
				return errors.Wrapf(err, "failed to read symlink %s", dscPath)
			}

			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 addr uint64
		if len(segmentSection) != 0 {
			parts := strings.Split(segmentSection, ".")
			if len(parts) != 2 {
				return fmt.Errorf("invalid section")
			}
			image, err := f.Image(fromImage)
			if err != nil {
				return err
			}
			m, err := image.GetPartialMacho()
			if err != nil {
				return err
			}
			if sec := m.Section(parts[0], parts[1]); sec != nil {
				addr = sec.Addr
				if size == 0 && count == 0 {
					size = sec.Size
				}
			} else {
				return fmt.Errorf("failed to find section %s", segmentSection)
			}
		} else {
			addr, err = utils.ConvertStrToInt(args[1])
			if err != nil {
				return err
			}
		}

		if asAddrs {
			if count == 0 {
				log.Info("Setting --count=20")
				count = 20
			}
			size = count * uint64(binary.Size(uint64(0)))
		} else {
			if size == 0 {
				log.Info("Setting --size=256")
				size = 256
			}
		}

		uuid, off, err := f.GetOffset(addr)
		if err != nil {
			log.Error(err.Error())
		} else {
			dat, err := f.ReadBytesForUUID(uuid, int64(off), size)
			if err != nil {
				return err
			}

			if asAddrs {
				addrs := make([]uint64, count)
				if err := binary.Read(bytes.NewReader(dat), f.ByteOrder, addrs); err != nil {
					return err
				}
				if len(outFile) > 0 {
					o, err := os.Create(outFile)
					if err != nil {
						return err
					}
					w := bufio.NewWriter(o)
					for _, a := range addrs {
						w.WriteString(fmt.Sprintf("%#x\n", f.SlideInfo.SlidePointer(a)))
					}
				} else {
					for _, a := range addrs {
						fmt.Printf("%#x\n", f.SlideInfo.SlidePointer(a))
					}
				}
			} else if asBytes {
				if _, err := os.Stdout.Write(dat); err != nil {
					return fmt.Errorf("failed to write bytes to stdout: %s", err)
				}
			} else {
				if len(outFile) > 0 {
					os.WriteFile(outFile, dat, 0660)
					log.Infof("Wrote data to file %s", outFile)
				} else {
					if image, err := f.GetImageContainingVMAddr(addr); err == nil {
						if m, err := image.GetMacho(); err == nil {
							defer m.Close()
							if s := m.FindSegmentForVMAddr(addr); s != nil {
								if s.Nsect > 0 {
									if c := m.FindSectionForVMAddr(addr); c != nil {
										log.WithFields(log.Fields{"dylib": image.Name, "section": fmt.Sprintf("%s.%s", c.Seg, c.Name)}).Info("Address location")
									}
								} else {
									log.WithFields(log.Fields{"dylib": image.Name, "segment": s.Name}).Info("Address location")
								}
							}
						}
					} else {
						if _, mapping, err := f.GetMappingForVMAddress(addr); err == nil {
							log.WithFields(log.Fields{
								"name": mapping.Name,
								"off":  fmt.Sprintf("%#x", mapping.FileOffset),
								"addr": fmt.Sprintf("%#x", mapping.Address),
								"size": fmt.Sprintf("%#x", mapping.Size),
							}).Info("Mapping")
						}
					}
					fmt.Println(utils.HexDump(dat, addr))
				}
			}
		}

		return nil
	},
}

DumpCmd represents the dump command

View Source
var DyldCmd = &cobra.Command{
	Use:     "dyld",
	Aliases: []string{"dsc"},
	Short:   "Parse dyld_shared_cache",
	Args:    cobra.NoArgs,
	PersistentPreRun: func(cmd *cobra.Command, args []string) {
		viper.BindPFlag("color", cmd.Flags().Lookup("color"))
		viper.BindPFlag("verbose", cmd.Flags().Lookup("verbose"))
		viper.BindPFlag("diff-tool", cmd.Flags().Lookup("diff-tool"))
	},
	Run: func(cmd *cobra.Command, args []string) {
		cmd.Help()
	},
}

DyldCmd represents the dyld command

View Source
var ImageCmd = &cobra.Command{
	Use:           "image <dyld_shared_cache> <IMAGE>",
	Aliases:       []string{"img"},
	Short:         "Dump image array info",
	Args:          cobra.MinimumNArgs(1),
	SilenceUsage:  true,
	SilenceErrors: true,
	Example: `  # List all the apps
  ❯ ipsw dyld image <dyld_shared_cache>
  # Dump the closure info for a in-cache dylib
  ❯ ipsw dyld image <dyld_shared_cache> Foundation
  # Dump the closure info for an app
  ❯ ipsw dyld image <dyld_shared_cache> /usr/libexec/timed`,
	RunE: func(cmd *cobra.Command, args []string) error {

		if viper.GetBool("verbose") {
			log.SetLevel(log.DebugLevel)
		}

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

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

		if fileInfo.Mode()&os.ModeSymlink != 0 {
			symlinkPath, err := os.Readlink(dscPath)
			if err != nil {
				return errors.Wrapf(err, "failed to read symlink %s", dscPath)
			}

			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 {
			if image, err := f.Image(args[1]); err == nil {
				if pbl, err := f.GetDylibPrebuiltLoader(image.Name); err == nil {
					fmt.Println(pbl.String(f))
				} else {
					if !errors.Is(err, dyld.ErrPrebuiltLoaderSetNotSupported) {
						return fmt.Errorf("failed parsing launch loader sets: %v", err)
					}

					idx, err := f.GetDylibIndex(image.Name)
					if err != nil {
						return err
					}
					if err := f.ParseImageArrays(); err != nil {
						return fmt.Errorf("failed parsing image arrays: %v", err)
					}
					ci := f.ImageArray[uint32(idx+1)]
					fmt.Println(ci.String(f, viper.GetBool("verbose")))
				}
				return nil
			} else {
				if pset, err := f.GetLaunchLoaderSet(args[1]); err == nil {
					fmt.Println(pset.String(f))
				} else {
					if !errors.Is(err, dyld.ErrPrebuiltLoaderSetNotSupported) {
						return fmt.Errorf("failed parsing launch loader sets: %v", err)
					}

					if id, err := f.GetDlopenOtherImageIndex(args[1]); err == nil {
						if err := f.ParseImageArrays(); err != nil {
							return fmt.Errorf("failed parsing image arrays: %v", err)
						}
						ci := f.ImageArray[uint32(id)]
						fmt.Println(ci.String(f, viper.GetBool("verbose")))
						return nil
					} else {
						for _, clos := range f.Closures {
							for _, img := range clos.Images {
								if img.Name == args[1] {
									fmt.Println(img.String(f, viper.GetBool("verbose")))
									return nil
								}
							}
						}
					}
					return fmt.Errorf("image %s not found (maybe try the FULL path)", args[1])
				}
			}
		} else {
			if err := f.ForEachLaunchLoaderSetPath(func(execPath string) {
				fmt.Println(execPath)
			}); err != nil {
				if !errors.Is(err, dyld.ErrPrebuiltLoaderSetNotSupported) {
					return fmt.Errorf("failed parsing launch loader sets: %v", err)
				}

				if err := f.ParseImageArrays(); err != nil {
					return fmt.Errorf("failed parsing image arrays: %v", err)
				}
				for _, img := range f.ImageArray {
					fmt.Printf("%s\n", img.Name)
				}
			}
		}

		return nil
	},
}

imageCmd represents the image command

View Source
var MachoCmd = &cobra.Command{
	Use:           "macho <dyld_shared_cache> <dylib>",
	Aliases:       []string{"m"},
	Short:         "Parse a dylib file",
	Args:          cobra.MinimumNArgs(1),
	SilenceUsage:  true,
	SilenceErrors: true,
	RunE: func(cmd *cobra.Command, args []string) error {

		if viper.GetBool("verbose") {
			log.SetLevel(log.DebugLevel)
		}

		showLoadCommands, _ := cmd.Flags().GetBool("loads")
		showLoadCommandsAsJSON, _ := cmd.Flags().GetBool("json")
		showObjC, _ := cmd.Flags().GetBool("objc")
		showObjcRefs, _ := cmd.Flags().GetBool("objc-refs")
		showSwift, _ := cmd.Flags().GetBool("swift")
		dumpSymbols, _ := cmd.Flags().GetBool("symbols")
		demangleSyms, _ := cmd.Flags().GetBool("demangle")
		showFuncStarts, _ := cmd.Flags().GetBool("starts")
		dumpStrings, _ := cmd.Flags().GetBool("strings")
		dumpStubs, _ := cmd.Flags().GetBool("stubs")
		searchPattern, _ := cmd.Flags().GetString("search")
		dumpALL, _ := cmd.Flags().GetBool("all")
		extractDylib, _ := cmd.Flags().GetBool("extract")
		extractPath, _ := cmd.Flags().GetString("output")
		forceExtract, _ := cmd.Flags().GetBool("force")

		onlyFuncStarts := !showLoadCommands && !showObjC && !showSwift && !dumpStubs && showFuncStarts
		onlyStubs := !showLoadCommands && !showObjC && !showSwift && !showFuncStarts && dumpStubs
		onlySearch := !showLoadCommands && !showObjC && !showSwift && !showFuncStarts && !dumpStubs && searchPattern != ""
		if demangleSyms && !dumpSymbols {
			return fmt.Errorf("you must also supply --symbols flag to demangle symbols")
		}

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

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

		if fileInfo.Mode()&os.ModeSymlink != 0 {
			symlinkPath, err := os.Readlink(dscPath)
			if err != nil {
				return errors.Wrapf(err, "failed to read symlink %s", dscPath)
			}

			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

			foundPattern := false

			if dumpALL {
				images = f.Images
			} else {
				image, err := f.Image(args[1])
				if err != nil {
					return fmt.Errorf("image not in %s: %v", dscPath, err)
				}
				images = append(images, image)
			}

			for _, image := range images {

				if dumpALL {
					log.WithField("name", image.Name).Debug("Image")
				}

				m, err := image.GetMacho()
				if err != nil {
					log.Warnf("failed to parse full MachO for %s: %v", image.Name, err)
					if extractDylib {
						continue
					}
					m, err = image.GetPartialMacho()
					if err != nil {
						return err
					}
				}
				defer m.Close()

				if extractDylib {

					folder := filepath.Dir(dscPath)
					if len(extractPath) > 0 {
						folder = extractPath
					}
					fname := filepath.Join(folder, filepath.Base(image.Name))
					if dumpALL {
						fname = filepath.Join(folder, image.Name)
					}

					if _, err := os.Stat(fname); os.IsNotExist(err) || forceExtract {
						var dcf *fixupchains.DyldChainedFixups
						if m.HasFixups() {
							dcf, err = m.DyldChainedFixups()
							if err != nil {
								return fmt.Errorf("failed to parse fixups from in memory MachO: %v", err)
							}
						}

						image.ParseLocalSymbols(false)

						err = m.Export(fname, dcf, m.GetBaseAddress(), image.GetLocalSymbolsAsMachoSymbols())
						if err != nil {
							return fmt.Errorf("failed to export entry MachO %s; %v", image.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 {
						if !dumpALL {
							log.Warnf("dylib already exists: %s", fname)
						}
					}
					continue
				}

				if showLoadCommands || !showObjC && !dumpSymbols && !dumpStrings && !showFuncStarts && !dumpStubs && searchPattern == "" {
					if showLoadCommandsAsJSON {
						dat, err := m.FileTOC.MarshalJSON()
						if err != nil {
							return fmt.Errorf("failed to marshal MachO table of contents as JSON: %v", err)
						}
						fmt.Println(string(dat))
					} else {
						fmt.Println(m.FileTOC.String())
					}
				}

				if showObjC {
					fmt.Println("Objective-C")
					fmt.Println("===========")
					if m.HasObjC() {
						if info, err := m.GetObjCImageInfo(); err == nil {
							fmt.Println(info.Flags)
						} else if !errors.Is(err, macho.ErrObjcSectionNotFound) {
							log.Error(err.Error())
						}
						if viper.GetBool("verbose") {
							fmt.Println(m.GetObjCToc())
						}
						if protos, err := m.GetObjCProtocols(); err == nil {
							seen := make(map[uint64]bool)
							for _, proto := range protos {
								if _, ok := seen[proto.Ptr]; !ok {
									if viper.GetBool("verbose") {
										if viper.GetBool("color") {
											quick.Highlight(os.Stdout, swift.DemangleBlob(proto.Verbose()), "objc", "terminal256", "nord")
											quick.Highlight(os.Stdout, "\n/****************************************/\n\n", "objc", "terminal256", "nord")
										} else {
											fmt.Println(swift.DemangleBlob(proto.Verbose()))
										}
									} else {
										if viper.GetBool("color") {
											quick.Highlight(os.Stdout, proto.String()+"\n", "objc", "terminal256", "nord")
										} else {
											fmt.Println(proto.String())
										}
									}
									seen[proto.Ptr] = true
								}
							}
						} else if !errors.Is(err, macho.ErrObjcSectionNotFound) {
							log.Error(err.Error())
						}
						if classes, err := m.GetObjCClasses(); err == nil {
							for _, class := range classes {
								if viper.GetBool("verbose") {
									if viper.GetBool("color") {
										quick.Highlight(os.Stdout, swift.DemangleBlob(class.Verbose()), "objc", "terminal256", "nord")
										quick.Highlight(os.Stdout, "\n/****************************************/\n\n", "objc", "terminal256", "nord")
									} else {
										fmt.Println(swift.DemangleBlob(class.Verbose()))
									}
								} else {
									if viper.GetBool("color") {
										quick.Highlight(os.Stdout, class.String()+"\n", "objc", "terminal256", "nord")
									} else {
										fmt.Println(class.String())
									}
								}
							}
						} else if !errors.Is(err, macho.ErrObjcSectionNotFound) {
							log.Error(err.Error())
						}
						if cats, err := m.GetObjCCategories(); err == nil {
							for _, cat := range cats {
								if viper.GetBool("verbose") {
									if viper.GetBool("color") {
										quick.Highlight(os.Stdout, swift.DemangleBlob(cat.Verbose()), "objc", "terminal256", "nord")
										quick.Highlight(os.Stdout, "\n/****************************************/\n\n", "objc", "terminal256", "nord")
									} else {
										fmt.Println(swift.DemangleBlob(cat.Verbose()))
									}
								} else {
									if viper.GetBool("color") {
										quick.Highlight(os.Stdout, cat.String()+"\n", "objc", "terminal256", "nord")
									} else {
										fmt.Println(cat.String())
									}
								}
							}
						} else if !errors.Is(err, macho.ErrObjcSectionNotFound) {
							log.Error(err.Error())
						}
						if showObjcRefs {
							if protRefs, err := m.GetObjCProtoReferences(); err == nil {
								fmt.Printf("\n@protocol refs\n")
								for off, prot := range protRefs {
									fmt.Printf("0x%011x => 0x%011x: %s\n", off, prot.Ptr, prot.Name)
								}
							} else if !errors.Is(err, macho.ErrObjcSectionNotFound) {
								log.Error(err.Error())
							}
							if clsRefs, err := m.GetObjCClassReferences(); err == nil {
								fmt.Printf("\n@class refs\n")
								for off, cls := range clsRefs {
									fmt.Printf("0x%011x => 0x%011x: %s\n", off, cls.ClassPtr, cls.Name)

								}
							} else if !errors.Is(err, macho.ErrObjcSectionNotFound) {
								log.Error(err.Error())
							}
							if supRefs, err := m.GetObjCSuperReferences(); err == nil {
								fmt.Printf("\n@super refs\n")
								for off, sup := range supRefs {
									fmt.Printf("0x%011x => 0x%011x: %s\n", off, sup.ClassPtr, sup.Name)
								}
							} else if !errors.Is(err, macho.ErrObjcSectionNotFound) {
								log.Error(err.Error())
							}
							if selRefs, err := m.GetObjCSelectorReferences(); err == nil {
								fmt.Printf("\n@selectors refs\n")
								for off, sel := range selRefs {
									fmt.Printf("0x%011x => 0x%011x: %s\n", off, sel.VMAddr, sel.Name)
								}
							} else if !errors.Is(err, macho.ErrObjcSectionNotFound) {
								log.Error(err.Error())
							}
							if viper.GetBool("verbose") {
								if classes, err := m.GetObjCClassNames(); err == nil {
									fmt.Printf("\n@objc_classname\n")
									for vmaddr, className := range classes {
										fmt.Printf("0x%011x: %s\n", vmaddr, className)
									}
								} else if !errors.Is(err, macho.ErrObjcSectionNotFound) {
									log.Error(err.Error())
								}
								if methods, err := m.GetObjCMethodNames(); err == nil {
									fmt.Printf("\n@objc_methname\n")
									for vmaddr, method := range methods {
										fmt.Printf("0x%011x: %s\n", vmaddr, method)
									}
								} else if !errors.Is(err, macho.ErrObjcSectionNotFound) {
									log.Error(err.Error())
								}
							}
						}

					} else {
						fmt.Println("  - no objc")
					}
					fmt.Println()
				}

				if showSwift {
					fmt.Println("Swift")
					fmt.Println("=====")
					info, err := m.GetObjCImageInfo()
					if err != nil {
						if !errors.Is(err, macho.ErrObjcSectionNotFound) {
							return err
						}
					}
					if info != nil && info.HasSwift() {
						if fields, err := m.GetSwiftFields(); err == nil {
							for _, field := range fields {
								if viper.GetBool("verbose") {
									if viper.GetBool("color") {
										quick.Highlight(os.Stdout, swift.DemangleBlob(field.String()), "swift", "terminal256", "nord")
										quick.Highlight(os.Stdout, "\n/****************************************/\n\n", "swift", "terminal256", "nord")
									} else {
										fmt.Println(swift.DemangleBlob(field.String()))
									}
								} else {
									if viper.GetBool("color") {
										quick.Highlight(os.Stdout, field.String(), "swift", "terminal256", "nord")
										quick.Highlight(os.Stdout, "\n/****************************************/\n\n", "swift", "terminal256", "nord")
									} else {
										fmt.Println(field.String())
									}
								}
							}
						} else if !errors.Is(err, macho.ErrSwiftSectionError) {
							log.Error(err.Error())
						}
					} else {
						fmt.Println("  - no swift")
					}
					fmt.Println()
				}

				if showFuncStarts {
					if !onlyFuncStarts {
						fmt.Println("FUNCTION STARTS")
						fmt.Println("===============")
					}
					if m.FunctionStarts() != nil {
						for _, fn := range m.GetFunctions() {
							if viper.GetBool("verbose") {
								fmt.Printf("%#016x-%#016x\n", fn.StartAddr, fn.EndAddr)
							} else {
								fmt.Printf("0x%016X\n", fn.StartAddr)
							}
						}
					}
				}

				if dumpSymbols {
					image.ParseLocalSymbols(false)
					w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0)
					if m.Symtab != nil {
						fmt.Println("SYMBOLS")
						fmt.Println("=======")
						undeflush := false
						for _, sym := range m.Symtab.Syms {
							if sym.Type.IsUndefinedSym() && !undeflush {
								w.Flush()
								undeflush = true
							}
							if demangleSyms {
								if strings.HasPrefix(sym.Name, "_associated conformance ") {
									if _, rest, ok := strings.Cut(sym.Name, "_associated conformance "); ok {
										sym.Name, _ = swift.Demangle("_$s" + rest)
										sym.Name = "_associated conformance " + sym.Name
									}
								} else if strings.HasPrefix(sym.Name, "_symbolic ") {
									if _, rest, ok := strings.Cut(sym.Name, "_symbolic "); ok {
										rest = strings.TrimPrefix(rest, "_____ ")
										if !strings.HasPrefix(rest, "$s") && !strings.HasPrefix(rest, "_$s") {
											rest = "_$s" + rest
										}
										sym.Name, _ = swift.Demangle(rest)
										sym.Name = "_symbolic " + sym.Name
									}
								} else if strings.HasPrefix(sym.Name, "_$s") || strings.HasPrefix(sym.Name, "$s") {
									sym.Name, _ = swift.Demangle(sym.Name)
								} else {
									sym.Name = demangle.Do(sym.Name, false, false)
								}
							}
							if sym.Value == 0 {
								fmt.Fprintf(w, "              %s\n", strings.Join([]string{symTypeColor(sym.GetType(m)), symNameColor(sym.Name), symLibColor(sym.GetLib(m))}, "\t"))
							} else {
								if sym.Name == "<redacted>" {
									if name, ok := f.AddressToSymbol[sym.Value]; ok {
										sym.Name = name
									}
								}
								fmt.Fprintf(w, "%s:  %s\n", symAddrColor("%#09x", sym.Value), strings.Join([]string{symTypeColor(sym.GetType(m)), symNameColor(sym.Name), symLibColor(sym.GetLib(m))}, "\t"))
							}
						}
						w.Flush()
					}
					if binds, err := m.GetBindInfo(); err == nil {
						fmt.Printf("\nDyld Binds\n")
						fmt.Println("----------")
						for _, bind := range binds {
							fmt.Fprintf(w, "%#09x:\t(%s.%s|from %s)\t%s\n", bind.Start+bind.Offset, bind.Segment, bind.Section, bind.Dylib, bind.Name)
						}
						w.Flush()
					}

					if m.DyldExportsTrie() != nil && m.DyldExportsTrie().Size > 0 && viper.GetBool("verbose") {
						fmt.Printf("\nDyld Exports\n")
						fmt.Println("------------")
						exports, err := m.DyldExports()
						if err != nil {
							return err
						}
						for _, export := range exports {
							if export.Flags.ReExport() {
								export.FoundInDylib = m.ImportedLibraries()[export.Other-1]
								reimg, err := f.Image(export.FoundInDylib)
								if err != nil {
									return err
								}
								if rexpSym, err := reimg.GetExport(export.ReExport); err == nil {
									export.Address = rexpSym.Address
								}
							}
							fmt.Println(export)
						}
					}
				}

				if dumpStrings {
					fmt.Printf("\nCStrings\n")
					fmt.Println("--------")
					for _, sec := range m.Sections {
						if sec.Flags.IsCstringLiterals() || sec.Seg == "__TEXT" && sec.Name == "__const" {
							uuid, off, err := f.GetOffset(sec.Addr)
							if err != nil {
								return fmt.Errorf("failed to get offset for %s.%s: %v", sec.Seg, sec.Name, err)
							}
							dat, err := f.ReadBytesForUUID(uuid, int64(off), sec.Size)
							if err != nil {
								return fmt.Errorf("failed to read cstrings in %s.%s: %v", sec.Seg, sec.Name, err)
							}

							csr := bytes.NewBuffer(dat)

							for {
								pos := sec.Addr + uint64(csr.Cap()-csr.Len())

								s, err := csr.ReadString('\x00')

								if err == io.EOF {
									break
								}

								if err != nil {
									return fmt.Errorf("failed to read string: %v", err)
								}

								s = strings.Trim(s, "\x00")

								if len(s) > 0 {
									if (sec.Seg == "__TEXT" && sec.Name == "__const") && !utils.IsASCII(s) {
										continue
									}
									fmt.Printf("%#x: %#v\n", pos, s)
								}
							}
						}
					}

					if cfstrs, err := m.GetCFStrings(); err == nil {
						if len(cfstrs) > 0 {
							fmt.Printf("\nCFStrings\n")
							fmt.Println("---------")
							for _, cfstr := range cfstrs {
								fmt.Printf("%#09x:  %#v\n", cfstr.Address, cfstr.Name)
							}
						}
					}
				}

				if dumpStubs {
					if !onlyStubs {
						fmt.Printf("\nStubs\n")
						fmt.Println("=====")
					}
					if err := image.Analyze(); err != nil {
						return err
					}
					for stubAddr, addr := range image.Analysis.SymbolStubs {
						if symName, ok := f.AddressToSymbol[addr]; ok {
							fmt.Printf("%#x => %#x: %s\n", stubAddr, addr, symName)
						}
					}
				}

				if len(searchPattern) > 0 {
					var run string
					var gadget [][]byte

					patternBytes := strings.Fields(searchPattern)

					for idx, abyte := range patternBytes {
						if abyte == "*" {
							if len(run) > 0 {
								pattern, err := hex.DecodeString(run)
								if err != nil {
									return fmt.Errorf("failed to decode pattern '%s': %v", run, err)
								}

								gadget = append(gadget, pattern)

								run = ""
							}
							gadget = append(gadget, []byte{})
						} else {
							run += abyte
							if idx == len(patternBytes)-1 {
								pattern, err := hex.DecodeString(run)
								if err != nil {
									return fmt.Errorf("failed to decode pattern '%s': %v", run, err)
								}

								gadget = append(gadget, pattern)
							}
						}
					}

					if !dumpALL || !onlySearch {
						fmt.Printf("\nSearch Results\n")
						fmt.Println("--------------")
					}

					if textSeg := m.Segment("__TEXT"); textSeg != nil {
						uuid, off, err := f.GetOffset(textSeg.Addr)
						if err != nil {
							return fmt.Errorf("failed to get offset for %s: %v", textSeg.Name, err)
						}
						data, err := f.ReadBytesForUUID(uuid, int64(off), textSeg.Filesz)
						if err != nil {
							return fmt.Errorf("failed to read cstrings in %s: %v", textSeg.Name, err)
						}

						i := 0
						found := 0
						foundOffset := uint64(0)

						done := func() {
							foundPattern = true
							if dumpALL {
								fmt.Printf("%#x\t%s\n", textSeg.Addr+foundOffset, image.Name)
							} else {
								fmt.Printf("%#x\n", textSeg.Addr+foundOffset)
							}
						}

						for found >= 0 && i < len(data) {
							foundFirstPart := false
							for idx, gad := range gadget {
								if len(gad) == 0 {
									if foundFirstPart && idx == len(gadget)-1 {
										done()
									}
									i += 1
								} else if found = bytes.Index(data[i:], gad); foundFirstPart && found == 0 {
									if idx == len(gadget)-1 {
										done()
									}
									i += len(gad)
								} else if !foundFirstPart && found >= 0 {
									if idx == 0 {
										foundFirstPart = true
										foundOffset = uint64(i + found)
									}
									if len(gadget) == 1 {
										done()
									}
									i += found + len(gad)
								} else if found < 0 {
									i += len(gad)
									break
								}
							}
						}
					}
				}
			}

			if len(searchPattern) > 0 && !foundPattern {
				return fmt.Errorf("pattern '%s' not found", searchPattern)
			}

		} else {
			log.Error("you must supply a dylib MachO to parse")
		}

		return nil
	},
}

MachoCmd represents the macho command

View Source
var ObjcCmd = &cobra.Command{
	Use:           "objc <dyld_shared_cache>",
	Short:         "Dump Objective-C Optimization Info",
	SilenceUsage:  true,
	SilenceErrors: true,
	Args:          cobra.MinimumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {

		if viper.GetBool("verbose") {
			log.SetLevel(log.DebugLevel)
		}

		printClasses, _ := cmd.Flags().GetBool("class")
		printSelectors, _ := cmd.Flags().GetBool("sel")
		printProtocols, _ := cmd.Flags().GetBool("proto")
		printImpCaches, _ := cmd.Flags().GetBool("imp-cache")

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

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

		if fileInfo.Mode()&os.ModeSymlink != 0 {
			symlinkPath, err := os.Readlink(dscPath)
			if err != nil {
				return errors.Wrapf(err, "failed to read symlink %s", dscPath)
			}

			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 printClasses {
			if _, err := f.GetAllObjCClasses(true); err != nil {
				return err
			}
		}

		if printSelectors {
			if _, err := f.GetAllObjCSelectors(true); err != nil {
				return err
			}
		}

		if printProtocols {
			if _, err := f.GetAllObjCProtocols(true); err != nil {
				return err
			}
		}

		if printImpCaches {
			if err = f.ImpCachesForImage(); err != nil {
				return err
			}
		}

		return nil
	},
}

ObjcCmd represents the objc command

View Source
var OffsetToAddrCmd = &cobra.Command{
	Use:           "o2a <dyld_shared_cache> <offset>",
	Short:         "Convert dyld_shared_cache offset to address",
	Args:          cobra.MinimumNArgs(2),
	SilenceUsage:  true,
	SilenceErrors: true,
	RunE: func(cmd *cobra.Command, args []string) error {

		if viper.GetBool("verbose") {
			log.SetLevel(log.DebugLevel)
		}

		inDec, _ := cmd.Flags().GetBool("dec")
		inHex, _ := cmd.Flags().GetBool("hex")

		if inDec && inHex {
			return fmt.Errorf("you can only use --dec OR --hex")
		}

		offset, err := utils.ConvertStrToInt(args[1])
		if err != nil {
			return err
		}

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

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

		if fileInfo.Mode()&os.ModeSymlink != 0 {
			symlinkPath, err := os.Readlink(dscPath)
			if err != nil {
				return errors.Wrapf(err, "failed to read symlink %s", dscPath)
			}

			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 f.Headers[f.UUID].CacheType == dyld.CacheTypeUniversal {
			utils.Indent(log.Warn, 2)("dyld4 cache with stub islands detected (will search within dyld_subcache_entry's cacheVMOffsets)")
		} else if f.IsDyld4 {
			utils.Indent(log.Warn, 2)("dyld4 cache detected (will search for offset in each subcache)")
		}

		addr, err := dsc.ConvertOffsetToAddress(f, offset)
		if err != nil {
			return err
		}

		for _, addr := range addr.Files {
			if inDec {
				fmt.Printf("%d\n", addr.Address)
			} else if inHex {
				fmt.Printf("%#x\n", addr.Address)
			} else {
				if f.IsDyld4 {
					var stubs string
					if addr.SubCache.InStubs {
						stubs = "STUB Island "
					}
					log.WithFields(log.Fields{
						"hex": fmt.Sprintf("%#x", addr.Address),
						"dec": fmt.Sprintf("%d", addr.Address),
						"sub_cache": fmt.Sprintf("%sdsc%-16s mapping: %s, UUID: %s",
							stubs,
							addr.SubCache.Extension,
							addr.SubCache.Mapping,
							addr.SubCache.UUID,
						),
					}).Info("Address")
				} else {
					log.WithFields(log.Fields{
						"hex":     fmt.Sprintf("%#x", addr.Address),
						"dec":     fmt.Sprintf("%d", addr.Address),
						"mapping": addr.SubCache.Mapping,
					}).Info("Address")
				}
			}
		}

		if addr.Cache != nil {
			var stubs string
			if addr.Cache.SubCache.InStubs {
				stubs = "STUB Island "
			}
			log.WithFields(log.Fields{
				"hex": fmt.Sprintf("%#x", addr.Cache.Address),
				"dec": fmt.Sprintf("%d", addr.Cache.Address),
				"sub_cache": fmt.Sprintf("%sdsc%-16s mapping: %s, UUID: %s",
					stubs,
					addr.Cache.SubCache.Extension,
					addr.Cache.SubCache.Mapping,
					addr.Cache.SubCache.UUID,
				),
			}).Info("CACHE address")
		}

		return nil
	},
}

OffsetToAddrCmd represents the o2a command

View Source
var PatchesCmd = &cobra.Command{
	Use:           "patches <dyld_shared_cache>",
	Aliases:       []string{"p"},
	Short:         "Dump dyld patch info",
	Args:          cobra.MinimumNArgs(1),
	SilenceUsage:  true,
	SilenceErrors: true,
	RunE: func(cmd *cobra.Command, args []string) error {

		if viper.GetBool("verbose") {
			log.SetLevel(log.DebugLevel)
		}

		imageName, _ := cmd.Flags().GetString("image")
		symbolName, _ := cmd.Flags().GetString("sym")

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

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

		if fileInfo.Mode()&os.ModeSymlink != 0 {
			symlinkPath, err := os.Readlink(dscPath)
			if err != nil {
				return errors.Wrapf(err, "failed to read symlink %s", dscPath)
			}

			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 err := f.ParsePatchInfo(); err != nil {
			return fmt.Errorf("failed to parse patch info: %s", err)
		}

		var images []*dyld.CacheImage

		if len(imageName) > 0 {
			image, err := f.Image(imageName)
			if err != nil {
				return fmt.Errorf("image not in %s: %v", dscPath, err)
			}
			images = append(images, image)
		} else {
			images = f.Images
		}

		for idx, image := range images {
			if image.PatchableExports == nil && image.PatchableGOTs == nil {
				continue
			}

			w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', tabwriter.DiscardEmptyColumns)

			if len(symbolName) > 0 {
				switch f.PatchInfoVersion {
				case 1:
					for _, patch := range image.PatchableExports {
						if strings.EqualFold(strings.ToLower(patch.GetName()), strings.ToLower(symbolName)) {
							log.Infof("%s patch locations", patch.GetName())
							for _, loc := range patch.GetPatchLocations().([]dyld.CachePatchableLocationV1) {
								fmt.Println(loc.String(f.Headers[f.UUID].SharedRegionStart))
							}
						}
					}
				case 2, 3:
					exp2uses := make(map[string][]dyld.Patch)
					for _, patch := range image.PatchableExports {
						exp2uses[patch.GetName()] = append(exp2uses[patch.GetName()], patch)
					}
					if patches, ok := exp2uses[symbolName]; ok {
						fmt.Printf("%#x: %s%s\n", image.LoadAddress+uint64(patches[0].GetImplOffset()), patches[0].GetKind(), symbolName)
						for _, patch := range patches {
							for _, loc := range patch.GetPatchLocations().([]dyld.CachePatchableLocationV2) {
								fmt.Fprintf(w, "    %s\t%s\n",
									loc.String(f.Images[patch.GetClientIndex()].LoadAddress),
									f.Images[patch.GetClientIndex()].Name)
							}
						}
					}
					if f.PatchInfoVersion == 3 {

						got2uses := make(map[string][]dyld.Patch)
						for _, got := range image.PatchableGOTs {
							got2uses[got.GetName()] = append(got2uses[got.GetName()], got)
						}
						if patches, ok := got2uses[symbolName]; ok {
							for _, patch := range patches {
								for _, got := range patch.GetGotLocations().([]dyld.CachePatchableLocationV3) {
									fmt.Fprintf(w, "    %s\tGOT\n",
										got.String(func(u uint64) uint64 {
											if _, addr, err := f.GetCacheVMAddress(u); err == nil {
												return addr
											}
											return 0
										}))
								}
							}
						}
					}
					w.Flush()
				case 4:

					exp2uses := make(map[string][]dyld.Patch)
					for _, patch := range image.PatchableExports {
						exp2uses[patch.GetName()] = append(exp2uses[patch.GetName()], patch)
					}
					if patches, ok := exp2uses[symbolName]; ok {
						fmt.Printf("%#x: %s%s\n", image.LoadAddress+uint64(patches[0].GetImplOffset()), patches[0].GetKind(), symbolName)
						for _, patch := range patches {
							for _, loc := range patch.GetPatchLocations().([]dyld.CachePatchableLocationV4) {
								fmt.Fprintf(w, "    %s\t%s\n",
									loc.String(f.Images[patch.GetClientIndex()].LoadAddress),
									colorImage(path.Base(f.Images[patch.GetClientIndex()].Name)))
							}
						}
					}

					got2uses := make(map[string][]dyld.Patch)
					for _, got := range image.PatchableGOTs {
						got2uses[got.GetName()] = append(got2uses[got.GetName()], got)
					}
					if patches, ok := got2uses[symbolName]; ok {
						for _, patch := range patches {
							for _, got := range patch.GetGotLocations().([]dyld.CachePatchableLocationV4Got) {
								fmt.Fprintf(w, "    %s\n",
									got.String(func(u uint64) uint64 {
										if _, addr, err := f.GetCacheVMAddress(u); err == nil {
											return addr
										}
										return 0
									}))
							}
						}
					}
					w.Flush()
				default:
					return fmt.Errorf("unsupported patch info version %d", f.PatchInfoVersion)
				}
			} else {
				if idx == 0 {
					fmt.Printf("[PATCHES] %s", image.Name)
				} else {
					fmt.Printf("\n[PATCHES] %s", image.Name)
				}
				switch f.PatchInfoVersion {
				case 1:
					fmt.Println()
					for _, patch := range image.PatchableExports {
						fmt.Fprintf(w, "%#x\t(%d patches)\t%s\n", patch.GetImplOffset(), len(patch.GetPatchLocations().([]dyld.CachePatchableLocationV1)), patch.GetName())
					}
					w.Flush()
				case 2, 3:
					exp2uses := make(map[string][]dyld.Patch)
					for _, patch := range image.PatchableExports {
						exp2uses[patch.GetName()] = append(exp2uses[patch.GetName()], patch)
					}
					if f.PatchInfoVersion == 3 {
						for _, got := range image.PatchableGOTs {
							exp2uses[got.GetName()] = append(exp2uses[got.GetName()], got)
						}
					}
					fmt.Printf("\t(%d symbols)\n", len(exp2uses))
					for name, patches := range exp2uses {
						fmt.Printf("%#x: %s\n", image.LoadAddress+uint64(patches[0].GetImplOffset()), name)
						for _, patch := range patches {
							switch patch := patch.(type) {
							case dyld.PatchableExport:
								for _, loc := range patch.PatchLocationsV2 {
									fmt.Fprintf(w, "    %s\t%s\n",
										loc.String(f.Images[patch.ClientIndex].LoadAddress),
										f.Images[patch.ClientIndex].Name)
								}
							case dyld.PatchableGotExport:
								for _, got := range patch.GotLocationsV3 {
									fmt.Fprintf(w, "    %s\tGOT\n",
										got.String(func(u uint64) uint64 {
											if _, addr, err := f.GetCacheVMAddress(u); err == nil {
												return addr
											}
											return 0
										}))
								}
							}
						}
						w.Flush()
					}
				case 4:
					exp2uses := make(map[string][]dyld.Patch)
					for _, patch := range image.PatchableExports {
						exp2uses[patch.GetName()] = append(exp2uses[patch.GetName()], patch)
					}
					for _, got := range image.PatchableGOTs {
						exp2uses[got.GetName()] = append(exp2uses[got.GetName()], got)
					}
					fmt.Printf("\t(%d symbols)\n", len(exp2uses))
					for name, patches := range exp2uses {
						fmt.Printf("%#x: %s\n", image.LoadAddress+uint64(patches[0].GetImplOffset()), name)
						for _, patch := range patches {
							switch patch := patch.(type) {
							case dyld.PatchableExport:
								for _, loc := range patch.PatchLocationsV4 {
									fmt.Fprintf(w, "    %s\t%s\n",
										loc.String(f.Images[patch.ClientIndex].LoadAddress),
										colorImage(path.Base(f.Images[patch.ClientIndex].Name)))
								}
							case dyld.PatchableGotExport:
								for _, got := range patch.GotLocationsV4 {
									fmt.Fprintf(w, "    %s\n",
										got.String(func(u uint64) uint64 {
											if _, addr, err := f.GetCacheVMAddress(u); err == nil {
												return addr
											}
											return 0
										}))
								}
							}
						}
						w.Flush()
					}
				default:
					return fmt.Errorf("unsupported patch info version %d", f.PatchInfoVersion)
				}
			}
		}

		return nil
	},
}

PatchesCmd represents the patches command

View Source
var SlideCmd = &cobra.Command{
	Use:   "slide <dyld_shared_cache>",
	Short: "Dump slide info",
	Args:  cobra.MinimumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {

		var enc *json.Encoder

		if viper.GetBool("verbose") {
			log.SetLevel(log.DebugLevel)
		}

		cacheFile := viper.GetString("dyld.slide.cache")

		if len(viper.GetString("dyld.slide.output")) > 0 && !viper.GetBool("dyld.slide.json") {
			return errors.New("must use --json flag when using --output flag")
		}

		if len(viper.GetString("dyld.slide.output")) > 0 {
			if err := os.MkdirAll(viper.GetString("dyld.slide.output"), 0750); err != nil {
				return errors.Wrapf(err, "failed to create output directory %s", viper.GetString("dyld.slide.output"))
			}
			f, err := os.Create(filepath.Join(viper.GetString("dyld.slide.output"), "slide_info.json"))
			if err != nil {
				return errors.Wrapf(err, "failed to create output file %s", viper.GetString("dyld.slide.output"))
			}
			defer f.Close()
			enc = json.NewEncoder(f)
		} else {
			enc = json.NewEncoder(os.Stdout)
		}

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

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

		if fileInfo.Mode()&os.ModeSymlink != 0 {
			symlinkPath, err := os.Readlink(dscPath)
			if err != nil {
				return errors.Wrapf(err, "failed to read symlink %s", dscPath)
			}

			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(cacheFile) == 0 {
			cacheFile = dscPath + ".a2s"
		}

		if err := f.OpenOrCreateA2SCache(cacheFile); err != nil {
			return err
		}

		for uuid := range f.Mappings {
			if f.Headers[uuid].SlideInfoOffsetUnused > 0 {
				mapping := &dyld.CacheMappingWithSlideInfo{CacheMappingAndSlideInfo: dyld.CacheMappingAndSlideInfo{
					Address:         f.Mappings[uuid][1].Address,
					Size:            f.Mappings[uuid][1].Size,
					FileOffset:      f.Mappings[uuid][1].FileOffset,
					SlideInfoOffset: f.Headers[uuid].SlideInfoOffsetUnused,
					SlideInfoSize:   f.Headers[uuid].SlideInfoSizeUnused,
				}, Name: "__DATA"}
				if viper.GetBool("dyld.slide.json") {
					rebases, err := f.GetRebaseInfoForPages(uuid, mapping, 0, 0)
					if err != nil {
						return err
					}
					enc.Encode(rebases)
				} else {
					f.DumpSlideInfo(uuid, mapping)
				}
			} else {
				for _, extMapping := range f.MappingsWithSlideInfo[uuid] {
					if viper.GetBool("dyld.slide.auth") && !extMapping.Flags.IsAuthData() {
						continue
					}
					if extMapping.SlideInfoSize > 0 {
						if viper.GetBool("dyld.slide.json") {
							rebases, err := f.GetRebaseInfoForPages(uuid, extMapping, 0, 0)
							if err != nil {
								return err
							}
							enc.Encode(rebases)
						} else {
							f.DumpSlideInfo(uuid, extMapping)
						}
					}
				}
			}
		}

		return nil
	},
}

SlideCmd represents the slide command

View Source
var StrSearchCmd = &cobra.Command{
	Use:   "str <dyld_shared_cache> <string>",
	Short: "Search dyld_shared_cache for string",
	Args:  cobra.MinimumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {

		if viper.GetBool("verbose") {
			log.SetLevel(log.DebugLevel)
		}

		pattern := viper.GetString("dyld.str.pattern")

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

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

		if fileInfo.Mode()&os.ModeSymlink != 0 {
			symlinkPath, err := os.Readlink(dscPath)
			if err != nil {
				return errors.Wrapf(err, "failed to read symlink %s", dscPath)
			}

			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()

		log.Info("Searching for strings...")
		strs, err := dscCmd.GetStrings(f, pattern)
		if err != nil {
			return err
		}

		w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0)
		for _, str := range strs {
			fmt.Fprintf(w, "%s: %s\t%s\n", colorAddr("%#x", str.Address), str.String, symImageColor(str.Image))
		}
		w.Flush()

		return nil
	},
}

StrSearchCmd represents the str command

View Source
var SwiftCmd = &cobra.Command{
	Use:           "swift",
	Short:         "Dump Swift Optimizations Info",
	SilenceUsage:  true,
	SilenceErrors: true,
	Args:          cobra.MinimumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {

		if viper.GetBool("verbose") {
			log.SetLevel(log.DebugLevel)
		}

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

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

		if fileInfo.Mode()&os.ModeSymlink != 0 {
			symlinkPath, err := os.Readlink(dscPath)
			if err != nil {
				return errors.Wrapf(err, "failed to read symlink %s", dscPath)
			}

			linkParent := filepath.Dir(dscPath)
			linkRoot := filepath.Dir(linkParent)

			dscPath = filepath.Join(linkRoot, symlinkPath)
		}

		f, err := dyld.Open(dscPath)
		if err != nil {
			return fmt.Errorf("failed to open dyld shared cache %s: %w", dscPath, err)
		}
		defer f.Close()

		cacheFile := viper.GetString("dyld.swift.cache")

		if len(cacheFile) == 0 {
			cacheFile = dscPath + ".a2s"
		}
		if err := f.OpenOrCreateA2SCache(cacheFile); err != nil {
			return err
		}

		if viper.GetBool("dyld.swift.types") {
			if err := f.GetAllSwiftTypes(true, viper.GetBool("dyld.swift.demangle")); err != nil {
				return fmt.Errorf("failed to get swift types: %w", err)
			}
		}
		if viper.GetBool("dyld.swift.metadata") {
			if err := f.GetAllSwiftMetadatas(true, viper.GetBool("dyld.swift.demangle")); err != nil {
				return fmt.Errorf("failed to get swift types: %w", err)
			}
		}
		if viper.GetBool("dyld.swift.foreign") {
			if err := f.GetAllSwiftForeignTypes(true, viper.GetBool("dyld.swift.demangle")); err != nil {
				return fmt.Errorf("failed to get swift types: %w", err)
			}
		}

		return nil
	},
}

SwiftCmd represents the swift command

View Source
var SymAddrCmd = &cobra.Command{
	Use:           "symaddr <dyld_shared_cache>",
	Aliases:       []string{"sym"},
	Short:         "Lookup or dump symbol(s)",
	SilenceUsage:  false,
	SilenceErrors: true,
	Args:          cobra.MinimumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {

		if viper.GetBool("verbose") {
			log.SetLevel(log.DebugLevel)
		}
		color.NoColor = !viper.GetBool("color")

		imageName, _ := cmd.Flags().GetString("image")
		symbolFile, _ := cmd.Flags().GetString("in")
		jsonFile, _ := cmd.Flags().GetString("out")
		allMatches, _ := cmd.Flags().GetBool("all")
		showBinds, _ := cmd.Flags().GetBool("binds")

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

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

		if fileInfo.Mode()&os.ModeSymlink != 0 {
			symlinkPath, err := os.Readlink(dscPath)
			if err != nil {
				return errors.Wrapf(err, "failed to read symlink %s", dscPath)
			}

			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(symbolFile) > 0 {
			/******************************************
			 * Search for symbols in JSON lookup file *
			 ******************************************/
			var lookups []dscCmd.Symbol

			lookupData, err := os.ReadFile(filepath.Clean(symbolFile))
			if err != nil {
				return fmt.Errorf("failed to read symbol lookup JSON file %s: %v", symbolFile, err)
			}
			if err := json.Unmarshal(lookupData, &lookups); err != nil {
				return fmt.Errorf("failed to parse symbol lookup JSON file %s: %v", symbolFile, err)
			}

			syms, err := dscCmd.GetSymbols(f, lookups)
			if err != nil {
				return fmt.Errorf("failed to lookup symbols from lookup JSON file: %v", err)
			}

			var enc *json.Encoder
			if len(jsonFile) > 0 {
				jf, err := os.Create(jsonFile)
				if err != nil {
					return err
				}
				defer jf.Close()
				enc = json.NewEncoder(jf)
			} else {
				enc = json.NewEncoder(os.Stdout)
			}

			if err := enc.Encode(syms); err != nil {
				return err
			}

			return nil
		} else if len(args) > 1 {
			if len(imageName) > 0 {

				i, err := f.Image(imageName)
				if err != nil {
					return fmt.Errorf("image not in %s: %v", dscPath, err)
				}

				if lsym, err := i.GetSymbol(args[1]); err == nil {
					fmt.Println(lsym.String(viper.GetBool("color")))
				}

				return nil
			}

			symChan, err := f.GetExportedSymbols(context.Background(), args[1])
			if err != nil {
				if !errors.Is(err, dyld.ErrNoPrebuiltLoadersInCache) {
					return fmt.Errorf("failed to get exported symbols: %v", err)
				}
			} else {
				for {
					sym, ok := <-symChan
					if !ok {
						break
					}
					fmt.Println(sym.String(viper.GetBool("color")))
					if !allMatches {
						return nil
					}
				}
			}
			for _, image := range f.Images {
				utils.Indent(log.Debug, 2)("Searching " + image.Name)
				if sym, err := image.GetSymbol(args[1]); err == nil {
					if (sym.Address > 0 || allMatches) && (sym.Kind != dyld.BIND || showBinds) {
						fmt.Println(sym.String(viper.GetBool("color")))
						if !allMatches {
							return nil
						}
					}
				}
			}
			return nil
		} else if len(imageName) > 0 {

			i, err := f.Image(imageName)
			if err != nil {
				return fmt.Errorf("image not in %s: %v", dscPath, err)
			}

			log.Warn("parsing private symbols for image...")
			if err := i.ParseLocalSymbols(true); err != nil {
				if errors.Is(err, dyld.ErrNoLocals) {
					utils.Indent(log.Warn, 2)(err.Error())
				} else if err != nil {
					log.Errorf("failed parse private symbols for image %s: %v", i.Name, err)
				}
			}

			log.Warn("parsing public symbols for image...")
			if err := i.ParsePublicSymbols(true); err != nil {
				log.Errorf("failed to parse public symbols for image %s: %v", i.Name, err)
			}

			return nil
		}

		log.Warn("parsing public symbols...")
		if err = f.ParsePublicSymbols(true); err != nil {
			log.Errorf("failed to get all public symbols: %v", err)
		}

		log.Warn("parsing private symbols...")
		if err = f.ParseLocalSyms(true); err != nil {
			log.Errorf("failed to parse private symbols: %v", err)
		}

		return nil
	},
}

SymAddrCmd represents the symaddr command

View Source
var TbdCmd = &cobra.Command{
	Use:     "tbd <dyld_shared_cache> <image>",
	Aliases: []string{"t"},
	Short:   "Generate a .tbd file for a dylib",
	Args:    cobra.MinimumNArgs(2),
	RunE: func(cmd *cobra.Command, args []string) error {

		if viper.GetBool("verbose") {
			log.SetLevel(log.DebugLevel)
		}

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

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

		if fileInfo.Mode()&os.ModeSymlink != 0 {
			symlinkPath, err := os.Readlink(dscPath)
			if err != nil {
				return fmt.Errorf("failed to read symlink %s: %v", dscPath, err)
			}

			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()

		image, err := f.Image(args[1])
		if err != nil {
			return fmt.Errorf("image not in %s: %v", dscPath, err)
		}
		t, err := tbd.NewTBD(f, image)
		if err != nil {
			return fmt.Errorf("failed to create tbd file for %s: %v", args[1], err)
		}

		outTBD, err := t.Generate()
		if err != nil {
			return fmt.Errorf("failed to create tbd file for %s: %v", args[1], err)
		}

		tbdFile := filepath.Base(t.Path)

		log.Info("Created " + tbdFile + ".tbd")
		err = os.WriteFile(tbdFile+".tbd", []byte(outTBD), 0660)
		if err != nil {
			return fmt.Errorf("failed to write tbd file %s: %v", tbdFile+".tbd", err)
		}

		return nil
	},
}

TbdCmd represents the tbd command

View Source
var WebkitCmd = &cobra.Command{
	Use:     "webkit <dyld_shared_cache>",
	Aliases: []string{"w"},
	Short:   "Get WebKit version from a dyld_shared_cache",
	Args:    cobra.MinimumNArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {

		if viper.GetBool("verbose") {
			log.SetLevel(log.DebugLevel)
		}

		getRev, _ := cmd.Flags().GetBool("rev")
		getGit, _ := cmd.Flags().GetBool("git")
		proxy, _ := cmd.Flags().GetString("proxy")
		insecure, _ := cmd.Flags().GetBool("insecure")
		apiToken, _ := cmd.Flags().GetString("api")
		diff, _ := cmd.Flags().GetBool("diff")
		asJSON, _ := cmd.Flags().GetBool("json")

		if len(apiToken) == 0 {
			if val, ok := os.LookupEnv("GITHUB_TOKEN"); ok {
				apiToken = val
			} else {
				if val, ok := os.LookupEnv("GITHUB_API_TOKEN"); ok {
					apiToken = val
				}
			}
		}

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

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

		if fileInfo.Mode()&os.ModeSymlink != 0 {
			symlinkPath, err := os.Readlink(dscPath)
			if err != nil {
				return errors.Wrapf(err, "failed to read symlink %s", dscPath)
			}

			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()

		webkit1, err := dcsCmd.GetWebkitVersion(f)
		if err != nil {
			return fmt.Errorf("failed to get WebKit version: %v", err)
		}

		if diff {
			dscPath2 := filepath.Clean(args[1])
			fileInfo, err := os.Lstat(dscPath2)
			if err != nil {
				return fmt.Errorf("file %s does not exist", dscPath2)
			}

			if fileInfo.Mode()&os.ModeSymlink != 0 {
				symlinkPath, err := os.Readlink(dscPath2)
				if err != nil {
					return errors.Wrapf(err, "failed to read symlink %s", dscPath2)
				}

				linkParent := filepath.Dir(dscPath2)
				linkRoot := filepath.Dir(linkParent)

				dscPath2 = filepath.Join(linkRoot, symlinkPath)
			}

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

			webkit2, err := dcsCmd.GetWebkitVersion(f)
			if err != nil {
				return fmt.Errorf("failed to get WebKit version: %v", err)
			}

			out, err := utils.GitDiff(
				webkit1+"\n",
				webkit2+"\n",
				&utils.GitDiffConfig{Color: viper.GetBool("color"), Tool: viper.GetString("diff-tool")})
			if err != nil {
				return err
			}
			if len(out) == 0 {
				log.Info("No differences found")
				return nil
			}
			log.Info("Differences found")
			fmt.Println(out)

			return nil
		}

		var svnRev string
		if getRev {
			log.Info("Querying https://trac.webkit.org...")
			ver, rev, err := dyld.ScrapeWebKitTRAC(webkit1)
			if err != nil {
				log.Infof("WebKit Version: %s", webkit1)
				return err
			}
			svnRev = fmt.Sprintf("%s (svn rev %s)", ver, rev)
		} else if getGit {
			log.Info("Querying https://github.com API...")
			var tags []download.GithubTag
			if len(apiToken) == 0 {
				tags, err = download.GetPreprocessedWebKitTags(proxy, insecure)
				if err != nil {
					log.Infof("WebKit Version: %s", webkit1)
					return err
				}
			} else {
				tags, err = download.WebKitGraphQLTags(proxy, insecure, apiToken)
				if err != nil {
					log.Infof("WebKit Version: %s", webkit1)
					return err
				}
			}
			for _, tag := range tags {
				if strings.Contains(tag.Name, webkit1) {
					if asJSON {
						b, err := json.Marshal(&struct {
							Version string             `json:"version"`
							Tag     download.GithubTag `json:"tag,omitempty"`
						}{
							Version: webkit1,
							Tag:     tag,
						})
						if err != nil {
							return err
						}
						fmt.Println(string(b))
					} else {
						log.Infof("WebKit Version: %s", webkit1)
						utils.Indent(log.Info, 2)(fmt.Sprintf("Tag:  %s", tag.Name))
						utils.Indent(log.Info, 2)(fmt.Sprintf("URL:  %s", tag.TarURL))
						utils.Indent(log.Info, 2)(fmt.Sprintf("Date: %s", tag.Commit.Date.Format("02Jan2006 15:04:05")))
					}
					return nil
				}
			}
		}

		if asJSON {
			b, err := json.Marshal(&struct {
				Version string `json:"version"`
				Rev     string `json:"rev,omitempty"`
			}{
				Version: webkit1,
				Rev:     svnRev,
			})
			if err != nil {
				return err
			}
			fmt.Println(string(b))
		} else {
			log.Infof("WebKit Version: %s", webkit1)
			if len(svnRev) > 0 {
				utils.Indent(log.Info, 2)(svnRev)
			}
		}

		return nil
	},
}

WebkitCmd represents the webkit command

View Source
var XrefCmd = &cobra.Command{
	Use:           "xref <dyld_shared_cache> <vaddr>",
	Aliases:       []string{"x"},
	Short:         "🚧 [WIP] Find all cross references to an address",
	Args:          cobra.MinimumNArgs(2),
	SilenceUsage:  true,
	SilenceErrors: true,
	RunE: func(cmd *cobra.Command, args []string) error {

		if viper.GetBool("verbose") {
			log.SetLevel(log.DebugLevel)
		}

		imageName := viper.GetString("dyld.xref.image")

		slide := viper.GetUint64("dyld.xref.slide")
		searchImports := viper.GetBool("dyld.xref.imports")
		allImages := viper.GetBool("dyld.xref.all")
		cacheFile := viper.GetString("dyld.xref.cache")

		addr, err := utils.ConvertStrToInt(args[1])
		if err != nil {
			return err
		}

		var unslidAddr uint64 = addr
		if slide > 0 {
			unslidAddr = addr - slide
		}

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

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

		if fileInfo.Mode()&os.ModeSymlink != 0 {
			symlinkPath, err := os.Readlink(dscPath)
			if err != nil {
				return fmt.Errorf("failed to read symlink %s: %v", dscPath, err)
			}

			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 !f.IsArm64() {
			log.Errorf("can only disassemble arm64 caches (disassembly required to find Xrefs)")
			return nil
		}

		var srcImage *dyld.CacheImage
		var images []*dyld.CacheImage
		if len(imageName) > 0 {
			srcImage, err = f.Image(imageName)
			if err != nil {
				return fmt.Errorf("image not in %s: %v", dscPath, err)
			}
			images = append(images, srcImage)
		} else if allImages {
			images = f.Images
		} else {
			srcImage, err = f.GetImageContainingVMAddr(unslidAddr)
			if err != nil {
				return err
			}
			images = append(images, srcImage)
		}

		if searchImports {
			log.Info("Searching for importing dylibs")
			for _, i := range f.Images {
				m, err := i.GetPartialMacho()
				if err != nil {
					return err
				}
				if utils.StrSliceHas(m.ImportedLibraries(), srcImage.Name) {
					images = append(images, i)
				}
				m.Close()
			}
		}

		if len(cacheFile) == 0 {
			cacheFile = dscPath + ".a2s"
		}
		if err := f.OpenOrCreateA2SCache(cacheFile); err != nil {
			return err
		}

		log.Info("Searching for xrefs (use -V for more progess output)")

		for _, img := range images {
			xrefs := make(map[uint64]string)

			if err := img.Analyze(); err != nil {
				return fmt.Errorf("failed to analyze image: %s; %v", img.Name, err)
			}

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

			if m.HasObjC() {
				log.Debug("Parsing ObjC runtime structures...")
				if err := f.ParseObjcForImage(img.Name); err != nil {
					return fmt.Errorf("failed to parse objc data for image %s: %v", img.Name, err)
				}
			}

			for _, fn := range m.GetFunctions() {
				uuid, soff, err := f.GetOffset(fn.StartAddr)
				if err != nil {
					return err
				}

				data, err := f.ReadBytesForUUID(uuid, int64(soff), uint64(fn.EndAddr-fn.StartAddr))
				if err != nil {
					return err
				}

				engine := dyld.NewDyldDisass(f, &disass.Config{
					Data:         data,
					StartAddress: fn.StartAddr,
					Quite:        true,
				})

				if err := engine.Triage(); err != nil {
					return fmt.Errorf("first pass triage failed: %v", err)
				}

				if ok, loc := engine.Contains(unslidAddr); ok {
					if sym, ok := f.AddressToSymbol[fn.StartAddr]; ok {
						xrefs[loc] = fmt.Sprintf("%s + %d", sym, loc-fn.StartAddr)
					} else {
						xrefs[loc] = fmt.Sprintf("func_%x + %d", fn.StartAddr, loc-fn.StartAddr)
					}

				}
			}

			if len(xrefs) == 0 {
				log.WithFields(log.Fields{
					"dylib": img.Name,
				}).Debug("No XREFS found")
			} else {
				if symName, ok := f.AddressToSymbol[unslidAddr]; ok {
					log.WithFields(log.Fields{
						"sym":   symName,
						"dylib": img.Name,
						"xrefs": len(xrefs),
					}).Info("XREFS")
				} else {
					log.WithFields(log.Fields{
						"dylib": img.Name,
						"xrefs": len(xrefs),
					}).Info("XREFS")
				}
				if len(xrefs) > 0 {
					for addr, sym := range xrefs {
						fmt.Printf("%s: %s\n", colorAddr("%#x", addr), sym)
					}
				}
			}

			img.Free()
		}

		return nil
	},
}

XrefCmd represents the xref command

Functions

This section is empty.

Types

type Func

type Func struct {
	Addr  uint64 `json:"addr,omitempty"`
	Start uint64 `json:"start,omitempty"`
	End   uint64 `json:"end,omitempty"`
	Size  uint64 `json:"size,omitempty"`
	Name  string `json:"name,omitempty"`
	Image string `json:"image,omitempty"`
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL