#!/usr/bin/env ruby require 'rex/machparsey/machbase' require 'rex/machparsey/exceptions' require 'rex/image_source' module Rex module MachParsey class Mach < MachBase attr_accessor :mach_header, :segments, :isource, :bits, :endian, :arch, :fat_offset def initialize(isource, offset = 0, fat = false) _parse_mach_header(isource, offset) if fat == true self.fat_offset = offset else self.fat_offset = 0 end self.isource = isource end def _parse_mach_header(isource, offset) self.mach_header = MachHeader.new(isource.read(offset, MACH_HEADER_SIZE_64)) bits = mach_header.bits endian = mach_header.endian ncmds = mach_header.ncmds if bits == BITS_32 offset += MACH_HEADER_SIZE else offset += MACH_HEADER_SIZE_64 end segments = [] ncmds.times do load_command = LoadCommand.new(isource.read(offset, LOAD_COMMAND_SIZE), endian) case load_command.cmd when LC_SEGMENT segments << Segment.new(isource.read(offset, SEGMENT_COMMAND_SIZE), bits, endian) when LC_SEGMENT_64 segments << Segment.new(isource.read(offset, SEGMENT_COMMAND_SIZE_64), bits, endian) end offset += load_command.cmdsize end self.mach_header = mach_header self.segments = segments self.isource = isource self.bits = bits self.endian = endian return segments end def self.new_from_file(filename, disk_backed = false) file = ::File.open(filename, "rb") if disk_backed return self.new(ImageSource::Disk.new(file)) else obj = new_from_string(file.read) file.close return obj end end def self.new_from_string(data) return self.new(ImageSource::Memory.new(data)) end def ptr_64? mach_header.bits == BITS_64 end def ptr_32? ptr_64? == false end def ptr_s(vaddr) (ptr_32?) ? ("0x%.8x" % vaddr) : ("0x%.16x" % vaddr) end def read(offset, len) isource.read(fat_offset + offset, len) end def index(*args) isource.index(*args) end def close isource.close end end class Fat < FatBase attr_accessor :fat_header, :fat_archs, :machos, :isource def initialize(isource, offset = 0) self.fat_archs = [] self.machos = [] self.isource = isource self.fat_header = FatHeader.new(isource.read(offset, FAT_HEADER_SIZE)) if !self.fat_header raise FatHeaderError, "Could not parse FAT header" end print "Detected " + self.fat_header.nfat_arch.to_s + " archs in binary.\n" offset += FAT_HEADER_SIZE self.fat_header.nfat_arch.times do fat_arch = FatArch.new(isource.read(offset, FAT_ARCH_SIZE), self.fat_header.endian) self.fat_archs << fat_arch self.machos << Mach.new(isource, fat_arch.offset, true) offset += FAT_ARCH_SIZE end end #this is useful for debugging but we don't use it for anything. def _parse_fat_header(isource, offset) archs = [] nfat_arch = self.fat_header.nfat_arch print "Number of archs in binary: " + nfat_arch.to_s + "\n" nfat_arch.times do arch = FatArch.new(isource.read(offset, FAT_ARCH_SIZE), self.endian) case arch.cpu_type when CPU_TYPE_I386 print "i386\n" when CPU_TYPE_X86_64 print "x86_64\n" when CPU_TYPE_ARM print "Arm\n" when CPU_TYPE_POWERPC print "Power PC\n" when CPU_TYPE_POWERPC64 print "Power PC 64\n" end offset += FAT_ARCH_SIZE end end def self.new_from_file(filename, disk_backed = false) file = ::File.open(filename, "rb") if disk_backed return self.new(ImageSource::Disk.new(file)) else obj = new_from_string(file.read) file.close return obj end end def self.new_from_string(data) return self.new(ImageSource::Memory.new(data)) end def ptr_64? mach_header.bits == BITS_64 end def ptr_32? ptr_64? == false end def ptr_s(vaddr) (ptr_32?) ? ("0x%.8x" % vaddr) : ("0x%.16x" % vaddr) end def read(offset, len) isource.read(offset, len) end def index(*args) isource.index(*args) end def close isource.close end end end end