metasploit-framework/lib/rex/machparsey/machbase.rb

408 lines
9.2 KiB
Ruby

#!/usr/bin/env ruby
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