an instance of a PE file, loaded in memory just change the rva_to_off and the section content decoding methods
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
no need to decode relocations on an already mapped image
# File metasm/exe_format/pe.rb, line 369 def decode_relocs end
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
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
# File metasm/exe_format/pe.rb, line 496 def pehash(digest) raise "cannot compute a PEhash from memory image" end