forked from rapid7/metasploit-framework
-
Notifications
You must be signed in to change notification settings - Fork 23
/
msfbinscan
executable file
·300 lines (252 loc) · 7.31 KB
/
msfbinscan
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
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
#!/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