410 lines
9.9 KiB
Ruby
410 lines
9.9 KiB
Ruby
#!/usr/bin/env ruby
|
|
# -*- coding: binary -*-
|
|
|
|
require 'rex/struct2'
|
|
|
|
module Rex
|
|
module MachParsey
|
|
|
|
require 'rex/machparsey/exceptions'
|
|
require 'rex/struct2'
|
|
|
|
class GenericStruct
|
|
attr_accessor :struct
|
|
def initialize(_struct)
|
|
self.struct = _struct
|
|
end
|
|
|
|
# Access a value
|
|
def v
|
|
struct.v
|
|
end
|
|
|
|
# Access a value by array
|
|
def [](*args)
|
|
struct[*args]
|
|
end
|
|
|
|
# Obtain an array of all fields
|
|
def keys
|
|
struct.keys
|
|
end
|
|
|
|
def method_missing(meth, *args)
|
|
v[meth.to_s] || (raise NoMethodError.new, meth)
|
|
end
|
|
end
|
|
|
|
class GenericHeader < GenericStruct
|
|
end
|
|
|
|
BITS_32 = 0
|
|
BITS_64 = 1
|
|
ENDIAN_LSB = 0
|
|
ENDIAN_MSB = 1
|
|
|
|
class MachBase
|
|
|
|
MH_MAGIC = 0xfeedface
|
|
MH_MAGIC_64 = 0xfeedfacf
|
|
MH_CIGAM = 0xcefaedfe
|
|
MH_CIGAM_64 = 0xcffaedfe
|
|
MACH_HEADER_SIZE = 28
|
|
MACH_HEADER_SIZE_64 = 32
|
|
|
|
|
|
MACH_HEADER_LSB = Rex::Struct2::CStructTemplate.new(
|
|
['uint32v', 'magic', 0],
|
|
['uint32v', 'cputype', 0],
|
|
['uint32v', 'cpusubtype',0],
|
|
['uint32v', 'filetype', 0],
|
|
['uint32v', 'ncmds', 0],
|
|
['uint32v', 'sizeofcmds',0],
|
|
['uint32v', 'flags', 0]
|
|
)
|
|
|
|
MACH_HEADER_MSB = Rex::Struct2::CStructTemplate.new(
|
|
['uint32n', 'magic', 0],
|
|
['uint32n', 'cputype', 0],
|
|
['uint32n', 'cpusubtype',0],
|
|
['uint32n', 'filetype', 0],
|
|
['uint32n', 'ncmds', 0],
|
|
['uint32n', 'sizeofcmds',0],
|
|
['uint32n', 'flags', 0]
|
|
)
|
|
|
|
|
|
MACH_HEADER_64_LSB = Rex::Struct2::CStructTemplate.new(
|
|
['uint32v', 'magic', 0],
|
|
['uint32v', 'cputype', 0],
|
|
['uint32v', 'cpusubtype',0],
|
|
['uint32v', 'filetype', 0],
|
|
['uint32v', 'ncmds', 0],
|
|
['uint32v', 'sizeofcmds',0],
|
|
['uint32v', 'flags', 0],
|
|
['uint32v', 'reserved', 0]
|
|
)
|
|
|
|
MACH_HEADER_64_MSB = Rex::Struct2::CStructTemplate.new(
|
|
['uint32n', 'magic', 0],
|
|
['uint32n', 'cputype', 0],
|
|
['uint32n', 'cpusubtype',0],
|
|
['uint32n', 'filetype', 0],
|
|
['uint32n', 'ncmds', 0],
|
|
['uint32n', 'sizeofcmds',0],
|
|
['uint32n', 'flags', 0],
|
|
['uint32n', 'reserved', 0]
|
|
)
|
|
|
|
#cpu types for Mach-O binaries
|
|
CPU_TYPE_I386 = 0x7
|
|
CPU_TYPE_X86_64 = 0x01000007
|
|
CPU_TYPE_ARM = 0xC
|
|
CPU_TYPE_POWERPC = 0x12
|
|
CPU_TYPE_POWERPC64 = 0x01000012
|
|
|
|
CPU_SUBTYPE_LITTLE_ENDIAN = 0
|
|
CPU_SUBTYPE_BIG_ENDIAN = 1
|
|
|
|
LC_SEGMENT = 0x1 #/* segment of this file to be mapped */
|
|
LC_SYMTAB = 0x2 #/* link-edit stab symbol table info */
|
|
LC_SYMSEG = 0x3 #/* link-edit gdb symbol table info (obsolete) */
|
|
LC_THREAD = 0x4 #/* thread */
|
|
LC_UNIXTHREAD = 0x5 #/* unix thread (includes a stack) */
|
|
LC_LOADFVMLIB = 0x6 #/* load a specified fixed VM shared library */
|
|
LC_IDFVMLIB = 0x7 #/* fixed VM shared library identification */
|
|
LC_IDENT = 0x8 #/* object identification info (obsolete) */
|
|
LC_FVMFILE = 0x9 #/* fixed VM file inclusion (internal use) */
|
|
LC_PREPAGE = 0xa #/* prepage command (internal use) */
|
|
LC_DYSYMTAB = 0xb #/* dynamic link-edit symbol table info */
|
|
LC_LOAD_DYLIB = 0xc #/* load a dynamicly linked shared library */
|
|
LC_ID_DYLIB = 0xd #/* dynamicly linked shared lib identification */
|
|
LC_LOAD_DYLINKER = 0xe #/* load a dynamic linker */
|
|
LC_ID_DYLINKER = 0xf #/* dynamic linker identification */
|
|
LC_PREBOUND_DYLIB = 0x10 #/* modules prebound for a dynamicly */
|
|
LC_SEGMENT_64 = 0x19 #/* segment of this file to be mapped */
|
|
|
|
|
|
|
|
|
|
class MachHeader < GenericHeader
|
|
attr_accessor :bits, :endian
|
|
|
|
def initialize(rawdata)
|
|
mach_header = MACH_HEADER_LSB.make_struct
|
|
if !mach_header.from_s(rawdata)
|
|
raise MachHeaderError, "Could't access Mach-O Magic", caller
|
|
end
|
|
|
|
if mach_header.v['magic'] == MH_MAGIC
|
|
endian = ENDIAN_LSB
|
|
bits = BITS_32
|
|
mach_header = MACH_HEADER_LSB.make_struct
|
|
elsif mach_header.v['magic'] == MH_CIGAM
|
|
bits = BITS_32
|
|
endian = ENDIAN_MSB
|
|
mach_header = MACH_HEADER_MSB.make_struct
|
|
elsif mach_header.v['magic'] == MH_MAGIC_64
|
|
endian = ENDIAN_LSB
|
|
bits = BITS_64
|
|
mach_header = MACH_HEADER_LSB.make_struct
|
|
elsif mach_header.v['magic'] == MH_CIGAM_64
|
|
endian = ENDIAN_MSB
|
|
bits = BITS_64
|
|
mach_header = MACH_HEADER_MSB.make_struct
|
|
else
|
|
raise MachHeaderError, "Couldn't find Mach Magic", caller
|
|
end
|
|
|
|
if !mach_header.from_s(rawdata)
|
|
raise MachHeaderError, "Could't process Mach-O Header", caller
|
|
end
|
|
|
|
self.struct = mach_header
|
|
self.endian = endian
|
|
self.bits = bits
|
|
end
|
|
end
|
|
|
|
LOAD_COMMAND_SIZE = 8
|
|
|
|
LOAD_COMMAND_LSB = Rex::Struct2::CStructTemplate.new(
|
|
['uint32v','cmd',0],
|
|
['uint32v','cmdsize',0]
|
|
)
|
|
|
|
LOAD_COMMAND_MSB = Rex::Struct2::CStructTemplate.new(
|
|
['uint32n','cmd',0],
|
|
['uint32n','cmdsize',0]
|
|
)
|
|
|
|
class LoadCommand < GenericHeader
|
|
def initialize(rawdata, endian)
|
|
|
|
if endian == ENDIAN_MSB
|
|
load_command = LOAD_COMMAND_MSB.make_struct
|
|
else
|
|
load_command = LOAD_COMMAND_LSB.make_struct
|
|
end
|
|
|
|
if !load_command.from_s(rawdata)
|
|
raise MachParseError, "Couldn't parse load command"
|
|
end
|
|
|
|
self.struct = load_command
|
|
|
|
end
|
|
end
|
|
|
|
SEGMENT_COMMAND_SIZE = 56
|
|
|
|
SEGMENT_COMMAND_LSB = Rex::Struct2::CStructTemplate.new(
|
|
['uint32v', 'cmd', 0],
|
|
['uint32v', 'cmdsize', 0],
|
|
['string', 'segname', 16, ''],
|
|
['uint32v', 'vmaddr', 0],
|
|
['uint32v', 'vmsize', 0],
|
|
['uint32v', 'fileoff', 0],
|
|
['uint32v', 'filesize', 0],
|
|
['uint32v', 'maxprot', 0],
|
|
['uint32v', 'initprot', 0],
|
|
['uint32v', 'nsects', 0],
|
|
['uint32v', 'flags', 0]
|
|
)
|
|
|
|
SEGMENT_COMMAND_MSB = Rex::Struct2::CStructTemplate.new(
|
|
['uint32n', 'cmd', 0],
|
|
['uint32n', 'cmdsize', 0],
|
|
['string', 'segname', 16, ''],
|
|
['uint32n', 'vmaddr', 0],
|
|
['uint32n', 'vmsize', 0],
|
|
['uint32n', 'fileoff', 0],
|
|
['uint32n', 'filesize', 0],
|
|
['uint32n', 'maxprot', 0],
|
|
['uint32n', 'initprot', 0],
|
|
['uint32n', 'nsects', 0],
|
|
['uint32n', 'flags', 0]
|
|
)
|
|
|
|
SEGMENT_COMMAND_SIZE_64 = 72
|
|
|
|
SEGMENT_COMMAND_64_LSB = Rex::Struct2::CStructTemplate.new(
|
|
['uint32v', 'cmd', 0],
|
|
['uint32v', 'cmdsize', 0],
|
|
['string', 'segname', 16, ''],
|
|
['uint64v', 'vmaddr', 0],
|
|
['uint64v', 'vmsize', 0],
|
|
['uint64v', 'fileoff', 0],
|
|
['uint64v', 'filesize', 0],
|
|
['uint32v', 'maxprot', 0],
|
|
['uint32v', 'initprot', 0],
|
|
['uint32v', 'nsects', 0],
|
|
['uint32v', 'flags', 0]
|
|
)
|
|
|
|
SEGMENT_COMMAND_64_MSB = Rex::Struct2::CStructTemplate.new(
|
|
['uint32n', 'cmd', 0],
|
|
['uint32n', 'cmdsize', 0],
|
|
['string', 'segname', 16, ''],
|
|
['uint64n', 'vmaddr', 0],
|
|
['uint64n', 'vmsize', 0],
|
|
['uint64n', 'fileoff', 0],
|
|
['uint64n', 'filesize', 0],
|
|
['uint32n', 'maxprot', 0],
|
|
['uint32n', 'initprot', 0],
|
|
['uint32n', 'nsects', 0],
|
|
['uint32n', 'flags', 0]
|
|
)
|
|
|
|
class Segment < GenericHeader
|
|
attr_accessor :_bits, :_endian
|
|
|
|
def initialize(rawdata, bits, endian)
|
|
self._bits = bits
|
|
|
|
if bits == BITS_64
|
|
if endian == ENDIAN_MSB
|
|
segment_command = SEGMENT_COMMAND_64_MSB.make_struct
|
|
else
|
|
segment_command = SEGMENT_COMMAND_64_LSB.make_struct
|
|
end
|
|
else
|
|
if endian == ENDIAN_MSB
|
|
segment_command = SEGMENT_COMMAND_MSB.make_struct
|
|
else
|
|
segment_command = SEGMENT_COMMAND_LSB.make_struct
|
|
end
|
|
end
|
|
if !segment_command.from_s(rawdata)
|
|
raise MachParseError, "Couldn't parse segment command"
|
|
end
|
|
|
|
self.struct = segment_command
|
|
end
|
|
|
|
def Segname
|
|
v['segname']
|
|
end
|
|
|
|
def Vmaddr
|
|
v['vmaddr']
|
|
end
|
|
|
|
def Vmsize
|
|
v['vmsize']
|
|
end
|
|
|
|
def FileOff
|
|
v['fileoff']
|
|
end
|
|
|
|
def FileSize
|
|
v['filesize']
|
|
end
|
|
end
|
|
|
|
class Thread < GenericHeader
|
|
def initialize(rawdata)
|
|
end
|
|
end
|
|
end
|
|
|
|
FAT_MAGIC = 0xcafebabe
|
|
FAT_CIGAM = 0xbebafeca
|
|
FAT_HEADER_SIZE = 8
|
|
|
|
FAT_HEADER_LSB = Rex::Struct2::CStructTemplate.new(
|
|
['uint32v', 'magic', 0],
|
|
['uint32v', 'nfat_arch',0]
|
|
)
|
|
|
|
FAT_HEADER_MSB = Rex::Struct2::CStructTemplate.new(
|
|
['uint32n', 'magic', 0],
|
|
['uint32n', 'nfat_arch',0]
|
|
)
|
|
|
|
|
|
FAT_ARCH_SIZE = 20
|
|
|
|
FAT_ARCH_LSB = Rex::Struct2::CStructTemplate.new(
|
|
['uint32v', 'cpu_type', 0],
|
|
['uint32v', 'cpu_subtype',0],
|
|
['uint32v', 'offset', 0],
|
|
['uint32v', 'size', 0],
|
|
['uint32v', 'align', 0]
|
|
)
|
|
|
|
FAT_ARCH_MSB = Rex::Struct2::CStructTemplate.new(
|
|
['uint32n', 'cpu_type', 0],
|
|
['uint32n', 'cpu_subtype',0],
|
|
['uint32n', 'offset', 0],
|
|
['uint32n', 'size', 0],
|
|
['uint32n', 'align', 0]
|
|
)
|
|
|
|
|
|
class FatBase
|
|
|
|
class FatHeader < GenericHeader
|
|
attr_accessor :nfat_arch, :endian, :exists
|
|
|
|
def initialize(rawdata)
|
|
fat_header = FAT_HEADER_LSB.make_struct
|
|
if !fat_header.from_s(rawdata)
|
|
#raise something
|
|
end
|
|
|
|
magic = fat_header.v['magic']
|
|
if magic == FAT_MAGIC
|
|
endian = ENDIAN_LSB
|
|
elsif magic == FAT_CIGAM
|
|
endian = ENDIAN_MSB
|
|
fat_header = FAT_HEADER_MSB.make_struct
|
|
if !fat_header.from_s(rawdata)
|
|
raise FatHeaderError, "Could not parse FAT header"
|
|
end
|
|
else
|
|
self.exists = 0
|
|
return
|
|
end
|
|
|
|
self.nfat_arch = fat_header.v['nfat_arch']
|
|
self.struct = fat_header
|
|
self.endian = endian
|
|
end
|
|
end
|
|
|
|
class FatArch < GenericHeader
|
|
attr_accessor :cpu_type, :cpu_subtype, :offset, :size
|
|
|
|
def initialize(rawdata, endian)
|
|
if endian == ENDIAN_LSB
|
|
fat_arch = FAT_ARCH_LSB.make_struct
|
|
else
|
|
fat_arch = FAT_ARCH_MSB.make_struct
|
|
end
|
|
|
|
if !fat_arch.from_s(rawdata)
|
|
raise FatHeaderError, "Could not parse arch from FAT header"
|
|
end
|
|
|
|
self.cpu_type = fat_arch.v['cpu_type']
|
|
self.cpu_subtype = fat_arch.v['cpu_subtype']
|
|
self.offset = fat_arch.v['offset']
|
|
self.size = fat_arch.v['size']
|
|
self.struct = fat_arch
|
|
end
|
|
|
|
end
|
|
|
|
class Thread < GenericHeader
|
|
def initialize(rawdata)
|
|
end
|
|
end
|
|
|
|
|
|
end
|
|
|
|
end
|
|
end
|