#!/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