#!/usr/bin/env ruby msfbase = File.symlink?(__FILE__) ? File.readlink(__FILE__) : __FILE__ $:.unshift(File.join(File.dirname(msfbase), 'lib')) $:.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB'] require 'rex/peparsey' require 'rex/pescan' require 'rex/arch/x86' require 'optparse' def opt2i(o) o.index("0x")==0 ? o.hex : o.to_i end # # Right now this program is a bit shakey... # # - It tries to error on the side of caution, so it will try for a # false negative vs a false positive. # - It doesn't account for the entire PE image neccesairly # - It wouldn't find hits that overlap sections # - etc etc # opt = OptionParser.new opt.banner = "Usage: #{$PROGRAM_NAME} [mode] [targets]" opt.separator('') opt.separator('Modes:') worker = nil param = {} pe_klass = Rex::PeParsey::Pe opt.on('-j', '--jump [regA,regB,regC]', 'Search for jump equivalent instructions') do |t| # take csv of register names (like eax,ebx) and convert # them to an array of register numbers regnums = t.split(',').collect { |o| Rex::Arch::X86.reg_number(o) } worker = Rex::PeScan::Scanner::JmpRegScanner param['args'] = regnums end opt.on('-p', '--poppopret', 'Search for pop+pop+ret combinations') do |t| worker = Rex::PeScan::Scanner::PopPopRetScanner param['args'] = t end opt.on('-r', '--regex [regex]', 'Search for regex match') do |t| worker = Rex::PeScan::Scanner::RegexScanner param['args'] = t end opt.on('-a', '--analyze-address [address]', 'Display the code at the specified address') do |t| worker = Rex::PeScan::Search::DumpRVA param['args'] = opt2i(t) end opt.on('-b', '--analyze-offset [offset]', 'Display the code at the specified offset') do |t| worker = Rex::PeScan::Search::DumpOffset param['args'] = opt2i(t) end opt.on('-f', '--fingerprint', 'Attempt to identify the packer/compiler') do |t| worker = Rex::PeScan::Analyze::Fingerprint param['database'] = File.join(File.dirname(msfbase), 'data', 'msfpescan', 'identify.txt') end opt.on('-i', '--info', 'Display detailed information about the image') do |t| worker = Rex::PeScan::Analyze::Information end opt.on('-R', '--ripper [directory]', 'Rip all module resources to disk ') do |t| worker = Rex::PeScan::Analyze::Ripper param['dir'] = t end opt.on('--context-map [directory]', 'Generate context-map files') do |t| worker = Rex::PeScan::Analyze::ContextMapDumper param['dir'] = t end opt.separator('') opt.separator('Options:') opt.on('-M', '--memdump', 'The targets are memdump.exe directories') do |t| pe_klass = Rex::PeParsey::PeMemDump end opt.on('-A', '--after [bytes]', 'Number of bytes to show after match (-a/-b)') do |t| param['after'] = opt2i(t) end opt.on('-B', '--before [bytes]', 'Number of bytes to show before match (-a/-b)') do |t| param['before'] = opt2i(t) end opt.on('-D', '--disasm', 'Disassemble the bytes at this address') do |t| param['disasm'] = true end opt.on('-I', '--image-base [address]', 'Specify an alternate ImageBase') do |t| param['imagebase'] = opt2i(t) end opt.on_tail("-h", "--help", "Show this message") do puts opt exit end opt.parse! if (! worker) puts opt exit(0) end ARGV.each do |file| param['file'] = file begin pe = pe_klass.new_from_file(file, true) rescue Rex::PeParsey::FileHeaderError next if $!.message == "Couldn't find the PE magic!" raise $! rescue Errno::ENOENT $stderr.puts("File does not exist: #{file}") next end if (param['imagebase']) pe.image_base = param['imagebase']; end o = worker.new(pe) o.scan(param) pe.close end