ruby-macho 3.0.0 → 4.0.0
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 +14 -1
- data/lib/macho/exceptions.rb +30 -8
- data/lib/macho/fat_file.rb +12 -0
- data/lib/macho/headers.rb +33 -138
- data/lib/macho/load_commands.rb +181 -647
- data/lib/macho/macho_file.rb +13 -6
- data/lib/macho/sections.rb +17 -48
- data/lib/macho/structure.rb +264 -22
- data/lib/macho/view.rb +10 -1
- data/lib/macho.rb +2 -2
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 669b2133517dc564c92df8ebdc0b2f6fdc91869939cd6a642cfbc2b1c6e20a09
|
4
|
+
data.tar.gz: 0077afabc9c4be7d891e5c7e5a8b4ec173223b00f73d550fc96fb7cf4f103c1d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4e8bd64595d93d50c0046c5bd292010a51a9d4154cbcda3a0a0a962882475e61d631d55da106e4655ba31a32011f583222d5b147129230e08ad11d12a34ee8e6
|
7
|
+
data.tar.gz: ccc53798eb56f378f5688ad7141636ecca274e552e8ad62f4fab5600b6a6fddcac0e5ffc75ed4924e62538a6b3ac7b291667870b6cd02bf0edf6286da1a4e62a
|
data/README.md
CHANGED
@@ -56,11 +56,24 @@ puts lc_vers.version_string # => "10.10.0"
|
|
56
56
|
|
57
57
|
* Unit and performance testing.
|
58
58
|
|
59
|
-
|
59
|
+
### Contributing, setting up `overcommit` and the linters
|
60
|
+
|
61
|
+
In order to keep the repo, docs and data tidy, we use a tool called [`overcommit`](https://github.com/sds/overcommit)
|
62
|
+
to connect up the git hooks to a set of quality checks. The fastest way to get setup is to run the following to make
|
63
|
+
sure you have all the tools:
|
64
|
+
|
65
|
+
```shell
|
66
|
+
gem install overcommit bundler
|
67
|
+
bundle install
|
68
|
+
overcommit --install
|
69
|
+
```
|
70
|
+
|
71
|
+
### Attribution
|
60
72
|
|
61
73
|
* Constants were taken from Apple, Inc's
|
62
74
|
[`loader.h` in `cctools/include/mach-o`](https://opensource.apple.com/source/cctools/cctools-973.0.1/include/mach-o/loader.h.auto.html).
|
63
75
|
(Apple Public Source License 2.0).
|
76
|
+
* Binary files used for testing were taken from The LLVM Project. ([Apache License v2.0 with LLVM Exceptions](test/bin/llvm/LICENSE.txt)).
|
64
77
|
|
65
78
|
### License
|
66
79
|
|
data/lib/macho/exceptions.rb
CHANGED
@@ -56,6 +56,28 @@ module MachO
|
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
59
|
+
# Raised when a a fat Mach-O file has zero architectures
|
60
|
+
class ZeroArchitectureError < NotAMachOError
|
61
|
+
def initialize
|
62
|
+
super "Fat file has zero internal architectures"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Raised when there is a mismatch between the fat arch
|
67
|
+
# and internal slice cputype or cpusubtype.
|
68
|
+
class CPUTypeMismatchError < NotAMachOError
|
69
|
+
def initialize(fat_cputype, fat_cpusubtype, macho_cputype, macho_cpusubtype)
|
70
|
+
# @param cputype_fat [Integer] the CPU type in the fat header
|
71
|
+
# @param cpusubtype_fat [Integer] the CPU subtype in the fat header
|
72
|
+
# @param cputype_macho [Integer] the CPU type in the macho header
|
73
|
+
# @param cpusubtype_macho [Integer] the CPU subtype in the macho header
|
74
|
+
super ("Mismatch between cputypes >> 0x%08<fat_cputype>x and 0x%08<macho_cputype>x\n" \
|
75
|
+
"and/or cpusubtypes >> 0x%08<fat_cpusubtype>x and 0x%08<macho_cpusubtype>x" %
|
76
|
+
{ :fat_cputype => fat_cputype, :macho_cputype => macho_cputype,
|
77
|
+
:fat_cpusubtype => fat_cpusubtype, :macho_cpusubtype => macho_cpusubtype })
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
59
81
|
# Raised when a fat binary is loaded with MachOFile.
|
60
82
|
class FatBinaryError < MachOError
|
61
83
|
def initialize
|
@@ -83,8 +105,8 @@ module MachO
|
|
83
105
|
# @param cputype [Integer] the CPU type of the unknown pair
|
84
106
|
# @param cpusubtype [Integer] the CPU sub-type of the unknown pair
|
85
107
|
def initialize(cputype, cpusubtype)
|
86
|
-
super "Unrecognized CPU sub-type: 0x%08<cpusubtype>x" \
|
87
|
-
"
|
108
|
+
super "Unrecognized CPU sub-type: 0x%08<cpusubtype>x " \
|
109
|
+
"(for CPU type: 0x%08<cputype>x" % { :cputype => cputype, :cpusubtype => cpusubtype }
|
88
110
|
end
|
89
111
|
end
|
90
112
|
|
@@ -119,8 +141,8 @@ module MachO
|
|
119
141
|
# @param expected_arity [Integer] the number of arguments expected
|
120
142
|
# @param actual_arity [Integer] the number of arguments received
|
121
143
|
def initialize(cmd_sym, expected_arity, actual_arity)
|
122
|
-
super "Expected #{expected_arity} arguments for #{cmd_sym} creation," \
|
123
|
-
"
|
144
|
+
super "Expected #{expected_arity} arguments for #{cmd_sym} creation, " \
|
145
|
+
"got #{actual_arity}"
|
124
146
|
end
|
125
147
|
end
|
126
148
|
|
@@ -136,8 +158,8 @@ module MachO
|
|
136
158
|
class LCStrMalformedError < MachOError
|
137
159
|
# @param lc [MachO::LoadCommand] the load command containing the string
|
138
160
|
def initialize(lc)
|
139
|
-
super "Load command #{lc.type} at offset #{lc.view.offset} contains a" \
|
140
|
-
"
|
161
|
+
super "Load command #{lc.type} at offset #{lc.view.offset} contains a " \
|
162
|
+
"malformed string"
|
141
163
|
end
|
142
164
|
end
|
143
165
|
|
@@ -203,8 +225,8 @@ module MachO
|
|
203
225
|
class FatArchOffsetOverflowError < MachOError
|
204
226
|
# @param offset [Integer] the offending offset
|
205
227
|
def initialize(offset)
|
206
|
-
super "Offset #{offset} exceeds the 32-bit width of a fat_arch offset." \
|
207
|
-
"
|
228
|
+
super "Offset #{offset} exceeds the 32-bit width of a fat_arch offset. " \
|
229
|
+
"Consider merging with `fat64: true`"
|
208
230
|
end
|
209
231
|
end
|
210
232
|
|
data/lib/macho/fat_file.rb
CHANGED
@@ -327,6 +327,8 @@ module MachO
|
|
327
327
|
# @raise [MagicError] if the magic is not valid Mach-O magic
|
328
328
|
# @raise [MachOBinaryError] if the magic is for a non-fat Mach-O file
|
329
329
|
# @raise [JavaClassFileError] if the file is a Java classfile
|
330
|
+
# @raise [ZeroArchitectureError] if the file has no internal slices
|
331
|
+
# (i.e., nfat_arch == 0) and the permissive option is not set
|
330
332
|
# @api private
|
331
333
|
def populate_fat_header
|
332
334
|
# the smallest fat Mach-O header is 8 bytes
|
@@ -346,6 +348,9 @@ module MachO
|
|
346
348
|
# formats.
|
347
349
|
raise JavaClassFileError if fh.nfat_arch > 30
|
348
350
|
|
351
|
+
# Rationale: return an error if the file has no internal slices.
|
352
|
+
raise ZeroArchitectureError if fh.nfat_arch.zero?
|
353
|
+
|
349
354
|
fh
|
350
355
|
end
|
351
356
|
|
@@ -374,6 +379,13 @@ module MachO
|
|
374
379
|
|
375
380
|
fat_archs.each do |arch|
|
376
381
|
machos << MachOFile.new_from_bin(@raw_data[arch.offset, arch.size], **options)
|
382
|
+
|
383
|
+
# Make sure that each fat_arch and internal slice.
|
384
|
+
# contain matching cputypes and cpusubtypes
|
385
|
+
next if machos.last.header.cputype == arch.cputype &&
|
386
|
+
machos.last.header.cpusubtype == arch.cpusubtype
|
387
|
+
|
388
|
+
raise CPUTypeMismatchError.new(arch.cputype, arch.cpusubtype, machos.last.header.cputype, machos.last.header.cpusubtype)
|
377
389
|
end
|
378
390
|
|
379
391
|
machos
|
data/lib/macho/headers.rb
CHANGED
@@ -505,30 +505,14 @@ module MachO
|
|
505
505
|
# @see MachO::FatArch
|
506
506
|
class FatHeader < MachOStructure
|
507
507
|
# @return [Integer] the magic number of the header (and file)
|
508
|
-
|
508
|
+
field :magic, :uint32, :endian => :big
|
509
509
|
|
510
510
|
# @return [Integer] the number of fat architecture structures following the header
|
511
|
-
|
512
|
-
|
513
|
-
# always big-endian
|
514
|
-
# @see MachOStructure::FORMAT
|
515
|
-
# @api private
|
516
|
-
FORMAT = "N2"
|
517
|
-
|
518
|
-
# @see MachOStructure::SIZEOF
|
519
|
-
# @api private
|
520
|
-
SIZEOF = 8
|
521
|
-
|
522
|
-
# @api private
|
523
|
-
def initialize(magic, nfat_arch)
|
524
|
-
super()
|
525
|
-
@magic = magic
|
526
|
-
@nfat_arch = nfat_arch
|
527
|
-
end
|
511
|
+
field :nfat_arch, :uint32, :endian => :big
|
528
512
|
|
529
513
|
# @return [String] the serialized fields of the fat header
|
530
514
|
def serialize
|
531
|
-
[magic, nfat_arch].pack(
|
515
|
+
[magic, nfat_arch].pack(self.class.format)
|
532
516
|
end
|
533
517
|
|
534
518
|
# @return [Hash] a hash representation of this {FatHeader}
|
@@ -548,42 +532,23 @@ module MachO
|
|
548
532
|
# @see MachO::Headers::FatHeader
|
549
533
|
class FatArch < MachOStructure
|
550
534
|
# @return [Integer] the CPU type of the Mach-O
|
551
|
-
|
535
|
+
field :cputype, :uint32, :endian => :big
|
552
536
|
|
553
537
|
# @return [Integer] the CPU subtype of the Mach-O
|
554
|
-
|
538
|
+
field :cpusubtype, :uint32, :endian => :big, :mask => CPU_SUBTYPE_MASK
|
555
539
|
|
556
540
|
# @return [Integer] the file offset to the beginning of the Mach-O data
|
557
|
-
|
541
|
+
field :offset, :uint32, :endian => :big
|
558
542
|
|
559
543
|
# @return [Integer] the size, in bytes, of the Mach-O data
|
560
|
-
|
544
|
+
field :size, :uint32, :endian => :big
|
561
545
|
|
562
546
|
# @return [Integer] the alignment, as a power of 2
|
563
|
-
|
564
|
-
|
565
|
-
# @note Always big endian.
|
566
|
-
# @see MachOStructure::FORMAT
|
567
|
-
# @api private
|
568
|
-
FORMAT = "L>5"
|
569
|
-
|
570
|
-
# @see MachOStructure::SIZEOF
|
571
|
-
# @api private
|
572
|
-
SIZEOF = 20
|
573
|
-
|
574
|
-
# @api private
|
575
|
-
def initialize(cputype, cpusubtype, offset, size, align)
|
576
|
-
super()
|
577
|
-
@cputype = cputype
|
578
|
-
@cpusubtype = cpusubtype & ~CPU_SUBTYPE_MASK
|
579
|
-
@offset = offset
|
580
|
-
@size = size
|
581
|
-
@align = align
|
582
|
-
end
|
547
|
+
field :align, :uint32, :endian => :big
|
583
548
|
|
584
549
|
# @return [String] the serialized fields of the fat arch
|
585
550
|
def serialize
|
586
|
-
[cputype, cpusubtype, offset, size, align].pack(
|
551
|
+
[cputype, cpusubtype, offset, size, align].pack(self.class.format)
|
587
552
|
end
|
588
553
|
|
589
554
|
# @return [Hash] a hash representation of this {FatArch}
|
@@ -606,27 +571,18 @@ module MachO
|
|
606
571
|
# Mach-Os that it points to necessarily *are* 64-bit.
|
607
572
|
# @see MachO::Headers::FatHeader
|
608
573
|
class FatArch64 < FatArch
|
609
|
-
# @return [
|
610
|
-
|
611
|
-
|
612
|
-
# @note Always big endian.
|
613
|
-
# @see MachOStructure::FORMAT
|
614
|
-
# @api private
|
615
|
-
FORMAT = "L>2Q>2L>2"
|
574
|
+
# @return [Integer] the file offset to the beginning of the Mach-O data
|
575
|
+
field :offset, :uint64, :endian => :big
|
616
576
|
|
617
|
-
# @
|
618
|
-
|
619
|
-
SIZEOF = 32
|
577
|
+
# @return [Integer] the size, in bytes, of the Mach-O data
|
578
|
+
field :size, :uint64, :endian => :big
|
620
579
|
|
621
|
-
# @
|
622
|
-
|
623
|
-
super(cputype, cpusubtype, offset, size, align)
|
624
|
-
@reserved = reserved
|
625
|
-
end
|
580
|
+
# @return [void]
|
581
|
+
field :reserved, :uint32, :endian => :big, :default => 0
|
626
582
|
|
627
583
|
# @return [String] the serialized fields of the fat arch
|
628
584
|
def serialize
|
629
|
-
[cputype, cpusubtype, offset, size, align, reserved].pack(
|
585
|
+
[cputype, cpusubtype, offset, size, align, reserved].pack(self.class.format)
|
630
586
|
end
|
631
587
|
|
632
588
|
# @return [Hash] a hash representation of this {FatArch64}
|
@@ -640,48 +596,25 @@ module MachO
|
|
640
596
|
# 32-bit Mach-O file header structure
|
641
597
|
class MachHeader < MachOStructure
|
642
598
|
# @return [Integer] the magic number
|
643
|
-
|
599
|
+
field :magic, :uint32
|
644
600
|
|
645
601
|
# @return [Integer] the CPU type of the Mach-O
|
646
|
-
|
602
|
+
field :cputype, :uint32
|
647
603
|
|
648
604
|
# @return [Integer] the CPU subtype of the Mach-O
|
649
|
-
|
605
|
+
field :cpusubtype, :uint32, :mask => CPU_SUBTYPE_MASK
|
650
606
|
|
651
607
|
# @return [Integer] the file type of the Mach-O
|
652
|
-
|
608
|
+
field :filetype, :uint32
|
653
609
|
|
654
610
|
# @return [Integer] the number of load commands in the Mach-O
|
655
|
-
|
611
|
+
field :ncmds, :uint32
|
656
612
|
|
657
613
|
# @return [Integer] the size of all load commands, in bytes, in the Mach-O
|
658
|
-
|
614
|
+
field :sizeofcmds, :uint32
|
659
615
|
|
660
616
|
# @return [Integer] the header flags associated with the Mach-O
|
661
|
-
|
662
|
-
|
663
|
-
# @see MachOStructure::FORMAT
|
664
|
-
# @api private
|
665
|
-
FORMAT = "L=7"
|
666
|
-
|
667
|
-
# @see MachOStructure::SIZEOF
|
668
|
-
# @api private
|
669
|
-
SIZEOF = 28
|
670
|
-
|
671
|
-
# @api private
|
672
|
-
def initialize(magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds,
|
673
|
-
flags)
|
674
|
-
super()
|
675
|
-
@magic = magic
|
676
|
-
@cputype = cputype
|
677
|
-
# For now we're not interested in additional capability bits also to be
|
678
|
-
# found in the `cpusubtype` field. We only care about the CPU sub-type.
|
679
|
-
@cpusubtype = cpusubtype & ~CPU_SUBTYPE_MASK
|
680
|
-
@filetype = filetype
|
681
|
-
@ncmds = ncmds
|
682
|
-
@sizeofcmds = sizeofcmds
|
683
|
-
@flags = flags
|
684
|
-
end
|
617
|
+
field :flags, :uint32
|
685
618
|
|
686
619
|
# @example
|
687
620
|
# puts "this mach-o has position-independent execution" if header.flag?(:MH_PIE)
|
@@ -787,22 +720,7 @@ module MachO
|
|
787
720
|
# 64-bit Mach-O file header structure
|
788
721
|
class MachHeader64 < MachHeader
|
789
722
|
# @return [void]
|
790
|
-
|
791
|
-
|
792
|
-
# @see MachOStructure::FORMAT
|
793
|
-
# @api private
|
794
|
-
FORMAT = "L=8"
|
795
|
-
|
796
|
-
# @see MachOStructure::SIZEOF
|
797
|
-
# @api private
|
798
|
-
SIZEOF = 32
|
799
|
-
|
800
|
-
# @api private
|
801
|
-
def initialize(magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds,
|
802
|
-
flags, reserved)
|
803
|
-
super(magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags)
|
804
|
-
@reserved = reserved
|
805
|
-
end
|
723
|
+
field :reserved, :uint32
|
806
724
|
|
807
725
|
# @return [Hash] a hash representation of this {MachHeader64}
|
808
726
|
def to_h
|
@@ -815,54 +733,31 @@ module MachO
|
|
815
733
|
# Prelinked kernel/"kernelcache" header structure
|
816
734
|
class PrelinkedKernelHeader < MachOStructure
|
817
735
|
# @return [Integer] the magic number for a compressed header ({COMPRESSED_MAGIC})
|
818
|
-
|
736
|
+
field :signature, :uint32, :endian => :big
|
819
737
|
|
820
738
|
# @return [Integer] the type of compression used
|
821
|
-
|
739
|
+
field :compress_type, :uint32, :endian => :big
|
822
740
|
|
823
741
|
# @return [Integer] a checksum for the uncompressed data
|
824
|
-
|
742
|
+
field :adler32, :uint32, :endian => :big
|
825
743
|
|
826
744
|
# @return [Integer] the size of the uncompressed data, in bytes
|
827
|
-
|
745
|
+
field :uncompressed_size, :uint32, :endian => :big
|
828
746
|
|
829
747
|
# @return [Integer] the size of the compressed data, in bytes
|
830
|
-
|
748
|
+
field :compressed_size, :uint32, :endian => :big
|
831
749
|
|
832
750
|
# @return [Integer] the version of the prelink format
|
833
|
-
|
751
|
+
field :prelink_version, :uint32, :endian => :big
|
834
752
|
|
835
753
|
# @return [void]
|
836
|
-
|
754
|
+
field :reserved, :string, :size => 40, :unpack => "L>10"
|
837
755
|
|
838
756
|
# @return [void]
|
839
|
-
|
757
|
+
field :platform_name, :string, :size => 64
|
840
758
|
|
841
759
|
# @return [void]
|
842
|
-
|
843
|
-
|
844
|
-
# @see MachOStructure::FORMAT
|
845
|
-
# @api private
|
846
|
-
FORMAT = "L>6a40a64a256"
|
847
|
-
|
848
|
-
# @see MachOStructure::SIZEOF
|
849
|
-
# @api private
|
850
|
-
SIZEOF = 384
|
851
|
-
|
852
|
-
# @api private
|
853
|
-
def initialize(signature, compress_type, adler32, uncompressed_size, compressed_size, prelink_version, reserved, platform_name, root_path)
|
854
|
-
super()
|
855
|
-
|
856
|
-
@signature = signature
|
857
|
-
@compress_type = compress_type
|
858
|
-
@adler32 = adler32
|
859
|
-
@uncompressed_size = uncompressed_size
|
860
|
-
@compressed_size = compressed_size
|
861
|
-
@prelink_version = prelink_version
|
862
|
-
@reserved = reserved.unpack("L>10")
|
863
|
-
@platform_name = platform_name
|
864
|
-
@root_path = root_path
|
865
|
-
end
|
760
|
+
field :root_path, :string, :size => 256
|
866
761
|
|
867
762
|
# @return [Boolean] whether this prelinked kernel supports KASLR
|
868
763
|
def kaslr?
|