metasploit-framework/lib/rex/pescan/analyze.rb

310 lines
5.9 KiB
Ruby

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