212 lines
5.5 KiB
Ruby
212 lines
5.5 KiB
Ruby
#!/usr/bin/env ruby
|
|
# -*- coding: binary -*-
|
|
|
|
require 'rex/image_source'
|
|
require 'rex/peparsey/exceptions'
|
|
require 'rex/peparsey/pebase'
|
|
require 'rex/peparsey/section'
|
|
require 'rex/struct2'
|
|
|
|
module Rex
|
|
module PeParsey
|
|
class Pe < PeBase
|
|
|
|
def initialize(isource)
|
|
|
|
#
|
|
# DOS Header
|
|
#
|
|
# Parse the initial dos header, starting at the file beginning
|
|
#
|
|
offset = 0
|
|
dos_header = self.class._parse_dos_header(isource.read(offset, IMAGE_DOS_HEADER_SIZE))
|
|
|
|
#
|
|
# File Header
|
|
#
|
|
# If there is going to be a PE, the dos header tells us where to find it
|
|
# So now we try to parse the file (pe) header
|
|
#
|
|
offset += dos_header.e_lfanew
|
|
|
|
# most likely an invalid e_lfanew...
|
|
if offset > isource.size
|
|
raise FileHeaderError, "e_lfanew looks invalid", caller
|
|
end
|
|
|
|
file_header = self.class._parse_file_header(isource.read(offset, IMAGE_FILE_HEADER_SIZE))
|
|
|
|
#
|
|
# Optional Header
|
|
#
|
|
# After the file header, we find the optional header. Right now
|
|
# we require a optional header. Despite it's name, all binaries
|
|
# that we are interested in should have one. We need this
|
|
# header for a lot of stuff, so we die without it...
|
|
#
|
|
offset += IMAGE_FILE_HEADER_SIZE
|
|
optional_header = self.class._parse_optional_header(
|
|
isource.read(offset, file_header.SizeOfOptionalHeader)
|
|
)
|
|
|
|
if !optional_header
|
|
raise OptionalHeaderError, "No optional header!", caller
|
|
end
|
|
|
|
base = optional_header.ImageBase
|
|
|
|
#
|
|
# Section Headers
|
|
#
|
|
# After the optional header should be the section headers.
|
|
# We know how many there should be from the file header...
|
|
#
|
|
offset += file_header.SizeOfOptionalHeader
|
|
|
|
num_sections = file_header.NumberOfSections
|
|
section_headers = self.class._parse_section_headers(
|
|
isource.read(offset, IMAGE_SIZEOF_SECTION_HEADER * num_sections)
|
|
)
|
|
|
|
#
|
|
# End of Headers
|
|
#
|
|
# After the section headers (which are padded to FileAlignment)
|
|
# we should find the section data, described by the section
|
|
# headers...
|
|
#
|
|
# So this is the end of our header data, lets store this
|
|
# in an image source for possible access later...
|
|
#
|
|
offset += IMAGE_SIZEOF_SECTION_HEADER * num_sections
|
|
offset = self.class._align_offset(offset, optional_header.FileAlignment)
|
|
|
|
header_section = Section.new(isource.subsource(0, offset), 0, nil)
|
|
|
|
#
|
|
# Sections
|
|
#
|
|
# So from here on out should be section data, and then any
|
|
# trailing data (like authenticode and stuff I think)
|
|
#
|
|
|
|
sections = [ ]
|
|
|
|
section_headers.each do |section_header|
|
|
|
|
rva = section_header.VirtualAddress
|
|
size = section_header.SizeOfRawData
|
|
file_offset = section_header.PointerToRawData
|
|
|
|
sections << Section.new(
|
|
isource.subsource(file_offset, size),
|
|
rva,
|
|
section_header
|
|
)
|
|
end
|
|
|
|
|
|
|
|
#
|
|
# Save the stuffs!
|
|
#
|
|
# We have parsed enough to load the file up here, now we just
|
|
# save off all of the structures and data... We will
|
|
# save our fake header section, the real sections, etc.
|
|
#
|
|
|
|
#
|
|
# These should not be accessed directly
|
|
#
|
|
|
|
self._isource = isource
|
|
|
|
self._dos_header = dos_header
|
|
self._file_header = file_header
|
|
self._optional_header = optional_header
|
|
self._section_headers = section_headers
|
|
|
|
self.image_base = base
|
|
self.sections = sections
|
|
self.header_section = header_section
|
|
|
|
self._config_header = _parse_config_header()
|
|
self._tls_header = _parse_tls_header()
|
|
|
|
# These can be accessed directly
|
|
self.hdr = HeaderAccessor.new
|
|
self.hdr.dos = self._dos_header
|
|
self.hdr.file = self._file_header
|
|
self.hdr.opt = self._optional_header
|
|
self.hdr.sections = self._section_headers
|
|
self.hdr.config = self._config_header
|
|
self.hdr.tls = self._tls_header
|
|
self.hdr.exceptions = self._exception_header
|
|
|
|
# We load the exception directory last as it relies on hdr.file to be created above.
|
|
self._exception_header = _load_exception_directory()
|
|
end
|
|
|
|
#
|
|
# Return everything that's going to be mapped in the process
|
|
# and accessable. This should include all of the sections
|
|
# and our "fake" section for the header data...
|
|
#
|
|
def all_sections
|
|
[ header_section ] + sections
|
|
end
|
|
|
|
#
|
|
# Returns true if this binary is for a 64-bit architecture.
|
|
#
|
|
def ptr_64?
|
|
[
|
|
IMAGE_FILE_MACHINE_IA64,
|
|
IMAGE_FILE_MACHINE_ALPHA64,
|
|
IMAGE_FILE_MACHINE_AMD64
|
|
].include?(self._file_header.Machine)
|
|
end
|
|
|
|
#
|
|
# Returns true if this binary is for a 32-bit architecture.
|
|
# This check does not take into account 16-bit binaries at the moment.
|
|
#
|
|
def ptr_32?
|
|
ptr_64? == false
|
|
end
|
|
|
|
#
|
|
# Converts a virtual address to a string representation based on the
|
|
# underlying architecture.
|
|
#
|
|
def ptr_s(va)
|
|
(ptr_32?) ? ("0x%.8x" % va) : ("0x%.16x" % va)
|
|
end
|
|
|
|
#
|
|
# Converts a file offset into a virtual address
|
|
#
|
|
def file_offset_to_va(offset)
|
|
image_base + file_offset_to_rva(offset)
|
|
end
|
|
|
|
#
|
|
# Read raw bytes from the specified offset in the underlying file
|
|
#
|
|
# NOTE: You should pass raw file offsets into this, not offsets from
|
|
# the beginning of the section. If you need to read from within a
|
|
# section, add section.file_offset prior to passing the offset in.
|
|
#
|
|
def read(offset, len)
|
|
_isource.read(offset, len)
|
|
end
|
|
|
|
def size
|
|
_isource.size
|
|
end
|
|
def length
|
|
_isource.size
|
|
end
|
|
|
|
end end end
|