module Rex module PeScan module Analyze require "rex/ui/text/table" class Fingerprint attr_accessor :pe def initialize(pe) self.pe = pe end def config(param) @sigs = {} name = nil regx = '' epon = 0 sidx = 0 fd = File.open(param['database'], 'rb') fd.each_line do |line| case line when /^\s*#/ next when /\[\s*(.*)\s*\]/ if (name) @sigs[ name ] = [regx, epon] end name = $1 + " [#{ sidx+=1 }]" epon = 0 next when /signature\s*=\s*(.*)/ pat = $1.strip regx = '' pat.split(/\s+/).each do |c| next if c.length != 2 regx << (c.index('?') ? '.' : "\\x#{c}") end when /ep_only\s*=\s*(.*)/ epon = ($1 =~ /^T/i) ? 1 : 0 end end if (name and ! @sigs[name]) @sigs[ name ] = [regx, epon] end fd.close end def scan(param) config(param) epa = pe.hdr.opt.AddressOfEntryPoint buf = pe.read_rva(epa, 256) @sigs.each_pair do |name, data| begin if (buf.match(Regexp.new('^' + data[0], nil, 'n'))) $stdout.puts param['file'] + ": " + name end rescue RegexpError $stderr.puts "Invalid signature: #{name} #{data[0]}" end end end end class Information attr_accessor :pe def initialize(pe) self.pe = pe end def add_fields(tbl, obj, fields) fields.each do |name| begin tbl << [name, "0x%.8x" % obj.send(name)] rescue ::NoMethodError => e $stderr.puts "Invalid field #{name}" end end end def scan(param) $stdout.puts "\n\n" tbl = table("Image Headers", ['Name', 'Value']) add_fields(tbl, pe.hdr.file, %W{ Characteristics SizeOfOptionalHeader PointerToSymbolTable TimeDateStamp NumberOfSections Machine }) $stdout.puts tbl.to_s $stdout.puts "\n\n" tbl = table("Optional Image Headers", ['Name', 'Value']) add_fields(tbl, pe.hdr.opt, %W{ ImageBase Magic MajorLinkerVersion MinorLinkerVersion SizeOfCode SizeOfInitializeData SizeOfUninitializeData AddressOfEntryPoint BaseOfCode BaseOfData SectionAlignment FileAlignment MajorOperatingSystemVersion MinorOperatingSystemVersion MajorImageVersion MinorImageVersion MajorSubsystemVersion MinorSubsystemVersion Win32VersionValue SizeOfImage SizeOfHeaders CheckSum Subsystem DllCharacteristics SizeOfStackReserve SizeOfStackCommit SizeOfHeapReserve SizeOfHeapCommit LoaderFlags NumberOfRvaAndSizes }) $stdout.puts tbl.to_s $stdout.puts "\n\n" if (pe.exports) tbl = table("Exported Functions", ['Ordinal', 'Name', 'Address']) pe.exports.entries.each do |ent| tbl << [ent.ordinal, ent.name, "0x%.8x" % pe.rva_to_vma(ent.rva)] end $stdout.puts tbl.to_s $stdout.puts "\n\n" end if (pe.imports) tbl = table("Imported Functions", ['Library', 'Ordinal', 'Name']) pe.imports.each do |lib| lib.entries.each do |ent| tbl << [lib.name, ent.ordinal, ent.name] end end $stdout.puts tbl.to_s $stdout.puts "\n\n" end if(pe.config) tbl = table("Configuration Header", ['Name', 'Value']) add_fields(tbl, pe.config, %W{ Size TimeDateStamp MajorVersion MinorVersion GlobalFlagsClear GlobalFlagsSet CriticalSectionDefaultTimeout DeCommitFreeBlockThreshold DeCommitTotalFreeThreshold LockPrefixTable MaximumAllocationSize VirtualMemoryThreshold ProcessAffinityMask ProcessHeapFlags CSDVersion Reserved1 EditList SecurityCookie SEHandlerTable SEHandlerCount }) $stdout.puts tbl.to_s $stdout.puts "\n\n" end if(pe.resources) tbl = table("Resources", ['ID', 'Language', 'Code Page', 'Size', 'Name']) pe.resources.keys.sort.each do |rkey| res = pe.resources[rkey] tbl << [rkey, res.lang, res.code, res.size, res.file] end $stdout.puts tbl.to_s $stdout.puts "\n\n" end tbl = table("Section Header", ["Name", "VirtualAddress", "SizeOfRawData", "Characteristics"]) pe.sections.each do |sec| tbl << [ sec.name, *[sec.vma, sec.raw_size, sec.flags].map{|x| "0x%.8x" % x} ] end $stdout.puts tbl.to_s $stdout.puts "\n\n" end def table(name, cols) Rex::Ui::Text::Table.new( 'Header' => name, 'Columns' => cols ) end end class Ripper require "fileutils" attr_accessor :pe def initialize(pe) self.pe = pe end def scan(param) dest = param['dir'] if (param['file']) dest = File.join(dest, File.basename(param['file'])) end ::FileUtils.mkdir_p(dest) pe.resources.keys.sort.each do |rkey| res = pe.resources[rkey] path = File.join(dest, rkey.split('/')[1] + '_' + res.file) fd = File.new(path, 'wb') fd.write(res.data) fd.close end end end class ContextMapDumper attr_accessor :pe def initialize(pe) self.pe = pe end def scan(param) dest = param['dir'] path = '' ::FileUtils.mkdir_p(dest) if(not (param['dir'] and param['file'])) $stderr.puts "No directory or file specified" return end if (param['file']) path = File.join(dest, File.basename(param['file']) + ".map") end fd = File.new(path, "wb") pe.all_sections.each do |section| # Skip over known bad sections next if section.name == ".data" next if section.name == ".reloc" offset = 0 while offset < section.size byte = section.read(offset, 1)[0] if byte != 0 chunkbase = pe.rva_to_vma(section.base_rva) + offset data = '' while byte != 0 data << byte offset += 1 byte = 0 byte = section.read(offset, 1)[0] if offset < section.size end buff = nil buff = [ 0x01, chunkbase, data.length, data].pack("CNNA*") if data.length > 0 fd.write(buff) if buff end offset += 1 end end fd.close end end # EOC end end end