metasploit-framework/msfopcode

370 lines
9.7 KiB
Ruby
Executable File

#!/usr/bin/env ruby
#
# This user interface provides a command line interface to the Metasploit
# Opcode Database. It provides users with the ability to search for opcodes
# and to display information about modules.
#
msfbase = __FILE__
while File.symlink?(msfbase)
msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase))
end
$:.unshift(File.join(File.dirname(msfbase), 'lib'))
$:.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB']
require 'rex'
require 'rex/ui'
require 'rex/exploitation/opcodedb'
if (ARGV.length == 0)
$stderr.puts("\n" + " Usage: #{File.basename($0)} command <options>\n\n" +
"SUPPORTED COMMANDS\n\n" +
" stats Display database statistics\n" +
" locales Display supported locales\n" +
" metatypes Display supported opcode meta types (Ex: jmp reg)\n" +
" groups Display supported opcode groups (Ex: esp => eip)\n" +
" types Display supported opcode types (Ex: jmp esp)\n" +
" platforms Display supported platforms\n" +
" modules Display information about specific modules\n" +
" search Search for opcodes given a set of criteria\n" +
"\n")
exit
end
# Command-specific argument parser instances
$platform_args = Rex::Parser::Arguments.new(
"-p" => [ true, "A comma separated list of operating system names to filter" ],
"-h" => [ false, "Help banner" ])
$module_args = Rex::Parser::Arguments.new(
"-p" => [ true, "A comma separated list of operating system names to filter (Ex: 2000,XP)" ],
"-l" => [ true, "A comma separated list of locales to filter (Ex: English)" ],
"-m" => [ true, "A comma separated list of module names to filter (Ex: kernel32.dll,user32.dll)" ],
"-d" => [ false, "Display detailed output" ],
"-S" => [ false, "Include module segment information" ],
"-I" => [ false, "Include module import information" ],
"-E" => [ false, "Include module export information" ],
"-x" => [ false, "Dump the raw XML response" ],
"-h" => [ false, "Help banner" ])
$search_args = Rex::Parser::Arguments.new(
"-p" => [ true, "A comma separated list of operating system names to filter (Ex: 2000,XP)" ],
"-l" => [ true, "A comma separated list of locales to filter (Ex: English)" ],
"-m" => [ true, "A comma separated list of module names to filter (Ex: kernel32.dll,user32.dll)" ],
"-t" => [ true, "A semi-colon separated list of opcode types to filter (Ex: jmp esp,call esp)" ],
"-g" => [ true, "A comma separated list of opcode groups to filter (Ex: esp => eip)" ],
"-M" => [ true, "A comma separated list of opcode meta types to filter (Ex: jmp reg)" ],
"-a" => [ true, "A comma separated list of addresses to filter (Ex: 0x41424344)" ],
"-P" => [ false, "Results must span more than one operating system version" ],
"-x" => [ false, "Dump the raw XML response" ],
"-h" => [ false, "Help banner"])
# Command specific option argument parsing association
cmd_args =
{
"platforms" =>
[
$platform_args,
Proc.new { |opt, val|
case opt
when "-p"
$filter['Names'] = val.split(/,/)
when "-x"
$dump_xml = true
when "-h"
$stderr.puts("\n Usage: #{File.basename($0)} platforms <options>\n" + $platform_args.usage)
exit
end
}
],
"modules" =>
[
$module_args,
Proc.new { |opt, val|
case opt
when "-p"
$filter['PlatformNames'] = val.split(/,/)
when "-l"
$filter['LocaleNames'] = val.split(/,/)
when "-m"
$filter['ModuleNames'] = val.split(/,/)
when "-S"
$filter['Segments'] = true
when "-I"
$filter['Imports'] = true
when "-E"
$filter['Exports'] = true
when "-d"
$filter['Detailed'] = true
when "-x"
$dump_xml = true
when "-h"
$stderr.puts("\n Usage: #{File.basename($0)} modules <options>\n" + $module_args.usage)
exit
end
}
],
"search" =>
[
$search_args,
Proc.new { |opt, val|
case opt
when "-p"
$filter['PlatformNames'] = val.split(/,/)
when "-l"
$filter['LocaleNames'] = val.split(/,/)
when "-m"
$filter['ModuleNames'] = val.split(/,/)
when "-t"
$filter['TypeNames'] = val.split(/;/)
when "-g"
$filter['GroupNames'] = val.split(/,/)
when "-M"
$filter['MetaTypeNames'] = val.split(/,/)
when "-a"
$filter['Addresses'] = val.split(/,/).map { |e| e.hex }
when "-P"
$filter['Portable'] = true
when "-x"
$dump_xml = true
when "-h"
$stderr.puts("\n Usage: #{File.basename($0)} search <options>\n" + $search_args.usage)
exit
end
}
],
}
# Default to not dumping the XML contents
$dump_xml = false
# Extract the command
cmd = ARGV.shift
# Create the opcode client instance
client = Rex::Exploitation::OpcodeDb::Client.new
# Initializes the filter to an empty hash
$filter = {}
# Parse the command specific arguments as necessary
if (args = cmd_args[cmd])
args[0].parse(ARGV) { |opt, idx, val|
args[1].call(opt, val)
}
end
# Process the specific command
case cmd
when "stats"
stats = client.statistics
puts(
"\n" +
"Last Updated : #{stats.last_update}\n" +
"Number of Opcodes : #{stats.opcodes}\n" +
"Number of Opcode Types : #{stats.opcode_types}\n" +
"Number of Platforms : #{stats.platforms}\n" +
"Number of Architectures : #{stats.architectures}\n" +
"Number of Modules : #{stats.modules}\n" +
"Number of Module Segments: #{stats.module_segments}\n" +
"Number of Module Imports : #{stats.module_imports}\n" +
"Number of Module Exports : #{stats.module_exports}\n\n")
when "locales"
client.locales.each { |locale| puts "#{locale.name}" }
when "metatypes"
client.meta_types.each { |mt| puts "#{mt.name}" }
when "groups"
client.groups.each { |g| puts "#{g.name}" }
when "types"
client.types.each { |g| puts "#{g.name}" }
when "platforms"
client.platforms($filter).each { |p| puts "#{p.desc}" }
when "modules"
if (ARGV.length == 0)
$stderr.puts("Filter criteria required -- specify '-h' for help.")
exit
end
modules = client.modules($filter)
if ($dump_xml)
puts client.last_xml
exit
end
# If we're displaying extra information on a per-module basis, then we
# need to not display in a single table format.
if ($filter['Segments'] or $filter['Imports'] or $filter['Exports'] or $filter['Detailed'])
modules.each { |mod|
puts(
".-============================================\n\n" +
" Name : #{mod.name}\n" +
" Base Address: #{"0x%.8x" % mod.base_address}\n" +
" Size : #{mod.image_size}\n" +
" Version : #{mod.maj_maj_ver}.#{mod.maj_min_ver}.#{mod.min_maj_ver}.#{mod.min_min_ver}\n" +
" Timestamp : #{mod.timestamp}\n" +
" Locale : #{mod.locale.name}\n" +
" Platforms : \n\n" +
"#{mod.platforms.map { |p| " " + p.desc }.join("\n")}\n\n")
# Display module segments
if ($filter['Segments'])
tbl = Rex::Ui::Text::Table.new(
'Indent' => 4,
'Columns' =>
[
"Type",
"Base Address",
"Size",
"Permissions"
])
mod.segments.each { |seg|
tbl << [
seg.type,
"0x%.8x" % seg.base_address,
seg.size.to_s,
(((seg.readable == true) ? "r" : "") +
((seg.writable == true) ? "w" : "") +
((seg.executable == true) ? "x" : ""))
]
}
puts("\n Module segments:\n\n" + tbl.to_s + "\n")
end
# Display module imports
if ($filter['Imports'])
tbl = Rex::Ui::Text::Table.new(
'Indent' => 4,
'Columns' =>
[
"Ordinal",
"Address",
"Name",
])
mod.imports.each { |imp|
tbl << [
imp.ordinal.to_s,
"0x%.8x" % imp.address,
imp.name
]
}
puts("\n Module imports:\n\n" + tbl.to_s + "\n")
end
# Display module exports
if ($filter['Exports'])
tbl = Rex::Ui::Text::Table.new(
'Indent' => 4,
'Columns' =>
[
"Ordinal",
"Address",
"Name",
])
mod.exports.each { |exp|
tbl << [
exp.ordinal.to_s,
"0x%.8x" % exp.address,
exp.name
]
}
puts("\n Module exports:\n\n" + tbl.to_s + "\n")
end
}
else
tbl = Rex::Ui::Text::Table.new(
'Indent' => 4,
'Header' => "Matching Modules",
'Columns' =>
[
"Name",
"Base Address",
"Size",
"Version",
"Timestamp",
"Locale",
])
modules.each { |mod|
tbl << [
mod.name,
"0x%.8x" % mod.base_address,
mod.image_size,
"#{mod.maj_maj_ver}.#{mod.maj_min_ver}.#{mod.min_maj_ver}.#{mod.min_min_ver}",
mod.timestamp.to_s,
mod.locale.name,
]
}
puts("\n" + tbl.to_s + "\n")
end
when "search"
if (ARGV.length == 0)
$stderr.puts("Filter criteria required -- specify '-h' for help.")
exit
end
opcodes = client.search($filter)
if ($dump_xml)
puts client.last_xml
exit
end
tbl = Rex::Ui::Text::Table.new(
'Indent' => 4,
'Header' => "Opcodes",
'Columns' =>
[
"Address",
"Type",
"OS"
])
opcodes.each { |opcode|
tbl << [
"0x%.8x" % opcode.address,
opcode.type.name,
opcode.modules[0].platforms[0].desc + " (#{opcode.modules[0].name})"
]
midx = 0
pidx = 1
until (opcode.modules[midx] == nil)
tbl << [ '', '', '' ] if (midx >= 1)
if (opcode.modules[midx].platforms.length > 1)
until (opcode.modules[midx].platforms[pidx] == nil)
tbl << [
'', '',
opcode.modules[midx].platforms[pidx].desc + " (#{opcode.modules[midx].name})"
]
pidx += 1
end
pidx = 0
end
midx += 1
end
}
puts("\n" + tbl.to_s + "\n")
else
$stderr.puts("Unsupported command: #{cmd}")
end