123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301 |
- #!/usr/bin/env ruby
- # -*- coding: binary -*-
- #
- # $Id$
- # $Revision$
- #
- msfbase = __FILE__
- while File.symlink?(msfbase)
- msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase))
- end
- $:.unshift(File.expand_path(File.join(File.dirname(msfbase), 'lib')))
- require 'msfenv'
- $:.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB']
- require 'metasm'
- require 'rex/elfparsey'
- require 'rex/elfscan'
- require 'rex/machparsey'
- require 'rex/machscan'
- 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
- opt = OptionParser.new
- opt.banner = "Usage: #{$PROGRAM_NAME} [mode] <options> [targets]"
- opt.separator('')
- opt.separator('Modes:')
- worker = nil
- param = {}
- files = []
- mode = ""
- opt.on('-j', '--jump [regA,regB,regC]', 'Search for jump equivalent instructions [PE|ELF|MACHO]') do |t|
- # take csv of register names (like eax,ebx) and convert
- # them to an array of register numbers
- mode = "jump"
- regnums = t.split(',').collect { |o|
- begin
- Rex::Arch::X86.reg_number(o)
- rescue
- puts "Invalid register \"#{o}\""
- exit(1)
- end
- }
- param['args'] = regnums
- end
- opt.on('-p', '--poppopret', 'Search for pop+pop+ret combinations [PE|ELF|MACHO]') do |t|
- mode = "pop"
- param['args'] = t
- end
- opt.on('-r', '--regex [regex]', 'Search for regex match [PE|ELF|MACHO]') do |t|
- mode = "regex"
- param['args'] = t
- end
- opt.on('-a', '--analyze-address [address]', 'Display the code at the specified address [PE|ELF]') do |t|
- mode = "analyze-address"
- param['args'] = opt2i(t)
- end
- opt.on('-b', '--analyze-offset [offset]', 'Display the code at the specified offset [PE|ELF]') do |t|
- mode = "analyze-offset"
- param['args'] = opt2i(t)
- end
- opt.on('-f', '--fingerprint', 'Attempt to identify the packer/compiler [PE]') do |t|
- mode = "fingerprint"
- param['database'] = File.join(File.dirname(msfbase), 'data', 'msfpescan', 'identify.txt')
- end
- opt.on('-i', '--info', 'Display detailed information about the image [PE]') do |t|
- mode = "info"
- end
- opt.on('-R', '--ripper [directory]', 'Rip all module resources to disk [PE]') do |t|
- mode = "ripper"
- param['dir'] = t
- end
- opt.on('--context-map [directory]', 'Generate context-map files [PE]') do |t|
- mode = "context"
- param['dir'] = t
- end
- opt.separator('')
- opt.separator('Options:')
- opt.on('-A', '--after [bytes]', 'Number of bytes to show after match (-a/-b) [PE|ELF|MACHO]') do |t|
- param['after'] = opt2i(t)
- end
- opt.on('-B', '--before [bytes]', 'Number of bytes to show before match (-a/-b) [PE|ELF|MACHO]') do |t|
- param['before'] = opt2i(t)
- end
- opt.on('-I', '--image-base [address]', 'Specify an alternate ImageBase [PE|ELF|MACHO]') do |t|
- param['imagebase'] = opt2i(t)
- end
- opt.on('-D', '--disasm', 'Disassemble the bytes at this address [PE|ELF]') do |t|
- param['disasm'] = true
- end
- opt.on('-F', '--filter-addresses [regex]', 'Filter addresses based on a regular expression [PE]') do |t|
- param['filteraddr'] = t
- end
- opt.on_tail("-h", "--help", "Show this message") do
- $stderr.puts opt
- exit(1)
- end
- begin
- opt.parse!
- rescue OptionParser::InvalidOption, OptionParser::MissingArgument
- $stderr.puts "Invalid option, try -h for usage"
- exit(1)
- end
- if mode.empty?
- $stderr.puts "A mode must be selected"
- $stderr.puts opt
- exit(1)
- end
- # check if the file is a directory if it is collect all the entries
- ARGV.each do |file|
- if(File.directory?(file))
- dir = Dir.open(file)
- dir.entries.each do |ent|
- path = File.join(file, ent)
- next if not File.file?(path)
- files << File.join(path)
- end
- else
- files << file
- end
- end
- # we need to do some work to figure out the file format
- files.each do |file|
- param['file'] = file
- bin = Metasm::AutoExe.decode_file(file) if not file.empty?
- if bin.kind_of?(Metasm::PE)
- case mode
- when "jump"
- worker = Rex::PeScan::Scanner::JmpRegScanner
- when "pop"
- worker = Rex::PeScan::Scanner::PopPopRetScanner
- when "regex"
- worker = Rex::PeScan::Scanner::RegexScanner
- when "analyze-address"
- worker = Rex::PeScan::Search::DumpRVA
- when "analyze-offset"
- worker = Rex::PeScan::Search::DumpOffset
- when "fingerprint"
- worker = Rex::PeScan::Analyze::Fingerprint
- when "info"
- worker = Rex::PeScan::Analyze::Information
- when "ripper"
- worker = Rex::PeScan::Analyze::Ripper
- when "context"
- worker = Rex::PeScan::Analyze::ContextMapDumper
- else
- $stderr.puts("Mode unsupported by file format")
- end
- pe_klass = Rex::PeParsey::Pe
- begin
- pe = pe_klass.new_from_file(file, true)
- rescue ::Interrupt
- raise $!
- rescue Rex::PeParsey::FileHeaderError
- next if $!.message == "Couldn't find the PE magic!"
- raise $!
- rescue Errno::ENOENT
- $stdout.puts("File does not exist: #{file}")
- next
- rescue ::Rex::PeParsey::SkipError
- next
- rescue ::Exception => e
- $stdout.puts "[#{file}] #{e.class}: #{e}"
- next
- end
- if (param['imagebase'])
- pe.image_base = param['imagebase'];
- end
- if not worker
- $stderr.puts("A mode could not be set for this file.")
- next
- end
- o = worker.new(pe)
- o.scan(param)
- pe.close
- elsif bin.kind_of?(Metasm::ELF)
- case mode
- when "jump"
- worker = Rex::ElfScan::Scanner::JmpRegScanner
- when "pop"
- worker = Rex::ElfScan::Scanner::PopPopRetScanner
- when "regex"
- worker = Rex::ElfScan::Scanner::RegexScanner
- when "analyze-address"
- worker = Rex::ElfScan::Search::DumpRVA
- when "analyze-offset"
- worker = Rex::ElfScan::Search::DumpOffset
- else
- $stderr.puts("Mode unsupported by file format")
- end
-
- begin
- elf = Rex::ElfParsey::Elf.new_from_file(file, true)
- rescue Rex::ElfParsey::ElfHeaderError
- if $!.message == 'Invalid magic number'
- $stderr.puts("Skipping #{file}: #{$!}")
- next
- end
- raise $!
- rescue Errno::ENOENT
- $stderr.puts("File does not exist: #{file}")
- next
- end
- if (param['imagebase'])
- elf.base_addr = param['imagebase'];
- end
- if not worker
- $stderr.puts("A mode could not be set for this file.")
- next
- end
-
- o = worker.new(elf)
- o.scan(param)
- elf.close
- elsif bin.kind_of?(Metasm::MachO)
- case mode
- when "jump"
- worker = Rex::MachScan::Scanner::JmpRegScanner
- when "pop"
- worker = Rex::MachScan::Scanner::PopPopRetScanner
- when "regex"
- worker = Rex::MachScan::Scanner::RegexScanner
- else
- $stderr.puts("Mode unsupported by file format")
- end
- begin
- mach = Rex::MachParsey::Mach.new_from_file(file, true)
- o = worker.new(mach)
- o.scan(param)
- mach.close
- rescue Rex::MachParsey::MachHeaderError
- $stderr.puts("File is not a Mach-O binary, trying Fat..\n")
- begin
- fat = Rex::MachParsey::Fat.new_from_file(file, true)
- o = worker.new(fat)
- o.scan(param)
- fat.close
- rescue
- $stderr.puts("Error: " + $!.to_s)
- $stderr.puts("Skipping #{file}")
- end
- rescue Errno::ENOENT
- $stderr.puts("File does not exist: #{file}")
- next
- end
- end
- if not worker
- $stderr.puts("Unsupported file format")
- $stderr.puts("Skipping #{file}")
- next
- end
- end
|