metasploit-framework/lib/rex/peparsey/pebase.rb

987 lines
27 KiB
Ruby

#!/usr/bin/env ruby
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 GenericHeader
attr_accessor :struct
def initialize(_struct)
self.struct = _struct
end
# this sucks...
def v
struct.v
end
def [](*args)
struct[*args]
end
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_SIZEOF_FILE_HEADER 20
#
IMAGE_NT_SIGNATURE = 0x00004550
IMAGE_FILE_MACHINE_I386 = 0x014c
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 ]
)
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 file_header.v['Machine'] != IMAGE_FILE_MACHINE_I386
raise FileHeaderError, "I only understand i386 images, not #{file_header.v['Machine']}", caller
end
self.struct = file_header
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_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 ]
)]
)
class OptionalHeader < GenericHeader
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
def ImageBase
v['ImageBase']
end
def FileAlignment
v['FileAlignment']
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
# bad, bad
else
raise OptionalHeaderError, "I don't know this header size, #{rawdata.length}", caller
end
return OptionalHeader.new(rawdata)
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 ]
)
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
end
end
class RelocationEntry
attr_accessor :rva, :reltype
def initialize(_rva, _type)
self.rva = _rva
self.reltype = _type
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 ],
[ '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 ]
)
#
# 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
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 :_loadconfig_cache, :_loadconfig_cached
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 convient 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 perfer 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
def loadconfig
if !_loadconfig_cached
self._loadconfig_cache = _load_loadconfig
self._loadconfig_cached = true
end
return _loadconfig_cache
end
def _load_loadconfig
#
# Get the data directory entry, size, etc
#
exports_entry = _optional_header['DataDirectory'][10]
rva = exports_entry.v['VirtualAddress']
size = exports_entry.v['Size']
return nil if size == 0
return size
end
end end end