#!/usr/bin/env ruby

# $Id: pebase.rb 7730 2009-12-07 12:56:54Z sf $

require 'rex/peparsey/exceptions'
require 'rex/struct2'

module Rex
module PeParsey
class PeBase


	# #define IMAGE_DOS_SIGNATURE                 0x5A4D      // MZ

	IMAGE_DOS_SIGNATURE = 0x5a4d

	#
	# typedef struct _IMAGE_DOS_HEADER {      // DOS .EXE header
	#     WORD   e_magic;                     // Magic number
	#     WORD   e_cblp;                      // Bytes on last page of file
	#     WORD   e_cp;                        // Pages in file
	#     WORD   e_crlc;                      // Relocations
	#     WORD   e_cparhdr;                   // Size of header in paragraphs
	#     WORD   e_minalloc;                  // Minimum extra paragraphs needed
	#     WORD   e_maxalloc;                  // Maximum extra paragraphs needed
	#     WORD   e_ss;                        // Initial (relative) SS value
	#     WORD   e_sp;                        // Initial SP value
	#     WORD   e_csum;                      // Checksum
	#     WORD   e_ip;                        // Initial IP value
	#     WORD   e_cs;                        // Initial (relative) CS value
	#     WORD   e_lfarlc;                    // File address of relocation table
	#     WORD   e_ovno;                      // Overlay number
	#     WORD   e_res[4];                    // Reserved words
	#     WORD   e_oemid;                     // OEM identifier (for e_oeminfo)
	#     WORD   e_oeminfo;                   // OEM information; e_oemid specific
	#     WORD   e_res2[10];                  // Reserved words
	#     LONG   e_lfanew;                    // File address of new exe header
	#   } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
	#

	IMAGE_DOS_HEADER_SIZE = 64
	IMAGE_DOS_HEADER = Rex::Struct2::CStructTemplate.new(
	  [ 'uint16v', 'e_magic',     IMAGE_DOS_SIGNATURE ],
	  [ 'uint16v', 'e_cblp',      0 ],
	  [ 'uint16v', 'e_cp',        0 ],
	  [ 'uint16v', 'e_crlc',      0 ],
	  [ 'uint16v', 'e_cparhdr',   0 ],
	  [ 'uint16v', 'e_minalloc',  0 ],
	  [ 'uint16v', 'e_maxalloc',  0 ],
	  [ 'uint16v', 'e_ss',        0 ],
	  [ 'uint16v', 'e_sp',        0 ],
	  [ 'uint16v', 'e_csum',      0 ],
	  [ 'uint16v', 'e_ip',        0 ],
	  [ 'uint16v', 'e_cs',        0 ],
	  [ 'uint16v', 'e_lfarlc',    0 ],
	  [ 'uint16v', 'e_ovno',      0 ],
	  [ 'template', 'e_res', Rex::Struct2::CStructTemplate.new(
	    [ 'uint16v', 'e_res_0', 0 ],
	    [ 'uint16v', 'e_res_1', 0 ],
	    [ 'uint16v', 'e_res_2', 0 ],
	    [ 'uint16v', 'e_res_3', 0 ]
	  )],
	  [ 'uint16v', 'e_oemid',     0 ],
	  [ 'uint16v', 'e_oeminfo',   0 ],
	  [ 'template', 'e_res2', Rex::Struct2::CStructTemplate.new(
	    [ 'uint16v', 'e_res2_0', 0 ],
	    [ 'uint16v', 'e_res2_1', 0 ],
	    [ 'uint16v', 'e_res2_2', 0 ],
	    [ 'uint16v', 'e_res2_3', 0 ],
	    [ 'uint16v', 'e_res2_4', 0 ],
	    [ 'uint16v', 'e_res2_5', 0 ],
	    [ 'uint16v', 'e_res2_6', 0 ],
	    [ 'uint16v', 'e_res2_7', 0 ],
	    [ 'uint16v', 'e_res2_8', 0 ],
	    [ 'uint16v', 'e_res2_9', 0 ]
	  )],
	  [ 'uint32v', 'e_lfanew',    0 ]
	)


	class HeaderAccessor
		attr_accessor :dos, :file, :opt, :sections, :config, :exceptions, :tls
		def initialize
		end
	end

	class GenericStruct
		attr_accessor :struct
		def initialize(_struct)
			self.struct = _struct
		end

		# The following methods are just pass-throughs for struct

		# 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

	class DosHeader < GenericHeader

		def initialize(rawdata)
			dos_header = IMAGE_DOS_HEADER.make_struct

			if !dos_header.from_s(rawdata)
				raise DosHeaderError, "Couldn't parse IMAGE_DOS_HEADER", caller
			end

			if dos_header.v['e_magic'] != IMAGE_DOS_SIGNATURE
				raise DosHeaderError, "Couldn't find DOS e_magic", caller
			end

			self.struct = dos_header
		end

		def e_lfanew
			v['e_lfanew']
		end
	end


	def self._parse_dos_header(rawdata)
		return DosHeader.new(rawdata)
	end

	#
	# typedef struct _IMAGE_FILE_HEADER {
	#     WORD    Machine;
	#     WORD    NumberOfSections;
	#     DWORD   TimeDateStamp;
	#     DWORD   PointerToSymbolTable;
	#     DWORD   NumberOfSymbols;
	#     WORD    SizeOfOptionalHeader;
	#     WORD    Characteristics;
	# } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
	#
	# #define IMAGE_NT_SIGNATURE                  0x00004550  // PE00
	# #define IMAGE_FILE_MACHINE_I386              0x014c  // Intel 386.
	# #define IMAGE_FILE_MACHINE_IA64              0x0200  // Intel 64
	# #define IMAGE_FILE_MACHINE_ALPHA64           0x0284  // ALPHA64
	# #define IMAGE_FILE_MACHINE_AMD64             0x8664  // AMD64 (K8)
	# #define IMAGE_SIZEOF_FILE_HEADER             20
	#

	IMAGE_NT_SIGNATURE       = 0x00004550
	IMAGE_FILE_MACHINE_I386  = 0x014c
	IMAGE_FILE_MACHINE_IA64  = 0x0200
	IMAGE_FILE_MACHINE_ALPHA64 = 0x0284
	IMAGE_FILE_MACHINE_AMD64 = 0x8664
	IMAGE_FILE_HEADER_SIZE   = 20+4  # because we include the signature
	IMAGE_FILE_HEADER = Rex::Struct2::CStructTemplate.new(
	  # not really in the header, but easier for us this way
	  [ 'uint32v', 'NtSignature',           0 ],
	  [ 'uint16v', 'Machine',               0 ],
	  [ 'uint16v', 'NumberOfSections',      0 ],
	  [ 'uint32v', 'TimeDateStamp',         0 ],
	  [ 'uint32v', 'PointerToSymbolTable',  0 ],
	  [ 'uint32v', 'NumberOfSymbols',       0 ],
	  [ 'uint16v', 'SizeOfOptionalHeader',  0 ],
	  [ 'uint16v', 'Characteristics',       0 ]
	)

	SUPPORTED_MACHINES = [
		IMAGE_FILE_MACHINE_I386,
		IMAGE_FILE_MACHINE_IA64,
		IMAGE_FILE_MACHINE_ALPHA64,
		IMAGE_FILE_MACHINE_AMD64
	]

	class FileHeader < GenericHeader
		def initialize(rawdata)
			file_header = IMAGE_FILE_HEADER.make_struct

			if !file_header.from_s(rawdata)
				raise FileHeaderError, "Couldn't parse IMAGE_FILE_HEADER", caller
			end

			if file_header.v['NtSignature'] != IMAGE_NT_SIGNATURE
				raise FileHeaderError, "Couldn't find the PE magic!"
			end

			if SUPPORTED_MACHINES.include?(file_header.v['Machine']) == false
				raise FileHeaderError, "Unsupported machine type: #{file_header.v['Machine']}", caller
			end

			self.struct = file_header
		end

		def Machine
			v['Machine']
		end

		def SizeOfOptionalHeader
			v['SizeOfOptionalHeader']
		end

		def NumberOfSections
			v['NumberOfSections']
		end
	end

	def self._parse_file_header(rawdata)
		return FileHeader.new(rawdata)
	end

	#
	# typedef struct _IMAGE_IMPORT_DESCRIPTOR {
	#     union {
	#         DWORD   Characteristics;            // 0 for terminating null import descriptor
	#         DWORD   OriginalFirstThunk;         // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
	#     };
	#     DWORD   TimeDateStamp;                  // 0 if not bound,
	#                                             // -1 if bound, and real date\time stamp
	#                                             //     in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
	#                                             // O.W. date/time stamp of DLL bound to (Old BIND)
	#
	#     DWORD   ForwarderChain;                 // -1 if no forwarders
	#     DWORD   Name;
	#     DWORD   FirstThunk;                     // RVA to IAT (if bound this IAT has actual addresses)
	# } IMAGE_IMPORT_DESCRIPTOR;
	#
	IMAGE_ORDINAL_FLAG32         = 0x80000000
	IMAGE_IMPORT_DESCRIPTOR_SIZE = 20
	IMAGE_IMPORT_DESCRIPTOR = Rex::Struct2::CStructTemplate.new(
	  [ 'uint32v', 'OriginalFirstThunk',           0 ],
	  [ 'uint32v', 'TimeDateStamp',                0 ],
	  [ 'uint32v', 'ForwarderChain',               0 ],
	  [ 'uint32v', 'Name',                         0 ],
	  [ 'uint32v', 'FirstThunk',                   0 ]
	)

	#
	# typedef struct _IMAGE_IMPORT_BY_NAME {
	#     WORD    Hint;
	#     BYTE    Name[1];
	# } IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
	#

	class ImportDescriptor
		attr_accessor :name, :entries
		def initialize(_name, _entries)
			self.name    = _name
			self.entries = _entries
		end
	end

	class ImportEntry
		attr_accessor :name, :ordinal
		def initialize(_name, _ordinal)
			self.name     = _name
			self.ordinal  = _ordinal
		end
	end

	#
	# typedef struct _IMAGE_EXPORT_DIRECTORY {
	#     DWORD   Characteristics;
	#     DWORD   TimeDateStamp;
	#     WORD    MajorVersion;
	#     WORD    MinorVersion;
	#     DWORD   Name;
	#     DWORD   Base;
	#     DWORD   NumberOfFunctions;
	#     DWORD   NumberOfNames;
	#     DWORD   AddressOfFunctions;     // RVA from base of image
	#     DWORD   AddressOfNames;         // RVA from base of image
	#     DWORD   AddressOfNameOrdinals;  // RVA from base of image
	# } IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
	#
	IMAGE_EXPORT_DESCRIPTOR_SIZE = 40
	IMAGE_EXPORT_DESCRIPTOR = Rex::Struct2::CStructTemplate.new(
	  [ 'uint32v', 'Characteristics',              0 ],
	  [ 'uint32v', 'TimeDateStamp',                0 ],
	  [ 'uint16v', 'MajorVersion',                 0 ],
	  [ 'uint16v', 'MinorVersion',                 0 ],
	  [ 'uint32v', 'Name',                         0 ],
	  [ 'uint32v', 'Base',                         0 ],
	  [ 'uint32v', 'NumberOfFunctions',            0 ],
	  [ 'uint32v', 'NumberOfNames',                0 ],
	  [ 'uint32v', 'AddressOfFunctions',           0 ],
	  [ 'uint32v', 'AddressOfNames',               0 ],
	  [ 'uint32v', 'AddressOfNameOrdinals',        0 ]
	)

	class ExportDirectory
		attr_accessor :name, :entries, :base

		def initialize(_name, _entries, _base)
			self.name    = _name
			self.entries = _entries
			self.base    = _base
		end
	end

	class ExportEntry
		attr_accessor :name, :ordinal, :rva
		def initialize(_name, _ordinal, _rva)
			self.name     = _name
			self.ordinal  = _ordinal
			self.rva      = _rva
		end
	end

	#
	# typedef struct _IMAGE_DATA_DIRECTORY {
	#     DWORD   VirtualAddress;
	#     DWORD   Size;
	# } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
	#
	IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16
	IMAGE_DATA_DIRECTORY_SIZE        = 8
	IMAGE_DIRECTORY_ENTRY_EXPORT         = 0
	IMAGE_DIRECTORY_ENTRY_IMPORT         = 1
	IMAGE_DIRECTORY_ENTRY_RESOURCE       = 2
	IMAGE_DIRECTORY_ENTRY_EXCEPTION      = 3
	IMAGE_DIRECTORY_ENTRY_SECURITY       = 4
	IMAGE_DIRECTORY_ENTRY_BASERELOC      = 5
	IMAGE_DIRECTORY_ENTRY_DEBUG          = 6
	IMAGE_DIRECTORY_ENTRY_COPYRIGHT      = 7
	IMAGE_DIRECTORY_ENTRY_ARCHITECTURE   = 7
	IMAGE_DIRECTORY_ENTRY_GLOBALPTR      = 8
	IMAGE_DIRECTORY_ENTRY_TLS            = 9
	IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG    = 10
	IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT   = 11
	IMAGE_DIRECTORY_ENTRY_IAT            = 12
	IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT   = 13
	IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = 14
	IMAGE_DATA_DIRECTORY = Rex::Struct2::CStructTemplate.new(
	  [ 'uint32v', 'VirtualAddress',               0 ],
	  [ 'uint32v', 'Size',                         0 ]
	)

	#
	# typedef struct _IMAGE_OPTIONAL_HEADER {
	#     //
	#     // Standard fields.
	#     //
	#
	#     WORD    Magic;
	#     BYTE    MajorLinkerVersion;
	#     BYTE    MinorLinkerVersion;
	#     DWORD   SizeOfCode;
	#     DWORD   SizeOfInitializedData;
	#     DWORD   SizeOfUninitializedData;
	#     DWORD   AddressOfEntryPoint;
	#     DWORD   BaseOfCode;
	#     DWORD   BaseOfData;
	#
	#     //
	#     // NT additional fields.
	#     //
	#
	#     DWORD   ImageBase;
	#     DWORD   SectionAlignment;
	#     DWORD   FileAlignment;
	#     WORD    MajorOperatingSystemVersion;
	#     WORD    MinorOperatingSystemVersion;
	#     WORD    MajorImageVersion;
	#     WORD    MinorImageVersion;
	#     WORD    MajorSubsystemVersion;
	#     WORD    MinorSubsystemVersion;
	#     DWORD   Win32VersionValue;
	#     DWORD   SizeOfImage;
	#     DWORD   SizeOfHeaders;
	#     DWORD   CheckSum;
	#     WORD    Subsystem;
	#     WORD    DllCharacteristics;
	#     DWORD   SizeOfStackReserve;
	#     DWORD   SizeOfStackCommit;
	#     DWORD   SizeOfHeapReserve;
	#     DWORD   SizeOfHeapCommit;
	#     DWORD   LoaderFlags;
	#     DWORD   NumberOfRvaAndSizes;
	#     IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
	# } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
	#
	# #define IMAGE_NT_OPTIONAL_HDR32_MAGIC      0x10b
	# #define IMAGE_SIZEOF_NT_OPTIONAL32_HEADER    224
	#

	IMAGE_NT_OPTIONAL_HDR32_MAGIC     = 0x10b
	IMAGE_SIZEOF_NT_OPTIONAL32_HEADER = 224
	IMAGE_OPTIONAL_HEADER32 = Rex::Struct2::CStructTemplate.new(
	  [ 'uint16v', 'Magic',                        0 ],
	  [ 'uint8',   'MajorLinkerVersion',           0 ],
	  [ 'uint8',   'MinorLinkerVersion',           0 ],
	  [ 'uint32v', 'SizeOfCode',                   0 ],
	  [ 'uint32v', 'SizeOfInitializeData',         0 ],
	  [ 'uint32v', 'SizeOfUninitializeData',       0 ],
	  [ 'uint32v', 'AddressOfEntryPoint',          0 ],
	  [ 'uint32v', 'BaseOfCode',                   0 ],
	  [ 'uint32v', 'BaseOfData',                   0 ],
	  [ 'uint32v', 'ImageBase',                    0 ],
	  [ 'uint32v', 'SectionAlignment',             0 ],
	  [ 'uint32v', 'FileAlignment',                0 ],
	  [ 'uint16v', 'MajorOperatingSystemVersion',  0 ],
	  [ 'uint16v', 'MinorOperatingSystemVersion',  0 ],
	  [ 'uint16v', 'MajorImageVersion',            0 ],
	  [ 'uint16v', 'MinorImageVersion',            0 ],
	  [ 'uint16v', 'MajorSubsystemVersion',        0 ],
	  [ 'uint16v', 'MinorSubsystemVersion',        0 ],
	  [ 'uint32v', 'Win32VersionValue',            0 ],
	  [ 'uint32v', 'SizeOfImage',                  0 ],
	  [ 'uint32v', 'SizeOfHeaders',                0 ],
	  [ 'uint32v', 'CheckSum',                     0 ],
	  [ 'uint16v', 'Subsystem',                    0 ],
	  [ 'uint16v', 'DllCharacteristics',           0 ],
	  [ 'uint32v', 'SizeOfStackReserve',           0 ],
	  [ 'uint32v', 'SizeOfStackCommit',            0 ],
	  [ 'uint32v', 'SizeOfHeapReserve',            0 ],
	  [ 'uint32v', 'SizeOfHeapCommit',             0 ],
	  [ 'uint32v', 'LoaderFlags',                  0 ],
	  [ 'uint32v', 'NumberOfRvaAndSizes',          0 ],
	  [ 'template', 'DataDirectory', Rex::Struct2::CStructTemplate.new(
	    [ 'template', 'DataDirectoryEntry_0', IMAGE_DATA_DIRECTORY ],
	    [ 'template', 'DataDirectoryEntry_1', IMAGE_DATA_DIRECTORY ],
	    [ 'template', 'DataDirectoryEntry_2', IMAGE_DATA_DIRECTORY ],
	    [ 'template', 'DataDirectoryEntry_3', IMAGE_DATA_DIRECTORY ],
	    [ 'template', 'DataDirectoryEntry_4', IMAGE_DATA_DIRECTORY ],
	    [ 'template', 'DataDirectoryEntry_5', IMAGE_DATA_DIRECTORY ],
	    [ 'template', 'DataDirectoryEntry_6', IMAGE_DATA_DIRECTORY ],
	    [ 'template', 'DataDirectoryEntry_7', IMAGE_DATA_DIRECTORY ],
	    [ 'template', 'DataDirectoryEntry_8', IMAGE_DATA_DIRECTORY ],
	    [ 'template', 'DataDirectoryEntry_9', IMAGE_DATA_DIRECTORY ],
	    [ 'template', 'DataDirectoryEntry_10', IMAGE_DATA_DIRECTORY ],
	    [ 'template', 'DataDirectoryEntry_11', IMAGE_DATA_DIRECTORY ],
	    [ 'template', 'DataDirectoryEntry_12', IMAGE_DATA_DIRECTORY ],
	    [ 'template', 'DataDirectoryEntry_13', IMAGE_DATA_DIRECTORY ],
	    [ 'template', 'DataDirectoryEntry_14', IMAGE_DATA_DIRECTORY ],
	    [ 'template', 'DataDirectoryEntry_15', IMAGE_DATA_DIRECTORY ]
	  )]
	)

	#
	# typedef struct _IMAGE_OPTIONAL_HEADER64 {
	# 	 USHORT      Magic;
	# 	 UCHAR       MajorLinkerVersion;
	# 	 UCHAR       MinorLinkerVersion;
	# 	 ULONG       SizeOfCode;
	# 	 ULONG       SizeOfInitializedData;
	# 	 ULONG       SizeOfUninitializedData;
	# 	 ULONG       AddressOfEntryPoint;
	# 	 ULONG       BaseOfCode;
	# 	 ULONGLONG   ImageBase;
	# 	 ULONG       SectionAlignment;
	# 	 ULONG       FileAlignment;
	# 	 USHORT      MajorOperatingSystemVersion;
	# 	 USHORT      MinorOperatingSystemVersion;
	# 	 USHORT      MajorImageVersion;
	# 	 USHORT      MinorImageVersion;
	# 	 USHORT      MajorSubsystemVersion;
	# 	 USHORT      MinorSubsystemVersion;
	# 	 ULONG       Win32VersionValue;
	# 	 ULONG       SizeOfImage;
	# 	 ULONG       SizeOfHeaders;
	# 	 ULONG       CheckSum;
	# 	 USHORT      Subsystem;
	# 	 USHORT      DllCharacteristics;
	# 	 ULONGLONG   SizeOfStackReserve;
	# 	 ULONGLONG   SizeOfStackCommit;
	# 	 ULONGLONG   SizeOfHeapReserve;
	# 	 ULONGLONG   SizeOfHeapCommit;
	# 	 ULONG       LoaderFlags;
	# 	 ULONG       NumberOfRvaAndSizes;
	# 	 IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
	# } IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64;
	#
	# #define IMAGE_NT_OPTIONAL_HDR64_MAGIC      0x20b
	# #define IMAGE_SIZEOF_NT_OPTIONAL64_HEADER    240
	#

	IMAGE_NT_OPTIONAL_HDR64_MAGIC     = 0x20b
	IMAGE_SIZEOF_NT_OPTIONAL64_HEADER = 240
	IMAGE_OPTIONAL_HEADER64 = Rex::Struct2::CStructTemplate.new(
	  [ 'uint16v', 'Magic',                        0 ],
	  [ 'uint8',   'MajorLinkerVersion',           0 ],
	  [ 'uint8',   'MinorLinkerVersion',           0 ],
	  [ 'uint32v', 'SizeOfCode',                   0 ],
	  [ 'uint32v', 'SizeOfInitializeData',         0 ],
	  [ 'uint32v', 'SizeOfUninitializeData',       0 ],
	  [ 'uint32v', 'AddressOfEntryPoint',          0 ],
	  [ 'uint32v', 'BaseOfCode',                   0 ],
	  [ 'uint64v', 'ImageBase',                    0 ],
	  [ 'uint32v', 'SectionAlignment',             0 ],
	  [ 'uint32v', 'FileAlignment',                0 ],
	  [ 'uint16v', 'MajorOperatingsystemVersion',  0 ],
	  [ 'uint16v', 'MinorOperatingsystemVersion',  0 ],
	  [ 'uint16v', 'MajorImageVersion',            0 ],
	  [ 'uint16v', 'MinorImageVersion',            0 ],
	  [ 'uint16v', 'MajorSubsystemVersion',        0 ],
	  [ 'uint16v', 'MinorSubsystemVersion',        0 ],
	  [ 'uint32v', 'Win32VersionValue',            0 ],
	  [ 'uint32v', 'SizeOfImage',                  0 ],
	  [ 'uint32v', 'SizeOfHeaders',                0 ],
	  [ 'uint32v', 'CheckSum',                     0 ],
	  [ 'uint16v', 'Subsystem',                    0 ],
	  [ 'uint16v', 'DllCharacteristics',           0 ],
	  [ 'uint64v', 'SizeOfStackReserve',           0 ],
	  [ 'uint64v', 'SizeOfStackCommit',            0 ],
	  [ 'uint64v', 'SizeOfHeapReserve',            0 ],
	  [ 'uint64v', 'SizeOfHeapCommit',             0 ],
	  [ 'uint32v', 'LoaderFlags',                  0 ],
	  [ 'uint32v', 'NumberOfRvaAndSizes',          0 ],
	  [ 'template', 'DataDirectory', Rex::Struct2::CStructTemplate.new(
	    [ 'template', 'DataDirectoryEntry_0', IMAGE_DATA_DIRECTORY ],
	    [ 'template', 'DataDirectoryEntry_1', IMAGE_DATA_DIRECTORY ],
	    [ 'template', 'DataDirectoryEntry_2', IMAGE_DATA_DIRECTORY ],
	    [ 'template', 'DataDirectoryEntry_3', IMAGE_DATA_DIRECTORY ],
	    [ 'template', 'DataDirectoryEntry_4', IMAGE_DATA_DIRECTORY ],
	    [ 'template', 'DataDirectoryEntry_5', IMAGE_DATA_DIRECTORY ],
	    [ 'template', 'DataDirectoryEntry_6', IMAGE_DATA_DIRECTORY ],
	    [ 'template', 'DataDirectoryEntry_7', IMAGE_DATA_DIRECTORY ],
	    [ 'template', 'DataDirectoryEntry_8', IMAGE_DATA_DIRECTORY ],
	    [ 'template', 'DataDirectoryEntry_9', IMAGE_DATA_DIRECTORY ],
	    [ 'template', 'DataDirectoryEntry_10', IMAGE_DATA_DIRECTORY ],
	    [ 'template', 'DataDirectoryEntry_11', IMAGE_DATA_DIRECTORY ],
	    [ 'template', 'DataDirectoryEntry_12', IMAGE_DATA_DIRECTORY ],
	    [ 'template', 'DataDirectoryEntry_13', IMAGE_DATA_DIRECTORY ],
	    [ 'template', 'DataDirectoryEntry_14', IMAGE_DATA_DIRECTORY ],
	    [ 'template', 'DataDirectoryEntry_15', IMAGE_DATA_DIRECTORY ]
	  )]
	)

	class OptionalHeader < GenericHeader
		def ImageBase
			v['ImageBase']
		end
		def FileAlignment
			v['FileAlignment']
		end
	end

	class OptionalHeader32 < OptionalHeader
		def initialize(rawdata)
			optional_header = IMAGE_OPTIONAL_HEADER32.make_struct

			if !optional_header.from_s(rawdata)
				raise OptionalHeaderError, "Couldn't parse IMAGE_OPTIONAL_HEADER32", caller
			end

			if optional_header.v['Magic'] != IMAGE_NT_OPTIONAL_HDR32_MAGIC
				raise OptionalHeaderError, "Magic did not match!", caller()
			end

			self.struct = optional_header
		end
	end

	class OptionalHeader64 < OptionalHeader
		def initialize(rawdata)
			optional_header = IMAGE_OPTIONAL_HEADER64.make_struct

			if !optional_header.from_s(rawdata)
				raise OptionalHeaderError, "Couldn't parse IMAGE_OPTIONAL_HEADER64", caller
			end

			if optional_header.v['Magic'] != IMAGE_NT_OPTIONAL_HDR64_MAGIC
				raise OptionalHeaderError, "Magic did not match!", caller()
			end

			self.struct = optional_header
		end
	end

	def self._parse_optional_header(rawdata)
		case rawdata.length
			# no optional header
			when 0
				return nil

			# good, good
			when IMAGE_SIZEOF_NT_OPTIONAL32_HEADER
				return OptionalHeader32.new(rawdata)

			when IMAGE_SIZEOF_NT_OPTIONAL64_HEADER
				return OptionalHeader64.new(rawdata)

			# bad, bad
			else
				raise OptionalHeaderError, "I don't know this header size, #{rawdata.length}", caller
		end

	end

	#
	# typedef struct _IMAGE_SECTION_HEADER {
	#     BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];
	#     union {
	#             DWORD   PhysicalAddress;
	#             DWORD   VirtualSize;
	#     } Misc;
	#     DWORD   VirtualAddress;
	#     DWORD   SizeOfRawData;
	#     DWORD   PointerToRawData;
	#     DWORD   PointerToRelocations;
	#     DWORD   PointerToLinenumbers;
	#     WORD    NumberOfRelocations;
	#     WORD    NumberOfLinenumbers;
	#     DWORD   Characteristics;
	# } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
	#
	# #define IMAGE_SIZEOF_SECTION_HEADER          40
	#

	IMAGE_SIZEOF_SECTION_HEADER = 40
	IMAGE_SECTION_HEADER = Rex::Struct2::CStructTemplate.new(
	  [ 'string',  'Name', 8,               '' ],
	  [ 'uint32v', 'Misc',                   0 ],
	  [ 'uint32v', 'VirtualAddress',         0 ],
	  [ 'uint32v', 'SizeOfRawData',          0 ],
	  [ 'uint32v', 'PointerToRawData',       0 ],
	  [ 'uint32v', 'PointerToRelocations',   0 ],
	  [ 'uint32v', 'NumberOfRelocations',    0 ],
	  [ 'uint32v', 'NumberOfLineNumbers',    0 ],
	  [ 'uint32v', 'Characteristics',        0 ]
	)

	class SectionHeader < GenericHeader
		def initialize(rawdata)
			section_header = IMAGE_SECTION_HEADER.make_struct

			if !section_header.from_s(rawdata)
				raise SectionHeaderError, "Could not parse header", caller
			end

			self.struct = section_header
		end

		def VirtualAddress
			v['VirtualAddress']
		end
		def SizeOfRawData
			v['SizeOfRawData']
		end
		def PointerToRawData
			v['PointerToRawData']
		end
	end

	def self._parse_section_headers(rawdata)
		section_headers = [ ]
		size = IMAGE_SIZEOF_SECTION_HEADER
		numsections = rawdata.length / size

		numsections.times do |i|
			data = rawdata[i * size, size]
			section_headers << SectionHeader.new(data)
		end

		return section_headers
	end

	#
	# typedef struct _IMAGE_BASE_RELOCATION {
	#     DWORD   VirtualAddress;
	#     DWORD   SizeOfBlock;
	# //  WORD    TypeOffset[1];
	# } IMAGE_BASE_RELOCATION;
	# typedef IMAGE_BASE_RELOCATION UNALIGNED * PIMAGE_BASE_RELOCATION;
	#
	# #define IMAGE_SIZEOF_BASE_RELOCATION         8
	#
	IMAGE_SIZEOF_BASE_RELOCATION = 8
	IMAGE_BASE_RELOCATION = Rex::Struct2::CStructTemplate.new(
	  [ 'uint32v', 'VirtualAddress',         0 ],
	  [ 'uint32v', 'SizeOfBlock',            0 ]
	)
	IMAGE_BASE_RELOCATION_TYPE_OFFSET = Rex::Struct2::CStructTemplate.new(
	  [ 'uint16v', 'TypeOffset',             0 ]
	)

	class RelocationDirectory
		attr_accessor :entries, :rva

		def initialize(rva, entries)
			self.rva     = rva
			self.entries = entries
			self.name    = name
			self.characteristics = chars
			self.timedate = timedate
			self.version = version
			self.entries = []
		end
	end

	class RelocationEntry
		attr_accessor :rva, :reltype

		def initialize(_rva, _type)
			self.rva     = _rva
			self.reltype = _type
		end
	end


	class ResourceDirectory
		attr_accessor :entries, :name

		def initialize(name, entries)
			self.name   = name
			self.entries = entries
		end
	end

	class ResourceEntry
		attr_accessor :path, :lang, :code, :rva, :size, :pe, :file

		def initialize(pe, path, lang, code, rva, size, file)
			self.pe    = pe
			self.path  = path
			self.lang  = lang
			self.code  = code
			self.rva   = rva
			self.size  = size
			self.file  = file.to_s
		end

		def data
			pe._isource.read(pe.rva_to_file_offset(rva), size)
		end
	end

	#
	# typedef struct {
	#     DWORD   Size;
	#     DWORD   TimeDateStamp;
	#     WORD    MajorVersion;
	#     WORD    MinorVersion;
	#     DWORD   GlobalFlagsClear;
	#     DWORD   GlobalFlagsSet;
	#     DWORD   CriticalSectionDefaultTimeout;
	#     DWORD   DeCommitFreeBlockThreshold;
	#     DWORD   DeCommitTotalFreeThreshold;
	#     DWORD   LockPrefixTable;            // VA
	#     DWORD   MaximumAllocationSize;
	#     DWORD   VirtualMemoryThreshold;
	#     DWORD   ProcessHeapFlags;
	#     DWORD   ProcessAffinityMask;
	#     WORD    CSDVersion;
	#     WORD    Reserved1;
	#     DWORD   EditList;                   // VA
	#     DWORD   SecurityCookie;             // VA
	#     DWORD   SEHandlerTable;             // VA
	#     DWORD   SEHandlerCount;
	# } IMAGE_LOAD_CONFIG_DIRECTORY32, *PIMAGE_LOAD_CONFIG_DIRECTORY32;
	#
	IMAGE_LOAD_CONFIG_DIRECTORY32 = Rex::Struct2::CStructTemplate.new(
	  [ 'uint32v', 'Size',                          0 ],
	  [ 'uint32v', 'TimeDateStamp',                 0 ],
	  [ 'uint16v', 'MajorVersion',                  0 ],
	  [ 'uint16v', 'MinorVersion',                  0 ],
	  [ 'uint32v', 'GlobalFlagsClear',              0 ],
	  [ 'uint32v', 'GlobalFlagsSet',                0 ],
	  [ 'uint32v', 'CriticalSectionDefaultTimeout', 0 ],
	  [ 'uint32v', 'DeCommitFreeBlockThreshold',    0 ],
	  [ 'uint32v', 'DeCommitTotalFreeThreshold',    0 ],
	  [ 'uint32v', 'LockPrefixTable',               0 ],
	  [ 'uint32v', 'MaximumAllocationSize',         0 ],
	  [ 'uint32v', 'VirtualMemoryThreshold',        0 ],
	  [ 'uint32v', 'ProcessHeapFlags',              0 ],
	  [ 'uint32v', 'ProcessAffinityMask',           0 ],
	  [ 'uint16v', 'CSDVersion',                    0 ],
	  [ 'uint16v', 'Reserved1',                     0 ],
	  [ 'uint32v', 'EditList',                      0 ],
	  [ 'uint32v', 'SecurityCookie',                0 ],
	  [ 'uint32v', 'SEHandlerTable',                0 ],
	  [ 'uint32v', 'SEHandlerCount',                0 ]
	)

	#
	# typedef struct {
	# 	 ULONG      Size;
	# 	 ULONG      TimeDateStamp;
	# 	 USHORT     MajorVersion;
	# 	 USHORT     MinorVersion;
	# 	 ULONG      GlobalFlagsClear;
	# 	 ULONG      GlobalFlagsSet;
	# 	 ULONG      CriticalSectionDefaultTimeout;
	# 	 ULONGLONG  DeCommitFreeBlockThreshold;
	# 	 ULONGLONG  DeCommitTotalFreeThreshold;
	# 	 ULONGLONG  LockPrefixTable;         // VA
	# 	 ULONGLONG  MaximumAllocationSize;
	# 	 ULONGLONG  VirtualMemoryThreshold;
	# 	 ULONGLONG  ProcessAffinityMask;
	# 	 ULONG      ProcessHeapFlags;
	# 	 USHORT     CSDVersion;
	# 	 USHORT     Reserved1;
	# 	 ULONGLONG  EditList;                // VA
	# 	 ULONGLONG  SecurityCookie;          // VA
	# 	 ULONGLONG  SEHandlerTable;          // VA
	# 	 ULONGLONG  SEHandlerCount;
	# } IMAGE_LOAD_CONFIG_DIRECTORY64, *PIMAGE_LOAD_CONFIG_DIRECTORY64;
	#
	IMAGE_LOAD_CONFIG_DIRECTORY64 = Rex::Struct2::CStructTemplate.new(
	  [ 'uint32v', 'Size',                          0 ],
	  [ 'uint32v', 'TimeDateStamp',                 0 ],
	  [ 'uint16v', 'MajorVersion',                  0 ],
	  [ 'uint16v', 'MinorVersion',                  0 ],
	  [ 'uint32v', 'GlobalFlagsClear',              0 ],
	  [ 'uint32v', 'GlobalFlagsSet',                0 ],
	  [ 'uint32v', 'CriticalSectionDefaultTimeout', 0 ],
	  [ 'uint64v', 'DeCommitFreeBlockThreshold',    0 ],
	  [ 'uint64v', 'DeCommitTotalFreeThreshold',    0 ],
	  [ 'uint64v', 'LockPrefixTable',               0 ],
	  [ 'uint64v', 'MaximumAllocationSize',         0 ],
	  [ 'uint64v', 'VirtualMemoryThreshold',        0 ],
	  [ 'uint64v', 'ProcessAffinityMask',           0 ],
	  [ 'uint32v', 'ProcessHeapFlags',              0 ],
	  [ 'uint16v', 'CSDVersion',                    0 ],
	  [ 'uint16v', 'Reserved1',                     0 ],
	  [ 'uint64v', 'EditList',                      0 ],
	  [ 'uint64v', 'SecurityCookie',                0 ],
	  [ 'uint64v', 'SEHandlerTable',                0 ],
	  [ 'uint64v', 'SEHandlerCount',                0 ]
	)


	class ConfigHeader < GenericHeader

	end

	# doesn't seem to be used -- not compatible with 64-bit
	#def self._parse_config_header(rawdata)
	#	header = IMAGE_LOAD_CONFIG_DIRECTORY32.make_struct
	#	header.from_s(rawdata)
	#	ConfigHeader.new(header)
	#end

	def _parse_config_header

		#
		# Get the data directory entry, size, etc
		#
		exports_entry = _optional_header['DataDirectory'][IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG]
		rva           = exports_entry.v['VirtualAddress']
		size          = exports_entry.v['Size']

		return nil if size == 0

		#
		# Ok, so we have the data directory, now lets parse it
		#

		dirdata = _isource.read(rva_to_file_offset(rva), size)
		klass   = (ptr_64?) ? IMAGE_LOAD_CONFIG_DIRECTORY64 : IMAGE_LOAD_CONFIG_DIRECTORY32
		header  = klass.make_struct

		header.from_s(dirdata)

		@config = ConfigHeader.new(header)
	end


	def config
		_parse_config_header if @config.nil?
		@config
	end

	#
	# TLS Directory
	#

	#
	# typedef struct {
	#     DWORD   Size;
	#     DWORD   TimeDateStamp;
	#     WORD    MajorVersion;
	#     WORD    MinorVersion;
	#     DWORD   GlobalFlagsClear;
	#     DWORD   GlobalFlagsSet;
	#     DWORD   CriticalSectionDefaultTimeout;
	#     DWORD   DeCommitFreeBlockThreshold;
	#     DWORD   DeCommitTotalFreeThreshold;
	#     DWORD   LockPrefixTable;            // VA
	#     DWORD   MaximumAllocationSize;
	#     DWORD   VirtualMemoryThreshold;
	#     DWORD   ProcessHeapFlags;
	#     DWORD   ProcessAffinityMask;
	#     WORD    CSDVersion;
	#     WORD    Reserved1;
	#     DWORD   EditList;                   // VA
	#     DWORD   SecurityCookie;             // VA
	#     DWORD   SEHandlerTable;             // VA
	#     DWORD   SEHandlerCount;
	# } IMAGE_LOAD_CONFIG_DIRECTORY32, *PIMAGE_LOAD_CONFIG_DIRECTORY32;
	#
	IMAGE_LOAD_TLS_DIRECTORY32 = Rex::Struct2::CStructTemplate.new(
	  [ 'uint32v', 'Size',                          0 ],
	  [ 'uint32v', 'TimeDateStamp',                 0 ],
	  [ 'uint16v', 'MajorVersion',                  0 ],
	  [ 'uint16v', 'MinorVersion',                  0 ],
	  [ 'uint32v', 'GlobalFlagsClear',              0 ],
	  [ 'uint32v', 'GlobalFlagsSet',                0 ],
	  [ 'uint32v', 'CriticalSectionDefaultTimeout', 0 ],
	  [ 'uint32v', 'DeCommitFreeBlockThreshold',    0 ],
	  [ 'uint32v', 'DeCommitTotalFreeThreshold',    0 ],
	  [ 'uint32v', 'LockPrefixTable',               0 ],
	  [ 'uint32v', 'MaximumAllocationSize',         0 ],
	  [ 'uint32v', 'VirtualMemoryThreshold',        0 ],
	  [ 'uint32v', 'ProcessHeapFlags',              0 ],
	  [ 'uint32v', 'ProcessAffinityMask',           0 ],
	  [ 'uint16v', 'CSDVersion',                    0 ],
	  [ 'uint16v', 'Reserved1',                     0 ],
	  [ 'uint32v', 'EditList',                      0 ],
	  [ 'uint32v', 'SecurityCookie',                0 ],
	  [ 'uint32v', 'SEHandlerTable',                0 ],
	  [ 'uint32v', 'SEHandlerCount',                0 ]
	)

	#
	# typedef struct {
	# 	 ULONG      Size;
	# 	 ULONG      TimeDateStamp;
	# 	 USHORT     MajorVersion;
	# 	 USHORT     MinorVersion;
	# 	 ULONG      GlobalFlagsClear;
	# 	 ULONG      GlobalFlagsSet;
	# 	 ULONG      CriticalSectionDefaultTimeout;
	# 	 ULONGLONG  DeCommitFreeBlockThreshold;
	# 	 ULONGLONG  DeCommitTotalFreeThreshold;
	# 	 ULONGLONG  LockPrefixTable;         // VA
	# 	 ULONGLONG  MaximumAllocationSize;
	# 	 ULONGLONG  VirtualMemoryThreshold;
	# 	 ULONGLONG  ProcessAffinityMask;
	# 	 ULONG      ProcessHeapFlags;
	# 	 USHORT     CSDVersion;
	# 	 USHORT     Reserved1;
	# 	 ULONGLONG  EditList;                // VA
	# 	 ULONGLONG  SecurityCookie;          // VA
	# 	 ULONGLONG  SEHandlerTable;          // VA
	# 	 ULONGLONG  SEHandlerCount;
	# } IMAGE_LOAD_CONFIG_DIRECTORY64, *PIMAGE_LOAD_CONFIG_DIRECTORY64;
	#
	IMAGE_LOAD_TLS_DIRECTORY64 = Rex::Struct2::CStructTemplate.new(
	  [ 'uint32v', 'Size',                          0 ],
	  [ 'uint32v', 'TimeDateStamp',                 0 ],
	  [ 'uint16v', 'MajorVersion',                  0 ],
	  [ 'uint16v', 'MinorVersion',                  0 ],
	  [ 'uint32v', 'GlobalFlagsClear',              0 ],
	  [ 'uint32v', 'GlobalFlagsSet',                0 ],
	  [ 'uint32v', 'CriticalSectionDefaultTimeout', 0 ],
	  [ 'uint64v', 'DeCommitFreeBlockThreshold',    0 ],
	  [ 'uint64v', 'DeCommitTotalFreeThreshold',    0 ],
	  [ 'uint64v', 'LockPrefixTable',               0 ],
	  [ 'uint64v', 'MaximumAllocationSize',         0 ],
	  [ 'uint64v', 'VirtualMemoryThreshold',        0 ],
	  [ 'uint64v', 'ProcessAffinityMask',           0 ],
	  [ 'uint32v', 'ProcessHeapFlags',              0 ],
	  [ 'uint16v', 'CSDVersion',                    0 ],
	  [ 'uint16v', 'Reserved1',                     0 ],
	  [ 'uint64v', 'EditList',                      0 ],
	  [ 'uint64v', 'SecurityCookie',                0 ],
	  [ 'uint64v', 'SEHandlerTable',                0 ],
	  [ 'uint64v', 'SEHandlerCount',                0 ]
	)


	class TLSHeader < GenericHeader

	end

	def _parse_tls_header

		#
		# Get the data directory entry, size, etc
		#
		exports_entry = _optional_header['DataDirectory'][IMAGE_DIRECTORY_ENTRY_TLS]
		rva           = exports_entry.v['VirtualAddress']
		size          = exports_entry.v['Size']

		return nil if size == 0

		#
		# Ok, so we have the data directory, now lets parse it
		#

		dirdata = _isource.read(rva_to_file_offset(rva), size)
		klass   = (ptr_64?) ? IMAGE_LOAD_TLS_DIRECTORY64 : IMAGE_LOAD_TLS_DIRECTORY32
		header  = klass.make_struct

		header.from_s(dirdata)

		@tls = TLSHeader.new(header)
	end


	def tls
		_parse_config_header if @tls.nil?
		@tls
	end

	##
	#
	# Exception directory
	#
	##

	#
	# typedef struct _IMAGE_RUNTIME_FUNCTION_ENTRY {
	#     DWORD BeginAddress;
	#     DWORD EndAddress;
	#     DWORD UnwindInfoAddress;
	# } _IMAGE_RUNTIME_FUNCTION_ENTRY, *_PIMAGE_RUNTIME_FUNCTION_ENTRY;
	#
	IMAGE_RUNTIME_FUNCTION_ENTRY_SZ = 12
	IMAGE_RUNTIME_FUNCTION_ENTRY = Rex::Struct2::CStructTemplate.new(
	  [ 'uint32v', 'BeginAddress',                  0 ],
	  [ 'uint32v', 'EndAddress',                    0 ],
	  [ 'uint32v', 'UnwindInfoAddress',             0 ]
	)

	UNWIND_INFO_HEADER_SZ = 4
	UNWIND_INFO_HEADER = Rex::Struct2::CStructTemplate.new(
	  [ 'uint8', 'VersionFlags',                  0 ],
	  [ 'uint8', 'SizeOfProlog',                  0 ],
	  [ 'uint8', 'CountOfCodes',                  0 ],
	  [ 'uint8', 'FrameRegisterAndOffset',        0 ]
	)

	UWOP_PUSH_NONVOL     = 0  # 1 node
	UWOP_ALLOC_LARGE     = 1  # 2 or 3 nodes
	UWOP_ALLOC_SMALL     = 2  # 1 node
	UWOP_SET_FPREG       = 3  # 1 node
	UWOP_SAVE_NONVOL     = 4  # 2 nodes
	UWOP_SAVE_NONVOL_FAR = 5  # 3 nodes
	UWOP_SAVE_XMM128     = 8  # 2 nodes
	UWOP_SAVE_XMM128_FAR = 9  # 3 nodes
	UWOP_PUSH_MACHFRAME  = 10 # 1 node

	UNW_FLAG_EHANDLER  = 1
	UNW_FLAG_UHANDLER  = 2
	UNW_FLAG_CHAININFO = 4

	class UnwindCode
		def initialize(data)

			self.code_offset  = data[0].to_i
			self.unwind_op    = data[1].to_i & 0xf
			self.op_info      = data[1].to_i >> 4
			self.frame_offset = data[2..3].unpack("v")[0]

			data.slice!(0, 4)
		end

		attr_reader :code_offset, :unwind_op, :op_info, :frame_offset
		attr_writer :code_offset, :unwind_op, :op_info, :frame_offset

	end

	class UnwindInfo
		def initialize(pe, unwind_rva)
			data = pe.read_rva(unwind_rva, UNWIND_INFO_HEADER_SZ)
		
			unwind  = UNWIND_INFO_HEADER.make_struct
			unwind.from_s(data)

			@version               = unwind.v['VersionFlags'] & 0x7
			@flags                 = unwind.v['VersionFlags'] >> 3
			@size_of_prolog        = unwind.v['SizeOfProlog']
			@count_of_codes        = unwind.v['CountOfCodes']
			@frame_register        = unwind.v['FrameRegisterAndOffset'] & 0xf
			@frame_register_offset = unwind.v['FrameRegisterAndOffset'] >> 4

			# Parse unwind codes
			clist = pe.read_rva(unwind_rva + UNWIND_INFO_HEADER_SZ, count_of_codes * 4)

			@unwind_codes = []

			while clist.length > 0
				@unwind_codes << UnwindCode.new(clist)
			end
		end

		attr_reader :version, :flags, :size_of_prolog, :count_of_codes
		attr_reader :frame_register, :frame_register_offset

		def unwind_codes
			@unwind_codes
		end

	end

	class RuntimeFunctionEntry

		def initialize(pe, data)
			@pe = pe
			@begin_address, @end_address, @unwind_info_address = data.unpack("VVV");
			self.unwind_info = UnwindInfo.new(pe, unwind_info_address)
		end

		attr_reader :begin_address, :end_address, :unwind_info_address
		attr_reader :unwind_info
		attr_writer :unwind_info

	end

	def _load_exception_directory
		@exception   = []
		
		exception_entry = _optional_header['DataDirectory'][IMAGE_DIRECTORY_ENTRY_EXCEPTION]
		rva             = exception_entry.v['VirtualAddress']
		size            = exception_entry.v['Size']
		
		return if (rva == 0)
		
		data = _isource.read(rva_to_file_offset(rva), size)
		
		case hdr.file.Machine
			when IMAGE_FILE_MACHINE_AMD64
				count = data.length / IMAGE_RUNTIME_FUNCTION_ENTRY_SZ
				
				count.times { |current|
					@exception << RuntimeFunctionEntry.new(self,
						data.slice!(0, IMAGE_RUNTIME_FUNCTION_ENTRY_SZ))
				}
			else
		end
		
		return @exception
	end


	def exception
		_load_exception_directory if @exception.nil?
		@exception
	end

	#
	# Just a stupid routine to round an offset up to it's alignment.
	#
	# For example, you're going to want this for FileAlignment and
	# SectionAlignment, etc...
	#
	def self._align_offset(offset, alignment)
		offset += alignment - 1
		offset -= offset % alignment
		return offset
	end

	#
	# instance stuff
	#

	attr_accessor :_isource
	attr_accessor :_dos_header, :_file_header, :_optional_header,
	              :_section_headers, :_config_header, :_tls_header, :_exception_header

	attr_accessor :sections, :header_section, :image_base

	attr_accessor :_imports_cache, :_imports_cached
	attr_accessor :_exports_cache, :_exports_cached
	attr_accessor :_relocations_cache, :_relocations_cached
	attr_accessor :_resources_cache, :_resources_cached

	attr_accessor :hdr

	def self.new_from_file(filename, disk_backed = false)

		file = ::File.new(filename)
		file.binmode # windows... :\

		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 close
		_isource.close
	end

	#
	#
	# Random rva, vma, file offset, section offset, etc
	# conversion routines...
	#
	#
	def rva_to_vma(rva)
		return rva + image_base
	end

	def vma_to_rva(vma)
		return vma - image_base
	end

	def rva_to_file_offset(rva)
		all_sections.each do |section|
			if section.contains_rva?(rva)
				return section.rva_to_file_offset(rva)
			end
		end
		raise WtfError, "wtf!", caller
	end

	def vma_to_file_offset(vma)
		return rva_to_file_offset(vma_to_rva(vma))
	end

	def file_offset_to_rva(foffset)
		if foffset < 0
			raise WtfError, "lame", caller
		end

		all_sections.each do |section|
			if section.contains_file_offset?(foffset)
				return section.file_offset_to_rva(foffset)
			end
		end

		raise WtfError, "wtf! #{foffset}", caller
	end

	def file_offset_to_vma(foffset)
		return rva_to_vma(file_offset_to_rva(foffset))
	end

	#
	#
	# Some routines to find which section something belongs
	# to.  These will search all_sections (so including
	# our fake header section, etc...
	#
	#

	#
	# Find a section by an RVA
	#
	def _find_section_by_rva(rva)
		all_sections.each do |section|
			if section.contains_rva?(rva)
				return section
			end
		end

		return nil
	end
	def find_section_by_rva(rva)
		section = _find_section_by_rva(rva)

		if !section
			raise WtfError, "Cannot find rva! #{rva}", caller
		end

		return section
	end

	#
	# Find a section by a VMA
	#
	def find_section_by_vma(vma)
		return find_section_by_rva(vma_to_rva(vma))
	end

	def valid_rva?(rva)
		_find_section_by_rva(rva) != nil
	end
	def valid_vma?(vma)
		_find_section_by_rva(vma_to_rva(vma)) != nil
	end

	#
	#
	# Some convenient methods to read a vma/rva without having
	# the section... (inefficent though I suppose...)
	#
	#

	def read_rva(rva, length)
		return find_section_by_rva(rva).read_rva(rva, length)
	end

	def read_vma(vma, length)
		return read_rva(vma_to_rva(vma), length)
	end

	def read_asciiz_rva(rva)
		return find_section_by_rva(rva).read_asciiz_rva(rva)
	end

	def read_asciiz_vma(vma)
		return read_asciiz_rva(vma_to_rva(vma))
	end

	#
	#
	# Imports, exports, and other stuff!
	#
	#

	#
	# We lazily parse the imports, and then cache it
	#
	def imports
		if !_imports_cached
			self._imports_cache  = _load_imports
			self._imports_cached = true
		end
		return _imports_cache
	end

	def _load_imports
		#
		# Get the data directory entry, size, etc
		#
		imports_entry = _optional_header['DataDirectory'][1]
		rva           = imports_entry.v['VirtualAddress']
		size          = imports_entry.v['Size']

		return nil if size == 0

		#
		# Ok, so we have the data directory, now lets parse it
		#

		imports = [ ]

		descriptors_data = _isource.read(rva_to_file_offset(rva), size)

		while descriptors_data.length >= IMAGE_IMPORT_DESCRIPTOR_SIZE
			descriptor = IMAGE_IMPORT_DESCRIPTOR.make_struct
			descriptor.from_s(descriptors_data)
			descriptors_data = descriptor.leftover

			othunk = descriptor.v['OriginalFirstThunk']
			fthunk = descriptor.v['FirstThunk']

			break if fthunk == 0

			dllname = _isource.read_asciiz(rva_to_file_offset(descriptor.v['Name']))

			import = ImportDescriptor.new(dllname, [ ])

			# we prefer the Characteristics/OriginalFirstThunk...
			thunk_off = rva_to_file_offset(othunk == 0 ? fthunk : othunk)

			while (orgrva = _isource.read(thunk_off, 4).unpack('V')[0]) != 0
				hint = nil
				name = nil

				if (orgrva & IMAGE_ORDINAL_FLAG32) != 0
					hint = orgrva & 0xffff
				else
					foff = rva_to_file_offset(orgrva)
					hint = _isource.read(foff, 2).unpack('v')[0]
					name = _isource.read_asciiz(foff + 2)
				end

				import.entries << ImportEntry.new(name, hint)

				thunk_off += 4
			end

			imports << import
		end

		return imports
	end



	#
	# We lazily parse the exports, and then cache it
	#
	def exports
		if !_exports_cached
			self._exports_cache  = _load_exports
			self._exports_cached = true
		end
		return _exports_cache
	end

	def _load_exports

		#
		# Get the data directory entry, size, etc
		#
		exports_entry = _optional_header['DataDirectory'][0]
		rva           = exports_entry.v['VirtualAddress']
		size          = exports_entry.v['Size']

		return nil if size == 0

		#
		# Ok, so we have the data directory, now lets parse it
		#

		directory = IMAGE_EXPORT_DESCRIPTOR.make_struct
		directory.from_s(_isource.read(rva_to_file_offset(rva), IMAGE_EXPORT_DESCRIPTOR_SIZE))

		#
		# We can have nameless exports, so we need to do the whole
		# NumberOfFunctions NumberOfNames foo
		#
		num_functions = directory.v['NumberOfFunctions']
		num_names     = directory.v['NumberOfNames']

		dllname_rva   = directory.v['Name']
		dllname       = _isource.read_asciiz(rva_to_file_offset(dllname_rva))

		# FIXME Base, etc
		fun_off       = rva_to_file_offset(directory.v['AddressOfFunctions'])
		name_off      = rva_to_file_offset(directory.v['AddressOfNames'])
		ord_off       = rva_to_file_offset(directory.v['AddressOfNameOrdinals'])
		base          = directory.v['Base']

		# Allocate the list of names
		names = Array.new(num_functions)

		#
		# Iterate the names and name/ordinal list, getting the names
		# and storing them in the name list...
		#
		num_names.times do |i|
			name_rva = _isource.read(name_off + (i * 4), 4).unpack('V')[0]
			ordinal  = _isource.read(ord_off + (i * 2), 2).unpack('v')[0]
			name     = _isource.read_asciiz(rva_to_file_offset(name_rva))

			# store the exported name in the name list
			names[ordinal] = name
		end

		exports = ExportDirectory.new(dllname, [ ], base)

		#
		# Now just iterate the functions (rvas) list..
		#
		num_functions.times do |i|
			rva      = _isource.read(fun_off + (i * 4), 4).unpack('V')[0]

			# ExportEntry.new(name, ordinal, rva)
			exports.entries << ExportEntry.new(names[i], i + base, rva)
		end

		return exports
	end

	#
	# Base relocations in the hizzy
	#
	def relocations
		if !_relocations_cached
			self._relocations_cache  = _load_relocations
			self._relocations_cached = true
		end
		return _relocations_cache
	end

	def _load_relocations

		#
		# Get the data directory entry, size, etc
		#
		exports_entry = _optional_header['DataDirectory'][5]
		rva           = exports_entry.v['VirtualAddress']
		size          = exports_entry.v['Size']

		return nil if size == 0

		#
		# Ok, so we have the data directory, now lets parse it
		#

		dirdata = _isource.read(rva_to_file_offset(rva), size)

		relocdirs = [ ]

		while dirdata.length >= IMAGE_SIZEOF_BASE_RELOCATION
			header = IMAGE_BASE_RELOCATION.make_struct
			header.from_s(dirdata)
			dirdata = header.leftover

			numrelocs = (header.v['SizeOfBlock'] - IMAGE_SIZEOF_BASE_RELOCATION) / 2

			relocbase = header.v['VirtualAddress']

			relocdir = RelocationDirectory.new(relocbase, [ ])

			numrelocs.times do
				reloc = IMAGE_BASE_RELOCATION_TYPE_OFFSET.make_struct
				reloc.from_s(dirdata)
				dirdata = reloc.leftover

				typeoffset = reloc.v['TypeOffset']

				relocrva  = relocbase + (typeoffset & 0xfff)
				reloctype = (typeoffset >> 12) & 0xf

				relocdir.entries << RelocationEntry.new(relocrva, reloctype)
			end

			relocdirs << relocdir
		end

		return relocdirs
	end


	#
	# We lazily parse the resources, and then cache them
	#
	def resources
		if !_resources_cached
			_load_resources
			self._resources_cached = true
		end

		return self._resources_cache
	end

	def _load_resources
		#
		# Get the data directory entry, size, etc
		#
		rsrc_entry = _optional_header['DataDirectory'][IMAGE_DIRECTORY_ENTRY_RESOURCE]
		rva        = rsrc_entry.v['VirtualAddress']
		size       = rsrc_entry.v['Size']

		return nil if size == 0

		#
		# Ok, so we have the data directory, now lets parse it
		#
		data = _isource.read(rva_to_file_offset(rva), size)

		self._resources_cache = {}
		_parse_resource_directory(data)
	end

	def _parse_resource_directory(data, rname=0, rvalue=0x80000000, path='0', pname=nil)

		pname = _parse_resource_name(data, rname)
		if (path.scan('/').length == 1)
			if (pname !~ /^\d+/)
				path = "/" + pname
			else
				path = "/" + _resource_lookup( (rname & ~0x80000000).to_s)
			end
		end


		rvalue &= ~0x80000000
		vals  = data[rvalue, 16].unpack('VVvvvv')

		chars = vals[0]
		tdate = vals[1]
		vers  = "#{vals[2]}#{vals[3]}"
		count = vals[4] + vals[5]

		0.upto(count-1) do |i|

			ename, evalue = data[rvalue + 16 + ( i * 8), 8].unpack('VV')
			epath = path + '/' + i.to_s

			if (ename & 0x80000000 != 0)
				pname = _parse_resource_name(data, ename)
			end

			if (evalue & 0x80000000 != 0)
				# This is a subdirectory
				_parse_resource_directory(data, ename, evalue, epath, pname)
			else
				# This is an entry
				_parse_resource_entry(data, ename, evalue, epath, pname)
			end
		end

	end

	def _resource_lookup(i)
		tbl = {
			'1'      => 'RT_CURSOR',
			'2'      => 'RT_BITMAP',
			'3'      => 'RT_ICON',
			'4'      => 'RT_MENU',
			'5'      => 'RT_DIALOG',
			'6'      => 'RT_STRING',
			'7'      => 'RT_FONTDIR',
			'8'      => 'RT_FONT',
			'9'      => 'RT_ACCELERATORS',
			'10'     => 'RT_RCDATA',
			'11'     => 'RT_MESSAGETABLE',
			'12'     => 'RT_GROUP_CURSOR',
			'14'     => 'RT_GROUP_ICON',
			'16'     => 'RT_VERSION',
			'17'     => 'RT_DLGINCLUDE',
			'19'     => 'RT_PLUGPLAY',
			'20'     => 'RT_VXD',
			'21'     => 'RT_ANICURSOR',
			'22'     => 'RT_ANIICON',
			'23'     => 'RT_HTML',
			'24'     => 'RT_MANIFEST',
			'32767'  => 'RT_ERROR',
			'8192'   => 'RT_NEWRESOURCE',
			'8194'   => 'RT_NEWBITMAP',
			'8196'   => 'RT_NEWMENU',
			'8197'   => 'RT_NEWDIALOG'
		}
		tbl[i] || i
	end

	def _parse_resource_entry(data, rname, rvalue, path, pname)

		rva, size, code = data[rvalue, 12].unpack('VVV')
		lang = _parse_resource_name(data, rname)

		ent = ResourceEntry.new(
			self,
			path,
			lang,
			code,
			rva,
			size,
			pname
		)
		self._resources_cache[path] = ent
	end

	def _parse_resource_name(data, rname)
		if (rname & 0x80000000 != 0)
			rname &= ~0x80000000
			unistr = data[rname+2, 2 * data[rname,2].unpack('v')[0] ]
			unistr, trash = unistr.split(/\x00\x00/, 2)
			return unistr ? unistr.gsub(/\x00/, '') : nil
		end

		rname.to_s
	end

end end end

