Class: MachO::MachOFile

Inherits:
Object
  • Object
show all
Defined in:
lib/macho/macho_file.rb

Overview

Represents a Mach-O file, which contains a header and load commands as well as binary executable instructions. Mach-O binaries are architecture specific.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(filename) ⇒ MachOFile

Creates a new FatFile from the given filename.

Parameters:

  • filename (String)

    the Mach-O file to load from

Raises:

  • (ArgumentError)

    if the given filename does not exist



28
29
30
31
32
33
34
35
# File 'lib/macho/macho_file.rb', line 28

def initialize(filename)
  raise ArgumentError.new("#{filetype}: no such file") unless File.exist?(filename)

  @filename = filename
  @raw_data = open(@filename, "rb") { |f| f.read }
  @header = get_mach_header
  @load_commands = get_load_commands
end

Instance Attribute Details

#headerMachO::MachHeader, MachO::MachHeader64 (readonly)

Returns:



10
11
12
# File 'lib/macho/macho_file.rb', line 10

def header
  @header
end

#load_commandsArray<MachO::LoadCommand> (readonly)

Returns an array of the file’s load commands.

Returns:



13
14
15
# File 'lib/macho/macho_file.rb', line 13

def load_commands
  @load_commands
end

Class Method Details

.new_from_bin(bin) ⇒ MachO::MachOFile

Creates a new MachOFile instance from a binary string.

Parameters:

  • bin (String)

    a binary string containing raw Mach-O data

Returns:



18
19
20
21
22
23
# File 'lib/macho/macho_file.rb', line 18

def self.new_from_bin(bin)
  instance = allocate
  instance.initialize_from_bin(bin)

  instance
end

Instance Method Details

#bundle?Boolean

Returns true if the Mach-O is of type ‘MH_BUNDLE`, false otherwise.

Returns:

  • (Boolean)

    true if the Mach-O is of type ‘MH_BUNDLE`, false otherwise



72
73
74
# File 'lib/macho/macho_file.rb', line 72

def bundle?
  header[:filetype] == MH_BUNDLE
end

#change_install_name(old_name, new_name) ⇒ void Also known as: change_dylib

This method returns an undefined value.

Changes the shared library ‘old_name` to `new_name`

Examples:

file.change_install_name("/usr/lib/libWhatever.dylib", "/usr/local/lib/libWhatever2.dylib")

Parameters:

  • old_name (String)

    the shared library’s old name

  • new_name (String)

    the shared library’s new name

Raises:



195
196
197
198
199
200
201
202
203
204
205
# File 'lib/macho/macho_file.rb', line 195

def change_install_name(old_name, new_name)
  idx = linked_dylibs.index(old_name)
  raise DylibUnknownError.new(old_name) if idx.nil?

  # this is a bit of a hack - since there is a 1-1 ordered association
  # between linked_dylibs and command('LC_LOAD_DYLIB'), we can use
  # their indices interchangeably to avoid having to loop.
  dylib_cmd = command("LC_LOAD_DYLIB")[idx]

  set_name_in_dylib(dylib_cmd, old_name, new_name)
end

#command(name) ⇒ Array<MachO::LoadCommand> Also known as: []

All load commands of a given name.

Examples:

file.command("LC_LOAD_DYLIB")
file["LC_LOAD_DYLIB"]

Returns:



121
122
123
# File 'lib/macho/macho_file.rb', line 121

def command(name)
  load_commands.select { |lc| lc.to_s == name }
end

#cpusubtypeString

Returns a string representation of the Mach-O’s CPU subtype.

Returns:

  • (String)

    a string representation of the Mach-O’s CPU subtype



97
98
99
# File 'lib/macho/macho_file.rb', line 97

def cpusubtype
  CPU_SUBTYPES[header[:cpusubtype]]
end

#cputypeString

Returns a string representation of the Mach-O’s CPU type.

Returns:

  • (String)

    a string representation of the Mach-O’s CPU type



92
93
94
# File 'lib/macho/macho_file.rb', line 92

def cputype
  CPU_TYPES[header[:cputype]]
end

#dylib?Boolean

Returns true if the Mach-O is of type ‘MH_DYLIB`, false otherwise.

Returns:

  • (Boolean)

    true if the Mach-O is of type ‘MH_DYLIB`, false otherwise



67
68
69
# File 'lib/macho/macho_file.rb', line 67

def dylib?
  header[:filetype] == MH_DYLIB
end

#dylib_idString?

The Mach-O’s dylib ID, or ‘nil` if not a dylib.

Examples:

file.dylib_id # => 'libBar.dylib'

Returns:

  • (String, nil)

    the Mach-O’s dylib ID



142
143
144
145
146
147
148
149
150
# File 'lib/macho/macho_file.rb', line 142

def dylib_id
  if !dylib?
    return nil
  end

  dylib_id_cmd = command("LC_ID_DYLIB").first

  dylib_id_cmd.name.to_s
end

#dylib_id=(new_id) ⇒ void

This method returns an undefined value.

Changes the Mach-O’s dylib ID to ‘new_id`. Does nothing if not a dylib.

Examples:

file.dylib_id = "libFoo.dylib"

Parameters:

  • new_id (String)

    the dylib’s new ID

Raises:

  • (ArgumentError)

    if ‘new_id` is not a String



158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/macho/macho_file.rb', line 158

def dylib_id=(new_id)
  if !new_id.is_a?(String)
    raise ArgumentError.new("argument must be a String")
  end

  if !dylib?
    return nil
  end

  dylib_cmd = command("LC_ID_DYLIB").first
  old_id = dylib_id

  set_name_in_dylib(dylib_cmd, old_id, new_id)
