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



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:



222
223
224
225
226
227
# File 'lib/macho/macho_file.rb', line 222

def change_install_name(old_name, new_name)
	dylib_cmd = command(:LC_LOAD_DYLIB).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

#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 Mach-O is of type MH_CORE, false otherwise.

Returns:

  • (Boolean)

    true if the Mach-O 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 Mach-O is of type MH_DSYM, false otherwise.

Returns:

  • (Boolean)

    true if the Mach-O 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 Mach-O is of type MH_DYLIB, false otherwise.

Returns:

  • (Boolean)

    true if the Mach-O 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



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

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



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

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

#dylinker?Boolean

Returns true if the Mach-O is of type MH_DYLINKER, false otherwise.

Returns:

  • (Boolean)

    true if the Mach-O 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 Mach-O is of type MH_EXECUTE, false otherwise.

Returns:

  • (Boolean)

    true if the Mach-O 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 Mach-O is of type MH_FVMLIB, false otherwise.

Returns:

  • (Boolean)

    true if the Mach-O 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 Mach-O is of type MH_KEXT_BUNDLE, false otherwise.

Returns:

  • (Boolean)

    true if the Mach-O 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



211
212
213
# File 'lib/macho/macho_file.rb', line 211

def linked_dylibs
	command(:LC_LOAD_DYLIB).map(&:name).map(&:to_s)
end

#magicFixnum

Returns the Mach-O's magic number.

Returns:

  • (Fixnum)

    the Mach-O'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 Mach-O's magic number.

Returns:

  • (String)

    a string representation of the Mach-O'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 Mach-O is of type MH_OBJECT, false otherwise.

Returns:

  • (Boolean)

    true if the Mach-O 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 Mach-O is of type MH_PRELOAD, false otherwise.

Returns:

  • (Boolean)

    true if the Mach-O 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

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

All sections of the segment segment.

Parameters:

Returns:



235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
# File 'lib/macho/macho_file.rb', line 235

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:



166
167
168
169
170
171
172
# File 'lib/macho/macho_file.rb', line 166

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



264
265
266
# File 'lib/macho/macho_file.rb', line 264

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



273
274
275
276
277
278
279
# File 'lib/macho/macho_file.rb', line 273

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