Class: EmergeCLI::MachOParser
- Inherits:
-
Object
- Object
- EmergeCLI::MachOParser
- Defined in:
- lib/utils/macho_parser.rb
Constant Summary collapse
- TYPE_METADATA_KIND_MASK =
0x7 << 3
- TYPE_METADATA_KIND_SHIFT =
3
- BIND_OPCODE_MASK =
Bind Codes
0xF0
- BIND_IMMEDIATE_MASK =
0x0F
- BIND_OPCODE_DONE =
0x00
- BIND_OPCODE_SET_DYLIB_ORDINAL_IMM =
0x10
- BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB =
0x20
- BIND_OPCODE_SET_DYLIB_SPECIAL_IMM =
0x30
- BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM =
0x40
- BIND_OPCODE_SET_TYPE_IMM =
0x50
- BIND_OPCODE_SET_ADDEND_SLEB =
0x60
- BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB =
0x70
- BIND_OPCODE_ADD_ADDR_ULEB =
0x80
- BIND_OPCODE_DO_BIND =
0x90
- BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB =
0xA0
- BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED =
0xB0
- BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB =
0xC0
- UINT64_SIZE =
8
- UINT64_MAX_VALUE =
0xFFFFFFFFFFFFFFFF
Instance Method Summary collapse
- #find_protocols_in_swift_proto(use_chained_fixups, imported_symbols, bound_symbols, search_symbols) ⇒ Object
- #load_binary(binary_path) ⇒ Object
- #read_dyld_info_only_command ⇒ Object
- #read_linkedit_data_command ⇒ Object
Instance Method Details
#find_protocols_in_swift_proto(use_chained_fixups, imported_symbols, bound_symbols, search_symbols) ⇒ Object
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
# File 'lib/utils/macho_parser.rb', line 118 def find_protocols_in_swift_proto(use_chained_fixups, imported_symbols, bound_symbols, search_symbols) found_section = nil @macho_file.segments.each do |segment| segment.sections.each do |section| if section.segname.strip == '__TEXT' && section.sectname.strip == '__swift5_proto' found_section = section break end end end unless found_section Logger.error 'The __swift5_proto section was not found.' return false end start = found_section.offset size = found_section.size offsets_list = parse_list(@binary_data, start, size) offsets_list.each do |relative_offset, offset_start| type_file_address = offset_start + relative_offset if type_file_address <= 0 || type_file_address >= @binary_data.size Logger.error 'Invalid protocol conformance offset' next end # ProtocolConformanceDescriptor -> ProtocolDescriptor protocol_descriptor = read_little_endian_signed_integer(@binary_data, type_file_address) # # ProtocolConformanceDescriptor -> ConformanceFlags conformance_flags = read_little_endian_signed_integer(@binary_data, type_file_address + 12) kind = (conformance_flags & TYPE_METADATA_KIND_MASK) >> TYPE_METADATA_KIND_SHIFT next unless kind == 0 indirect_relative_offset = get_indirect_relative_offset(type_file_address, protocol_descriptor) bound_symbol = bound_symbols.find { |symbol| symbol.address == indirect_relative_offset } if bound_symbol return true if search_symbols.include?(bound_symbol.symbol) elsif use_chained_fixups descriptor_offset = protocol_descriptor & ~1 jump_ptr = type_file_address + descriptor_offset address = @binary_data[jump_ptr, 4].unpack1('I<') symbol_name = imported_symbols[address] return true if search_symbols.include?(symbol_name) end end false end |
#load_binary(binary_path) ⇒ Object
28 29 30 31 |
# File 'lib/utils/macho_parser.rb', line 28 def load_binary(binary_path) @macho_file = MachO::MachOFile.new(binary_path) @binary_data = File.binread(binary_path) end |
#read_dyld_info_only_command ⇒ Object
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
# File 'lib/utils/macho_parser.rb', line 79 def read_dyld_info_only_command dyld_info_only_command = nil @macho_file.load_commands.each do |lc| dyld_info_only_command = lc if lc.type == :LC_DYLD_INFO_ONLY end if dyld_info_only_command.nil? Logger.debug 'No LC_DYLD_INFO_ONLY found' return [] end bound_symbols = [] start_address = dyld_info_only_command.bind_off end_address = dyld_info_only_command.bind_off + dyld_info_only_command.bind_size current_address = start_address current_symbol = BoundSymbol.new(segment_offset: 0, library: nil, offset: 0, symbol: '') while current_address < end_address results, current_address, current_symbol = read_next_symbol(@binary_data, current_address, end_address, current_symbol) # Dup items to avoid pointer issues results.each do |res| bound_symbols << res.dup end end # Filter only swift symbols starting with _$s swift_symbols = bound_symbols.select { |bound_symbol| bound_symbol.symbol.start_with?('_$s') } load_commands = @macho_file.load_commands.select { |lc| lc.type == :LC_SEGMENT_64 || lc.type == :LC_SEGMENT } # rubocop:disable Naming/VariableNumber swift_symbols.each do |swift_symbol| swift_symbol.address = load_commands[swift_symbol.segment_offset].vmaddr + swift_symbol.offset end swift_symbols end |
#read_linkedit_data_command ⇒ Object
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/utils/macho_parser.rb', line 33 def read_linkedit_data_command chained_fixups_command = nil @macho_file.load_commands.each do |lc| chained_fixups_command = lc if lc.type == :LC_DYLD_CHAINED_FIXUPS end if chained_fixups_command.nil? Logger.debug 'No LC_DYLD_CHAINED_FIXUPS found' return false, [] end # linkedit_data_command _, _, dataoff, datasize = @binary_data[chained_fixups_command.offset, 16].unpack('L<L<L<L<') header = @binary_data[dataoff, datasize].unpack('L<L<L<L<L<L<L<') # dyld_chained_fixups_header _, _, imports_offset, symbols_offset, imports_count, imports_format, = header imports_start = dataoff + imports_offset symbols_start = dataoff + symbols_offset imported_symbols = [] import_size, name_offset_proc = case imports_format when 1, nil # DYLD_CHAINED_IMPORT [4, ->(ptr) { ptr.unpack1('L<') >> 9 }] when 2 # DYLD_CHAINED_IMPORT_ADDEND [8, ->(ptr) { ptr.unpack1('L<') >> 9 }] when 3 # DYLD_CHAINED_IMPORT_ADDEND64 [16, ->(ptr) { ptr.unpack1('Q<') >> 32 }] end # Extract imported symbol names imports_count.times do |i| import_offset = imports_start + (i * import_size) name_offset = name_offset_proc.call(@binary_data[import_offset, import_size]) name_start = symbols_start + name_offset name = read_null_terminated_string(@binary_data[name_start..]) imported_symbols << name end [true, imported_symbols] end |