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 file is of type MH_BUNDLE, false otherwise.

Returns:

  • (Boolean)

    true if the file is of type MH_BUNDLE, false otherwise



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

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:



228
229
230
231
232
233
# File 'lib/macho/macho_file.rb', line 228

def change_install_name(old_name, new_name)
	dylib_cmd = dylib_load_commands.find { |d| d.name.to_s == old_name }
	raise DylibUnknownError.new(old_name) if dylib_cmd.nil?

	set_name_in_dylib(dylib_cmd, old_name, new_name)
end

#change_rpath(old_path, new_path) ⇒ void

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.

This method returns an undefined value.

Changes the runtime path old_path to new_path

Examples:

file.change_rpath("/usr/lib", "/usr/local/lib")

Parameters:

  • old_path (String)

    the old runtime path

  • new_path (String)

    the new runtime path

Raises:



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

def change_rpath(old_path, new_path)
	rpath_cmd = command(:LC_RPATH).find { |r| r.path.to_s == old_path }
	raise RpathUnknownError.new(old_path) if rpath_cmd.nil?

	set_path_in_rpath(rpath_cmd, old_path, new_path)
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]

Parameters:

  • name (String, Symbol)

    the load command ID

Returns:



157
158
159
# File 'lib/macho/macho_file.rb', line 157

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

#core?Boolean

Returns true if the file is of type MH_CORE, false otherwise.

Returns:

  • (Boolean)

    true if the file is of type MH_CORE, false otherwise



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

def core?
	header.filetype == MH_CORE
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



132
133
134
# File 'lib/macho/macho_file.rb', line 132

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



127
128
129
# File 'lib/macho/macho_file.rb', line 127

def cputype
	CPU_TYPES[header.cputype]
end

#dsym?Boolean

Returns true if the file is of type MH_DSYM, false otherwise.

Returns:

  • (Boolean)

    true if the file is of type MH_DSYM, false otherwise



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

def dsym?
	header.filetype == MH_DSYM
end

#dylib?Boolean

Returns true if the file is of type MH_DYLIB, false otherwise.

Returns:

  • (Boolean)

    true if the file is of type MH_DYLIB, false otherwise



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

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



184
185
186
187
188
189
190
191
192
# File 'lib/macho/macho_file.rb', line 184

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



200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/macho/macho_file.rb', line 200

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

#dylib_load_commandsArray<MachO::DylibCommand>

All load commands responsible for loading dylibs.

Returns:



165
166
167
# File 'lib/macho/macho_file.rb', line 165

def dylib_load_commands
	load_commands.select { |lc| DYLIB_LOAD_COMMANDS.include?(lc.type) }
end

#dylinker?Boolean

Returns true if the file is of type MH_DYLINKER, false otherwise.

Returns:

  • (Boolean)

    true if the file is of type MH_DYLINKER, false otherwise



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

def dylinker?
	header.filetype == MH_DYLINKER
end

#executable?Boolean

Returns true if the file is of type MH_EXECUTE, false otherwise.

Returns:

  • (Boolean)

    true if the file is of type MH_EXECUTE, false otherwise



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

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



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

def filetype
	MH_FILETYPES[header.filetype]
end

#flagsFixnum

Returns execution flags set by the linker.

Returns:

  • (Fixnum)

    execution flags set by the linker



147
148
149
# File 'lib/macho/macho_file.rb', line 147

def flags
	header.flags
end

#fvmlib?Boolean

Returns true if the file is of type MH_FVMLIB, false otherwise.

Returns:

  • (Boolean)

    true if the file is of type MH_FVMLIB, false otherwise



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

def fvmlib?
	header.filetype == MH_FVMLIB
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

#kext?Boolean

Returns true if the file is of type MH_KEXT_BUNDLE, false otherwise.

Returns:

  • (Boolean)

    true if the file is of type MH_KEXT_BUNDLE, false otherwise



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

def kext?
	header.filetype == MH_KEXT_BUNDLE
end

#linked_dylibsArray<String>

All shared libraries linked to the Mach-O.

Returns:

  • (Array<String>)

    an array of all shared libraries



217
218
219
# File 'lib/macho/macho_file.rb', line 217

def linked_dylibs
	dylib_load_commands.map(&:name).map(&:to_s)
end

#magicFixnum

Returns the file's magic number.

Returns:

  • (Fixnum)

    the file's magic number



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

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 file's magic number.

Returns:

  • (String)

    a string representation of the file's magic number



117
118
119
# File 'lib/macho/macho_file.rb', line 117

def magic_string
	MH_MAGICS[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



137
138
139
# File 'lib/macho/macho_file.rb', line 137

def ncmds
	header.ncmds
end

#object?Boolean

Returns true if the file is of type MH_OBJECT, false otherwise.

Returns:

  • (Boolean)

    true if the file is of type MH_OBJECT, false otherwise



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

def object?
	header.filetype == MH_OBJECT
end

#preload?Boolean

Returns true if the file is of type MH_PRELOAD, false otherwise.

Returns:

  • (Boolean)

    true if the file is of type MH_PRELOAD, false otherwise



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

def preload?
	header.filetype == MH_PRELOAD
end

#rpathsArray<String>

All runtime paths searched by the dynamic linker for the Mach-O.

Returns:

  • (Array<String>)

    an array of all runtime paths



239
240
241
# File 'lib/macho/macho_file.rb', line 239

def rpaths
	command(:LC_RPATH).map(&:path).map(&:to_s)
end

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

All sections of the segment segment.

Parameters:

Returns:



262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
# File 'lib/macho/macho_file.rb', line 262

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:



172
173
174
175
176
177
178
# File 'lib/macho/macho_file.rb', line 172

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



142
143
144
# File 'lib/macho/macho_file.rb', line 142

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



291
292
293
# File 'lib/macho/macho_file.rb', line 291

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



300
301
302
303
304
305
306
# File 'lib/macho/macho_file.rb', line 300

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