ruby-macho 1.4.0 → 2.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +8 -1
- data/lib/macho.rb +24 -2
- data/lib/macho/exceptions.rb +14 -11
- data/lib/macho/fat_file.rb +27 -23
- data/lib/macho/headers.rb +10 -5
- data/lib/macho/load_commands.rb +48 -40
- data/lib/macho/macho_file.rb +34 -21
- data/lib/macho/sections.rb +5 -2
- data/lib/macho/structure.rb +3 -1
- data/lib/macho/tools.rb +14 -0
- data/lib/macho/utils.rb +5 -3
- data/lib/macho/view.rb +2 -0
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8b98417732314a92f3493ffd08e9221edbd342941bee24245b88b07fb292c2f8
|
4
|
+
data.tar.gz: cc17a19152fac25565b9a2759f7aa9aa2674318623e6bf620527e32e1796acda
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7135b6f32de153de839197779a1770e4a18c071a063ec8fa0b88791171fc6d45e208dd8df7a4338ca1f663d74cc25b2b78c791c61873598177f314f01c4ed108
|
7
|
+
data.tar.gz: 16132d807fd316cddd68638cdf9877e638134b438c5fddeb7901619cd8f52aba244ca6d2e7613981bd7d645612bce66434dc2f17400bea8fcd750e7444e782d1
|
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 = "
|
19
|
+
VERSION = "2.4.0"
|
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,22 @@ 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
|
+
# codesign binary is not available on Linux
|
53
|
+
return if RUBY_PLATFORM !~ /darwin/
|
54
|
+
raise ArgumentError, "#{filename}: no such file" unless File.file?(filename)
|
55
|
+
|
56
|
+
_, _, status = Open3.capture3("codesign", "--sign", "-", "--force",
|
57
|
+
"--preserve-metadata=entitlements,requirements,flags,runtime",
|
58
|
+
filename)
|
59
|
+
|
60
|
+
raise CodeSigningError, "#{filename}: signing failed!" unless status.success?
|
61
|
+
end
|
40
62
|
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
|
|
@@ -39,11 +45,7 @@ module MachO
|
|
39
45
|
# put the smaller alignments further forwards in fat macho, so that we do less padding
|
40
46
|
machos = machos.sort_by(&:segment_alignment)
|
41
47
|
|
42
|
-
bin =
|
43
|
-
+""
|
44
|
-
else
|
45
|
-
""
|
46
|
-
end
|
48
|
+
bin = +""
|
47
49
|
|
48
50
|
bin << Headers::FatHeader.new(magic, machos.size).serialize
|
49
51
|
offset = Headers::FatHeader.bytesize + (machos.size * fa_klass.bytesize)
|
@@ -53,9 +55,7 @@ module MachO
|
|
53
55
|
machos.each do |macho|
|
54
56
|
macho_offset = Utils.round(offset, 2**macho.segment_alignment)
|
55
57
|
|
56
|
-
if !fat64 && macho_offset > (2**32 - 1)
|
57
|
-
raise FatArchOffsetOverflowError, macho_offset
|
58
|
-
end
|
58
|
+
raise FatArchOffsetOverflowError, macho_offset if !fat64 && macho_offset > (2**32 - 1)
|
59
59
|
|
60
60
|
macho_pads[macho] = Utils.padding_for(offset, 2**macho.segment_alignment)
|
61
61
|
|
@@ -66,7 +66,7 @@ module MachO
|
|
66
66
|
offset += (macho.serialize.bytesize + macho_pads[macho])
|
67
67
|
end
|
68
68
|
|
69
|
-
machos.each do |macho|
|
69
|
+
machos.each do |macho| # rubocop:disable Style/CombinableLoops
|
70
70
|
bin << Utils.nullpad(macho_pads[macho])
|
71
71
|
bin << macho.serialize
|
72
72
|
end
|
@@ -76,30 +76,36 @@ module MachO
|
|
76
76
|
|
77
77
|
# Creates a new FatFile instance from a binary string.
|
78
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
|
79
81
|
# @return [FatFile] a new FatFile
|
80
|
-
def self.new_from_bin(bin)
|
82
|
+
def self.new_from_bin(bin, **opts)
|
81
83
|
instance = allocate
|
82
|
-
instance.initialize_from_bin(bin)
|
84
|
+
instance.initialize_from_bin(bin, opts)
|
83
85
|
|
84
86
|
instance
|
85
87
|
end
|
86
88
|
|
87
89
|
# Creates a new FatFile from the given filename.
|
88
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
|
89
93
|
# @raise [ArgumentError] if the given file does not exist
|
90
|
-
def initialize(filename)
|
94
|
+
def initialize(filename, **opts)
|
91
95
|
raise ArgumentError, "#{filename}: no such file" unless File.file?(filename)
|
92
96
|
|
93
97
|
@filename = filename
|
98
|
+
@options = opts
|
94
99
|
@raw_data = File.open(@filename, "rb", &:read)
|
95
100
|
populate_fields
|
96
101
|
end
|
97
102
|
|
98
|
-
# Initializes a new FatFile instance from a binary string.
|
103
|
+
# Initializes a new FatFile instance from a binary string with the given options.
|
99
104
|
# @see new_from_bin
|
100
105
|
# @api private
|
101
|
-
def initialize_from_bin(bin)
|
106
|
+
def initialize_from_bin(bin, opts)
|
102
107
|
@filename = nil
|
108
|
+
@options = opts
|
103
109
|
@raw_data = bin
|
104
110
|
populate_fields
|
105
111
|
end
|
@@ -362,7 +368,7 @@ module MachO
|
|
362
368
|
machos = []
|
363
369
|
|
364
370
|
fat_archs.each do |arch|
|
365
|
-
machos << MachOFile.new_from_bin(@raw_data[arch.offset, arch.size])
|
371
|
+
machos << MachOFile.new_from_bin(@raw_data[arch.offset, arch.size], **options)
|
366
372
|
end
|
367
373
|
|
368
374
|
machos
|
@@ -392,16 +398,14 @@ module MachO
|
|
392
398
|
errors = []
|
393
399
|
|
394
400
|
machos.each_with_index do |macho, index|
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
error.macho_slice = index
|
401
|
+
yield macho
|
402
|
+
rescue RecoverableModificationError => e
|
403
|
+
e.macho_slice = index
|
399
404
|
|
400
|
-
|
401
|
-
|
405
|
+
# Strict mode: Immediately re-raise. Otherwise: Retain, check later.
|
406
|
+
raise e if strict
|
402
407
|
|
403
|
-
|
404
|
-
end
|
408
|
+
errors << e
|
405
409
|
end
|
406
410
|
|
407
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
|
@@ -490,7 +492,7 @@ module MachO
|
|
490
492
|
# always big-endian
|
491
493
|
# @see MachOStructure::FORMAT
|
492
494
|
# @api private
|
493
|
-
FORMAT = "N2"
|
495
|
+
FORMAT = "N2"
|
494
496
|
|
495
497
|
# @see MachOStructure::SIZEOF
|
496
498
|
# @api private
|
@@ -498,6 +500,7 @@ module MachO
|
|
498
500
|
|
499
501
|
# @api private
|
500
502
|
def initialize(magic, nfat_arch)
|
503
|
+
super()
|
501
504
|
@magic = magic
|
502
505
|
@nfat_arch = nfat_arch
|
503
506
|
end
|
@@ -541,7 +544,7 @@ module MachO
|
|
541
544
|
# @note Always big endian.
|
542
545
|
# @see MachOStructure::FORMAT
|
543
546
|
# @api private
|
544
|
-
FORMAT = "L>5"
|
547
|
+
FORMAT = "L>5"
|
545
548
|
|
546
549
|
# @see MachOStructure::SIZEOF
|
547
550
|
# @api private
|
@@ -549,6 +552,7 @@ module MachO
|
|
549
552
|
|
550
553
|
# @api private
|
551
554
|
def initialize(cputype, cpusubtype, offset, size, align)
|
555
|
+
super()
|
552
556
|
@cputype = cputype
|
553
557
|
@cpusubtype = cpusubtype & ~CPU_SUBTYPE_MASK
|
554
558
|
@offset = offset
|
@@ -587,7 +591,7 @@ module MachO
|
|
587
591
|
# @note Always big endian.
|
588
592
|
# @see MachOStructure::FORMAT
|
589
593
|
# @api private
|
590
|
-
FORMAT = "L>2Q>2L>2"
|
594
|
+
FORMAT = "L>2Q>2L>2"
|
591
595
|
|
592
596
|
# @see MachOStructure::SIZEOF
|
593
597
|
# @api private
|
@@ -637,7 +641,7 @@ module MachO
|
|
637
641
|
|
638
642
|
# @see MachOStructure::FORMAT
|
639
643
|
# @api private
|
640
|
-
FORMAT = "L=7"
|
644
|
+
FORMAT = "L=7"
|
641
645
|
|
642
646
|
# @see MachOStructure::SIZEOF
|
643
647
|
# @api private
|
@@ -646,6 +650,7 @@ module MachO
|
|
646
650
|
# @api private
|
647
651
|
def initialize(magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds,
|
648
652
|
flags)
|
653
|
+
super()
|
649
654
|
@magic = magic
|
650
655
|
@cputype = cputype
|
651
656
|
# For now we're not interested in additional capability bits also to be
|
@@ -760,7 +765,7 @@ module MachO
|
|
760
765
|
|
761
766
|
# @see MachOStructure::FORMAT
|
762
767
|
# @api private
|
763
|
-
FORMAT = "L=8"
|
768
|
+
FORMAT = "L=8"
|
764
769
|
|
765
770
|
# @see MachOStructure::SIZEOF
|
766
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)
|
@@ -471,7 +478,7 @@ module MachO
|
|
471
478
|
# @raise [FatBinaryError] if the magic is for a Fat file
|
472
479
|
# @api private
|
473
480
|
def populate_and_check_magic
|
474
|
-
magic = @raw_data[0..3].
|
481
|
+
magic = @raw_data[0..3].unpack1("N")
|
475
482
|
|
476
483
|
raise MagicError, magic unless Utils.magic?(magic)
|
477
484
|
raise FatBinaryError if Utils.fat_magic?(magic)
|
@@ -511,19 +518,25 @@ module MachO
|
|
511
518
|
# @raise [LoadCommandError] if an unknown load command is encountered
|
512
519
|
# @api private
|
513
520
|
def populate_load_commands
|
521
|
+
permissive = options.fetch(:permissive, false)
|
514
522
|
offset = header.class.bytesize
|
515
523
|
load_commands = []
|
516
524
|
|
517
525
|
header.ncmds.times do
|
518
526
|
fmt = Utils.specialize_format("L=", endianness)
|
519
|
-
cmd = @raw_data.slice(offset, 4).
|
527
|
+
cmd = @raw_data.slice(offset, 4).unpack1(fmt)
|
520
528
|
cmd_sym = LoadCommands::LOAD_COMMANDS[cmd]
|
521
529
|
|
522
|
-
raise LoadCommandError, cmd
|
530
|
+
raise LoadCommandError, cmd unless cmd_sym || permissive
|
531
|
+
|
532
|
+
# If we're here, then either cmd_sym represents a valid load
|
533
|
+
# command *or* we're in permissive mode.
|
534
|
+
klass = if (klass_str = LoadCommands::LC_STRUCTURES[cmd_sym])
|
535
|
+
LoadCommands.const_get klass_str
|
536
|
+
else
|
537
|
+
LoadCommands::LoadCommand
|
538
|
+
end
|
523
539
|
|
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
540
|
view = MachOView.new(@raw_data, endianness, offset)
|
528
541
|
command = klass.new_from_bin(view)
|
529
542
|
|
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
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module MachO
|
2
4
|
# A collection of convenient methods for common operations on Mach-O and Fat
|
3
5
|
# binaries.
|
@@ -23,6 +25,8 @@ module MachO
|
|
23
25
|
|
24
26
|
file.change_dylib_id(new_id, options)
|
25
27
|
file.write!
|
28
|
+
|
29
|
+
MachO.codesign!(filename)
|
26
30
|
end
|
27
31
|
|
28
32
|
# Changes a shared library install name in a Mach-O or Fat binary,
|
@@ -39,6 +43,8 @@ module MachO
|
|
39
43
|
|
40
44
|
file.change_install_name(old_name, new_name, options)
|
41
45
|
file.write!
|
46
|
+
|
47
|
+
MachO.codesign!(filename)
|
42
48
|
end
|
43
49
|
|
44
50
|
# Changes a runtime path in a Mach-O or Fat binary, overwriting the source
|
@@ -55,6 +61,8 @@ module MachO
|
|
55
61
|
|
56
62
|
file.change_rpath(old_path, new_path, options)
|
57
63
|
file.write!
|
64
|
+
|
65
|
+
MachO.codesign!(filename)
|
58
66
|
end
|
59
67
|
|
60
68
|
# Add a runtime path to a Mach-O or Fat binary, overwriting the source file.
|
@@ -69,6 +77,8 @@ module MachO
|
|
69
77
|
|
70
78
|
file.add_rpath(new_path, options)
|
71
79
|
file.write!
|
80
|
+
|
81
|
+
MachO.codesign!(filename)
|
72
82
|
end
|
73
83
|
|
74
84
|
# Delete a runtime path from a Mach-O or Fat binary, overwriting the source
|
@@ -84,6 +94,8 @@ module MachO
|
|
84
94
|
|
85
95
|
file.delete_rpath(old_path, options)
|
86
96
|
file.write!
|
97
|
+
|
98
|
+
MachO.codesign!(filename)
|
87
99
|
end
|
88
100
|
|
89
101
|
# Merge multiple Mach-Os into one universal (Fat) binary.
|
@@ -104,6 +116,8 @@ module MachO
|
|
104
116
|
|
105
117
|
fat_macho = MachO::FatFile.new_from_machos(*machos, :fat64 => fat64)
|
106
118
|
fat_macho.write(filename)
|
119
|
+
|
120
|
+
MachO.codesign!(filename)
|
107
121
|
end
|
108
122
|
end
|
109
123
|
end
|
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
|
@@ -27,7 +29,7 @@ module MachO
|
|
27
29
|
# @return [String] the null string (or empty string, for `size = 0`)
|
28
30
|
# @raise [ArgumentError] if a non-positive nullpad is requested
|
29
31
|
def self.nullpad(size)
|
30
|
-
raise ArgumentError, "size < 0: #{size}" if size
|
32
|
+
raise ArgumentError, "size < 0: #{size}" if size.negative?
|
31
33
|
|
32
34
|
"\x00" * size
|
33
35
|
end
|
@@ -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:
|
4
|
+
version: 2.4.0
|
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: 2020-11-06 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,14 +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
|
-
rubygems_version: 3.
|
52
|
+
rubygems_version: 3.1.2
|
53
53
|
signing_key:
|
54
54
|
specification_version: 4
|
55
55
|
summary: ruby-macho - Mach-O file analyzer.
|