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