ruby-macho 3.0.0 → 4.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ba2fc7b81345cb788c6e1de15bace8d35f7ac1f9a17b6b6a12289a4e51b978de
4
- data.tar.gz: 395dd63235823f33f11463d395b790b1cee2003ab430bb54326c03ca3de402b8
3
+ metadata.gz: 9880662397dbd1b2db647be1945cf0ab8bdcf08014b165258810287979f44ddf
4
+ data.tar.gz: a6f379de4b7c17344b01f4b44e178255165c6c2983f756d5d15b4bd62dfb8847
5
5
  SHA512:
6
- metadata.gz: c048978f3e0b1becaa3b050db8730f2f914cc8a8d662e1e7d24ca0a24c03c043747485e7e6033bad5789069795d8c04daad053d817f26c54dd809082975a01fe
7
- data.tar.gz: 53a576e666842a464af2f961825becfa51da0b26f3dd9ec656ce9859a6c0e9c017fc6622ec330bb0fc04e63089053a6ecd3740f505eb0e5aefecfb5728672d72
6
+ metadata.gz: 50fb7106b9d6f8fcee1de541ee570db1fb7e4316896f4b4aecfbe50e922380c3b86241c8c96f43079880baa7ef88373f89b1360d92d4a81645a2dc4a7eb607ce
7
+ data.tar.gz: 148edd4572c6e1655086ef828bf5c6ef90df03b326eeedbe07a08b8f47462e4f979dc69b9956c58992898398129cdf2c30e744904fb171b4226fe68dfaa4d2a3
data/README.md CHANGED
@@ -2,7 +2,7 @@ ruby-macho
2
2
  ================
3
3
 
4
4
  [![Gem Version](https://badge.fury.io/rb/ruby-macho.svg)](http://badge.fury.io/rb/ruby-macho)
5
- [![Build Status](https://travis-ci.org/Homebrew/ruby-macho.svg?branch=master)](https://travis-ci.org/Homebrew/ruby-macho)
5
+ [![CI](https://github.com/Homebrew/ruby-macho/actions/workflows/tests.yml/badge.svg)](https://github.com/Homebrew/ruby-macho/actions/workflows/tests.yml)
6
6
  [![Coverage Status](https://codecov.io/gh/Homebrew/ruby-macho/branch/master/graph/badge.svg)](https://codecov.io/gh/Homebrew/ruby-macho)
7
7
 
8
8
  A Ruby library for examining and modifying Mach-O files.
@@ -56,11 +56,24 @@ puts lc_vers.version_string # => "10.10.0"
56
56
 
57
57
  * Unit and performance testing.
58
58
 
59
- Attribution:
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
 
@@ -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
- " (for CPU type: 0x%08<cputype>x" % { :cputype => cputype, :cpusubtype => cpusubtype }
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
- " got #{actual_arity}"
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
- " malformed string"
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
- " Consider merging with `fat64: true`"
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
 
@@ -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
- attr_reader :magic
508
+ field :magic, :uint32, :endian => :big
509
509
 
510
510
  # @return [Integer] the number of fat architecture structures following the header
511
- attr_reader :nfat_arch
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(FORMAT)
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
- attr_reader :cputype
535
+ field :cputype, :uint32, :endian => :big
552
536
 
553
537
  # @return [Integer] the CPU subtype of the Mach-O
554
- attr_reader :cpusubtype
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
- attr_reader :offset
541
+ field :offset, :uint32, :endian => :big
558
542
 
559
543
  # @return [Integer] the size, in bytes, of the Mach-O data
560
- attr_reader :size
544
+ field :size, :uint32, :endian => :big
561
545
 
562
546
  # @return [Integer] the alignment, as a power of 2
563
- attr_reader :align
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(FORMAT)
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 [void]
610
- attr_reader :reserved
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
- # @see MachOStructure::SIZEOF
618
- # @api private
619
- SIZEOF = 32
577
+ # @return [Integer] the size, in bytes, of the Mach-O data
578
+ field :size, :uint64, :endian => :big
620
579
 
621
- # @api private
622
- def initialize(cputype, cpusubtype, offset, size, align, reserved = 0)
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(FORMAT)
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
- attr_reader :magic
599
+ field :magic, :uint32
644
600
 
645
601
  # @return [Integer] the CPU type of the Mach-O
646
- attr_reader :cputype
602
+ field :cputype, :uint32
647
603
 
648
604
  # @return [Integer] the CPU subtype of the Mach-O
649
- attr_reader :cpusubtype
605
+ field :cpusubtype, :uint32, :mask => CPU_SUBTYPE_MASK
650
606
 
651
607
  # @return [Integer] the file type of the Mach-O
652
- attr_reader :filetype
608
+ field :filetype, :uint32
653
609
 
654
610
  # @return [Integer] the number of load commands in the Mach-O
655
- attr_reader :ncmds
611
+ field :ncmds, :uint32
656
612
 
657
613
  # @return [Integer] the size of all load commands, in bytes, in the Mach-O
658
- attr_reader :sizeofcmds
614
+ field :sizeofcmds, :uint32
659
615
 
660
616
  # @return [Integer] the header flags associated with the Mach-O
661
- attr_reader :flags
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
- attr_reader :reserved
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
- attr_reader :signature
736
+ field :signature, :uint32, :endian => :big
819
737
 
820
738
  # @return [Integer] the type of compression used
821
- attr_reader :compress_type
739
+ field :compress_type, :uint32, :endian => :big
822
740
 
823
741
  # @return [Integer] a checksum for the uncompressed data
824
- attr_reader :adler32
742
+ field :adler32, :uint32, :endian => :big
825
743
 
826
744
  # @return [Integer] the size of the uncompressed data, in bytes
827
- attr_reader :uncompressed_size
745
+ field :uncompressed_size, :uint32, :endian => :big
828
746
 
829
747
  # @return [Integer] the size of the compressed data, in bytes
830
- attr_reader :compressed_size
748
+ field :compressed_size, :uint32, :endian => :big
831
749
 
832
750
  # @return [Integer] the version of the prelink format
833
- attr_reader :prelink_version
751
+ field :prelink_version, :uint32, :endian => :big
834
752
 
835
753
  # @return [void]
836
- attr_reader :reserved
754
+ field :reserved, :string, :size => 40, :unpack => "L>10"
837
755
 
838
756
  # @return [void]
839
- attr_reader :platform_name
757
+ field :platform_name, :string, :size => 64
840
758
 
841
759
  # @return [void]
842
- attr_reader :root_path
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?