forked from rapid7/metasploit-framework
-
Notifications
You must be signed in to change notification settings - Fork 4
/
msfpescan
executable file
·200 lines (156 loc) · 4.32 KB
/
msfpescan
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
#!/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 'fastlib'
require 'msfenv'
$:.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] <options> [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|
begin
Rex::Arch::X86.reg_number(o)
rescue
puts "Invalid register \"#{o}\""
exit(1)
end
}
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('-F', '--filter-addresses [regex]', 'Filter addresses based on a regular expression') do |t|
param['filteraddr'] = t
end
opt.on_tail("-h", "--help", "Show this message") do
puts opt
exit(1)
end
begin
opt.parse!
rescue OptionParser::InvalidOption
puts "Invalid option, try -h for usage"
exit(1)
end
if (! worker)
puts opt
exit(1)
end
files = []
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
files.each do |file|
$stdout.puts ""
param['file'] = file
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
o = worker.new(pe)
o.scan(param)
pe.close
end
$stdout.puts ""