end

#executable?Boolean

Returns true if the Mach-O is of type ‘MH_EXECUTE`, false otherwise.

Returns:

  • (Boolean)

    true if the Mach-O is of type ‘MH_EXECUTE`, false otherwise



62
63
64
# File 'lib/macho/macho_file.rb', line 62

def executable?
  header[:filetype] == MH_EXECUTE
end

#filetypeString

Returns a string representation of the Mach-O’s filetype.

Returns:

  • (String)

    a string representation of the Mach-O’s filetype



87
88
89
# File 'lib/macho/macho_file.rb', line 87

def filetype
  MH_FILETYPES[header[:filetype]]
end

#flagsFixnum

Returns execution flags set by the linker.

Returns:

  • (Fixnum)

    execution flags set by the linker



112
113
114
# File 'lib/macho/macho_file.rb', line 112

def flags
  header[:flags]
end

#initialize_from_bin(bin) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



38
39
40
41
42
43
# File 'lib/macho/macho_file.rb', line 38

def initialize_from_bin(bin)
  @filename = nil
  @raw_data = bin
  @header = get_mach_header
  @load_commands = get_load_commands
end

#linked_dylibsArray<String>

All shared libraries linked to the Mach-O.

Returns:

  • (Array<String>)

    an array of all shared libraries



175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/macho/macho_file.rb', line 175

def linked_dylibs
  dylibs = []
  dylib_cmds = command("LC_LOAD_DYLIB")

  dylib_cmds.each do |dylib_cmd|
    dylib = dylib_cmd.name.to_s

    dylibs << dylib
  end

  dylibs
end

#magicFixnum

Returns the Mach-O’s magic number.

Returns:

  • (Fixnum)

    the Mach-O’s magic number



77
78
79
# File 'lib/macho/macho_file.rb', line 77

def magic
  header[:magic]
end

#magic32?Boolean

Returns true if the Mach-O has 32-bit magic, false otherwise.

Returns:

  • (Boolean)

    true if the Mach-O has 32-bit magic, false otherwise



52
53
54
# File 'lib/macho/macho_file.rb', line 52

def magic32?
  MachO.magic32?(header[:magic])
end

#magic64?Boolean

Returns true if the Mach-O has 64-bit magic, false otherwise.

Returns:

  • (Boolean)

    true if the Mach-O has 64-bit magic, false otherwise



57
58
59
# File 'lib/macho/macho_file.rb', line 57

def magic64?
  MachO.magic64?(header[:magic])
end

#magic_stringString

Returns a string representation of the Mach-O’s magic number.

Returns:

  • (String)

    a string representation of the Mach-O’s magic number



82
83
84
# File 'lib/macho/macho_file.rb', line 82

def magic_string
  MH_MAGICS[header[:magic]]
end

#ncmdsFixnum

Returns the number of load commands in the Mach-O’s header.

Returns:

  • (Fixnum)

    the number of load commands in the Mach-O’s header



102
103
104
# File 'lib/macho/macho_file.rb', line 102

def ncmds
  header[:ncmds]
end

#sections(segment) ⇒ Array<MachO::Section>, Array<MachO::Section64>

All sections of the segment ‘segment`.

Parameters:

Returns:



213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
# File 'lib/macho/macho_file.rb', line 213

def sections(segment)
  sections = []

  if !segment.is_a?(SegmentCommand) && !segment.is_a?(SegmentCommand64)
    raise ArgumentError.new("not a valid segment")
  end

  if segment.nsects.zero?
    return sections
  end

  offset = segment.offset + segment.class.bytesize

  segment.nsects.times do
    if segment.is_a? SegmentCommand
      sections << Section.new_from_bin(@raw_data.slice(offset, Section.bytesize))
      offset += Section.bytesize
    else
      sections << Section64.new_from_bin(@raw_data.slice(offset, Section64.bytesize))
      offset += Section64.bytesize
    end
  end

  sections
end

#segmentsArray<MachO::SegmentCommand>, Array<MachO::SegmentCommand64>

All segment load commands in the Mach-O.

Returns:



130
131
132
133
134
135
136
# File 'lib/macho/macho_file.rb', line 130

def segments
  if magic32?
    command("LC_SEGMENT")
  else
    command("LC_SEGMENT_64")
  end
end

#serializeString

The file’s raw Mach-O data.

Returns:

  • (String)

    the raw Mach-O data



47
48
49
# File 'lib/macho/macho_file.rb', line 47

def serialize
  @raw_data
end

#sizeofcmdsFixnum

Returns the size of all load commands, in bytes.

Returns:

  • (Fixnum)

    the size of all load commands, in bytes



107
108
109
# File 'lib/macho/macho_file.rb', line 107

def sizeofcmds
  header[:sizeofcmds]
end

#write(filename) ⇒ void

This method returns an undefined value.

Write all Mach-O data to the given filename.

Parameters:

  • filename (String)

    the file to write to



242
243
244
# File 'lib/macho/macho_file.rb', line 242

def write(filename)
  File.open(filename, "wb") { |f| f.write(@raw_data) }
end

#write!void

Note:

Overwrites all data in the file!

This method returns an undefined value.

Write all Mach-O data to the file used to initialize the instance.

Raises:

  • (MachOError)

    if the instance was created from a binary string

  • (MachO::MachOError)

    if the instance was initialized without a file



251
252
253
254
255
256
257
# File 'lib/macho/macho_file.rb', line 251

def write!
  if @filename.nil?
    raise MachOError.new("cannot write to a default file when initialized from a binary string")
  else
    File.open(@filename, "wb") { |f| f.write(@raw_data) }
  end
end