mootool 0.1.1 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.DS_Store +0 -0
- data/.github/workflows/ci.yml +14 -14
- data/.github/workflows/gem-push.yml +26 -28
- data/.overcommit.yml +79 -0
- data/.rubocop.yml +147 -0
- data/.ruby-version +1 -1
- data/.run/All Specs.run.xml +46 -0
- data/CODE_OF_CONDUCT.md +2 -1
- data/Gemfile +14 -11
- data/Gemfile.lock +92 -31
- data/README.md +12 -6
- data/bin/tapioca +27 -0
- data/lib/mootool/command.rb +1 -0
- data/lib/mootool/controller_base.rb +4 -1
- data/lib/mootool/controllers/dwarf.rb +8 -0
- data/lib/mootool/controllers/dyld_linker.rb +8 -0
- data/lib/mootool/controllers/dyld_shared_cache.rb +8 -0
- data/lib/mootool/controllers/fat_binary.rb +8 -0
- data/lib/mootool/controllers/kernel_collection.rb +21 -15
- data/lib/mootool/controllers/sections.rb +8 -0
- data/lib/mootool/core_extensions.rb +26 -0
- data/lib/mootool/models/device_tree.rb +85 -0
- data/lib/mootool/models/img4.rb +49 -0
- data/lib/mootool/models/ipsw.rb +19 -0
- data/lib/mootool/version.rb +1 -1
- data/lib/mootool/views/sections.full.erb +2 -2
- data/lib/mootool.rb +10 -3
- data/mootool.gemspec +17 -12
- data/sorbet/rbi/annotations/rainbow.rbi +269 -0
- data/sorbet/rbi/gems/ast@2.4.2.rbi +618 -0
- data/sorbet/rbi/gems/childprocess@4.1.0.rbi +447 -0
- data/sorbet/rbi/gems/coderay@1.1.3.rbi +8 -0
- data/sorbet/rbi/gems/diff-lcs@1.5.0.rbi +1219 -0
- data/sorbet/rbi/gems/iniparse@1.5.0.rbi +1007 -0
- data/sorbet/rbi/gems/json@2.6.2.rbi +1650 -0
- data/sorbet/rbi/gems/method_source@1.0.0.rbi +8 -0
- data/sorbet/rbi/gems/netrc@0.11.0.rbi +186 -0
- data/sorbet/rbi/gems/overcommit@0.59.1.rbi +2747 -0
- data/sorbet/rbi/gems/parallel@1.22.1.rbi +353 -0
- data/sorbet/rbi/gems/parser@3.1.2.1.rbi +6198 -0
- data/sorbet/rbi/gems/plist@3.6.0.rbi +212 -0
- data/sorbet/rbi/gems/pry@0.14.1.rbi +8 -0
- data/sorbet/rbi/gems/rainbow@3.1.1.rbi +452 -0
- data/sorbet/rbi/gems/rake@13.0.6.rbi +3587 -0
- data/sorbet/rbi/gems/rbi@0.0.15.rbi +3619 -0
- data/sorbet/rbi/gems/regexp_parser@2.5.0.rbi +3927 -0
- data/sorbet/rbi/gems/rexml@3.2.5.rbi +5238 -0
- data/sorbet/rbi/gems/rspec-core@3.11.0.rbi +12834 -0
- data/sorbet/rbi/gems/rspec-expectations@3.11.0.rbi +9151 -0
- data/sorbet/rbi/gems/rspec-mocks@3.11.1.rbi +6506 -0
- data/sorbet/rbi/gems/rspec-support@3.11.0.rbi +2040 -0
- data/sorbet/rbi/gems/rspec@3.11.0.rbi +120 -0
- data/sorbet/rbi/gems/rubocop-ast@1.21.0.rbi +7990 -0
- data/sorbet/rbi/gems/rubocop-rake@0.6.0.rbi +413 -0
- data/sorbet/rbi/gems/rubocop-rspec@2.12.1.rbi +7604 -0
- data/sorbet/rbi/gems/rubocop@1.35.0.rbi +62260 -0
- data/sorbet/rbi/gems/ruby-macho@3.0.0.rbi +5039 -0
- data/sorbet/rbi/gems/ruby-progressbar@1.11.0.rbi +1445 -0
- data/sorbet/rbi/gems/rubyzip@2.3.2.rbi +2868 -0
- data/sorbet/rbi/gems/spoom@1.1.12.rbi +2829 -0
- data/sorbet/rbi/gems/tapioca@0.9.3.rbi +2151 -0
- data/sorbet/rbi/gems/thor@1.2.1.rbi +4532 -0
- data/sorbet/rbi/gems/unicode-display_width@2.2.0.rbi +60 -0
- data/sorbet/rbi/gems/unparser@0.6.5.rbi +8 -0
- data/sorbet/rbi/gems/webrick@1.7.0.rbi +3075 -0
- data/sorbet/rbi/gems/yard-sorbet@0.6.1.rbi +458 -0
- data/sorbet/rbi/gems/yard@0.9.28.rbi +20844 -0
- data/sorbet/rbi/sorbet-typed/lib/rainbow/all/rainbow.rbi +72 -24
- data/sorbet/rbi/sorbet-typed/lib/rake/all/rake.rbi +1697 -385
- data/sorbet/rbi/sorbet-typed/lib/rspec-core/all/rspec-core.rbi +30 -7
- data/sorbet/rbi/sorbet-typed/lib/rubocop/>=1.8/rubocop.rbi +3 -1
- data/sorbet/rbi/todo.rbi +7 -0
- data/sorbet/tapioca/config.yml +13 -0
- data/sorbet/tapioca/require.rb +4 -0
- metadata +89 -9
data/bin/tapioca
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'tapioca' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
|
12
|
+
|
13
|
+
bundle_binstub = File.expand_path('bundle', __dir__)
|
14
|
+
|
15
|
+
if File.file?(bundle_binstub)
|
16
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
17
|
+
load(bundle_binstub)
|
18
|
+
else
|
19
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
20
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
require 'rubygems'
|
25
|
+
require 'bundler/setup'
|
26
|
+
|
27
|
+
load Gem.bin_path('tapioca', 'tapioca')
|
data/lib/mootool/command.rb
CHANGED
@@ -3,7 +3,10 @@
|
|
3
3
|
CONTROLLERS_PATH = File.join(File.dirname(__FILE__), 'controllers')
|
4
4
|
|
5
5
|
module MooTool
|
6
|
+
# Controller base is the base class for all controllers that respond to command line requests.
|
6
7
|
class ControllerBase
|
8
|
+
@controllers = []
|
9
|
+
|
7
10
|
def self.load_all
|
8
11
|
Dir.glob(File.join(CONTROLLERS_PATH, '*')).each do |file|
|
9
12
|
require file
|
@@ -15,8 +18,8 @@ module MooTool
|
|
15
18
|
end
|
16
19
|
|
17
20
|
def self.inherited(child)
|
18
|
-
@@controllers ||= []
|
19
21
|
@@controllers << child
|
22
|
+
super
|
20
23
|
end
|
21
24
|
|
22
25
|
class << self
|
@@ -1,24 +1,30 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module MooTool
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
module Controllers
|
5
|
+
# Controller for extracting or viewing a KernelCollection (.kc file)
|
6
|
+
class KernelCollection < ControllerBase
|
7
|
+
command 'kc'
|
8
|
+
description 'Kernel Collections'
|
7
9
|
|
8
|
-
|
9
|
-
|
10
|
-
|
10
|
+
def extract(command, output_folder)
|
11
|
+
file = MachO.open command.file
|
12
|
+
input = File.open(command.file, 'rb')
|
11
13
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
14
|
+
file.command(:LC_FILESET_ENTRY).each do |entry|
|
15
|
+
output_path = File.join(output_folder, entry.entry_id.to_s)
|
16
|
+
puts "Writing to #{output_path}"
|
17
|
+
output_file = File.open(output_path, 'wb')
|
18
|
+
# rubocop:disable Naming/VariableNumber
|
19
|
+
# We do not have control of this name as it is part of ruby-macho
|
20
|
+
matching = file.command(:LC_SEGMENT_64).find { |c| c.fileoff == entry.fileoff }
|
21
|
+
# rubocop:enable Naming/VariableNumber
|
17
22
|
|
18
|
-
|
19
|
-
|
20
|
-
|
23
|
+
input.seek(matching.fileoff)
|
24
|
+
output_file.write input.read(matching.filesize)
|
25
|
+
output_file.close
|
26
|
+
end
|
21
27
|
end
|
22
28
|
end
|
23
29
|
end
|
24
|
-
end
|
30
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
# rubocop:disable Style/Documentation
|
5
|
+
|
6
|
+
class Integer
|
7
|
+
# rubocop:enable Style/Documentation
|
8
|
+
extend T::Sig
|
9
|
+
# Aligns the integer to the nearest multiple of the alignment.
|
10
|
+
# @param alignment [Integer] the alignment
|
11
|
+
# @return [Integer] the aligned integer
|
12
|
+
sig { params(alignment: T.nilable(Integer)).returns(Integer) }
|
13
|
+
def align(alignment = nil)
|
14
|
+
alignment ||= Integer.size
|
15
|
+
|
16
|
+
return self if alignment < 2
|
17
|
+
|
18
|
+
alignment -= 1
|
19
|
+
|
20
|
+
if (self & alignment).zero?
|
21
|
+
self
|
22
|
+
else
|
23
|
+
(self | alignment) + 1
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
require 'sorbet-runtime'
|
5
|
+
|
6
|
+
module MooTool
|
7
|
+
module Models
|
8
|
+
# DeviceTree is a Apple Device Tree node that has been parsed
|
9
|
+
class DeviceTree
|
10
|
+
extend T::Sig
|
11
|
+
|
12
|
+
NODE_FORMAT = 'VV'
|
13
|
+
PROP_FORMAT = 'A32V'
|
14
|
+
|
15
|
+
# Represents a single node in the device tree
|
16
|
+
class Node
|
17
|
+
extend T::Sig
|
18
|
+
|
19
|
+
attr_reader :children, :properties
|
20
|
+
|
21
|
+
sig { params(data: T.any(IO, StringIO)).void }
|
22
|
+
def initialize(data)
|
23
|
+
vals = T.must(data.read(8)).unpack(NODE_FORMAT)
|
24
|
+
property_count = T.cast(vals[0], Integer)
|
25
|
+
child_count = T.cast(vals[1], Integer)
|
26
|
+
|
27
|
+
@properties = T.let({}, T::Hash[String, Property])
|
28
|
+
@children = T.let([], T::Array[Node])
|
29
|
+
|
30
|
+
property_count.times do
|
31
|
+
prop = Property.new(data)
|
32
|
+
@properties[prop.name] = prop
|
33
|
+
end
|
34
|
+
child_count.times { @children << Node.new(data) }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Represents a single property and it's value
|
39
|
+
class Property
|
40
|
+
extend T::Sig
|
41
|
+
|
42
|
+
attr_accessor :name, :value
|
43
|
+
|
44
|
+
sig { params(data: T.any(StringIO, IO)).void }
|
45
|
+
def initialize(data)
|
46
|
+
args = T.must(data.read(36)).unpack(PROP_FORMAT)
|
47
|
+
|
48
|
+
@name = T.let(T.cast(args[0], String), String)
|
49
|
+
@size = T.let(T.cast(args[1], Integer), Integer)
|
50
|
+
|
51
|
+
if @size & 0x80000000 != 0
|
52
|
+
@template = true
|
53
|
+
@size &= 0x7fffffff
|
54
|
+
end
|
55
|
+
|
56
|
+
@value = data.read(@size.align(4))
|
57
|
+
@value = T.must(@value)[0..@size]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
attr_reader :root
|
62
|
+
|
63
|
+
# @param [String] data
|
64
|
+
sig { params(data: T.any(IO, String, StringIO, Pathname)).void }
|
65
|
+
def initialize(data)
|
66
|
+
case data
|
67
|
+
when Pathname
|
68
|
+
data = T.assert_type!(data, Pathname)
|
69
|
+
@data = File.open(data.realpath, 'rb')
|
70
|
+
when String
|
71
|
+
data = T.assert_type!(data, String)
|
72
|
+
@data = StringIO.new(data)
|
73
|
+
when IO
|
74
|
+
@data = T.cast(data, IO)
|
75
|
+
end
|
76
|
+
@root = Node.new(@data)
|
77
|
+
end
|
78
|
+
|
79
|
+
sig { params(path: String).returns(DeviceTree) }
|
80
|
+
def self.open(path)
|
81
|
+
DeviceTree.new(Pathname.new(path))
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'openssl'
|
4
|
+
|
5
|
+
module MooTool
|
6
|
+
# Module for Apple's IMG4 encryption and signing format
|
7
|
+
module Img4
|
8
|
+
# An instance of a IMG4 file
|
9
|
+
class File
|
10
|
+
attr_reader :payload, :manifest
|
11
|
+
|
12
|
+
def initialize(path)
|
13
|
+
@path = path
|
14
|
+
der = ::File.binread(path)
|
15
|
+
@data = OpenSSL::ASN1.decode(der)
|
16
|
+
|
17
|
+
case @data.first.value
|
18
|
+
when 'IM4P'
|
19
|
+
@type = @data.value[1].value
|
20
|
+
@build = @data.value[2].value
|
21
|
+
@payload = @data.value[3].value
|
22
|
+
when 'IMG4', 'IM4M'
|
23
|
+
raise 'Not implemented'
|
24
|
+
else
|
25
|
+
raise "Unknown IMG4 type #{@data.first.value}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def payload?
|
30
|
+
!@payload.nil?
|
31
|
+
end
|
32
|
+
|
33
|
+
def manifest?
|
34
|
+
!@manifest.nil?
|
35
|
+
end
|
36
|
+
|
37
|
+
def basename
|
38
|
+
basename = ::File.basename(@path)
|
39
|
+
extension = ::File.extname(basename)
|
40
|
+
"#{basename.chomp(extension)}.#{@type}"
|
41
|
+
end
|
42
|
+
|
43
|
+
def extract_payload
|
44
|
+
output_path = ::File.join(::File.dirname(@path), basename)
|
45
|
+
::File.write(output_path, @payload)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'zip'
|
4
|
+
require 'plist'
|
5
|
+
|
6
|
+
module MooTool
|
7
|
+
# Represents a single IPSW package
|
8
|
+
class IPSW
|
9
|
+
def initialize(file)
|
10
|
+
@file = file
|
11
|
+
@zip = Zip::File.open(file)
|
12
|
+
manifest = @zip.find_entry('BuildManifest.plist')
|
13
|
+
|
14
|
+
raise 'Invalid IPSW, does not contain BuildManifest.plist' unless manifest
|
15
|
+
|
16
|
+
@manifest = Plist.parse_xml(manifest.get_input_stream.read)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/mootool/version.rb
CHANGED
data/lib/mootool.rb
CHANGED
@@ -1,14 +1,21 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
require 'bundler/setup'
|
3
|
+
require_relative 'mootool/version'
|
6
4
|
|
7
5
|
require 'macho'
|
6
|
+
require 'zip'
|
7
|
+
|
8
|
+
require_relative 'mootool/core_extensions'
|
8
9
|
|
10
|
+
# MooTool
|
9
11
|
module MooTool
|
10
12
|
class Error < StandardError; end
|
13
|
+
|
11
14
|
# Your code goes here...
|
12
15
|
autoload :Command, 'mootool/command'
|
13
16
|
autoload :ControllerBase, 'mootool/controller_base'
|
17
|
+
|
18
|
+
autoload :Img4, 'mootool/models/img4'
|
19
|
+
autoload :DeviceTree, 'mootool/models/device_tree'
|
20
|
+
autoload :IPSW, 'mootool/models/ipsw'
|
14
21
|
end
|
data/mootool.gemspec
CHANGED
@@ -5,15 +5,17 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
5
5
|
require 'mootool/version'
|
6
6
|
|
7
7
|
Gem::Specification.new do |spec|
|
8
|
-
spec.name
|
9
|
-
spec.version
|
10
|
-
spec.authors
|
11
|
-
spec.email
|
8
|
+
spec.name = 'mootool'
|
9
|
+
spec.version = MooTool::VERSION
|
10
|
+
spec.authors = ['Rick Mark']
|
11
|
+
spec.email = ['rickmark@outlook.com']
|
12
12
|
|
13
|
-
spec.summary
|
14
|
-
spec.description
|
15
|
-
spec.homepage
|
16
|
-
spec.license
|
13
|
+
spec.summary = "Mach-O's Other Tool"
|
14
|
+
spec.description = 'mootool is a swiss army knife for dealing with Apple Mach-O files'
|
15
|
+
spec.homepage = 'https://github.com/hack-different/mootool'
|
16
|
+
spec.license = 'MIT'
|
17
|
+
|
18
|
+
spec.required_ruby_version = '>= 3.0'
|
17
19
|
|
18
20
|
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
19
21
|
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
@@ -23,7 +25,7 @@ Gem::Specification.new do |spec|
|
|
23
25
|
spec.metadata['changelog_uri'] = 'https://github.com/hack-different/mootool'
|
24
26
|
else
|
25
27
|
raise 'RubyGems 2.0 or newer is required to protect against ' \
|
26
|
-
|
28
|
+
'public gem pushes.'
|
27
29
|
end
|
28
30
|
|
29
31
|
# Specify which files should be added to the gem when it is released.
|
@@ -31,10 +33,13 @@ Gem::Specification.new do |spec|
|
|
31
33
|
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
32
34
|
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
33
35
|
end
|
34
|
-
spec.bindir
|
35
|
-
spec.executables
|
36
|
+
spec.bindir = 'exe'
|
37
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
36
38
|
spec.require_paths = ['lib']
|
37
39
|
|
38
|
-
spec.add_runtime_dependency '
|
40
|
+
spec.add_runtime_dependency 'plist', '~> 3'
|
39
41
|
spec.add_runtime_dependency 'ruby-macho', '~> 3'
|
42
|
+
spec.add_runtime_dependency 'rubyzip', '~> 2'
|
43
|
+
spec.add_runtime_dependency 'sorbet-runtime', '~> 0'
|
44
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
40
45
|
end
|