ruby-macho 2.1.0 → 2.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +8 -1
- data/lib/macho.rb +23 -2
- data/lib/macho/exceptions.rb +14 -11
- data/lib/macho/fat_file.rb +26 -18
- data/lib/macho/headers.rb +15 -5
- data/lib/macho/load_commands.rb +48 -40
- data/lib/macho/macho_file.rb +36 -26
- data/lib/macho/sections.rb +5 -2
- data/lib/macho/structure.rb +3 -1
- data/lib/macho/tools.rb +2 -0
- data/lib/macho/utils.rb +4 -2
- data/lib/macho/view.rb +2 -0
- metadata +4 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cfe1d04c6c841958f4ac428e7207d89e02371fa9ca9ac3cb48e3e2a34a65dc24
|
|
4
|
+
data.tar.gz: 21dc14db70485e37ff5e430d5f8a99af6cb1c0ac14f02ae1943121fa2c482629
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d668c48f840f4423ae4bbc89d30c53e26a709491d2f8ca66bc98c637563e596d107638779bd517bf1fe89458d5f05beadf76ea60d80a44578aa0b409be61fc43
|
|
7
|
+
data.tar.gz: 84009cdfbe5c61bc84ffd8838c7fe033171ebc05af18b7748c53ced99a0e92fd7f45f1a7c0f50001c05eb253e00564b3f8dbe7735fe04df087b3fef72d2be7e6
|
data/README.md
CHANGED
|
@@ -13,6 +13,14 @@ The [Mach-O file format](https://en.wikipedia.org/wiki/Mach-O) is used by macOS
|
|
|
13
13
|
and iOS (among others) as a general purpose binary format for object files,
|
|
14
14
|
executables, dynamic libraries, and so forth.
|
|
15
15
|
|
|
16
|
+
### Installation
|
|
17
|
+
|
|
18
|
+
ruby-macho can be installed via RubyGems:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
$ gem install ruby-macho
|
|
22
|
+
```
|
|
23
|
+
|
|
16
24
|
### Documentation
|
|
17
25
|
|
|
18
26
|
Full documentation is available on [RubyDoc](http://www.rubydoc.info/gems/ruby-macho/).
|
|
@@ -46,7 +54,6 @@ puts lc_vers.version_string # => "10.10.0"
|
|
|
46
54
|
|
|
47
55
|
### What needs to be done?
|
|
48
56
|
|
|
49
|
-
* Documentation.
|
|
50
57
|
* Unit and performance testing.
|
|
51
58
|
|
|
52
59
|
Attribution:
|
data/lib/macho.rb
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "open3"
|
|
4
|
+
|
|
1
5
|
require_relative "macho/structure"
|
|
2
6
|
require_relative "macho/view"
|
|
3
7
|
require_relative "macho/headers"
|
|
@@ -12,7 +16,7 @@ require_relative "macho/tools"
|
|
|
12
16
|
# The primary namespace for ruby-macho.
|
|
13
17
|
module MachO
|
|
14
18
|
# release version
|
|
15
|
-
VERSION = "2.1
|
|
19
|
+
VERSION = "2.5.1"
|
|
16
20
|
|
|
17
21
|
# Opens the given filename as a MachOFile or FatFile, depending on its magic.
|
|
18
22
|
# @param filename [String] the file being opened
|
|
@@ -25,7 +29,7 @@ module MachO
|
|
|
25
29
|
raise ArgumentError, "#{filename}: no such file" unless File.file?(filename)
|
|
26
30
|
raise TruncatedFileError unless File.stat(filename).size >= 4
|
|
27
31
|
|
|
28
|
-
magic = File.open(filename, "rb") { |f| f.read(4) }.
|
|
32
|
+
magic = File.open(filename, "rb") { |f| f.read(4) }.unpack1("N")
|
|
29
33
|
|
|
30
34
|
if Utils.fat_magic?(magic)
|
|
31
35
|
file = FatFile.new(filename)
|
|
@@ -37,4 +41,21 @@ module MachO
|
|
|
37
41
|
|
|
38
42
|
file
|
|
39
43
|
end
|
|
44
|
+
|
|
45
|
+
# Signs the dylib using an ad-hoc identity.
|
|
46
|
+
# Necessary after making any changes to a dylib, since otherwise
|
|
47
|
+
# changing a signed file invalidates its signature.
|
|
48
|
+
# @param filename [String] the file being opened
|
|
49
|
+
# @return [void]
|
|
50
|
+
# @raise [ModificationError] if the operation fails
|
|
51
|
+
def self.codesign!(filename)
|
|
52
|
+
raise ArgumentError, "codesign binary is not available on Linux" if RUBY_PLATFORM !~ /darwin/
|
|
53
|
+
raise ArgumentError, "#{filename}: no such file" unless File.file?(filename)
|
|
54
|
+
|
|
55
|
+
_, _, status = Open3.capture3("codesign", "--sign", "-", "--force",
|
|
56
|
+
"--preserve-metadata=entitlements,requirements,flags,runtime",
|
|
57
|
+
filename)
|
|
58
|
+
|
|
59
|
+
raise CodeSigningError, "#{filename}: signing failed!" unless status.success?
|
|
60
|
+
end
|
|
40
61
|
end
|
data/lib/macho/exceptions.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module MachO
|
|
2
4
|
# A generic Mach-O error in execution.
|
|
3
5
|
class MachOError < RuntimeError
|
|
@@ -7,6 +9,11 @@ module MachO
|
|
|
7
9
|
class ModificationError < MachOError
|
|
8
10
|
end
|
|
9
11
|
|
|
12
|
+
# Raised when codesigning fails. Certain environments
|
|
13
|
+
# may want to rescue this to treat it as non-fatal.
|
|
14
|
+
class CodeSigningError < MachOError
|
|
15
|
+
end
|
|
16
|
+
|
|
10
17
|
# Raised when a Mach-O file modification fails but can be recovered when
|
|
11
18
|
# operating on multiple Mach-O slices of a fat binary in non-strict mode.
|
|
12
19
|
class RecoverableModificationError < ModificationError
|
|
@@ -25,10 +32,6 @@ module MachO
|
|
|
25
32
|
|
|
26
33
|
# Raised when a file is not a Mach-O.
|
|
27
34
|
class NotAMachOError < MachOError
|
|
28
|
-
# @param error [String] the error in question
|
|
29
|
-
def initialize(error)
|
|
30
|
-
super error
|
|
31
|
-
end
|
|
32
35
|
end
|
|
33
36
|
|
|
34
37
|
# Raised when a file is too short to be a valid Mach-O file.
|
|
@@ -41,8 +44,8 @@ module MachO
|
|
|
41
44
|
# Raised when a file's magic bytes are not valid Mach-O magic.
|
|
42
45
|
class MagicError < NotAMachOError
|
|
43
46
|
# @param num [Integer] the unknown number
|
|
44
|
-
def initialize(
|
|
45
|
-
super "Unrecognized Mach-O magic: 0x
|
|
47
|
+
def initialize(magic)
|
|
48
|
+
super "Unrecognized Mach-O magic: 0x%02<magic>x" % { :magic => magic }
|
|
46
49
|
end
|
|
47
50
|
end
|
|
48
51
|
|
|
@@ -71,7 +74,7 @@ module MachO
|
|
|
71
74
|
class CPUTypeError < MachOError
|
|
72
75
|
# @param cputype [Integer] the unknown CPU type
|
|
73
76
|
def initialize(cputype)
|
|
74
|
-
super "Unrecognized CPU type: 0x
|
|
77
|
+
super "Unrecognized CPU type: 0x%08<cputype>x" % { :cputype => cputype }
|
|
75
78
|
end
|
|
76
79
|
end
|
|
77
80
|
|
|
@@ -80,8 +83,8 @@ module MachO
|
|
|
80
83
|
# @param cputype [Integer] the CPU type of the unknown pair
|
|
81
84
|
# @param cpusubtype [Integer] the CPU sub-type of the unknown pair
|
|
82
85
|
def initialize(cputype, cpusubtype)
|
|
83
|
-
super "Unrecognized CPU sub-type: 0x
|
|
84
|
-
" (for CPU type: 0x
|
|
86
|
+
super "Unrecognized CPU sub-type: 0x%08<cpusubtype>x" \
|
|
87
|
+
" (for CPU type: 0x%08<cputype>x" % { :cputype => cputype, :cpusubtype => cpusubtype }
|
|
85
88
|
end
|
|
86
89
|
end
|
|
87
90
|
|
|
@@ -89,7 +92,7 @@ module MachO
|
|
|
89
92
|
class FiletypeError < MachOError
|
|
90
93
|
# @param num [Integer] the unknown number
|
|
91
94
|
def initialize(num)
|
|
92
|
-
super "Unrecognized Mach-O filetype code: 0x
|
|
95
|
+
super "Unrecognized Mach-O filetype code: 0x%02<num>x" % { :num => num }
|
|
93
96
|
end
|
|
94
97
|
end
|
|
95
98
|
|
|
@@ -97,7 +100,7 @@ module MachO
|
|
|
97
100
|
class LoadCommandError < MachOError
|
|
98
101
|
# @param num [Integer] the unknown number
|
|
99
102
|
def initialize(num)
|
|
100
|
-
super "Unrecognized Mach-O load command: 0x
|
|
103
|
+
super "Unrecognized Mach-O load command: 0x%02<num>x" % { :num => num }
|
|
101
104
|
end
|
|
102
105
|
end
|
|
103
106
|
|
data/lib/macho/fat_file.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require "forwardable"
|
|
2
4
|
|
|
3
5
|
module MachO
|
|
@@ -11,6 +13,10 @@ module MachO
|
|
|
11
13
|
# @return [String] the filename loaded from, or nil if loaded from a binary string
|
|
12
14
|
attr_accessor :filename
|
|
13
15
|
|
|
16
|
+
# @return [Hash] any parser options that the instance was created with
|
|
17
|
+
# @note Options specified in a {FatFile} trickle down into the internal {MachOFile}s.
|
|
18
|
+
attr_reader :options
|
|
19
|
+
|
|
14
20
|
# @return [Headers::FatHeader] the file's header
|
|
15
21
|
attr_reader :header
|
|
16
22
|
|
|
@@ -49,9 +55,7 @@ module MachO
|
|
|
49
55
|
machos.each do |macho|
|
|
50
56
|
macho_offset = Utils.round(offset, 2**macho.segment_alignment)
|
|
51
57
|
|
|
52
|
-
if !fat64 && macho_offset > (2**32 - 1)
|
|
53
|
-
raise FatArchOffsetOverflowError, macho_offset
|
|
54
|
-
end
|
|
58
|
+
raise FatArchOffsetOverflowError, macho_offset if !fat64 && macho_offset > (2**32 - 1)
|
|
55
59
|
|
|
56
60
|
macho_pads[macho] = Utils.padding_for(offset, 2**macho.segment_alignment)
|
|
57
61
|
|
|
@@ -62,7 +66,7 @@ module MachO
|
|
|
62
66
|
offset += (macho.serialize.bytesize + macho_pads[macho])
|
|
63
67
|
end
|
|
64
68
|
|
|
65
|
-
machos.each do |macho|
|
|
69
|
+
machos.each do |macho| # rubocop:disable Style/CombinableLoops
|
|
66
70
|
bin << Utils.nullpad(macho_pads[macho])
|
|
67
71
|
bin << macho.serialize
|
|
68
72
|
end
|
|
@@ -72,30 +76,36 @@ module MachO
|
|
|
72
76
|
|
|
73
77
|
# Creates a new FatFile instance from a binary string.
|
|
74
78
|
# @param bin [String] a binary string containing raw Mach-O data
|
|
79
|
+
# @param opts [Hash] options to control the parser with
|
|
80
|
+
# @note see {MachOFile#initialize} for currently valid options
|
|
75
81
|
# @return [FatFile] a new FatFile
|
|
76
|
-
def self.new_from_bin(bin)
|
|
82
|
+
def self.new_from_bin(bin, **opts)
|
|
77
83
|
instance = allocate
|
|
78
|
-
instance.initialize_from_bin(bin)
|
|
84
|
+
instance.initialize_from_bin(bin, opts)
|
|
79
85
|
|
|
80
86
|
instance
|
|
81
87
|
end
|
|
82
88
|
|
|
83
89
|
# Creates a new FatFile from the given filename.
|
|
84
90
|
# @param filename [String] the fat file to load from
|
|
91
|
+
# @param opts [Hash] options to control the parser with
|
|
92
|
+
# @note see {MachOFile#initialize} for currently valid options
|
|
85
93
|
# @raise [ArgumentError] if the given file does not exist
|
|
86
|
-
def initialize(filename)
|
|
94
|
+
def initialize(filename, **opts)
|
|
87
95
|
raise ArgumentError, "#{filename}: no such file" unless File.file?(filename)
|
|
88
96
|
|
|
89
97
|
@filename = filename
|
|
98
|
+
@options = opts
|
|
90
99
|
@raw_data = File.open(@filename, "rb", &:read)
|
|
91
100
|
populate_fields
|
|
92
101
|
end
|
|
93
102
|
|
|
94
|
-
# Initializes a new FatFile instance from a binary string.
|
|
103
|
+
# Initializes a new FatFile instance from a binary string with the given options.
|
|
95
104
|
# @see new_from_bin
|
|
96
105
|
# @api private
|
|
97
|
-
def initialize_from_bin(bin)
|
|
106
|
+
def initialize_from_bin(bin, opts)
|
|
98
107
|
@filename = nil
|
|
108
|
+
@options = opts
|
|
99
109
|
@raw_data = bin
|
|
100
110
|
populate_fields
|
|
101
111
|
end
|
|
@@ -358,7 +368,7 @@ module MachO
|
|
|
358
368
|
machos = []
|
|
359
369
|
|
|
360
370
|
fat_archs.each do |arch|
|
|
361
|
-
machos << MachOFile.new_from_bin(@raw_data[arch.offset, arch.size])
|
|
371
|
+
machos << MachOFile.new_from_bin(@raw_data[arch.offset, arch.size], **options)
|
|
362
372
|
end
|
|
363
373
|
|
|
364
374
|
machos
|
|
@@ -388,16 +398,14 @@ module MachO
|
|
|
388
398
|
errors = []
|
|
389
399
|
|
|
390
400
|
machos.each_with_index do |macho, index|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
error.macho_slice = index
|
|
401
|
+
yield macho
|
|
402
|
+
rescue RecoverableModificationError => e
|
|
403
|
+
e.macho_slice = index
|
|
395
404
|
|
|
396
|
-
|
|
397
|
-
|
|
405
|
+
# Strict mode: Immediately re-raise. Otherwise: Retain, check later.
|
|
406
|
+
raise e if strict
|
|
398
407
|
|
|
399
|
-
|
|
400
|
-
end
|
|
408
|
+
errors << e
|
|
401
409
|
end
|
|
402
410
|
|
|
403
411
|
# Non-strict mode: Raise first error if *all* Mach-O slices failed.
|
data/lib/macho/headers.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module MachO
|
|
2
4
|
# Classes and constants for parsing the headers of Mach-O binaries.
|
|
3
5
|
module Headers
|
|
@@ -241,6 +243,10 @@ module MachO
|
|
|
241
243
|
# @api private
|
|
242
244
|
CPU_SUBTYPE_ARM64_32_V8 = 1
|
|
243
245
|
|
|
246
|
+
# the e (A12) sub-type for `CPU_TYPE_ARM64`
|
|
247
|
+
# @api private
|
|
248
|
+
CPU_SUBTYPE_ARM64E = 2
|
|
249
|
+
|
|
244
250
|
# the lowest common sub-type for `CPU_TYPE_MC88000`
|
|
245
251
|
# @api private
|
|
246
252
|
CPU_SUBTYPE_MC88000_ALL = 0
|
|
@@ -350,6 +356,7 @@ module MachO
|
|
|
350
356
|
CPU_TYPE_ARM64 => {
|
|
351
357
|
CPU_SUBTYPE_ARM64_ALL => :arm64,
|
|
352
358
|
CPU_SUBTYPE_ARM64_V8 => :arm64v8,
|
|
359
|
+
CPU_SUBTYPE_ARM64E => :arm64e,
|
|
353
360
|
}.freeze,
|
|
354
361
|
CPU_TYPE_ARM64_32 => {
|
|
355
362
|
CPU_SUBTYPE_ARM64_32_V8 => :arm64_32v8,
|
|
@@ -485,7 +492,7 @@ module MachO
|
|
|
485
492
|
# always big-endian
|
|
486
493
|
# @see MachOStructure::FORMAT
|
|
487
494
|
# @api private
|
|
488
|
-
FORMAT = "N2"
|
|
495
|
+
FORMAT = "N2"
|
|
489
496
|
|
|
490
497
|
# @see MachOStructure::SIZEOF
|
|
491
498
|
# @api private
|
|
@@ -493,6 +500,7 @@ module MachO
|
|
|
493
500
|
|
|
494
501
|
# @api private
|
|
495
502
|
def initialize(magic, nfat_arch)
|
|
503
|
+
super()
|
|
496
504
|
@magic = magic
|
|
497
505
|
@nfat_arch = nfat_arch
|
|
498
506
|
end
|
|
@@ -536,7 +544,7 @@ module MachO
|
|
|
536
544
|
# @note Always big endian.
|
|
537
545
|
# @see MachOStructure::FORMAT
|
|
538
546
|
# @api private
|
|
539
|
-
FORMAT = "L>5"
|
|
547
|
+
FORMAT = "L>5"
|
|
540
548
|
|
|
541
549
|
# @see MachOStructure::SIZEOF
|
|
542
550
|
# @api private
|
|
@@ -544,6 +552,7 @@ module MachO
|
|
|
544
552
|
|
|
545
553
|
# @api private
|
|
546
554
|
def initialize(cputype, cpusubtype, offset, size, align)
|
|
555
|
+
super()
|
|
547
556
|
@cputype = cputype
|
|
548
557
|
@cpusubtype = cpusubtype & ~CPU_SUBTYPE_MASK
|
|
549
558
|
@offset = offset
|
|
@@ -582,7 +591,7 @@ module MachO
|
|
|
582
591
|
# @note Always big endian.
|
|
583
592
|
# @see MachOStructure::FORMAT
|
|
584
593
|
# @api private
|
|
585
|
-
FORMAT = "L>2Q>2L>2"
|
|
594
|
+
FORMAT = "L>2Q>2L>2"
|
|
586
595
|
|
|
587
596
|
# @see MachOStructure::SIZEOF
|
|
588
597
|
# @api private
|
|
@@ -632,7 +641,7 @@ module MachO
|
|
|
632
641
|
|
|
633
642
|
# @see MachOStructure::FORMAT
|
|
634
643
|
# @api private
|
|
635
|
-
FORMAT = "L=7"
|
|
644
|
+
FORMAT = "L=7"
|
|
636
645
|
|
|
637
646
|
# @see MachOStructure::SIZEOF
|
|
638
647
|
# @api private
|
|
@@ -641,6 +650,7 @@ module MachO
|
|
|
641
650
|
# @api private
|
|
642
651
|
def initialize(magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds,
|
|
643
652
|
flags)
|
|
653
|
+
super()
|
|
644
654
|
@magic = magic
|
|
645
655
|
@cputype = cputype
|
|
646
656
|
# For now we're not interested in additional capability bits also to be
|
|
@@ -755,7 +765,7 @@ module MachO
|
|
|
755
765
|
|
|
756
766
|
# @see MachOStructure::FORMAT
|
|
757
767
|
# @api private
|
|
758
|
-
FORMAT = "L=8"
|
|
768
|
+
FORMAT = "L=8"
|
|
759
769
|
|
|
760
770
|
# @see MachOStructure::SIZEOF
|
|
761
771
|
# @api private
|
data/lib/macho/load_commands.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module MachO
|
|
2
4
|
# Classes and constants for parsing load commands in Mach-O binaries.
|
|
3
5
|
module LoadCommands
|
|
@@ -60,6 +62,8 @@ module MachO
|
|
|
60
62
|
0x30 => :LC_VERSION_MIN_WATCHOS,
|
|
61
63
|
0x31 => :LC_NOTE,
|
|
62
64
|
0x32 => :LC_BUILD_VERSION,
|
|
65
|
+
(0x33 | LC_REQ_DYLD) => :LC_DYLD_EXPORTS_TRIE,
|
|
66
|
+
(0x34 | LC_REQ_DYLD) => :LD_DYLD_CHAINED_FIXUPS,
|
|
63
67
|
}.freeze
|
|
64
68
|
|
|
65
69
|
# association of symbol representations to load command constants
|
|
@@ -145,6 +149,8 @@ module MachO
|
|
|
145
149
|
:LC_VERSION_MIN_WATCHOS => "VersionMinCommand",
|
|
146
150
|
:LC_NOTE => "NoteCommand",
|
|
147
151
|
:LC_BUILD_VERSION => "BuildVersionCommand",
|
|
152
|
+
:LC_DYLD_EXPORTS_TRIE => "LinkeditDataCommand",
|
|
153
|
+
:LD_DYLD_CHAINED_FIXUPS => "LinkeditDataCommand",
|
|
148
154
|
}.freeze
|
|
149
155
|
|
|
150
156
|
# association of segment name symbols to names
|
|
@@ -186,7 +192,7 @@ module MachO
|
|
|
186
192
|
|
|
187
193
|
# @see MachOStructure::FORMAT
|
|
188
194
|
# @api private
|
|
189
|
-
FORMAT = "L=2"
|
|
195
|
+
FORMAT = "L=2"
|
|
190
196
|
|
|
191
197
|
# @see MachOStructure::SIZEOF
|
|
192
198
|
# @api private
|
|
@@ -225,6 +231,7 @@ module MachO
|
|
|
225
231
|
# @param cmdsize [Integer] the size of the load command in bytes
|
|
226
232
|
# @api private
|
|
227
233
|
def initialize(view, cmd, cmdsize)
|
|
234
|
+
super()
|
|
228
235
|
@view = view
|
|
229
236
|
@cmd = cmd
|
|
230
237
|
@cmdsize = cmdsize
|
|
@@ -365,7 +372,7 @@ module MachO
|
|
|
365
372
|
|
|
366
373
|
# @see MachOStructure::FORMAT
|
|
367
374
|
# @api private
|
|
368
|
-
FORMAT = "L=2a16"
|
|
375
|
+
FORMAT = "L=2a16"
|
|
369
376
|
|
|
370
377
|
# @see MachOStructure::SIZEOF
|
|
371
378
|
# @api private
|
|
@@ -379,7 +386,7 @@ module MachO
|
|
|
379
386
|
|
|
380
387
|
# @return [String] a string representation of the UUID
|
|
381
388
|
def uuid_string
|
|
382
|
-
hexes = uuid.map { |
|
|
389
|
+
hexes = uuid.map { |elem| "%02<elem>x" % { :elem => elem } }
|
|
383
390
|
segs = [
|
|
384
391
|
hexes[0..3].join, hexes[4..5].join, hexes[6..7].join,
|
|
385
392
|
hexes[8..9].join, hexes[10..15].join
|
|
@@ -429,7 +436,7 @@ module MachO
|
|
|
429
436
|
|
|
430
437
|
# @see MachOStructure::FORMAT
|
|
431
438
|
# @api private
|
|
432
|
-
FORMAT = "L=2Z16L=4l=2L=2"
|
|
439
|
+
FORMAT = "L=2Z16L=4l=2L=2"
|
|
433
440
|
|
|
434
441
|
# @see MachOStructure::SIZEOF
|
|
435
442
|
# @api private
|
|
@@ -524,7 +531,7 @@ module MachO
|
|
|
524
531
|
class SegmentCommand64 < SegmentCommand
|
|
525
532
|
# @see MachOStructure::FORMAT
|
|
526
533
|
# @api private
|
|
527
|
-
FORMAT = "L=2Z16Q=4l=2L=2"
|
|
534
|
+
FORMAT = "L=2Z16Q=4l=2L=2"
|
|
528
535
|
|
|
529
536
|
# @see MachOStructure::SIZEOF
|
|
530
537
|
# @api private
|
|
@@ -550,7 +557,7 @@ module MachO
|
|
|
550
557
|
|
|
551
558
|
# @see MachOStructure::FORMAT
|
|
552
559
|
# @api private
|
|
553
|
-
FORMAT = "L=6"
|
|
560
|
+
FORMAT = "L=6"
|
|
554
561
|
|
|
555
562
|
# @see MachOStructure::SIZEOF
|
|
556
563
|
# @api private
|
|
@@ -601,7 +608,7 @@ module MachO
|
|
|
601
608
|
|
|
602
609
|
# @see MachOStructure::FORMAT
|
|
603
610
|
# @api private
|
|
604
|
-
FORMAT = "L=3"
|
|
611
|
+
FORMAT = "L=3"
|
|
605
612
|
|
|
606
613
|
# @see MachOStructure::SIZEOF
|
|
607
614
|
# @api private
|
|
@@ -649,7 +656,7 @@ module MachO
|
|
|
649
656
|
|
|
650
657
|
# @see MachOStructure::FORMAT
|
|
651
658
|
# @api private
|
|
652
|
-
FORMAT = "L=5"
|
|
659
|
+
FORMAT = "L=5"
|
|
653
660
|
|
|
654
661
|
# @see MachOStructure::SIZEOF
|
|
655
662
|
# @api private
|
|
@@ -679,7 +686,7 @@ module MachO
|
|
|
679
686
|
class ThreadCommand < LoadCommand
|
|
680
687
|
# @see MachOStructure::FORMAT
|
|
681
688
|
# @api private
|
|
682
|
-
FORMAT = "L=2"
|
|
689
|
+
FORMAT = "L=2"
|
|
683
690
|
|
|
684
691
|
# @see MachOStructure::SIZEOF
|
|
685
692
|
# @api private
|
|
@@ -717,7 +724,7 @@ module MachO
|
|
|
717
724
|
|
|
718
725
|
# @see MachOStructure::FORMAT
|
|
719
726
|
# @api private
|
|
720
|
-
FORMAT = "L=10"
|
|
727
|
+
FORMAT = "L=10"
|
|
721
728
|
|
|
722
729
|
# @see MachOStructure::SIZEOF
|
|
723
730
|
# @api private
|
|
@@ -758,7 +765,7 @@ module MachO
|
|
|
758
765
|
class RoutinesCommand64 < RoutinesCommand
|
|
759
766
|
# @see MachOStructure::FORMAT
|
|
760
767
|
# @api private
|
|
761
|
-
FORMAT = "L=2Q=8"
|
|
768
|
+
FORMAT = "L=2Q=8"
|
|
762
769
|
|
|
763
770
|
# @see MachOStructure::SIZEOF
|
|
764
771
|
# @api private
|
|
@@ -773,7 +780,7 @@ module MachO
|
|
|
773
780
|
|
|
774
781
|
# @see MachOStructure::FORMAT
|
|
775
782
|
# @api private
|
|
776
|
-
FORMAT = "L=3"
|
|
783
|
+
FORMAT = "L=3"
|
|
777
784
|
|
|
778
785
|
# @see MachOStructure::SIZEOF
|
|
779
786
|
# @api private
|
|
@@ -801,7 +808,7 @@ module MachO
|
|
|
801
808
|
|
|
802
809
|
# @see MachOStructure::FORMAT
|
|
803
810
|
# @api private
|
|
804
|
-
FORMAT = "L=3"
|
|
811
|
+
FORMAT = "L=3"
|
|
805
812
|
|
|
806
813
|
# @see MachOStructure::SIZEOF
|
|
807
814
|
# @api private
|
|
@@ -829,7 +836,7 @@ module MachO
|
|
|
829
836
|
|
|
830
837
|
# @see MachOStructure::FORMAT
|
|
831
838
|
# @api private
|
|
832
|
-
FORMAT = "L=3"
|
|
839
|
+
FORMAT = "L=3"
|
|
833
840
|
|
|
834
841
|
# @see MachOStructure::SIZEOF
|
|
835
842
|
# @api private
|
|
@@ -857,7 +864,7 @@ module MachO
|
|
|
857
864
|
|
|
858
865
|
# @see MachOStructure::FORMAT
|
|
859
866
|
# @api private
|
|
860
|
-
FORMAT = "L=3"
|
|
867
|
+
FORMAT = "L=3"
|
|
861
868
|
|
|
862
869
|
# @see MachOStructure::SIZEOF
|
|
863
870
|
# @api private
|
|
@@ -894,7 +901,7 @@ module MachO
|
|
|
894
901
|
|
|
895
902
|
# @see MachOStructure::FORMAT
|
|
896
903
|
# @api private
|
|
897
|
-
FORMAT = "L=6"
|
|
904
|
+
FORMAT = "L=6"
|
|
898
905
|
|
|
899
906
|
# @see MachOStructure::SIZEOF
|
|
900
907
|
# @api private
|
|
@@ -979,7 +986,7 @@ module MachO
|
|
|
979
986
|
|
|
980
987
|
# @see MachOStructure::FORMAT
|
|
981
988
|
# @api private
|
|
982
|
-
FORMAT = "L=20"
|
|
989
|
+
FORMAT = "L=20"
|
|
983
990
|
|
|
984
991
|
# @see MachOStructure::SIZEOF
|
|
985
992
|
# @api private
|
|
@@ -1052,7 +1059,7 @@ module MachO
|
|
|
1052
1059
|
|
|
1053
1060
|
# @see MachOStructure::FORMAT
|
|
1054
1061
|
# @api private
|
|
1055
|
-
FORMAT = "L=4"
|
|
1062
|
+
FORMAT = "L=4"
|
|
1056
1063
|
|
|
1057
1064
|
# @see MachOStructure::SIZEOF
|
|
1058
1065
|
# @api private
|
|
@@ -1127,7 +1134,7 @@ module MachO
|
|
|
1127
1134
|
|
|
1128
1135
|
# @see MachOStructure::FORMAT
|
|
1129
1136
|
# @api private
|
|
1130
|
-
FORMAT = "L=3"
|
|
1137
|
+
FORMAT = "L=3"
|
|
1131
1138
|
|
|
1132
1139
|
# @see MachOStructure::SIZEOF
|
|
1133
1140
|
# @api private
|
|
@@ -1156,7 +1163,7 @@ module MachO
|
|
|
1156
1163
|
|
|
1157
1164
|
# @see MachOStructure::FORMAT
|
|
1158
1165
|
# @api private
|
|
1159
|
-
FORMAT = "L=3"
|
|
1166
|
+
FORMAT = "L=3"
|
|
1160
1167
|
|
|
1161
1168
|
# @see MachOStructure::SIZEOF
|
|
1162
1169
|
# @api private
|
|
@@ -1191,7 +1198,8 @@ module MachO
|
|
|
1191
1198
|
# A load command representing the offsets and sizes of a blob of data in
|
|
1192
1199
|
# the __LINKEDIT segment. Corresponds to LC_CODE_SIGNATURE,
|
|
1193
1200
|
# LC_SEGMENT_SPLIT_INFO, LC_FUNCTION_STARTS, LC_DATA_IN_CODE,
|
|
1194
|
-
# LC_DYLIB_CODE_SIGN_DRS,
|
|
1201
|
+
# LC_DYLIB_CODE_SIGN_DRS, LC_LINKER_OPTIMIZATION_HINT, LC_DYLD_EXPORTS_TRIE,
|
|
1202
|
+
# or LC_DYLD_CHAINED_FIXUPS.
|
|
1195
1203
|
class LinkeditDataCommand < LoadCommand
|
|
1196
1204
|
# @return [Integer] offset to the data in the __LINKEDIT segment
|
|
1197
1205
|
attr_reader :dataoff
|
|
@@ -1201,7 +1209,7 @@ module MachO
|
|
|
1201
1209
|
|
|
1202
1210
|
# @see MachOStructure::FORMAT
|
|
1203
1211
|
# @api private
|
|
1204
|
-
FORMAT = "L=4"
|
|
1212
|
+
FORMAT = "L=4"
|
|
1205
1213
|
|
|
1206
1214
|
# @see MachOStructure::SIZEOF
|
|
1207
1215
|
# @api private
|
|
@@ -1237,7 +1245,7 @@ module MachO
|
|
|
1237
1245
|
|
|
1238
1246
|
# @see MachOStructure::FORMAT
|
|
1239
1247
|
# @api private
|
|
1240
|
-
FORMAT = "L=5"
|
|
1248
|
+
FORMAT = "L=5"
|
|
1241
1249
|
|
|
1242
1250
|
# @see MachOStructure::SIZEOF
|
|
1243
1251
|
# @api private
|
|
@@ -1269,7 +1277,7 @@ module MachO
|
|
|
1269
1277
|
|
|
1270
1278
|
# @see MachOStructure::FORMAT
|
|
1271
1279
|
# @api private
|
|
1272
|
-
FORMAT = "L=6"
|
|
1280
|
+
FORMAT = "L=6"
|
|
1273
1281
|
|
|
1274
1282
|
# @see MachOStructure::SIZEOF
|
|
1275
1283
|
# @api private
|
|
@@ -1301,7 +1309,7 @@ module MachO
|
|
|
1301
1309
|
|
|
1302
1310
|
# @see MachOStructure::FORMAT
|
|
1303
1311
|
# @api private
|
|
1304
|
-
FORMAT = "L=4"
|
|
1312
|
+
FORMAT = "L=4"
|
|
1305
1313
|
|
|
1306
1314
|
# @see MachOStructure::SIZEOF
|
|
1307
1315
|
# @api private
|
|
@@ -1317,7 +1325,7 @@ module MachO
|
|
|
1317
1325
|
# A string representation of the binary's minimum OS version.
|
|
1318
1326
|
# @return [String] a string representing the minimum OS version.
|
|
1319
1327
|
def version_string
|
|
1320
|
-
binary = "%
|
|
1328
|
+
binary = "%032<version>b" % { :version => version }
|
|
1321
1329
|
segs = [
|
|
1322
1330
|
binary[0..15], binary[16..23], binary[24..31]
|
|
1323
1331
|
].map { |s| s.to_i(2) }
|
|
@@ -1328,7 +1336,7 @@ module MachO
|
|
|
1328
1336
|
# A string representation of the binary's SDK version.
|
|
1329
1337
|
# @return [String] a string representing the SDK version.
|
|
1330
1338
|
def sdk_string
|
|
1331
|
-
binary = "%
|
|
1339
|
+
binary = "%032<sdk>b" % { :sdk => sdk }
|
|
1332
1340
|
segs = [
|
|
1333
1341
|
binary[0..15], binary[16..23], binary[24..31]
|
|
1334
1342
|
].map { |s| s.to_i(2) }
|
|
@@ -1365,7 +1373,7 @@ module MachO
|
|
|
1365
1373
|
|
|
1366
1374
|
# @see MachOStructure::FORMAT
|
|
1367
1375
|
# @api private
|
|
1368
|
-
FORMAT = "L=6"
|
|
1376
|
+
FORMAT = "L=6"
|
|
1369
1377
|
|
|
1370
1378
|
# @see MachOStructure::SIZEOF
|
|
1371
1379
|
# @api private
|
|
@@ -1383,7 +1391,7 @@ module MachO
|
|
|
1383
1391
|
# A string representation of the binary's minimum OS version.
|
|
1384
1392
|
# @return [String] a string representing the minimum OS version.
|
|
1385
1393
|
def minos_string
|
|
1386
|
-
binary = "%
|
|
1394
|
+
binary = "%032<minos>b" % { :minos => minos }
|
|
1387
1395
|
segs = [
|
|
1388
1396
|
binary[0..15], binary[16..23], binary[24..31]
|
|
1389
1397
|
].map { |s| s.to_i(2) }
|
|
@@ -1394,7 +1402,7 @@ module MachO
|
|
|
1394
1402
|
# A string representation of the binary's SDK version.
|
|
1395
1403
|
# @return [String] a string representing the SDK version.
|
|
1396
1404
|
def sdk_string
|
|
1397
|
-
binary = "%
|
|
1405
|
+
binary = "%032<sdk>b" % { :sdk => sdk }
|
|
1398
1406
|
segs = [
|
|
1399
1407
|
binary[0..15], binary[16..23], binary[24..31]
|
|
1400
1408
|
].map { |s| s.to_i(2) }
|
|
@@ -1494,7 +1502,7 @@ module MachO
|
|
|
1494
1502
|
|
|
1495
1503
|
# @see MachOStructure::FORMAT
|
|
1496
1504
|
# @api private
|
|
1497
|
-
FORMAT = "L=12"
|
|
1505
|
+
FORMAT = "L=12"
|
|
1498
1506
|
|
|
1499
1507
|
# @see MachOStructure::SIZEOF
|
|
1500
1508
|
# @api private
|
|
@@ -1542,7 +1550,7 @@ module MachO
|
|
|
1542
1550
|
|
|
1543
1551
|
# @see MachOStructure::FORMAT
|
|
1544
1552
|
# @api private
|
|
1545
|
-
FORMAT = "L=3"
|
|
1553
|
+
FORMAT = "L=3"
|
|
1546
1554
|
|
|
1547
1555
|
# @see MachOStructure::SIZEOF
|
|
1548
1556
|
# @api private
|
|
@@ -1572,7 +1580,7 @@ module MachO
|
|
|
1572
1580
|
|
|
1573
1581
|
# @see MachOStructure::FORMAT
|
|
1574
1582
|
# @api private
|
|
1575
|
-
FORMAT = "L=2Q=2"
|
|
1583
|
+
FORMAT = "L=2Q=2"
|
|
1576
1584
|
|
|
1577
1585
|
# @see MachOStructure::SIZEOF
|
|
1578
1586
|
# @api private
|
|
@@ -1602,7 +1610,7 @@ module MachO
|
|
|
1602
1610
|
|
|
1603
1611
|
# @see MachOStructure::FORMAT
|
|
1604
1612
|
# @api private
|
|
1605
|
-
FORMAT = "L=2Q=1"
|
|
1613
|
+
FORMAT = "L=2Q=1"
|
|
1606
1614
|
|
|
1607
1615
|
# @see MachOStructure::SIZEOF
|
|
1608
1616
|
# @api private
|
|
@@ -1617,7 +1625,7 @@ module MachO
|
|
|
1617
1625
|
# A string representation of the sources used to build the binary.
|
|
1618
1626
|
# @return [String] a string representation of the version
|
|
1619
1627
|
def version_string
|
|
1620
|
-
binary = "%
|
|
1628
|
+
binary = "%064<version>b" % { :version => version }
|
|
1621
1629
|
segs = [
|
|
1622
1630
|
binary[0..23], binary[24..33], binary[34..43], binary[44..53],
|
|
1623
1631
|
binary[54..63]
|
|
@@ -1646,7 +1654,7 @@ module MachO
|
|
|
1646
1654
|
|
|
1647
1655
|
# @see MachOStructure::FORMAT
|
|
1648
1656
|
# @api private
|
|
1649
|
-
FORMAT = "L=4"
|
|
1657
|
+
FORMAT = "L=4"
|
|
1650
1658
|
|
|
1651
1659
|
# @see MachOStructure::SIZEOF
|
|
1652
1660
|
# @api private
|
|
@@ -1674,7 +1682,7 @@ module MachO
|
|
|
1674
1682
|
class IdentCommand < LoadCommand
|
|
1675
1683
|
# @see MachOStructure::FORMAT
|
|
1676
1684
|
# @api private
|
|
1677
|
-
FORMAT = "L=2"
|
|
1685
|
+
FORMAT = "L=2"
|
|
1678
1686
|
|
|
1679
1687
|
# @see MachOStructure::SIZEOF
|
|
1680
1688
|
# @api private
|
|
@@ -1692,7 +1700,7 @@ module MachO
|
|
|
1692
1700
|
|
|
1693
1701
|
# @see MachOStructure::FORMAT
|
|
1694
1702
|
# @api private
|
|
1695
|
-
FORMAT = "L=4"
|
|
1703
|
+
FORMAT = "L=4"
|
|
1696
1704
|
|
|
1697
1705
|
# @see MachOStructure::SIZEOF
|
|
1698
1706
|
# @api private
|
|
@@ -1727,7 +1735,7 @@ module MachO
|
|
|
1727
1735
|
|
|
1728
1736
|
# @see MachOStructure::FORMAT
|
|
1729
1737
|
# @api private
|
|
1730
|
-
FORMAT = "L=5"
|
|
1738
|
+
FORMAT = "L=5"
|
|
1731
1739
|
|
|
1732
1740
|
# @see MachOStructure::SIZEOF
|
|
1733
1741
|
# @api private
|
|
@@ -1764,7 +1772,7 @@ module MachO
|
|
|
1764
1772
|
|
|
1765
1773
|
# @see MachOStructure::FORMAT
|
|
1766
1774
|
# @api private
|
|
1767
|
-
FORMAT = "L=2Z16Q=2"
|
|
1775
|
+
FORMAT = "L=2Z16Q=2"
|
|
1768
1776
|
|
|
1769
1777
|
# @see MachOStructure::SIZEOF
|
|
1770
1778
|
# @api private
|
data/lib/macho/macho_file.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require "forwardable"
|
|
2
4
|
|
|
3
5
|
module MachO
|
|
@@ -9,10 +11,13 @@ module MachO
|
|
|
9
11
|
class MachOFile
|
|
10
12
|
extend Forwardable
|
|
11
13
|
|
|
12
|
-
# @return [String] the filename loaded from, or nil if loaded from a binary
|
|
14
|
+
# @return [String, nil] the filename loaded from, or nil if loaded from a binary
|
|
13
15
|
# string
|
|
14
16
|
attr_accessor :filename
|
|
15
17
|
|
|
18
|
+
# @return [Hash] any parser options that the instance was created with
|
|
19
|
+
attr_reader :options
|
|
20
|
+
|
|
16
21
|
# @return [Symbol] the endianness of the file, :big or :little
|
|
17
22
|
attr_reader :endianness
|
|
18
23
|
|
|
@@ -27,30 +32,36 @@ module MachO
|
|
|
27
32
|
|
|
28
33
|
# Creates a new instance from a binary string.
|
|
29
34
|
# @param bin [String] a binary string containing raw Mach-O data
|
|
35
|
+
# @param opts [Hash] options to control the parser with
|
|
36
|
+
# @option opts [Boolean] :permissive whether to ignore unknown load commands
|
|
30
37
|
# @return [MachOFile] a new MachOFile
|
|
31
|
-
def self.new_from_bin(bin)
|
|
38
|
+
def self.new_from_bin(bin, **opts)
|
|
32
39
|
instance = allocate
|
|
33
|
-
instance.initialize_from_bin(bin)
|
|
40
|
+
instance.initialize_from_bin(bin, opts)
|
|
34
41
|
|
|
35
42
|
instance
|
|
36
43
|
end
|
|
37
44
|
|
|
38
45
|
# Creates a new instance from data read from the given filename.
|
|
39
46
|
# @param filename [String] the Mach-O file to load from
|
|
47
|
+
# @param opts [Hash] options to control the parser with
|
|
48
|
+
# @option opts [Boolean] :permissive whether to ignore unknown load commands
|
|
40
49
|
# @raise [ArgumentError] if the given file does not exist
|
|
41
|
-
def initialize(filename)
|
|
50
|
+
def initialize(filename, **opts)
|
|
42
51
|
raise ArgumentError, "#{filename}: no such file" unless File.file?(filename)
|
|
43
52
|
|
|
44
53
|
@filename = filename
|
|
54
|
+
@options = opts
|
|
45
55
|
@raw_data = File.open(@filename, "rb", &:read)
|
|
46
56
|
populate_fields
|
|
47
57
|
end
|
|
48
58
|
|
|
49
|
-
# Initializes a new MachOFile instance from a binary string.
|
|
59
|
+
# Initializes a new MachOFile instance from a binary string with the given options.
|
|
50
60
|
# @see MachO::MachOFile.new_from_bin
|
|
51
61
|
# @api private
|
|
52
|
-
def initialize_from_bin(bin)
|
|
62
|
+
def initialize_from_bin(bin, opts)
|
|
53
63
|
@filename = nil
|
|
64
|
+
@options = opts
|
|
54
65
|
@raw_data = bin
|
|
55
66
|
populate_fields
|
|
56
67
|
end
|
|
@@ -146,16 +157,13 @@ module MachO
|
|
|
146
157
|
def insert_command(offset, lc, options = {})
|
|
147
158
|
context = LoadCommands::LoadCommand::SerializationContext.context_for(self)
|
|
148
159
|
cmd_raw = lc.serialize(context)
|
|
160
|
+
fileoff = offset + cmd_raw.bytesize
|
|
149
161
|
|
|
150
|
-
if offset < header.class.bytesize ||
|
|
151
|
-
raise OffsetInsertionError, offset
|
|
152
|
-
end
|
|
162
|
+
raise OffsetInsertionError, offset if offset < header.class.bytesize || fileoff > low_fileoff
|
|
153
163
|
|
|
154
164
|
new_sizeofcmds = sizeofcmds + cmd_raw.bytesize
|
|
155
165
|
|
|
156
|
-
if header.class.bytesize + new_sizeofcmds > low_fileoff
|
|
157
|
-
raise HeaderPadError, @filename
|
|
158
|
-
end
|
|
166
|
+
raise HeaderPadError, @filename if header.class.bytesize + new_sizeofcmds > low_fileoff
|
|
159
167
|
|
|
160
168
|
# update Mach-O header fields to account for inserted load command
|
|
161
169
|
update_ncmds(ncmds + 1)
|
|
@@ -178,9 +186,8 @@ module MachO
|
|
|
178
186
|
context = LoadCommands::LoadCommand::SerializationContext.context_for(self)
|
|
179
187
|
cmd_raw = new_lc.serialize(context)
|
|
180
188
|
new_sizeofcmds = sizeofcmds + cmd_raw.bytesize - old_lc.cmdsize
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
end
|
|
189
|
+
|
|
190
|
+
raise HeaderPadError, @filename if header.class.bytesize + new_sizeofcmds > low_fileoff
|
|
184
191
|
|
|
185
192
|
delete_command(old_lc)
|
|
186
193
|
insert_command(old_lc.view.offset, new_lc)
|
|
@@ -411,11 +418,8 @@ module MachO
|
|
|
411
418
|
rpath_cmds = command(:LC_RPATH).select { |r| r.path.to_s == path }
|
|
412
419
|
raise RpathUnknownError, path if rpath_cmds.empty?
|
|
413
420
|
|
|
414
|
-
# delete the commands in reverse order, offset descending.
|
|
415
|
-
|
|
416
|
-
rpath_cmds.reverse_each { |cmd| delete_command(cmd, :repopulate => false) }
|
|
417
|
-
|
|
418
|
-
populate_fields
|
|
421
|
+
# delete the commands in reverse order, offset descending.
|
|
422
|
+
rpath_cmds.reverse_each { |cmd| delete_command(cmd) }
|
|
419
423
|
end
|
|
420
424
|
|
|
421
425
|
# Write all Mach-O data to the given filename.
|
|
@@ -471,7 +475,7 @@ module MachO
|
|
|
471
475
|
# @raise [FatBinaryError] if the magic is for a Fat file
|
|
472
476
|
# @api private
|
|
473
477
|
def populate_and_check_magic
|
|
474
|
-
magic = @raw_data[0..3].
|
|
478
|
+
magic = @raw_data[0..3].unpack1("N")
|
|
475
479
|
|
|
476
480
|
raise MagicError, magic unless Utils.magic?(magic)
|
|
477
481
|
raise FatBinaryError if Utils.fat_magic?(magic)
|
|
@@ -511,19 +515,25 @@ module MachO
|
|
|
511
515
|
# @raise [LoadCommandError] if an unknown load command is encountered
|
|
512
516
|
# @api private
|
|
513
517
|
def populate_load_commands
|
|
518
|
+
permissive = options.fetch(:permissive, false)
|
|
514
519
|
offset = header.class.bytesize
|
|
515
520
|
load_commands = []
|
|
516
521
|
|
|
517
522
|
header.ncmds.times do
|
|
518
523
|
fmt = Utils.specialize_format("L=", endianness)
|
|
519
|
-
cmd = @raw_data.slice(offset, 4).
|
|
524
|
+
cmd = @raw_data.slice(offset, 4).unpack1(fmt)
|
|
520
525
|
cmd_sym = LoadCommands::LOAD_COMMANDS[cmd]
|
|
521
526
|
|
|
522
|
-
raise LoadCommandError, cmd
|
|
527
|
+
raise LoadCommandError, cmd unless cmd_sym || permissive
|
|
528
|
+
|
|
529
|
+
# If we're here, then either cmd_sym represents a valid load
|
|
530
|
+
# command *or* we're in permissive mode.
|
|
531
|
+
klass = if (klass_str = LoadCommands::LC_STRUCTURES[cmd_sym])
|
|
532
|
+
LoadCommands.const_get klass_str
|
|
533
|
+
else
|
|
534
|
+
LoadCommands::LoadCommand
|
|
535
|
+
end
|
|
523
536
|
|
|
524
|
-
# why do I do this? i don't like declaring constants below
|
|
525
|
-
# classes, and i need them to resolve...
|
|
526
|
-
klass = LoadCommands.const_get LoadCommands::LC_STRUCTURES[cmd_sym]
|
|
527
537
|
view = MachOView.new(@raw_data, endianness, offset)
|
|
528
538
|
command = klass.new_from_bin(view)
|
|
529
539
|
|
data/lib/macho/sections.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module MachO
|
|
2
4
|
# Classes and constants for parsing sections in Mach-O binaries.
|
|
3
5
|
module Sections
|
|
@@ -108,7 +110,7 @@ module MachO
|
|
|
108
110
|
attr_reader :reserved2
|
|
109
111
|
|
|
110
112
|
# @see MachOStructure::FORMAT
|
|
111
|
-
FORMAT = "Z16Z16L=9"
|
|
113
|
+
FORMAT = "Z16Z16L=9"
|
|
112
114
|
|
|
113
115
|
# @see MachOStructure::SIZEOF
|
|
114
116
|
SIZEOF = 68
|
|
@@ -116,6 +118,7 @@ module MachO
|
|
|
116
118
|
# @api private
|
|
117
119
|
def initialize(sectname, segname, addr, size, offset, align, reloff,
|
|
118
120
|
nreloc, flags, reserved1, reserved2)
|
|
121
|
+
super()
|
|
119
122
|
@sectname = sectname
|
|
120
123
|
@segname = segname
|
|
121
124
|
@addr = addr
|
|
@@ -180,7 +183,7 @@ module MachO
|
|
|
180
183
|
attr_reader :reserved3
|
|
181
184
|
|
|
182
185
|
# @see MachOStructure::FORMAT
|
|
183
|
-
FORMAT = "Z16Z16Q=2L=8"
|
|
186
|
+
FORMAT = "Z16Z16Q=2L=8"
|
|
184
187
|
|
|
185
188
|
# @see MachOStructure::SIZEOF
|
|
186
189
|
SIZEOF = 80
|
data/lib/macho/structure.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module MachO
|
|
2
4
|
# A general purpose pseudo-structure.
|
|
3
5
|
# @abstract
|
|
@@ -5,7 +7,7 @@ module MachO
|
|
|
5
7
|
# The String#unpack format of the data structure.
|
|
6
8
|
# @return [String] the unpacking format
|
|
7
9
|
# @api private
|
|
8
|
-
FORMAT = ""
|
|
10
|
+
FORMAT = ""
|
|
9
11
|
|
|
10
12
|
# The size of the data structure, in bytes.
|
|
11
13
|
# @return [Integer] the size, in bytes
|
data/lib/macho/tools.rb
CHANGED
data/lib/macho/utils.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module MachO
|
|
2
4
|
# A collection of utility functions used throughout ruby-macho.
|
|
3
5
|
module Utils
|
|
@@ -51,7 +53,7 @@ module MachO
|
|
|
51
53
|
def self.pack_strings(fixed_offset, alignment, strings = {})
|
|
52
54
|
offsets = {}
|
|
53
55
|
next_offset = fixed_offset
|
|
54
|
-
payload = ""
|
|
56
|
+
payload = +""
|
|
55
57
|
|
|
56
58
|
strings.each do |key, string|
|
|
57
59
|
offsets[key] = next_offset
|
|
@@ -61,7 +63,7 @@ module MachO
|
|
|
61
63
|
end
|
|
62
64
|
|
|
63
65
|
payload << Utils.nullpad(padding_for(fixed_offset + payload.bytesize, alignment))
|
|
64
|
-
[payload, offsets]
|
|
66
|
+
[payload.freeze, offsets]
|
|
65
67
|
end
|
|
66
68
|
|
|
67
69
|
# Compares the given number to valid Mach-O magic numbers.
|
data/lib/macho/view.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ruby-macho
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.1
|
|
4
|
+
version: 2.5.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- William Woodruff
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2021-05-15 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: A library for viewing and manipulating Mach-O files in Ruby.
|
|
14
14
|
email: william@yossarian.net
|
|
@@ -42,15 +42,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
42
42
|
requirements:
|
|
43
43
|
- - ">="
|
|
44
44
|
- !ruby/object:Gem::Version
|
|
45
|
-
version: '2.
|
|
45
|
+
version: '2.5'
|
|
46
46
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
47
47
|
requirements:
|
|
48
48
|
- - ">="
|
|
49
49
|
- !ruby/object:Gem::Version
|
|
50
50
|
version: '0'
|
|
51
51
|
requirements: []
|
|
52
|
-
|
|
53
|
-
rubygems_version: 2.7.6
|
|
52
|
+
rubygems_version: 3.0.3
|
|
54
53
|
signing_key:
|
|
55
54
|
specification_version: 4
|
|
56
55
|
summary: ruby-macho - Mach-O file analyzer.
|