class Metasm::LoadedPE

an instance of a PE file, loaded in memory just change the rva_to_off and the section content decoding methods

Attributes

load_address[RW]

Public Class Methods

memdump(memory, baseaddr, entrypoint=nil, iat_p=nil) click to toggle source

reads a loaded PE from memory, returns a PE object dumps the header, optheader and all sections ; try to rebuild IAT (memdump_imports)

# File metasm/exe_format/pe.rb, line 374
def self.memdump(memory, baseaddr, entrypoint=nil, iat_p=nil)
        loaded = LoadedPE.load memory[baseaddr, 0x1000_0000]
        loaded.load_address = baseaddr
        loaded.decode

        dump = PE.new(loaded.cpu_from_headers)
        dump.share_namespace loaded
        dump.optheader.image_base = baseaddr
        dump.optheader.entrypoint = (entrypoint || loaded.optheader.entrypoint + baseaddr) - baseaddr
        dump.directory['resource_table'] = loaded.directory['resource_table']

        loaded.sections.each { |s|
                ss = Section.new
                ss.name = s.name
                ss.virtaddr = s.virtaddr
                ss.encoded = s.encoded
                ss.characteristics = s.characteristics
                dump.sections << ss
        }

        loaded.memdump_imports(memory, dump, iat_p)

        dump
end

Public Instance Methods

decode_relocs() click to toggle source

no need to decode relocations on an already mapped image

# File metasm/exe_format/pe.rb, line 369
def decode_relocs
end
decode_section_body(s) click to toggle source

use the virtualaddr/virtualsize fields of the section header

# File metasm/exe_format/pe.rb, line 364
def decode_section_body(s)
        s.encoded = @encoded[s.virtaddr, s.virtsize] || EncodedData.new
end
memdump_imports(memory, dump, unk_iat_p=nil) click to toggle source

rebuilds an IAT from the loaded pe and the memory

for each loaded iat, find the matching dll in memory
for each loaded iat entry, retrieve the exported name from the loaded dll

OR

from a base iat address in memory (unk_iat_p, rva), retrieve the 1st dll, find
all iat pointers/forwarders to this dll, on failure try to find another dll
allows gaps of 5 invalid pointers between libraries

dll found by scanning pages 16 by 16 backward from the first iat address (XXX the 1st must not be forwarded) TODO bound imports

# File metasm/exe_format/pe.rb, line 408
def memdump_imports(memory, dump, unk_iat_p=nil)
        puts 'rebuilding imports...' if $VERBOSE
        if unk_iat_p
                # read iat data from unk_iat_p
                iat_p = unk_iat_p
        else
                return if not imports
                # read iat data from @imports
                imports = @imports.dup
                imports.each { |id| id.iat = id.iat.dup }
                iat_p = imports.first.iat_p  # used for iat_p
        end

        failcnt = 0           # bad pointers in iat table (unk_ only)
        dump.imports ||= []
        loaded_dll = nil      # the dll from who we're importing the current importdirectory
        ptrsz = (@optheader.signature == 'PE+' ? 8 : 4)
        cache = []    # optimize forwarder target search
        loop do
                if unk_iat_p
                        # read imported pointer from the table
                        ptr = decode_xword(EncodedData.new(memory[@load_address + iat_p, ptrsz]))
                        iat_p += ptrsz
                else
                        # read imported pointer from the import structure
                        while not ptr = imports.first.iat.shift
                                imports.shift
                                break if imports.empty?
                                iat_p = imports.first.iat_p
                        end
                        break if imports.empty?
                        iat_p += ptrsz
                end

                if not loaded_dll or not e = loaded_dll.export.exports.find { |e_| loaded_dll.label_rva(e_.target) == ptr - loaded_dll.load_address }
                        # points to unknown space
                        # find pointed module start
                        if not dll = cache.find { |dll_| ptr >= dll_.load_address and ptr < dll_.load_address + dll_.optheader.image_size }
                                addr = ptr & ~0xffff
                                256.times { break if memory[addr, 2] == MZ::MAGIC or addr < 0x10000 ; addr -= 0x10000 }
                                if memory[addr, 2] == MZ::MAGIC
                                        dll = LoadedPE.load memory[addr, 0x1000_0000]
                                        dll.load_address = addr
                                        dll.decode_header
                                        dll.decode_exports
                                        cache << dll
                                end
                        end
                        if dll and dll.export and e = dll.export.exports.find { |e_| dll.label_rva(e_.target) == ptr - dll.load_address }
                                if loaded_dll and ee = loaded_dll.export.exports.find { |ee_| ee_.forwarder_name == e.name }
                                        # it's a forwarder from the current loaded_dll
                                        puts "forwarder #{ee.name} -> #{dll.export.libname}!#{e.name}" if $DEBUG
                                        e = ee
                                else
                                        # new library, start a new importdirectory
                                        # XXX if 1st import is forwarded, loaded_dll will points to the bad module...
                                        loaded_dll = dll
                                        id = ImportDirectory.new
                                        id.libname = loaded_dll.export.libname
                                        puts "lib #{id.libname}" if $VERBOSE
                                        id.imports = []
                                        id.iat_p = iat_p - ptrsz
                                        dump.imports << id
                                end
                        else
                                puts 'unknown ptr %x' % ptr if $DEBUG
                                # allow holes in the unk_iat_p table
                                break if not unk_iat_p or failcnt > 4
                                loaded_dll = nil
                                failcnt += 1
                                next
                        end
                        failcnt = 0
                end

                # dumped last importdirectory is correct, append the import field
                i = ImportDirectory::Import.new
                if e.name
                        puts e.name if $DEBUG
                        i.name = e.name
                else
                        puts "##{e.ordinal}" if $DEBUG
                        i.ordinal = e.ordinal
                end
                dump.imports.last.imports << i
        end
end
pehash(digest) click to toggle source
# File metasm/exe_format/pe.rb, line 496
def pehash(digest)
        raise "cannot compute a PEhash from memory image"
end