Fix recent metasploit-framework.gemspec conflict.
commit
9d5a276e91
|
@ -34,6 +34,7 @@ PATH
|
||||||
recog
|
recog
|
||||||
redcarpet
|
redcarpet
|
||||||
rex-arch
|
rex-arch
|
||||||
|
rex-bin_tools
|
||||||
rex-java
|
rex-java
|
||||||
rex-mime
|
rex-mime
|
||||||
rex-ole
|
rex-ole
|
||||||
|
@ -230,6 +231,13 @@ GEM
|
||||||
redcarpet (3.3.4)
|
redcarpet (3.3.4)
|
||||||
rex-arch (0.1.1)
|
rex-arch (0.1.1)
|
||||||
rex-text
|
rex-text
|
||||||
|
rex-bin_tools (0.1.0)
|
||||||
|
metasm
|
||||||
|
rex-arch
|
||||||
|
rex-core
|
||||||
|
rex-struct2
|
||||||
|
rex-text
|
||||||
|
rex-core (0.1.1)
|
||||||
rex-java (0.1.2)
|
rex-java (0.1.2)
|
||||||
rex-mime (0.1.1)
|
rex-mime (0.1.1)
|
||||||
rex-text
|
rex-text
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,104 +0,0 @@
|
||||||
# -*- coding: binary -*-
|
|
||||||
|
|
||||||
require 'tempfile'
|
|
||||||
require 'rex/file'
|
|
||||||
require 'rex/text'
|
|
||||||
|
|
||||||
module Rex
|
|
||||||
module Assembly
|
|
||||||
|
|
||||||
###
|
|
||||||
#
|
|
||||||
# This class uses nasm to assemble and disassemble stuff.
|
|
||||||
#
|
|
||||||
###
|
|
||||||
class Nasm
|
|
||||||
|
|
||||||
@@nasm_path = 'nasm'
|
|
||||||
@@ndisasm_path = 'ndisasm'
|
|
||||||
|
|
||||||
#
|
|
||||||
# Ensures that the nasm environment is sane.
|
|
||||||
#
|
|
||||||
def self.check
|
|
||||||
@@nasm_path =
|
|
||||||
Rex::FileUtils.find_full_path('nasm') ||
|
|
||||||
Rex::FileUtils.find_full_path('nasm.exe') ||
|
|
||||||
Rex::FileUtils.find_full_path('nasmw.exe') ||
|
|
||||||
raise(RuntimeError, "No nasm installation was found.")
|
|
||||||
|
|
||||||
@@ndisasm_path =
|
|
||||||
Rex::FileUtils.find_full_path('ndisasm') ||
|
|
||||||
Rex::FileUtils.find_full_path('ndisasm.exe') ||
|
|
||||||
Rex::FileUtils.find_full_path('ndisasmw.exe') ||
|
|
||||||
raise(RuntimeError, "No ndisasm installation was found.")
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# Assembles the supplied assembly and returns the raw opcodes.
|
|
||||||
#
|
|
||||||
def self.assemble(assembly, bits=32)
|
|
||||||
check
|
|
||||||
|
|
||||||
# Open the temporary file
|
|
||||||
tmp = Tempfile.new('nasmXXXX')
|
|
||||||
tmp.binmode
|
|
||||||
|
|
||||||
tpath = tmp.path
|
|
||||||
opath = tmp.path + '.out'
|
|
||||||
|
|
||||||
# Write the assembly data to a file
|
|
||||||
tmp.write("BITS #{bits}\n" + assembly)
|
|
||||||
tmp.flush()
|
|
||||||
tmp.seek(0)
|
|
||||||
|
|
||||||
# Run nasm
|
|
||||||
if (system(@@nasm_path, '-f', 'bin', '-o', opath, tpath) == false)
|
|
||||||
raise RuntimeError, "Assembler did not complete successfully: #{$?.exitstatus}"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Read the assembled text
|
|
||||||
rv = ::IO.read(opath)
|
|
||||||
|
|
||||||
# Remove temporary files
|
|
||||||
File.unlink(opath)
|
|
||||||
tmp.close(true)
|
|
||||||
|
|
||||||
rv
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# Disassembles the supplied raw opcodes
|
|
||||||
#
|
|
||||||
def self.disassemble(raw, bits=32)
|
|
||||||
check
|
|
||||||
|
|
||||||
tmp = Tempfile.new('nasmout')
|
|
||||||
tmp.binmode
|
|
||||||
|
|
||||||
tfd = File.open(tmp.path, "wb")
|
|
||||||
|
|
||||||
tfd.write(raw)
|
|
||||||
tfd.flush()
|
|
||||||
tfd.close
|
|
||||||
|
|
||||||
p = ::IO.popen("\"#{@@ndisasm_path}\" -b #{bits} \"#{tmp.path}\"")
|
|
||||||
o = ''
|
|
||||||
|
|
||||||
begin
|
|
||||||
until p.eof?
|
|
||||||
o += p.read
|
|
||||||
end
|
|
||||||
ensure
|
|
||||||
p.close
|
|
||||||
end
|
|
||||||
|
|
||||||
tmp.close(true)
|
|
||||||
|
|
||||||
o
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,385 +0,0 @@
|
||||||
# -*- coding: binary -*-
|
|
||||||
module Rex
|
|
||||||
|
|
||||||
###
|
|
||||||
#
|
|
||||||
# This class provides os-specific functionality
|
|
||||||
#
|
|
||||||
###
|
|
||||||
module Compat
|
|
||||||
|
|
||||||
STD_INPUT_HANDLE = -10
|
|
||||||
STD_OUTPUT_HANDLE = -11
|
|
||||||
STD_ERROR_HANDLE = -12
|
|
||||||
|
|
||||||
GENERIC_READ = 0x80000000
|
|
||||||
GENERIC_WRITE = 0x40000000
|
|
||||||
GENERIC_EXECUTE = 0x20000000
|
|
||||||
|
|
||||||
FILE_SHARE_READ = 0x00000001
|
|
||||||
FILE_SHARE_WRITE = 0x00000002
|
|
||||||
OPEN_EXISTING = 0x00000003
|
|
||||||
|
|
||||||
ENABLE_LINE_INPUT = 2
|
|
||||||
ENABLE_ECHO_INPUT = 4
|
|
||||||
ENABLE_PROCESSED_INPUT = 1
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# Platform detection
|
|
||||||
#
|
|
||||||
|
|
||||||
@@is_windows = @@is_cygwin = @@is_macosx = @@is_linux = @@is_bsdi = @@is_freebsd = @@is_netbsd = @@is_openbsd = @@is_java = false
|
|
||||||
@@loaded_win32api = false
|
|
||||||
@@loaded_tempfile = false
|
|
||||||
@@loaded_fileutils = false
|
|
||||||
|
|
||||||
|
|
||||||
def self.is_windows
|
|
||||||
return @@is_windows if @@is_windows
|
|
||||||
@@is_windows = (RUBY_PLATFORM =~ /mswin(32|64)|mingw(32|64)/) ? true : false
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.is_cygwin
|
|
||||||
return @@is_cygwin if @@is_cygwin
|
|
||||||
@@is_cygwin = (RUBY_PLATFORM =~ /cygwin/) ? true : false
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.is_macosx
|
|
||||||
return @@is_macosx if @@is_macosx
|
|
||||||
@@is_macosx = (RUBY_PLATFORM =~ /darwin/) ? true : false
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.is_linux
|
|
||||||
return @@is_linux if @@is_linux
|
|
||||||
@@is_linux = (RUBY_PLATFORM =~ /linux/) ? true : false
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.is_bsdi
|
|
||||||
return @@is_bsdi if @@is_bsdi
|
|
||||||
@@is_bsdi = (RUBY_PLATFORM =~ /bsdi/i) ? true : false
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.is_netbsd
|
|
||||||
return @@is_netbsd if @@is_netbsd
|
|
||||||
@@is_netbsd = (RUBY_PLATFORM =~ /netbsd/) ? true : false
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.is_freebsd
|
|
||||||
return @@is_freebsd if @@is_freebsd
|
|
||||||
@@is_freebsd = (RUBY_PLATFORM =~ /freebsd/) ? true : false
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.is_openbsd
|
|
||||||
return @@is_openbsd if @@is_openbsd
|
|
||||||
@@is_openbsd = (RUBY_PLATFORM =~ /openbsd/) ? true : false
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.is_java
|
|
||||||
return @@is_java if @@is_java
|
|
||||||
@@is_java = (RUBY_PLATFORM =~ /java/) ? true : false
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.is_wow64
|
|
||||||
return false if not is_windows
|
|
||||||
is64 = false
|
|
||||||
begin
|
|
||||||
buff = "\x00" * 4
|
|
||||||
Win32API.new("kernel32","IsWow64Process",['L','P'],'L').call(-1, buff)
|
|
||||||
is64 = (buff.unpack("V")[0]) == 1 ? true : false
|
|
||||||
rescue ::Exception
|
|
||||||
end
|
|
||||||
is64
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.cygwin_to_win32(path)
|
|
||||||
if(path !~ /^\/cygdrive/)
|
|
||||||
return ::IO.popen("cygpath -w #{path}", "rb").read.strip
|
|
||||||
end
|
|
||||||
dir = path.split("/")
|
|
||||||
dir.shift
|
|
||||||
dir.shift
|
|
||||||
dir[0] = dir[0] + ":"
|
|
||||||
dir.join("\\")
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.open_file(url='')
|
|
||||||
case RUBY_PLATFORM
|
|
||||||
when /cygwin/
|
|
||||||
path = self.cygwin_to_win32(url)
|
|
||||||
system(["cmd", "cmd"], "/c", "explorer", path)
|
|
||||||
else
|
|
||||||
self.open_browser(url)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.open_browser(url='http://google.com/')
|
|
||||||
case RUBY_PLATFORM
|
|
||||||
when /cygwin/
|
|
||||||
if(url[0,1] == "/")
|
|
||||||
self.open_file(url)
|
|
||||||
end
|
|
||||||
return if not @@loaded_win32api
|
|
||||||
Win32API.new("shell32.dll", "ShellExecute", ["PPPPPL"], "L").call(nil, "open", url, nil, nil, 0)
|
|
||||||
when /mswin32|mingw/
|
|
||||||
return if not @@loaded_win32api
|
|
||||||
Win32API.new("shell32.dll", "ShellExecute", ["PPPPPL"], "L").call(nil, "open", url, nil, nil, 0)
|
|
||||||
when /darwin/
|
|
||||||
system("open #{url}")
|
|
||||||
else
|
|
||||||
# Search through the PATH variable (if it exists) and chose a browser
|
|
||||||
# We are making an assumption about the nature of "PATH" so tread lightly
|
|
||||||
if defined? ENV['PATH']
|
|
||||||
# "xdg-open" is more general than "sensible-browser" and can be useful for lots of
|
|
||||||
# file types -- text files, pcaps, or URLs. It's nearly always
|
|
||||||
# going to use the application the user is expecting. If we're not
|
|
||||||
# on something Debian-based, fall back to likely browsers.
|
|
||||||
['xdg-open', 'sensible-browser', 'firefox', 'firefox-bin', 'opera', 'konqueror', 'chromium-browser'].each do |browser|
|
|
||||||
ENV['PATH'].split(':').each do |path|
|
|
||||||
# Does the browser exists?
|
|
||||||
if File.exist?("#{path}/#{browser}")
|
|
||||||
system("#{browser} #{url} &")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.open_webrtc_browser(url='http://google.com/')
|
|
||||||
case RUBY_PLATFORM
|
|
||||||
when /mswin2|mingw|cygwin/
|
|
||||||
paths = [
|
|
||||||
"Google\\Chrome\\Application\\chrome.exe",
|
|
||||||
"Mozilla Firefox\\firefox.exe",
|
|
||||||
"Opera\\launcher.exe"
|
|
||||||
]
|
|
||||||
|
|
||||||
prog_files = ENV['ProgramFiles']
|
|
||||||
paths = paths.map { |p| "#{prog_files}\\#{p}" }
|
|
||||||
|
|
||||||
# Old chrome path
|
|
||||||
app_data = ENV['APPDATA']
|
|
||||||
paths << "#{app_data}\\Google\\Chrome\\Application\\chrome.exe"
|
|
||||||
|
|
||||||
paths.each do |path|
|
|
||||||
if File.exist?(path)
|
|
||||||
args = (path =~ /chrome\.exe/) ? "--allow-file-access-from-files" : ""
|
|
||||||
system("\"#{path}\" #{args} \"#{url}\"")
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
when /darwin/
|
|
||||||
['Google Chrome.app', 'Firefox.app'].each do |browser|
|
|
||||||
browser_path = "/Applications/#{browser}"
|
|
||||||
if File.directory?(browser_path)
|
|
||||||
args = (browser_path =~ /Chrome/) ? "--args --allow-file-access-from-files" : ""
|
|
||||||
|
|
||||||
system("open #{url} -a \"#{browser_path}\" #{args} &")
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if defined? ENV['PATH']
|
|
||||||
['google-chrome', 'chrome', 'chromium', 'firefox' , 'firefox', 'opera'].each do |browser|
|
|
||||||
ENV['PATH'].split(':').each do |path|
|
|
||||||
browser_path = "#{path}/#{browser}"
|
|
||||||
if File.exist?(browser_path)
|
|
||||||
args = (browser_path =~ /Chrome/) ? "--allow-file-access-from-files" : ""
|
|
||||||
system("#{browser_path} #{args} #{url} &")
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
false
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.open_email(addr)
|
|
||||||
case RUBY_PLATFORM
|
|
||||||
when /mswin32|cygwin/
|
|
||||||
return if not @@loaded_win32api
|
|
||||||
Win32API.new("shell32.dll", "ShellExecute", ["PPPPPL"], "L").call(nil, "open", "mailto:"+addr, nil, nil, 0)
|
|
||||||
when /darwin/
|
|
||||||
system("open mailto:#{addr}")
|
|
||||||
else
|
|
||||||
# ?
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.play_sound(path)
|
|
||||||
case RUBY_PLATFORM
|
|
||||||
when /cygwin/
|
|
||||||
path = self.cygwin_to_win32(path)
|
|
||||||
return if not @@loaded_win32api
|
|
||||||
Win32API.new("winmm.dll", "sndPlaySoundA", ["SI"], "I").call(path, 0x20000)
|
|
||||||
when /mswin32/
|
|
||||||
return if not @@loaded_win32api
|
|
||||||
Win32API.new("winmm.dll", "sndPlaySoundA", ["SI"], "I").call(path, 0x20000)
|
|
||||||
when /darwin/
|
|
||||||
system("afplay #{path} >/dev/null 2>&1")
|
|
||||||
else
|
|
||||||
system("aplay #{path} >/dev/null 2>&1")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.getenv(var)
|
|
||||||
if (is_windows and @@loaded_win32api)
|
|
||||||
f = Win32API.new("kernel32", "GetEnvironmentVariable", ["P", "P", "I"], "I")
|
|
||||||
buff = "\x00" * 16384
|
|
||||||
sz = f.call(var, buff, buff.length)
|
|
||||||
return nil if sz == 0
|
|
||||||
buff[0,sz]
|
|
||||||
else
|
|
||||||
ENV[var]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.setenv(var,val)
|
|
||||||
if (is_windows and @@loaded_win32api)
|
|
||||||
f = Win32API.new("kernel32", "SetEnvironmentVariable", ["P", "P"], "I")
|
|
||||||
f.call(var, val + "\x00")
|
|
||||||
else
|
|
||||||
ENV[var]= val
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# Obtain the path to our interpreter
|
|
||||||
#
|
|
||||||
def self.win32_ruby_path
|
|
||||||
return nil if ! (is_windows and @@loaded_win32api)
|
|
||||||
gmh = Win32API.new("kernel32", "GetModuleHandle", ["P"], "L")
|
|
||||||
gmf = Win32API.new("kernel32", "GetModuleFileName", ["LPL"], "L")
|
|
||||||
mod = gmh.call(nil)
|
|
||||||
inf = "\x00" * 1024
|
|
||||||
gmf.call(mod, inf, 1024)
|
|
||||||
inf.unpack("Z*")[0]
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# Call WinExec (equiv to system("cmd &"))
|
|
||||||
#
|
|
||||||
def self.win32_winexec(cmd)
|
|
||||||
return nil if ! (is_windows and @@loaded_win32api)
|
|
||||||
exe = Win32API.new("kernel32", "WinExec", ["PL"], "L")
|
|
||||||
exe.call(cmd, 0)
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# Verify the Console2 environment
|
|
||||||
#
|
|
||||||
def self.win32_console2_verify
|
|
||||||
return nil if ! (is_windows and @@loaded_win32api)
|
|
||||||
buf = "\x00" * 512
|
|
||||||
out = Win32API.new("kernel32", "GetStdHandle", ["L"], "L").call(STD_OUTPUT_HANDLE)
|
|
||||||
res = Win32API.new("kernel32","GetConsoleTitle", ["PL"], "L").call(buf, buf.length-1) rescue 0
|
|
||||||
( res > 0 and buf.index("Console2 command").nil? ) ? false : true
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# Expand a 8.3 path to a full path
|
|
||||||
#
|
|
||||||
def self.win32_expand_path(path)
|
|
||||||
return nil if ! (is_windows and @@loaded_win32api)
|
|
||||||
glp = Win32API.new('kernel32', 'GetLongPathName', 'PPL', 'L')
|
|
||||||
buf = "\x00" * 260
|
|
||||||
len = glp.call(path, buf, buf.length)
|
|
||||||
buf[0, len]
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# Platform independent socket pair
|
|
||||||
#
|
|
||||||
def self.pipe
|
|
||||||
|
|
||||||
if (! is_windows())
|
|
||||||
# Standard pipes should be fine
|
|
||||||
return ::IO.pipe
|
|
||||||
end
|
|
||||||
|
|
||||||
# Create a socket connection for Windows
|
|
||||||
serv = nil
|
|
||||||
port = 1024
|
|
||||||
|
|
||||||
while (! serv and port < 65535)
|
|
||||||
begin
|
|
||||||
serv = TCPServer.new('127.0.0.1', (port += 1))
|
|
||||||
rescue ::Exception
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
pipe1 = TCPSocket.new('127.0.0.1', port)
|
|
||||||
|
|
||||||
# Accept the forked child
|
|
||||||
pipe2 = serv.accept
|
|
||||||
|
|
||||||
# Shutdown the server
|
|
||||||
serv.close
|
|
||||||
|
|
||||||
return [pipe1, pipe2]
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# Copy a file to a temporary path
|
|
||||||
#
|
|
||||||
|
|
||||||
def self.temp_copy(path)
|
|
||||||
raise RuntimeError,"missing Tempfile" if not @@loaded_tempfile
|
|
||||||
fd = File.open(path, "rb")
|
|
||||||
tp = Tempfile.new("msftemp")
|
|
||||||
tp.binmode
|
|
||||||
tp.write(fd.read(File.size(path)))
|
|
||||||
tp.close
|
|
||||||
fd.close
|
|
||||||
tp
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# Delete an opened temporary file
|
|
||||||
#
|
|
||||||
|
|
||||||
def self.temp_delete(tp)
|
|
||||||
raise RuntimeError,"missing FileUtils" if not @@loaded_fileutils
|
|
||||||
begin
|
|
||||||
FileUtils.rm(tp.path)
|
|
||||||
rescue
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# Initialization
|
|
||||||
#
|
|
||||||
|
|
||||||
if(is_windows or is_cygwin)
|
|
||||||
begin
|
|
||||||
require "Win32API"
|
|
||||||
@@loaded_win32api = true
|
|
||||||
rescue ::Exception
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
begin
|
|
||||||
require "tempfile"
|
|
||||||
@@loaded_tempfile = true
|
|
||||||
rescue ::Exception
|
|
||||||
end
|
|
||||||
|
|
||||||
begin
|
|
||||||
require "fileutils"
|
|
||||||
@@loaded_fileutils = true
|
|
||||||
rescue ::Exception
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
# -*- coding: binary -*-
|
|
||||||
|
|
||||||
module Rex
|
|
||||||
module ElfParsey
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
require 'rex/elfparsey/elf'
|
|
|
@ -1,121 +0,0 @@
|
||||||
# -*- coding: binary -*-
|
|
||||||
|
|
||||||
require 'rex/elfparsey/elfbase'
|
|
||||||
require 'rex/elfparsey/exceptions'
|
|
||||||
require 'rex/image_source'
|
|
||||||
|
|
||||||
module Rex
|
|
||||||
module ElfParsey
|
|
||||||
class Elf < ElfBase
|
|
||||||
|
|
||||||
attr_accessor :elf_header, :program_header, :base_addr, :isource
|
|
||||||
|
|
||||||
def initialize(isource)
|
|
||||||
offset = 0
|
|
||||||
base_addr = 0
|
|
||||||
|
|
||||||
# ELF Header
|
|
||||||
elf_header = ElfHeader.new(isource.read(offset, ELF_HEADER_SIZE))
|
|
||||||
|
|
||||||
# Data encoding
|
|
||||||
ei_data = elf_header.e_ident[EI_DATA,1].unpack("C")[0]
|
|
||||||
|
|
||||||
e_phoff = elf_header.e_phoff
|
|
||||||
e_phentsize = elf_header.e_phentsize
|
|
||||||
e_phnum = elf_header.e_phnum
|
|
||||||
|
|
||||||
# Program Header Table
|
|
||||||
program_header = []
|
|
||||||
|
|
||||||
e_phnum.times do |i|
|
|
||||||
offset = e_phoff + (e_phentsize * i)
|
|
||||||
|
|
||||||
program_header << ProgramHeader.new(
|
|
||||||
isource.read(offset, PROGRAM_HEADER_SIZE), ei_data
|
|
||||||
)
|
|
||||||
|
|
||||||
if program_header[-1].p_type == PT_LOAD && program_header[-1].p_flags & PF_EXEC > 0
|
|
||||||
base_addr = program_header[-1].p_vaddr
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
self.elf_header = elf_header
|
|
||||||
self.program_header = program_header
|
|
||||||
self.base_addr = base_addr
|
|
||||||
self.isource = isource
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.new_from_file(filename, disk_backed = false)
|
|
||||||
|
|
||||||
file = ::File.new(filename)
|
|
||||||
# file.binmode # windows... :\
|
|
||||||
|
|
||||||
if disk_backed
|
|
||||||
return self.new(ImageSource::Disk.new(file))
|
|
||||||
else
|
|
||||||
obj = new_from_string(file.read)
|
|
||||||
file.close
|
|
||||||
return obj
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.new_from_string(data)
|
|
||||||
return self.new(ImageSource::Memory.new(data))
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# Returns true if this binary is for a 64-bit architecture.
|
|
||||||
#
|
|
||||||
def ptr_64?
|
|
||||||
unless [ ELFCLASS32, ELFCLASS64 ].include?(
|
|
||||||
elf_header.e_ident[EI_CLASS,1].unpack("C*")[0])
|
|
||||||
raise ElfHeaderError, 'Invalid class', caller
|
|
||||||
end
|
|
||||||
|
|
||||||
elf_header.e_ident[EI_CLASS,1].unpack("C*")[0] == ELFCLASS64
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# Returns true if this binary is for a 32-bit architecture.
|
|
||||||
# This check does not take into account 16-bit binaries at the moment.
|
|
||||||
#
|
|
||||||
def ptr_32?
|
|
||||||
ptr_64? == false
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# Converts a virtual address to a string representation based on the
|
|
||||||
# underlying architecture.
|
|
||||||
#
|
|
||||||
def ptr_s(rva)
|
|
||||||
(ptr_32?) ? ("0x%.8x" % rva) : ("0x%.16x" % rva)
|
|
||||||
end
|
|
||||||
|
|
||||||
def offset_to_rva(offset)
|
|
||||||
base_addr + offset
|
|
||||||
end
|
|
||||||
|
|
||||||
def rva_to_offset(rva)
|
|
||||||
rva - base_addr
|
|
||||||
end
|
|
||||||
|
|
||||||
def read(offset, len)
|
|
||||||
isource.read(offset, len)
|
|
||||||
end
|
|
||||||
|
|
||||||
def read_rva(rva, len)
|
|
||||||
isource.read(rva_to_offset(rva), len)
|
|
||||||
end
|
|
||||||
|
|
||||||
def index(*args)
|
|
||||||
isource.index(*args)
|
|
||||||
end
|
|
||||||
|
|
||||||
def close
|
|
||||||
isource.close
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,265 +0,0 @@
|
||||||
# -*- coding: binary -*-
|
|
||||||
|
|
||||||
require 'rex/struct2'
|
|
||||||
|
|
||||||
module Rex
|
|
||||||
module ElfParsey
|
|
||||||
class ElfBase
|
|
||||||
|
|
||||||
# ELF Header
|
|
||||||
|
|
||||||
ELF_HEADER_SIZE = 52
|
|
||||||
|
|
||||||
EI_NIDENT = 16
|
|
||||||
|
|
||||||
ELF32_EHDR_LSB = Rex::Struct2::CStructTemplate.new(
|
|
||||||
[ 'string', 'e_ident', EI_NIDENT, '' ],
|
|
||||||
[ 'uint16v', 'e_type', 0 ],
|
|
||||||
[ 'uint16v', 'e_machine', 0 ],
|
|
||||||
[ 'uint32v', 'e_version', 0 ],
|
|
||||||
[ 'uint32v', 'e_entry', 0 ],
|
|
||||||
[ 'uint32v', 'e_phoff', 0 ],
|
|
||||||
[ 'uint32v', 'e_shoff', 0 ],
|
|
||||||
[ 'uint32v', 'e_flags', 0 ],
|
|
||||||
[ 'uint16v', 'e_ehsize', 0 ],
|
|
||||||
[ 'uint16v', 'e_phentsize', 0 ],
|
|
||||||
[ 'uint16v', 'e_phnum', 0 ],
|
|
||||||
[ 'uint16v', 'e_shentsize', 0 ],
|
|
||||||
[ 'uint16v', 'e_shnum', 0 ],
|
|
||||||
[ 'uint16v', 'e_shstrndx', 0 ]
|
|
||||||
)
|
|
||||||
|
|
||||||
ELF32_EHDR_MSB = Rex::Struct2::CStructTemplate.new(
|
|
||||||
[ 'string', 'e_ident', EI_NIDENT, '' ],
|
|
||||||
[ 'uint16n', 'e_type', 0 ],
|
|
||||||
[ 'uint16n', 'e_machine', 0 ],
|
|
||||||
[ 'uint32n', 'e_version', 0 ],
|
|
||||||
[ 'uint32n', 'e_entry', 0 ],
|
|
||||||
[ 'uint32n', 'e_phoff', 0 ],
|
|
||||||
[ 'uint32n', 'e_shoff', 0 ],
|
|
||||||
[ 'uint32n', 'e_flags', 0 ],
|
|
||||||
[ 'uint16n', 'e_ehsize', 0 ],
|
|
||||||
[ 'uint16n', 'e_phentsize', 0 ],
|
|
||||||
[ 'uint16n', 'e_phnum', 0 ],
|
|
||||||
[ 'uint16n', 'e_shentsize', 0 ],
|
|
||||||
[ 'uint16n', 'e_shnum', 0 ],
|
|
||||||
[ 'uint16n', 'e_shstrndx', 0 ]
|
|
||||||
)
|
|
||||||
|
|
||||||
# e_type This member identifies the object file type
|
|
||||||
|
|
||||||
ET_NONE = 0 # No file type
|
|
||||||
ET_REL = 1 # Relocatable file
|
|
||||||
ET_EXEC = 2 # Executable file
|
|
||||||
ET_DYN = 3 # Shared object file
|
|
||||||
ET_CORE = 4 # Core file
|
|
||||||
ET_LOPROC = 0xff00 # Processor-specific
|
|
||||||
ET_HIPROC = 0xffff # Processor-specific
|
|
||||||
|
|
||||||
#
|
|
||||||
# e_machine This member's value specifies the required architecture for an
|
|
||||||
# individual file.
|
|
||||||
#
|
|
||||||
|
|
||||||
# ET_NONE = 0 # No machine
|
|
||||||
EM_M32 = 1 # AT&T WE 32100
|
|
||||||
EM_SPARC = 2 # SPARC
|
|
||||||
EM_386 = 3 # Intel Architecture
|
|
||||||
EM_68K = 4 # Motorola 68000
|
|
||||||
EM_88K = 5 # Motorola 88000
|
|
||||||
EM_860 = 7 # Intel 80860
|
|
||||||
EM_MIPS = 8 # MIPS RS3000 Big-Endian
|
|
||||||
EM_MIPS_RS4_BE = 10 # MIPS RS4000 Big-Endian
|
|
||||||
|
|
||||||
# e_version This member identifies the object file version
|
|
||||||
|
|
||||||
EV_NONE = 0 # Invalid version
|
|
||||||
EV_CURRENT = 1 # Current version
|
|
||||||
|
|
||||||
|
|
||||||
# ELF Identification
|
|
||||||
|
|
||||||
# e_ident[] Identification indexes
|
|
||||||
|
|
||||||
EI_MAG0 = 0 # File identification
|
|
||||||
EI_MAG1 = 1 # File identification
|
|
||||||
EI_MAG2 = 2 # File identification
|
|
||||||
EI_MAG3 = 3 # File identification
|
|
||||||
EI_CLASS = 4 # File class
|
|
||||||
EI_DATA = 5 # Data encoding
|
|
||||||
EI_VERSION = 6 # File version
|
|
||||||
EI_PAD = 7 # Start of padding bytes
|
|
||||||
# EI_NIDENT = 16 # Size of e_ident[]
|
|
||||||
|
|
||||||
#
|
|
||||||
# EI_MAG0 to EI_MAG3 A file's first 4 bytes hold a "magic number",
|
|
||||||
# identifying the file as an ELF object file.
|
|
||||||
#
|
|
||||||
|
|
||||||
ELFMAG0 = 0x7f # e_ident[EI_MAG0]
|
|
||||||
ELFMAG1 = ?E # e_ident[EI_MAG1]
|
|
||||||
ELFMAG2 = ?L # e_ident[EI_MAG2]
|
|
||||||
ELFMAG3 = ?F # e_ident[EI_MAG3]
|
|
||||||
|
|
||||||
ELFMAG = ELFMAG0.chr + ELFMAG1.chr + ELFMAG2.chr + ELFMAG3.chr
|
|
||||||
|
|
||||||
# EI_CLASS Identifies the file's class, or capacity
|
|
||||||
|
|
||||||
ELFCLASSNONE = 0 # Invalid class
|
|
||||||
ELFCLASS32 = 1 # 32-bit objects
|
|
||||||
ELFCLASS64 = 2 # 64-bit objects
|
|
||||||
|
|
||||||
#
|
|
||||||
# EI_DATA Specifies the data encoding of the processor-specific data in
|
|
||||||
# the object file. The following encodings are currently defined.
|
|
||||||
#
|
|
||||||
|
|
||||||
ELFDATANONE = 0 # Invalid data encoding
|
|
||||||
ELFDATA2LSB = 1 # Least significant byte first
|
|
||||||
ELFDATA2MSB = 2 # Most significant byte first
|
|
||||||
|
|
||||||
class GenericStruct
|
|
||||||
attr_accessor :struct
|
|
||||||
def initialize(_struct)
|
|
||||||
self.struct = _struct
|
|
||||||
end
|
|
||||||
|
|
||||||
# The following methods are just pass-throughs for struct
|
|
||||||
|
|
||||||
# Access a value
|
|
||||||
def v
|
|
||||||
struct.v
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
# Access a value by array
|
|
||||||
def [](*args)
|
|
||||||
struct[*args]
|
|
||||||
end
|
|
||||||
|
|
||||||
# Obtain an array of all fields
|
|
||||||
def keys
|
|
||||||
struct.keys
|
|
||||||
end
|
|
||||||
|
|
||||||
def method_missing(meth, *args)
|
|
||||||
v[meth.to_s] || (raise NoMethodError.new, meth)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class GenericHeader < GenericStruct
|
|
||||||
end
|
|
||||||
|
|
||||||
class ElfHeader < GenericHeader
|
|
||||||
def initialize(rawdata)
|
|
||||||
|
|
||||||
# Identify the data encoding and parse ELF Header
|
|
||||||
elf_header = ELF32_EHDR_LSB.make_struct
|
|
||||||
|
|
||||||
if !elf_header.from_s(rawdata)
|
|
||||||
raise ElfHeaderError, "Couldn't parse ELF Header", caller
|
|
||||||
end
|
|
||||||
|
|
||||||
if elf_header.v['e_ident'][EI_DATA,1].unpack('C')[0] == ELFDATA2MSB
|
|
||||||
elf_header = ELF32_EHDR_MSB.make_struct
|
|
||||||
|
|
||||||
if !elf_header.from_s(rawdata)
|
|
||||||
raise ElfHeaderError, "Couldn't parse ELF Header", caller
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
unless [ ELFDATA2LSB, ELFDATA2MSB ].include?(
|
|
||||||
elf_header.v['e_ident'][EI_DATA,1].unpack('C')[0])
|
|
||||||
raise ElfHeaderError, "Invalid data encoding", caller
|
|
||||||
end
|
|
||||||
|
|
||||||
# Identify the file as an ELF object file
|
|
||||||
unless elf_header.v['e_ident'][EI_MAG0, 4] == ELFMAG
|
|
||||||
raise ElfHeaderError, 'Invalid magic number', caller
|
|
||||||
end
|
|
||||||
|
|
||||||
self.struct = elf_header
|
|
||||||
end
|
|
||||||
|
|
||||||
def e_ident
|
|
||||||
struct.v['e_ident']
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# Program Header
|
|
||||||
|
|
||||||
PROGRAM_HEADER_SIZE = 32
|
|
||||||
|
|
||||||
ELF32_PHDR_LSB = Rex::Struct2::CStructTemplate.new(
|
|
||||||
[ 'uint32v', 'p_type', 0 ],
|
|
||||||
[ 'uint32v', 'p_offset', 0 ],
|
|
||||||
[ 'uint32v', 'p_vaddr', 0 ],
|
|
||||||
[ 'uint32v', 'p_paddr', 0 ],
|
|
||||||
[ 'uint32v', 'p_filesz', 0 ],
|
|
||||||
[ 'uint32v', 'p_memsz', 0 ],
|
|
||||||
[ 'uint32v', 'p_flags', 0 ],
|
|
||||||
[ 'uint32v', 'p_align', 0 ]
|
|
||||||
)
|
|
||||||
|
|
||||||
ELF32_PHDR_MSB = Rex::Struct2::CStructTemplate.new(
|
|
||||||
[ 'uint32n', 'p_type', 0 ],
|
|
||||||
[ 'uint32n', 'p_offset', 0 ],
|
|
||||||
[ 'uint32n', 'p_vaddr', 0 ],
|
|
||||||
[ 'uint32n', 'p_paddr', 0 ],
|
|
||||||
[ 'uint32n', 'p_filesz', 0 ],
|
|
||||||
[ 'uint32n', 'p_memsz', 0 ],
|
|
||||||
[ 'uint32n', 'p_flags', 0 ],
|
|
||||||
[ 'uint32n', 'p_align', 0 ]
|
|
||||||
)
|
|
||||||
|
|
||||||
# p_flags This member tells which permissions should have the segment
|
|
||||||
|
|
||||||
# Flags
|
|
||||||
|
|
||||||
PF_EXEC = 1
|
|
||||||
PF_WRITE = 2
|
|
||||||
PF_READ = 4
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# p_type This member tells what kind of segment this array element
|
|
||||||
# describes or how to interpret the array element's information.
|
|
||||||
#
|
|
||||||
|
|
||||||
# Segment Types
|
|
||||||
|
|
||||||
PT_NULL = 0
|
|
||||||
PT_LOAD = 1
|
|
||||||
PT_DYNAMIC = 2
|
|
||||||
PT_INTERP = 3
|
|
||||||
PT_NOTE = 4
|
|
||||||
PT_SHLIB = 5
|
|
||||||
PT_PHDR = 6
|
|
||||||
PT_LOPROC = 0x70000000
|
|
||||||
PT_HIPROC = 0x7fffffff
|
|
||||||
|
|
||||||
class ProgramHeader < GenericHeader
|
|
||||||
def initialize(rawdata, ei_data)
|
|
||||||
# Identify the data encoding and parse Program Header
|
|
||||||
if ei_data == ELFDATA2LSB
|
|
||||||
program_header = ELF32_PHDR_LSB.make_struct
|
|
||||||
elsif ei_data == ELFDATA2MSB
|
|
||||||
program_header = ELF32_PHDR_MSB.make_struct
|
|
||||||
else
|
|
||||||
raise ElfHeaderError, "Invalid data encoding", caller
|
|
||||||
end
|
|
||||||
|
|
||||||
if !program_header.from_s(rawdata)
|
|
||||||
raise ProgramHeaderError, "Couldn't parse Program Header", caller
|
|
||||||
end
|
|
||||||
|
|
||||||
self.struct = program_header
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,25 +0,0 @@
|
||||||
# -*- coding: binary -*-
|
|
||||||
|
|
||||||
module Rex
|
|
||||||
module ElfParsey
|
|
||||||
|
|
||||||
class ElfError < ::RuntimeError
|
|
||||||
end
|
|
||||||
|
|
||||||
class ParseError < ElfError
|
|
||||||
end
|
|
||||||
|
|
||||||
class ElfHeaderError < ParseError
|
|
||||||
end
|
|
||||||
|
|
||||||
class ProgramHeaderError < ParseError
|
|
||||||
end
|
|
||||||
|
|
||||||
class BoundsError < ElfError
|
|
||||||
end
|
|
||||||
|
|
||||||
class ElfParseyError < ElfError
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,10 +0,0 @@
|
||||||
# -*- coding: binary -*-
|
|
||||||
|
|
||||||
module Rex
|
|
||||||
module ElfScan
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
require 'rex/elfscan/scanner'
|
|
||||||
require 'rex/elfscan/search'
|
|
|
@ -1,226 +0,0 @@
|
||||||
# -*- coding: binary -*-
|
|
||||||
require 'metasm'
|
|
||||||
|
|
||||||
module Rex
|
|
||||||
module ElfScan
|
|
||||||
module Scanner
|
|
||||||
class Generic
|
|
||||||
|
|
||||||
attr_accessor :elf, :regex
|
|
||||||
|
|
||||||
def initialize(elf)
|
|
||||||
self.elf = elf
|
|
||||||
end
|
|
||||||
|
|
||||||
def config(param)
|
|
||||||
end
|
|
||||||
|
|
||||||
def scan(param)
|
|
||||||
config(param)
|
|
||||||
|
|
||||||
$stdout.puts "[#{param['file']}]"
|
|
||||||
elf.program_header.each do |program_header|
|
|
||||||
|
|
||||||
# Scan only loadable segment entries in the program header table
|
|
||||||
if program_header.p_type == Rex::ElfParsey::ElfBase::PT_LOAD
|
|
||||||
hits = scan_segment(program_header, param)
|
|
||||||
hits.each do |hit|
|
|
||||||
rva = hit[0]
|
|
||||||
message = hit[1].is_a?(Array) ? hit[1].join(" ") : hit[1]
|
|
||||||
$stdout.puts elf.ptr_s(rva) + " " + message
|
|
||||||
if(param['disasm'])
|
|
||||||
message.gsub!("; ", "\n")
|
|
||||||
if message.include?("retn")
|
|
||||||
message.gsub!("retn", "ret")
|
|
||||||
end
|
|
||||||
|
|
||||||
begin
|
|
||||||
d2 = Metasm::Shellcode.assemble(Metasm::Ia32.new, message).disassemble
|
|
||||||
rescue Metasm::ParseError
|
|
||||||
d2 = Metasm::Shellcode.disassemble(Metasm::Ia32.new, [message].pack('H*'))
|
|
||||||
end
|
|
||||||
|
|
||||||
addr = 0
|
|
||||||
while ((di = d2.disassemble_instruction(addr)))
|
|
||||||
disasm = "0x%08x\t" % (rva + addr)
|
|
||||||
disasm << di.instruction.to_s
|
|
||||||
$stdout.puts disasm
|
|
||||||
addr = di.next_addr
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def scan_segment(program_header, param={})
|
|
||||||
[]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class JmpRegScanner < Generic
|
|
||||||
|
|
||||||
def config(param)
|
|
||||||
regnums = param['args']
|
|
||||||
|
|
||||||
# build a list of the call bytes
|
|
||||||
calls = _build_byte_list(0xd0, regnums - [4]) # note call esp's don't work..
|
|
||||||
jmps = _build_byte_list(0xe0, regnums)
|
|
||||||
pushs1 = _build_byte_list(0x50, regnums)
|
|
||||||
pushs2 = _build_byte_list(0xf0, regnums)
|
|
||||||
|
|
||||||
regexstr = '('
|
|
||||||
if !calls.empty?
|
|
||||||
regexstr += "\xff[#{calls}]|"
|
|
||||||
end
|
|
||||||
|
|
||||||
regexstr += "\xff[#{jmps}]|([#{pushs1}]|\xff[#{pushs2}])(\xc3|\xc2..))"
|
|
||||||
|
|
||||||
self.regex = Regexp.new(regexstr, nil, 'n')
|
|
||||||
end
|
|
||||||
|
|
||||||
# build a list for regex of the possible bytes, based on a base
|
|
||||||
# byte and a list of register numbers..
|
|
||||||
def _build_byte_list(base, regnums)
|
|
||||||
regnums.collect { |regnum| Regexp.escape((base | regnum).chr) }.join('')
|
|
||||||
end
|
|
||||||
|
|
||||||
def _ret_size(offset)
|
|
||||||
case elf.read(offset, 1)
|
|
||||||
when "\xc3"
|
|
||||||
return 1
|
|
||||||
when "\xc2"
|
|
||||||
return 3
|
|
||||||
end
|
|
||||||
|
|
||||||
raise "Cannot read at offset: #{offset}"
|
|
||||||
end
|
|
||||||
|
|
||||||
def _parse_ret(data)
|
|
||||||
if data.length == 1
|
|
||||||
return "ret"
|
|
||||||
else
|
|
||||||
return "retn 0x%04x" % data[1, 2].unpack('v')[0]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
def scan_segment(program_header, param={})
|
|
||||||
offset = program_header.p_offset
|
|
||||||
|
|
||||||
hits = []
|
|
||||||
|
|
||||||
while (offset = elf.index(regex, offset)) != nil
|
|
||||||
|
|
||||||
rva = elf.offset_to_rva(offset)
|
|
||||||
message = ''
|
|
||||||
|
|
||||||
parse_ret = false
|
|
||||||
|
|
||||||
byte1 = elf.read(offset, 1).unpack('C')[0]
|
|
||||||
|
|
||||||
if byte1 == 0xff
|
|
||||||
byte2 = elf.read(offset+1, 1).unpack('C')[0]
|
|
||||||
regname = Rex::Arch::X86.reg_name32(byte2 & 0x7)
|
|
||||||
|
|
||||||
case byte2 & 0xf8
|
|
||||||
when 0xd0
|
|
||||||
message = "call #{regname}"
|
|
||||||
offset += 2
|
|
||||||
when 0xe0
|
|
||||||
message = "jmp #{regname}"
|
|
||||||
offset += 2
|
|
||||||
when 0xf0
|
|
||||||
retsize = _ret_size(offset+2)
|
|
||||||
message = "push #{regname}; " + _parse_ret(elf.read(offset+2, retsize))
|
|
||||||
offset += 2 + retsize
|
|
||||||
else
|
|
||||||
raise "Unexpected value at #{offset}"
|
|
||||||
end
|
|
||||||
else
|
|
||||||
regname = Rex::Arch::X86.reg_name32(byte1 & 0x7)
|
|
||||||
retsize = _ret_size(offset+1)
|
|
||||||
message = "push #{regname}; " + _parse_ret(elf.read(offset+1, retsize))
|
|
||||||
offset += 1 + retsize
|
|
||||||
end
|
|
||||||
|
|
||||||
hits << [ rva, message ]
|
|
||||||
end
|
|
||||||
|
|
||||||
return hits
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class PopPopRetScanner < JmpRegScanner
|
|
||||||
|
|
||||||
def config(param)
|
|
||||||
pops = _build_byte_list(0x58, (0 .. 7).to_a - [4]) # we don't want pop esp's...
|
|
||||||
self.regex = Regexp.new("[#{pops}][#{pops}](\xc3|\xc2..)", nil, 'n')
|
|
||||||
end
|
|
||||||
|
|
||||||
def scan_segment(program_header, param={})
|
|
||||||
offset = program_header.p_offset
|
|
||||||
|
|
||||||
hits = []
|
|
||||||
|
|
||||||
while offset < program_header.p_offset + program_header.p_filesz &&
|
|
||||||
(offset = elf.index(regex, offset)) != nil
|
|
||||||
|
|
||||||
rva = elf.offset_to_rva(offset)
|
|
||||||
message = ''
|
|
||||||
|
|
||||||
pops = elf.read(offset, 2)
|
|
||||||
reg1 = Rex::Arch::X86.reg_name32(pops[0,1].unpack('C*')[0] & 0x7)
|
|
||||||
reg2 = Rex::Arch::X86.reg_name32(pops[1,1].unpack('C*')[0] & 0x7)
|
|
||||||
|
|
||||||
message = "pop #{reg1}; pop #{reg2}; "
|
|
||||||
|
|
||||||
retsize = _ret_size(offset+2)
|
|
||||||
message += _parse_ret(elf.read(offset+2, retsize))
|
|
||||||
|
|
||||||
offset += 2 + retsize
|
|
||||||
|
|
||||||
hits << [ rva, message ]
|
|
||||||
end
|
|
||||||
|
|
||||||
return hits
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class RegexScanner < JmpRegScanner
|
|
||||||
|
|
||||||
def config(param)
|
|
||||||
self.regex = Regexp.new(param['args'], nil, 'n')
|
|
||||||
end
|
|
||||||
|
|
||||||
def scan_segment(program_header, param={})
|
|
||||||
offset = program_header.p_offset
|
|
||||||
|
|
||||||
hits = []
|
|
||||||
|
|
||||||
while offset < program_header.p_offset + program_header.p_filesz &&
|
|
||||||
(offset = elf.index(regex, offset)) != nil
|
|
||||||
|
|
||||||
idx = offset
|
|
||||||
buf = ''
|
|
||||||
mat = nil
|
|
||||||
|
|
||||||
while (! (mat = buf.match(regex)))
|
|
||||||
buf << elf.read(idx, 1)
|
|
||||||
idx += 1
|
|
||||||
end
|
|
||||||
|
|
||||||
rva = elf.offset_to_rva(offset)
|
|
||||||
|
|
||||||
hits << [ rva, buf.unpack("H*") ]
|
|
||||||
offset += buf.length
|
|
||||||
end
|
|
||||||
|
|
||||||
return hits
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,44 +0,0 @@
|
||||||
# -*- coding: binary -*-
|
|
||||||
|
|
||||||
module Rex
|
|
||||||
module ElfScan
|
|
||||||
module Search
|
|
||||||
|
|
||||||
class DumpRVA
|
|
||||||
attr_accessor :elf
|
|
||||||
|
|
||||||
def initialize(elf)
|
|
||||||
self.elf = elf
|
|
||||||
end
|
|
||||||
|
|
||||||
def config(param)
|
|
||||||
@address = param['args']
|
|
||||||
end
|
|
||||||
|
|
||||||
def scan(param)
|
|
||||||
config(param)
|
|
||||||
|
|
||||||
$stdout.puts "[#{param['file']}]"
|
|
||||||
|
|
||||||
# Adjust based on -A and -B flags
|
|
||||||
pre = param['before'] || 0
|
|
||||||
suf = param['after'] || 16
|
|
||||||
|
|
||||||
@address -= pre
|
|
||||||
@address = 0 if (@address < 0 || ! @address)
|
|
||||||
buf = elf.read_rva(@address, suf)
|
|
||||||
$stdout.puts elf.ptr_s(@address) + " " + buf.unpack("H*")[0]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class DumpOffset < DumpRVA
|
|
||||||
def config(param)
|
|
||||||
begin
|
|
||||||
@address = elf.offset_to_rva(param['args'])
|
|
||||||
rescue Rex::ElfParsey::BoundsError
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
160
lib/rex/file.rb
160
lib/rex/file.rb
|
@ -1,160 +0,0 @@
|
||||||
# -*- coding: binary -*-
|
|
||||||
require 'find'
|
|
||||||
require 'rex/compat'
|
|
||||||
require 'tempfile'
|
|
||||||
|
|
||||||
module Rex
|
|
||||||
|
|
||||||
###
|
|
||||||
#
|
|
||||||
# This class provides helper methods for dealing with files that are not
|
|
||||||
# supplied by the standard ruby API.
|
|
||||||
#
|
|
||||||
###
|
|
||||||
module FileUtils
|
|
||||||
|
|
||||||
#
|
|
||||||
# This method joins the paths together in Unix format.
|
|
||||||
#
|
|
||||||
def self.normalize_unix_path(*strs)
|
|
||||||
new_str = strs * '/'
|
|
||||||
new_str = new_str.gsub!("//", "/") while new_str.index("//")
|
|
||||||
|
|
||||||
new_str
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# This method joins the paths together in Windows format.
|
|
||||||
# All reserved characters will be filtered out, including:
|
|
||||||
# " * : < > ? \ / |
|
|
||||||
#
|
|
||||||
def self.normalize_win_path(*strs)
|
|
||||||
# Convert to the same format so the parsing is easier
|
|
||||||
s = strs * '\\'
|
|
||||||
|
|
||||||
# Filter out double slashes
|
|
||||||
s = s.gsub(/\\\\/, '\\') while s.index('\\\\')
|
|
||||||
|
|
||||||
# Keep the trailing slash if exists
|
|
||||||
trailing_s = ('\\' if s =~ /\\$/) || ''
|
|
||||||
|
|
||||||
# Check the items (fie/dir) individually
|
|
||||||
s = s.split(/\\/)
|
|
||||||
|
|
||||||
# Parse the path prefix
|
|
||||||
prefix = (s[0] || '').gsub(/[\*<>\?\/]/, '')
|
|
||||||
|
|
||||||
# Delete the original prefix. We want the new one later.
|
|
||||||
s.delete_at(0)
|
|
||||||
|
|
||||||
# Filter out all the reserved characters
|
|
||||||
s.map! {|e| e.gsub(/["\*:<>\?\\\/|]/, '') }
|
|
||||||
|
|
||||||
# Put the modified prefix back
|
|
||||||
s.insert(0, prefix)
|
|
||||||
|
|
||||||
# And then safely join the items
|
|
||||||
s *= '\\'
|
|
||||||
|
|
||||||
# Add the trailing slash back if exists
|
|
||||||
s << trailing_s
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# This method cleans the supplied path of directory traversal sequences
|
|
||||||
# It must accept path/with/..a/folder../starting/or/ending/in/two/dots
|
|
||||||
# but clean ../something as well as path/with/..\traversal
|
|
||||||
#
|
|
||||||
def self.clean_path(old)
|
|
||||||
path = old
|
|
||||||
while(path.index(/\/..\/|\/..\\|\\..\\|\\..\/|\A..\\|\A..\//) != nil)
|
|
||||||
path.gsub!(/\A..\\|\A..\//,'') #eliminate starting ..\ or ../
|
|
||||||
path.gsub!(/\/..\/|\/..\\/,'/') #clean linux style
|
|
||||||
path.gsub!(/\\..\\|\\..\//,'\\') #clean windows style
|
|
||||||
end
|
|
||||||
path
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# This method searches the PATH environment variable for
|
|
||||||
# a fully qualified path to the supplied file name.
|
|
||||||
#
|
|
||||||
def self.find_full_path(file_name)
|
|
||||||
|
|
||||||
# Check for the absolute fast first
|
|
||||||
if (file_name[0,1] == "/" and ::File.exist?(file_name) and ::File::Stat.new(file_name))
|
|
||||||
return file_name
|
|
||||||
end
|
|
||||||
|
|
||||||
path = Rex::Compat.getenv('PATH')
|
|
||||||
if (path)
|
|
||||||
path.split(::File::PATH_SEPARATOR).each { |base|
|
|
||||||
begin
|
|
||||||
# Deal with Windows paths surrounded by quotes. Prevents
|
|
||||||
# silliness like trying to look for
|
|
||||||
# '"C:\\framework\\nmap"\\nmap.exe' which will always fail.
|
|
||||||
base = $1 if base =~ /^"(.*)"$/
|
|
||||||
path = base + ::File::SEPARATOR + file_name
|
|
||||||
if (::File::Stat.new(path) and not ::File.directory?(path))
|
|
||||||
return path
|
|
||||||
end
|
|
||||||
rescue
|
|
||||||
end
|
|
||||||
}
|
|
||||||
end
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
class Quickfile < ::Tempfile
|
|
||||||
def initialize(*args)
|
|
||||||
super(*args)
|
|
||||||
self.binmode
|
|
||||||
ObjectSpace.undefine_finalizer(self)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
module Find
|
|
||||||
#
|
|
||||||
# Identical to Find.find from Ruby, but follows symlinks to directories.
|
|
||||||
# See http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/68671
|
|
||||||
#
|
|
||||||
def self.find(*paths)
|
|
||||||
paths.collect!{|d| d.dup}
|
|
||||||
while file = paths.shift
|
|
||||||
catch(:prune) do
|
|
||||||
yield file.dup.taint
|
|
||||||
next unless File.exist? file
|
|
||||||
begin
|
|
||||||
if File.stat(file).directory? then
|
|
||||||
d = Dir.open(file)
|
|
||||||
begin
|
|
||||||
for f in d
|
|
||||||
next if f == "." or f == ".."
|
|
||||||
if File::ALT_SEPARATOR and file =~ /^(?:[\/\\]|[A-Za-z]:[\/\\]?)$/ then
|
|
||||||
f = file + f
|
|
||||||
elsif file == "/" then
|
|
||||||
f = "/" + f
|
|
||||||
else
|
|
||||||
f = File.join(file, f)
|
|
||||||
end
|
|
||||||
paths.unshift f.untaint
|
|
||||||
end
|
|
||||||
ensure
|
|
||||||
d.close
|
|
||||||
end
|
|
||||||
end
|
|
||||||
rescue Errno::ENOENT, Errno::EACCES
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.prune
|
|
||||||
throw :prune
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
|
@ -1,10 +0,0 @@
|
||||||
# -*- coding: binary -*-
|
|
||||||
|
|
||||||
module Rex
|
|
||||||
module ImageSource
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
require 'rex/image_source/disk'
|
|
||||||
require 'rex/image_source/memory'
|
|
|
@ -1,58 +0,0 @@
|
||||||
# -*- coding: binary -*-
|
|
||||||
|
|
||||||
require 'rex/image_source/image_source'
|
|
||||||
require 'rex/struct2'
|
|
||||||
|
|
||||||
module Rex
|
|
||||||
module ImageSource
|
|
||||||
class Disk < ImageSource
|
|
||||||
|
|
||||||
attr_accessor :file, :file_offset, :size
|
|
||||||
|
|
||||||
WINDOW_SIZE = 4096
|
|
||||||
WINDOW_OVERLAP = 64
|
|
||||||
|
|
||||||
def initialize(_file, _offset = 0, _len = nil)
|
|
||||||
_len = _file.stat.size if !_len
|
|
||||||
|
|
||||||
self.file = _file
|
|
||||||
self.file_offset = _offset
|
|
||||||
self.size = _len
|
|
||||||
end
|
|
||||||
|
|
||||||
def read(offset, len)
|
|
||||||
if offset < 0 || offset+len > size
|
|
||||||
raise RangeError, "Offset #{offset} outside of image source", caller
|
|
||||||
end
|
|
||||||
|
|
||||||
file.seek(file_offset + offset)
|
|
||||||
file.read(len)
|
|
||||||
end
|
|
||||||
|
|
||||||
def index(search, offset = 0)
|
|
||||||
# do a sliding window search across the disk
|
|
||||||
while offset < size
|
|
||||||
|
|
||||||
# get a full window size if we can, we
|
|
||||||
# don't want to read past our boundaries
|
|
||||||
wsize = size - offset
|
|
||||||
wsize = WINDOW_SIZE if wsize > WINDOW_SIZE
|
|
||||||
|
|
||||||
window = self.read(offset, wsize)
|
|
||||||
res = window.index(search)
|
|
||||||
return res + offset if res
|
|
||||||
offset += WINDOW_SIZE - WINDOW_OVERLAP
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def subsource(offset, len)
|
|
||||||
self.class.new(file, file_offset+offset, len)
|
|
||||||
end
|
|
||||||
|
|
||||||
def close
|
|
||||||
file.close
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,48 +0,0 @@
|
||||||
# -*- coding: binary -*-
|
|
||||||
|
|
||||||
module Rex
|
|
||||||
module ImageSource
|
|
||||||
class ImageSource
|
|
||||||
|
|
||||||
#
|
|
||||||
# Um, just some abstract class stuff I guess, this is the interface
|
|
||||||
# that any image sources should subscribe to...
|
|
||||||
#
|
|
||||||
|
|
||||||
def subsource(offset, len)
|
|
||||||
raise "do something"
|
|
||||||
end
|
|
||||||
|
|
||||||
def size
|
|
||||||
raise "do something"
|
|
||||||
end
|
|
||||||
|
|
||||||
def file_offset
|
|
||||||
raise "do something"
|
|
||||||
end
|
|
||||||
|
|
||||||
def close
|
|
||||||
raise "do something"
|
|
||||||
end
|
|
||||||
|
|
||||||
def read_asciiz(offset)
|
|
||||||
# FIXME, make me better
|
|
||||||
string = ''
|
|
||||||
loop do
|
|
||||||
begin
|
|
||||||
char = read(offset, 1)
|
|
||||||
rescue RangeError
|
|
||||||
break
|
|
||||||
end
|
|
||||||
break if char.nil? || char == "\x00"
|
|
||||||
offset += 1
|
|
||||||
string << char
|
|
||||||
end
|
|
||||||
return string
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,35 +0,0 @@
|
||||||
# -*- coding: binary -*-
|
|
||||||
|
|
||||||
require 'rex/image_source/image_source'
|
|
||||||
require 'rex/struct2'
|
|
||||||
|
|
||||||
module Rex
|
|
||||||
module ImageSource
|
|
||||||
class Memory < ImageSource
|
|
||||||
|
|
||||||
attr_accessor :rawdata, :size, :file_offset
|
|
||||||
|
|
||||||
def initialize(_rawdata, _file_offset = 0)
|
|
||||||
self.rawdata = _rawdata
|
|
||||||
self.size = _rawdata.length
|
|
||||||
self.file_offset = _file_offset
|
|
||||||
end
|
|
||||||
|
|
||||||
def read(offset, len)
|
|
||||||
rawdata[offset, len]
|
|
||||||
end
|
|
||||||
|
|
||||||
def subsource(offset, len)
|
|
||||||
self.class.new(rawdata[offset, len], offset + file_offset)
|
|
||||||
end
|
|
||||||
|
|
||||||
def close
|
|
||||||
end
|
|
||||||
|
|
||||||
def index(*args)
|
|
||||||
rawdata.index(*args)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,9 +0,0 @@
|
||||||
# -*- coding: binary -*-
|
|
||||||
|
|
||||||
module Rex
|
|
||||||
module MachParsey
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
require 'rex/machparsey/mach'
|
|
|
@ -1,31 +0,0 @@
|
||||||
# -*- coding: binary -*-
|
|
||||||
|
|
||||||
module Rex
|
|
||||||
module MachParsey
|
|
||||||
|
|
||||||
class MachError < ::RuntimeError
|
|
||||||
end
|
|
||||||
|
|
||||||
class MachParseError < MachError
|
|
||||||
end
|
|
||||||
|
|
||||||
class MachHeaderError < MachParseError
|
|
||||||
end
|
|
||||||
|
|
||||||
class ProgramHeaderError < MachParseError
|
|
||||||
end
|
|
||||||
|
|
||||||
class BoundsError < MachError
|
|
||||||
end
|
|
||||||
|
|
||||||
class FatError < ::RuntimeError
|
|
||||||
end
|
|
||||||
|
|
||||||
class FatParseError < FatError
|
|
||||||
end
|
|
||||||
|
|
||||||
class FatHeaderError < FatParseError
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,209 +0,0 @@
|
||||||
# -*- coding: binary -*-
|
|
||||||
|
|
||||||
require 'rex/machparsey/machbase'
|
|
||||||
require 'rex/machparsey/exceptions'
|
|
||||||
require 'rex/image_source'
|
|
||||||
|
|
||||||
module Rex
|
|
||||||
module MachParsey
|
|
||||||
|
|
||||||
|
|
||||||
class Mach < MachBase
|
|
||||||
attr_accessor :mach_header, :segments, :isource, :bits, :endian, :arch, :fat_offset
|
|
||||||
|
|
||||||
def initialize(isource, offset = 0, fat = false)
|
|
||||||
_parse_mach_header(isource, offset)
|
|
||||||
if fat == true
|
|
||||||
self.fat_offset = offset
|
|
||||||
else
|
|
||||||
self.fat_offset = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
self.isource = isource
|
|
||||||
end
|
|
||||||
|
|
||||||
def _parse_mach_header(isource, offset)
|
|
||||||
self.mach_header = MachHeader.new(isource.read(offset, MACH_HEADER_SIZE_64))
|
|
||||||
bits = mach_header.bits
|
|
||||||
endian = mach_header.endian
|
|
||||||
ncmds = mach_header.ncmds
|
|
||||||
|
|
||||||
if bits == BITS_32
|
|
||||||
offset += MACH_HEADER_SIZE
|
|
||||||
else
|
|
||||||
offset += MACH_HEADER_SIZE_64
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
segments = []
|
|
||||||
ncmds.times do
|
|
||||||
load_command = LoadCommand.new(isource.read(offset, LOAD_COMMAND_SIZE), endian)
|
|
||||||
|
|
||||||
case load_command.cmd
|
|
||||||
when LC_SEGMENT
|
|
||||||
segments << Segment.new(isource.read(offset, SEGMENT_COMMAND_SIZE), bits, endian)
|
|
||||||
when LC_SEGMENT_64
|
|
||||||
segments << Segment.new(isource.read(offset, SEGMENT_COMMAND_SIZE_64), bits, endian)
|
|
||||||
end
|
|
||||||
|
|
||||||
offset += load_command.cmdsize
|
|
||||||
end
|
|
||||||
|
|
||||||
self.mach_header = mach_header
|
|
||||||
self.segments = segments
|
|
||||||
self.isource = isource
|
|
||||||
self.bits = bits
|
|
||||||
self.endian = endian
|
|
||||||
|
|
||||||
return segments
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.new_from_file(filename, disk_backed = false)
|
|
||||||
|
|
||||||
file = ::File.open(filename, "rb")
|
|
||||||
|
|
||||||
if disk_backed
|
|
||||||
return self.new(ImageSource::Disk.new(file))
|
|
||||||
else
|
|
||||||
obj = new_from_string(file.read)
|
|
||||||
file.close
|
|
||||||
return obj
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.new_from_string(data)
|
|
||||||
return self.new(ImageSource::Memory.new(data))
|
|
||||||
end
|
|
||||||
|
|
||||||
def ptr_64?
|
|
||||||
mach_header.bits == BITS_64
|
|
||||||
end
|
|
||||||
|
|
||||||
def ptr_32?
|
|
||||||
ptr_64? == false
|
|
||||||
end
|
|
||||||
|
|
||||||
def ptr_s(vaddr)
|
|
||||||
(ptr_32?) ? ("0x%.8x" % vaddr) : ("0x%.16x" % vaddr)
|
|
||||||
end
|
|
||||||
|
|
||||||
def read(offset, len)
|
|
||||||
isource.read(fat_offset + offset, len)
|
|
||||||
end
|
|
||||||
|
|
||||||
def index(*args)
|
|
||||||
isource.index(*args)
|
|
||||||
end
|
|
||||||
|
|
||||||
def close
|
|
||||||
isource.close
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
class Fat < FatBase
|
|
||||||
attr_accessor :fat_header, :fat_archs, :machos, :isource
|
|
||||||
|
|
||||||
def initialize(isource, offset = 0)
|
|
||||||
self.fat_archs = []
|
|
||||||
self.machos = []
|
|
||||||
self.isource = isource
|
|
||||||
self.fat_header = FatHeader.new(isource.read(offset, FAT_HEADER_SIZE))
|
|
||||||
|
|
||||||
if !self.fat_header
|
|
||||||
raise FatHeaderError, "Could not parse FAT header"
|
|
||||||
end
|
|
||||||
|
|
||||||
print "Detected " + self.fat_header.nfat_arch.to_s + " archs in binary.\n"
|
|
||||||
|
|
||||||
offset += FAT_HEADER_SIZE
|
|
||||||
|
|
||||||
self.fat_header.nfat_arch.times do
|
|
||||||
fat_arch = FatArch.new(isource.read(offset, FAT_ARCH_SIZE), self.fat_header.endian)
|
|
||||||
self.fat_archs << fat_arch
|
|
||||||
self.machos << Mach.new(isource, fat_arch.offset, true)
|
|
||||||
offset += FAT_ARCH_SIZE
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
#this is useful for debugging but we don't use it for anything.
|
|
||||||
def _parse_fat_header(isource, offset)
|
|
||||||
archs = []
|
|
||||||
nfat_arch = self.fat_header.nfat_arch
|
|
||||||
|
|
||||||
print "Number of archs in binary: " + nfat_arch.to_s + "\n"
|
|
||||||
|
|
||||||
nfat_arch.times do
|
|
||||||
arch = FatArch.new(isource.read(offset, FAT_ARCH_SIZE), self.endian)
|
|
||||||
|
|
||||||
case arch.cpu_type
|
|
||||||
|
|
||||||
when CPU_TYPE_I386
|
|
||||||
print "i386\n"
|
|
||||||
|
|
||||||
when CPU_TYPE_X86_64
|
|
||||||
print "x86_64\n"
|
|
||||||
|
|
||||||
when CPU_TYPE_ARM
|
|
||||||
print "Arm\n"
|
|
||||||
|
|
||||||
when CPU_TYPE_POWERPC
|
|
||||||
print "Power PC\n"
|
|
||||||
|
|
||||||
when CPU_TYPE_POWERPC64
|
|
||||||
print "Power PC 64\n"
|
|
||||||
end
|
|
||||||
|
|
||||||
offset += FAT_ARCH_SIZE
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.new_from_file(filename, disk_backed = false)
|
|
||||||
|
|
||||||
file = ::File.open(filename, "rb")
|
|
||||||
|
|
||||||
if disk_backed
|
|
||||||
return self.new(ImageSource::Disk.new(file))
|
|
||||||
else
|
|
||||||
obj = new_from_string(file.read)
|
|
||||||
file.close
|
|
||||||
return obj
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
def self.new_from_string(data)
|
|
||||||
return self.new(ImageSource::Memory.new(data))
|
|
||||||
end
|
|
||||||
|
|
||||||
def ptr_64?
|
|
||||||
mach_header.bits == BITS_64
|
|
||||||
end
|
|
||||||
|
|
||||||
def ptr_32?
|
|
||||||
ptr_64? == false
|
|
||||||
end
|
|
||||||
|
|
||||||
def ptr_s(vaddr)
|
|
||||||
(ptr_32?) ? ("0x%.8x" % vaddr) : ("0x%.16x" % vaddr)
|
|
||||||
end
|
|
||||||
|
|
||||||
def read(offset, len)
|
|
||||||
isource.read(offset, len)
|
|
||||||
end
|
|
||||||
|
|
||||||
def index(*args)
|
|
||||||
isource.index(*args)
|
|
||||||
end
|
|
||||||
|
|
||||||
def close
|
|
||||||
isource.close
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,408 +0,0 @@
|
||||||
# -*- coding: binary -*-
|
|
||||||
|
|
||||||
require 'rex/struct2'
|
|
||||||
|
|
||||||
module Rex
|
|
||||||
module MachParsey
|
|
||||||
|
|
||||||
require 'rex/machparsey/exceptions'
|
|
||||||
require 'rex/struct2'
|
|
||||||
|
|
||||||
class GenericStruct
|
|
||||||
attr_accessor :struct
|
|
||||||
def initialize(_struct)
|
|
||||||
self.struct = _struct
|
|
||||||
end
|
|
||||||
|
|
||||||
# Access a value
|
|
||||||
def v
|
|
||||||
struct.v
|
|
||||||
end
|
|
||||||
|
|
||||||
# Access a value by array
|
|
||||||
def [](*args)
|
|
||||||
struct[*args]
|
|
||||||
end
|
|
||||||
|
|
||||||
# Obtain an array of all fields
|
|
||||||
def keys
|
|
||||||
struct.keys
|
|
||||||
end
|
|
||||||
|
|
||||||
def method_missing(meth, *args)
|
|
||||||
v[meth.to_s] || (raise NoMethodError.new, meth)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class GenericHeader < GenericStruct
|
|
||||||
end
|
|
||||||
|
|
||||||
BITS_32 = 0
|
|
||||||
BITS_64 = 1
|
|
||||||
ENDIAN_LSB = 0
|
|
||||||
ENDIAN_MSB = 1
|
|
||||||
|
|
||||||
class MachBase
|
|
||||||
|
|
||||||
MH_MAGIC = 0xfeedface
|
|
||||||
MH_MAGIC_64 = 0xfeedfacf
|
|
||||||
MH_CIGAM = 0xcefaedfe
|
|
||||||
MH_CIGAM_64 = 0xcffaedfe
|
|
||||||
MACH_HEADER_SIZE = 28
|
|
||||||
MACH_HEADER_SIZE_64 = 32
|
|
||||||
|
|
||||||
|
|
||||||
MACH_HEADER_LSB = Rex::Struct2::CStructTemplate.new(
|
|
||||||
['uint32v', 'magic', 0],
|
|
||||||
['uint32v', 'cputype', 0],
|
|
||||||
['uint32v', 'cpusubtype',0],
|
|
||||||
['uint32v', 'filetype', 0],
|
|
||||||
['uint32v', 'ncmds', 0],
|
|
||||||
['uint32v', 'sizeofcmds',0],
|
|
||||||
['uint32v', 'flags', 0]
|
|
||||||
)
|
|
||||||
|
|
||||||
MACH_HEADER_MSB = Rex::Struct2::CStructTemplate.new(
|
|
||||||
['uint32n', 'magic', 0],
|
|
||||||
['uint32n', 'cputype', 0],
|
|
||||||
['uint32n', 'cpusubtype',0],
|
|
||||||
['uint32n', 'filetype', 0],
|
|
||||||
['uint32n', 'ncmds', 0],
|
|
||||||
['uint32n', 'sizeofcmds',0],
|
|
||||||
['uint32n', 'flags', 0]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
MACH_HEADER_64_LSB = Rex::Struct2::CStructTemplate.new(
|
|
||||||
['uint32v', 'magic', 0],
|
|
||||||
['uint32v', 'cputype', 0],
|
|
||||||
['uint32v', 'cpusubtype',0],
|
|
||||||
['uint32v', 'filetype', 0],
|
|
||||||
['uint32v', 'ncmds', 0],
|
|
||||||
['uint32v', 'sizeofcmds',0],
|
|
||||||
['uint32v', 'flags', 0],
|
|
||||||
['uint32v', 'reserved', 0]
|
|
||||||
)
|
|
||||||
|
|
||||||
MACH_HEADER_64_MSB = Rex::Struct2::CStructTemplate.new(
|
|
||||||
['uint32n', 'magic', 0],
|
|
||||||
['uint32n', 'cputype', 0],
|
|
||||||
['uint32n', 'cpusubtype',0],
|
|
||||||
['uint32n', 'filetype', 0],
|
|
||||||
['uint32n', 'ncmds', 0],
|
|
||||||
['uint32n', 'sizeofcmds',0],
|
|
||||||
['uint32n', 'flags', 0],
|
|
||||||
['uint32n', 'reserved', 0]
|
|
||||||
)
|
|
||||||
|
|
||||||
#cpu types for Mach-O binaries
|
|
||||||
CPU_TYPE_I386 = 0x7
|
|
||||||
CPU_TYPE_X86_64 = 0x01000007
|
|
||||||
CPU_TYPE_ARM = 0xC
|
|
||||||
CPU_TYPE_POWERPC = 0x12
|
|
||||||
CPU_TYPE_POWERPC64 = 0x01000012
|
|
||||||
|
|
||||||
CPU_SUBTYPE_LITTLE_ENDIAN = 0
|
|
||||||
CPU_SUBTYPE_BIG_ENDIAN = 1
|
|
||||||
|
|
||||||
LC_SEGMENT = 0x1 #/* segment of this file to be mapped */
|
|
||||||
LC_SYMTAB = 0x2 #/* link-edit stab symbol table info */
|
|
||||||
LC_SYMSEG = 0x3 #/* link-edit gdb symbol table info (obsolete) */
|
|
||||||
LC_THREAD = 0x4 #/* thread */
|
|
||||||
LC_UNIXTHREAD = 0x5 #/* unix thread (includes a stack) */
|
|
||||||
LC_LOADFVMLIB = 0x6 #/* load a specified fixed VM shared library */
|
|
||||||
LC_IDFVMLIB = 0x7 #/* fixed VM shared library identification */
|
|
||||||
LC_IDENT = 0x8 #/* object identification info (obsolete) */
|
|
||||||
LC_FVMFILE = 0x9 #/* fixed VM file inclusion (internal use) */
|
|
||||||
LC_PREPAGE = 0xa #/* prepage command (internal use) */
|
|
||||||
LC_DYSYMTAB = 0xb #/* dynamic link-edit symbol table info */
|
|
||||||
LC_LOAD_DYLIB = 0xc #/* load a dynamicly linked shared library */
|
|
||||||
LC_ID_DYLIB = 0xd #/* dynamicly linked shared lib identification */
|
|
||||||
LC_LOAD_DYLINKER = 0xe #/* load a dynamic linker */
|
|
||||||
LC_ID_DYLINKER = 0xf #/* dynamic linker identification */
|
|
||||||
LC_PREBOUND_DYLIB = 0x10 #/* modules prebound for a dynamicly */
|
|
||||||
LC_SEGMENT_64 = 0x19 #/* segment of this file to be mapped */
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class MachHeader < GenericHeader
|
|
||||||
attr_accessor :bits, :endian
|
|
||||||
|
|
||||||
def initialize(rawdata)
|
|
||||||
mach_header = MACH_HEADER_LSB.make_struct
|
|
||||||
if !mach_header.from_s(rawdata)
|
|
||||||
raise MachHeaderError, "Could't access Mach-O Magic", caller
|
|
||||||
end
|
|
||||||
|
|
||||||
if mach_header.v['magic'] == MH_MAGIC
|
|
||||||
endian = ENDIAN_LSB
|
|
||||||
bits = BITS_32
|
|
||||||
mach_header = MACH_HEADER_LSB.make_struct
|
|
||||||
elsif mach_header.v['magic'] == MH_CIGAM
|
|
||||||
bits = BITS_32
|
|
||||||
endian = ENDIAN_MSB
|
|
||||||
mach_header = MACH_HEADER_MSB.make_struct
|
|
||||||
elsif mach_header.v['magic'] == MH_MAGIC_64
|
|
||||||
endian = ENDIAN_LSB
|
|
||||||
bits = BITS_64
|
|
||||||
mach_header = MACH_HEADER_LSB.make_struct
|
|
||||||
elsif mach_header.v['magic'] == MH_CIGAM_64
|
|
||||||
endian = ENDIAN_MSB
|
|
||||||
bits = BITS_64
|
|
||||||
mach_header = MACH_HEADER_MSB.make_struct
|
|
||||||
else
|
|
||||||
raise MachHeaderError, "Couldn't find Mach Magic", caller
|
|
||||||
end
|
|
||||||
|
|
||||||
if !mach_header.from_s(rawdata)
|
|
||||||
raise MachHeaderError, "Could't process Mach-O Header", caller
|
|
||||||
end
|
|
||||||
|
|
||||||
self.struct = mach_header
|
|
||||||
self.endian = endian
|
|
||||||
self.bits = bits
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
LOAD_COMMAND_SIZE = 8
|
|
||||||
|
|
||||||
LOAD_COMMAND_LSB = Rex::Struct2::CStructTemplate.new(
|
|
||||||
['uint32v','cmd',0],
|
|
||||||
['uint32v','cmdsize',0]
|
|
||||||
)
|
|
||||||
|
|
||||||
LOAD_COMMAND_MSB = Rex::Struct2::CStructTemplate.new(
|
|
||||||
['uint32n','cmd',0],
|
|
||||||
['uint32n','cmdsize',0]
|
|
||||||
)
|
|
||||||
|
|
||||||
class LoadCommand < GenericHeader
|
|
||||||
def initialize(rawdata, endian)
|
|
||||||
|
|
||||||
if endian == ENDIAN_MSB
|
|
||||||
load_command = LOAD_COMMAND_MSB.make_struct
|
|
||||||
else
|
|
||||||
load_command = LOAD_COMMAND_LSB.make_struct
|
|
||||||
end
|
|
||||||
|
|
||||||
if !load_command.from_s(rawdata)
|
|
||||||
raise MachParseError, "Couldn't parse load command"
|
|
||||||
end
|
|
||||||
|
|
||||||
self.struct = load_command
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
SEGMENT_COMMAND_SIZE = 56
|
|
||||||
|
|
||||||
SEGMENT_COMMAND_LSB = Rex::Struct2::CStructTemplate.new(
|
|
||||||
['uint32v', 'cmd', 0],
|
|
||||||
['uint32v', 'cmdsize', 0],
|
|
||||||
['string', 'segname', 16, ''],
|
|
||||||
['uint32v', 'vmaddr', 0],
|
|
||||||
['uint32v', 'vmsize', 0],
|
|
||||||
['uint32v', 'fileoff', 0],
|
|
||||||
['uint32v', 'filesize', 0],
|
|
||||||
['uint32v', 'maxprot', 0],
|
|
||||||
['uint32v', 'initprot', 0],
|
|
||||||
['uint32v', 'nsects', 0],
|
|
||||||
['uint32v', 'flags', 0]
|
|
||||||
)
|
|
||||||
|
|
||||||
SEGMENT_COMMAND_MSB = Rex::Struct2::CStructTemplate.new(
|
|
||||||
['uint32n', 'cmd', 0],
|
|
||||||
['uint32n', 'cmdsize', 0],
|
|
||||||
['string', 'segname', 16, ''],
|
|
||||||
['uint32n', 'vmaddr', 0],
|
|
||||||
['uint32n', 'vmsize', 0],
|
|
||||||
['uint32n', 'fileoff', 0],
|
|
||||||
['uint32n', 'filesize', 0],
|
|
||||||
['uint32n', 'maxprot', 0],
|
|
||||||
['uint32n', 'initprot', 0],
|
|
||||||
['uint32n', 'nsects', 0],
|
|
||||||
['uint32n', 'flags', 0]
|
|
||||||
)
|
|
||||||
|
|
||||||
SEGMENT_COMMAND_SIZE_64 = 72
|
|
||||||
|
|
||||||
SEGMENT_COMMAND_64_LSB = Rex::Struct2::CStructTemplate.new(
|
|
||||||
['uint32v', 'cmd', 0],
|
|
||||||
['uint32v', 'cmdsize', 0],
|
|
||||||
['string', 'segname', 16, ''],
|
|
||||||
['uint64v', 'vmaddr', 0],
|
|
||||||
['uint64v', 'vmsize', 0],
|
|
||||||
['uint64v', 'fileoff', 0],
|
|
||||||
['uint64v', 'filesize', 0],
|
|
||||||
['uint32v', 'maxprot', 0],
|
|
||||||
['uint32v', 'initprot', 0],
|
|
||||||
['uint32v', 'nsects', 0],
|
|
||||||
['uint32v', 'flags', 0]
|
|
||||||
)
|
|
||||||
|
|
||||||
SEGMENT_COMMAND_64_MSB = Rex::Struct2::CStructTemplate.new(
|
|
||||||
['uint32n', 'cmd', 0],
|
|
||||||
['uint32n', 'cmdsize', 0],
|
|
||||||
['string', 'segname', 16, ''],
|
|
||||||
['uint64n', 'vmaddr', 0],
|
|
||||||
['uint64n', 'vmsize', 0],
|
|
||||||
['uint64n', 'fileoff', 0],
|
|
||||||
['uint64n', 'filesize', 0],
|
|
||||||
['uint32n', 'maxprot', 0],
|
|
||||||
['uint32n', 'initprot', 0],
|
|
||||||
['uint32n', 'nsects', 0],
|
|
||||||
['uint32n', 'flags', 0]
|
|
||||||
)
|
|
||||||
|
|
||||||
class Segment < GenericHeader
|
|
||||||
attr_accessor :_bits, :_endian
|
|
||||||
|
|
||||||
def initialize(rawdata, bits, endian)
|
|
||||||
self._bits = bits
|
|
||||||
|
|
||||||
if bits == BITS_64
|
|
||||||
if endian == ENDIAN_MSB
|
|
||||||
segment_command = SEGMENT_COMMAND_64_MSB.make_struct
|
|
||||||
else
|
|
||||||
segment_command = SEGMENT_COMMAND_64_LSB.make_struct
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if endian == ENDIAN_MSB
|
|
||||||
segment_command = SEGMENT_COMMAND_MSB.make_struct
|
|
||||||
else
|
|
||||||
segment_command = SEGMENT_COMMAND_LSB.make_struct
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if !segment_command.from_s(rawdata)
|
|
||||||
raise MachParseError, "Couldn't parse segment command"
|
|
||||||
end
|
|
||||||
|
|
||||||
self.struct = segment_command
|
|
||||||
end
|
|
||||||
|
|
||||||
def Segname
|
|
||||||
v['segname']
|
|
||||||
end
|
|
||||||
|
|
||||||
def Vmaddr
|
|
||||||
v['vmaddr']
|
|
||||||
end
|
|
||||||
|
|
||||||
def Vmsize
|
|
||||||
v['vmsize']
|
|
||||||
end
|
|
||||||
|
|
||||||
def FileOff
|
|
||||||
v['fileoff']
|
|
||||||
end
|
|
||||||
|
|
||||||
def FileSize
|
|
||||||
v['filesize']
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Thread < GenericHeader
|
|
||||||
def initialize(rawdata)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
FAT_MAGIC = 0xcafebabe
|
|
||||||
FAT_CIGAM = 0xbebafeca
|
|
||||||
FAT_HEADER_SIZE = 8
|
|
||||||
|
|
||||||
FAT_HEADER_LSB = Rex::Struct2::CStructTemplate.new(
|
|
||||||
['uint32v', 'magic', 0],
|
|
||||||
['uint32v', 'nfat_arch',0]
|
|
||||||
)
|
|
||||||
|
|
||||||
FAT_HEADER_MSB = Rex::Struct2::CStructTemplate.new(
|
|
||||||
['uint32n', 'magic', 0],
|
|
||||||
['uint32n', 'nfat_arch',0]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
FAT_ARCH_SIZE = 20
|
|
||||||
|
|
||||||
FAT_ARCH_LSB = Rex::Struct2::CStructTemplate.new(
|
|
||||||
['uint32v', 'cpu_type', 0],
|
|
||||||
['uint32v', 'cpu_subtype',0],
|
|
||||||
['uint32v', 'offset', 0],
|
|
||||||
['uint32v', 'size', 0],
|
|
||||||
['uint32v', 'align', 0]
|
|
||||||
)
|
|
||||||
|
|
||||||
FAT_ARCH_MSB = Rex::Struct2::CStructTemplate.new(
|
|
||||||
['uint32n', 'cpu_type', 0],
|
|
||||||
['uint32n', 'cpu_subtype',0],
|
|
||||||
['uint32n', 'offset', 0],
|
|
||||||
['uint32n', 'size', 0],
|
|
||||||
['uint32n', 'align', 0]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class FatBase
|
|
||||||
|
|
||||||
class FatHeader < GenericHeader
|
|
||||||
attr_accessor :nfat_arch, :endian, :exists
|
|
||||||
|
|
||||||
def initialize(rawdata)
|
|
||||||
fat_header = FAT_HEADER_LSB.make_struct
|
|
||||||
if !fat_header.from_s(rawdata)
|
|
||||||
#raise something
|
|
||||||
end
|
|
||||||
|
|
||||||
magic = fat_header.v['magic']
|
|
||||||
if magic == FAT_MAGIC
|
|
||||||
endian = ENDIAN_LSB
|
|
||||||
elsif magic == FAT_CIGAM
|
|
||||||
endian = ENDIAN_MSB
|
|
||||||
fat_header = FAT_HEADER_MSB.make_struct
|
|
||||||
if !fat_header.from_s(rawdata)
|
|
||||||
raise FatHeaderError, "Could not parse FAT header"
|
|
||||||
end
|
|
||||||
else
|
|
||||||
self.exists = 0
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
self.nfat_arch = fat_header.v['nfat_arch']
|
|
||||||
self.struct = fat_header
|
|
||||||
self.endian = endian
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class FatArch < GenericHeader
|
|
||||||
attr_accessor :cpu_type, :cpu_subtype, :offset, :size
|
|
||||||
|
|
||||||
def initialize(rawdata, endian)
|
|
||||||
if endian == ENDIAN_LSB
|
|
||||||
fat_arch = FAT_ARCH_LSB.make_struct
|
|
||||||
else
|
|
||||||
fat_arch = FAT_ARCH_MSB.make_struct
|
|
||||||
end
|
|
||||||
|
|
||||||
if !fat_arch.from_s(rawdata)
|
|
||||||
raise FatHeaderError, "Could not parse arch from FAT header"
|
|
||||||
end
|
|
||||||
|
|
||||||
self.cpu_type = fat_arch.v['cpu_type']
|
|
||||||
self.cpu_subtype = fat_arch.v['cpu_subtype']
|
|
||||||
self.offset = fat_arch.v['offset']
|
|
||||||
self.size = fat_arch.v['size']
|
|
||||||
self.struct = fat_arch
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
class Thread < GenericHeader
|
|
||||||
def initialize(rawdata)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,9 +0,0 @@
|
||||||
# -*- coding: binary -*-
|
|
||||||
|
|
||||||
module Rex
|
|
||||||
module MachScan
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
require 'rex/machscan/scanner'
|
|
|
@ -1,217 +0,0 @@
|
||||||
# -*- coding: binary -*-
|
|
||||||
|
|
||||||
module Rex
|
|
||||||
module MachScan
|
|
||||||
module Scanner
|
|
||||||
class Generic
|
|
||||||
|
|
||||||
attr_accessor :mach, :fat, :regex
|
|
||||||
|
|
||||||
def initialize(binary)
|
|
||||||
if binary.class == Rex::MachParsey::Mach
|
|
||||||
self.mach = binary
|
|
||||||
else
|
|
||||||
self.fat = binary
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def config(param)
|
|
||||||
end
|
|
||||||
|
|
||||||
def scan(param)
|
|
||||||
config(param)
|
|
||||||
|
|
||||||
$stdout.puts "[#{param['file']}]"
|
|
||||||
|
|
||||||
if !self.mach
|
|
||||||
for mach in fat.machos
|
|
||||||
if mach.mach_header.cputype == 0x7 #since we only support intel for the time being its all we process
|
|
||||||
self.mach = mach
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
self.mach.segments.each do |segment|
|
|
||||||
if segment.segname.include? "__TEXT"
|
|
||||||
scan_segment(segment, param).each do |hit|
|
|
||||||
vaddr = hit[0]
|
|
||||||
message = hit[1].is_a?(Array) ? hit[1].join(" ") : hit[1]
|
|
||||||
$stdout.puts self.mach.ptr_s(vaddr - self.mach.fat_offset) + " " + message
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
def scan_segment(segment, param={})
|
|
||||||
[]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class JmpRegScanner < Generic
|
|
||||||
|
|
||||||
def config(param)
|
|
||||||
regnums = param['args']
|
|
||||||
|
|
||||||
# build a list of the call bytes
|
|
||||||
calls = _build_byte_list(0xd0, regnums - [4]) # note call esp's don't work..
|
|
||||||
jmps = _build_byte_list(0xe0, regnums)
|
|
||||||
pushs1 = _build_byte_list(0x50, regnums)
|
|
||||||
pushs2 = _build_byte_list(0xf0, regnums)
|
|
||||||
|
|
||||||
regexstr = '('
|
|
||||||
if !calls.empty?
|
|
||||||
regexstr += "\xff[#{calls}]|"
|
|
||||||
end
|
|
||||||
|
|
||||||
regexstr += "\xff[#{jmps}]|([#{pushs1}]|\xff[#{pushs2}])(\xc3|\xc2..))"
|
|
||||||
|
|
||||||
self.regex = Regexp.new(regexstr, nil, 'n')
|
|
||||||
end
|
|
||||||
|
|
||||||
# build a list for regex of the possible bytes, based on a base
|
|
||||||
# byte and a list of register numbers..
|
|
||||||
def _build_byte_list(base, regnums)
|
|
||||||
regnums.collect { |regnum| Regexp.escape((base | regnum).chr) }.join('')
|
|
||||||
end
|
|
||||||
|
|
||||||
def _ret_size(offset)
|
|
||||||
case mach.read(offset, 1)
|
|
||||||
when "\xc3"
|
|
||||||
return 1
|
|
||||||
when "\xc2"
|
|
||||||
return 3
|
|
||||||
end
|
|
||||||
$stderr.puts("Invalid return instruction")
|
|
||||||
end
|
|
||||||
|
|
||||||
def _parse_ret(data)
|
|
||||||
if data.length == 1
|
|
||||||
return "ret"
|
|
||||||
else
|
|
||||||
return "retn 0x%04x" % data[1, 2].unpack('v')[0]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def scan_segment(segment, param={})
|
|
||||||
base_addr = segment.vmaddr
|
|
||||||
segment_offset = segment.fileoff
|
|
||||||
offset = segment_offset
|
|
||||||
|
|
||||||
hits = []
|
|
||||||
|
|
||||||
while (offset = mach.index(regex, offset)) != nil
|
|
||||||
|
|
||||||
vaddr = base_addr + (offset - segment_offset)
|
|
||||||
message = ''
|
|
||||||
|
|
||||||
parse_ret = false
|
|
||||||
|
|
||||||
byte1 = mach.read(offset, 1).unpack("C*")[0]
|
|
||||||
|
|
||||||
if byte1 == 0xff
|
|
||||||
byte2 = mach.read(offset+1, 1).unpack("C*")[0]
|
|
||||||
regname = Rex::Arch::X86.reg_name32(byte2 & 0x7)
|
|
||||||
|
|
||||||
case byte2 & 0xf8
|
|
||||||
when 0xd0
|
|
||||||
message = "call #{regname}"
|
|
||||||
offset += 2
|
|
||||||
when 0xe0
|
|
||||||
message = "jmp #{regname}"
|
|
||||||
offset += 2
|
|
||||||
when 0xf0
|
|
||||||
retsize = _ret_size(offset+2)
|
|
||||||
message = "push #{regname}; " + _parse_ret(mach.read(offset+2, retsize))
|
|
||||||
offset += 2 + retsize
|
|
||||||
else
|
|
||||||
raise "Unexpected value at offset: #{offset}"
|
|
||||||
end
|
|
||||||
else
|
|
||||||
regname = Rex::Arch::X86.reg_name32(byte1 & 0x7)
|
|
||||||
retsize = _ret_size(offset+1)
|
|
||||||
message = "push #{regname}; " + _parse_ret(mach.read(offset+1, retsize))
|
|
||||||
offset += 1 + retsize
|
|
||||||
end
|
|
||||||
|
|
||||||
hits << [ vaddr, message ]
|
|
||||||
end
|
|
||||||
|
|
||||||
return hits
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class PopPopRetScanner < JmpRegScanner
|
|
||||||
|
|
||||||
def config(param)
|
|
||||||
pops = _build_byte_list(0x58, (0 .. 7).to_a - [4]) # we don't want pop esp's...
|
|
||||||
self.regex = Regexp.new("[#{pops}][#{pops}](\xc3|\xc2..)", nil, 'n')
|
|
||||||
end
|
|
||||||
|
|
||||||
def scan_segment(segment, param={})
|
|
||||||
base_addr = segment.vmaddr
|
|
||||||
segment_offset = segment.fileoff
|
|
||||||
offset = segment_offset
|
|
||||||
|
|
||||||
hits = []
|
|
||||||
|
|
||||||
while offset < segment.fileoff + segment.filesize && (offset = mach.index(regex, offset)) != nil
|
|
||||||
|
|
||||||
vaddr = base_addr + (offset - segment_offset)
|
|
||||||
message = ''
|
|
||||||
|
|
||||||
pops = mach.read(offset, 2)
|
|
||||||
reg1 = Rex::Arch::X86.reg_name32(pops[0,1].unpack("C*")[0] & 0x7)
|
|
||||||
reg2 = Rex::Arch::X86.reg_name32(pops[1,1].unpack("C*")[0] & 0x7)
|
|
||||||
|
|
||||||
message = "pop #{reg1}; pop #{reg2}; "
|
|
||||||
|
|
||||||
retsize = _ret_size(offset+2)
|
|
||||||
message += _parse_ret(mach.read(offset+2, retsize))
|
|
||||||
|
|
||||||
offset += 2 + retsize
|
|
||||||
|
|
||||||
hits << [ vaddr, message ]
|
|
||||||
end
|
|
||||||
|
|
||||||
return hits
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class RegexScanner < JmpRegScanner
|
|
||||||
|
|
||||||
def config(param)
|
|
||||||
self.regex = Regexp.new(param['args'], nil, 'n')
|
|
||||||
end
|
|
||||||
|
|
||||||
def scan_segment(segment, param={})
|
|
||||||
base_addr = segment.vmaddr
|
|
||||||
segment_offset = segment.fileoff
|
|
||||||
offset = segment_offset
|
|
||||||
|
|
||||||
hits = []
|
|
||||||
|
|
||||||
while offset < segment.fileoff + segment.filesize && (offset = mach.index(regex, offset)) != nil
|
|
||||||
|
|
||||||
idx = offset
|
|
||||||
buf = ''
|
|
||||||
mat = nil
|
|
||||||
|
|
||||||
while (! (mat = buf.match(regex)))
|
|
||||||
buf << mach.read(idx, 1)
|
|
||||||
idx += 1
|
|
||||||
end
|
|
||||||
|
|
||||||
vaddr = base_addr + (offset - segment_offset)
|
|
||||||
|
|
||||||
hits << [ vaddr, buf.unpack("H*") ]
|
|
||||||
offset += buf.length
|
|
||||||
end
|
|
||||||
return hits
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
# -*- coding: binary -*-
|
|
||||||
|
|
||||||
module Rex
|
|
||||||
module PeParsey
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
require 'rex/peparsey/pe'
|
|
||||||
require 'rex/peparsey/pe_memdump'
|
|
|
@ -1,30 +0,0 @@
|
||||||
# -*- coding: binary -*-
|
|
||||||
|
|
||||||
module Rex
|
|
||||||
module PeParsey
|
|
||||||
|
|
||||||
class PeError < ::RuntimeError
|
|
||||||
end
|
|
||||||
|
|
||||||
class ParseError < PeError
|
|
||||||
end
|
|
||||||
|
|
||||||
class DosHeaderError < ParseError
|
|
||||||
end
|
|
||||||
|
|
||||||
class FileHeaderError < ParseError
|
|
||||||
end
|
|
||||||
|
|
||||||
class OptionalHeaderError < ParseError
|
|
||||||
end
|
|
||||||
|
|
||||||
class BoundsError < PeError
|
|
||||||
end
|
|
||||||
|
|
||||||
class PeParseyError < PeError
|
|
||||||
end
|
|
||||||
|
|
||||||
class SkipError < PeError
|
|
||||||
end
|
|
||||||
|
|
||||||
end end
|
|
|
@ -1,210 +0,0 @@
|
||||||
# -*- coding: binary -*-
|
|
||||||
|
|
||||||
require 'rex/image_source'
|
|
||||||
require 'rex/peparsey/exceptions'
|
|
||||||
require 'rex/peparsey/pebase'
|
|
||||||
require 'rex/peparsey/section'
|
|
||||||
require 'rex/struct2'
|
|
||||||
|
|
||||||
module Rex
|
|
||||||
module PeParsey
|
|
||||||
class Pe < PeBase
|
|
||||||
|
|
||||||
def initialize(isource)
|
|
||||||
|
|
||||||
#
|
|
||||||
# DOS Header
|
|
||||||
#
|
|
||||||
# Parse the initial dos header, starting at the file beginning
|
|
||||||
#
|
|
||||||
offset = 0
|
|
||||||
dos_header = self.class._parse_dos_header(isource.read(offset, IMAGE_DOS_HEADER_SIZE))
|
|
||||||
|
|
||||||
#
|
|
||||||
# File Header
|
|
||||||
#
|
|
||||||
# If there is going to be a PE, the dos header tells us where to find it
|
|
||||||
# So now we try to parse the file (pe) header
|
|
||||||
#
|
|
||||||
offset += dos_header.e_lfanew
|
|
||||||
|
|
||||||
# most likely an invalid e_lfanew...
|
|
||||||
if offset > isource.size
|
|
||||||
raise FileHeaderError, "e_lfanew looks invalid", caller
|
|
||||||
end
|
|
||||||
|
|
||||||
file_header = self.class._parse_file_header(isource.read(offset, IMAGE_FILE_HEADER_SIZE))
|
|
||||||
|
|
||||||
#
|
|
||||||
# Optional Header
|
|
||||||
#
|
|
||||||
# After the file header, we find the optional header. Right now
|
|
||||||
# we require a optional header. Despite it's name, all binaries
|
|
||||||
# that we are interested in should have one. We need this
|
|
||||||
# header for a lot of stuff, so we die without it...
|
|
||||||
#
|
|
||||||
offset += IMAGE_FILE_HEADER_SIZE
|
|
||||||
optional_header = self.class._parse_optional_header(
|
|
||||||
isource.read(offset, file_header.SizeOfOptionalHeader)
|
|
||||||
)
|
|
||||||
|
|
||||||
if !optional_header
|
|
||||||
raise OptionalHeaderError, "No optional header!", caller
|
|
||||||
end
|
|
||||||
|
|
||||||
base = optional_header.ImageBase
|
|
||||||
|
|
||||||
#
|
|
||||||
# Section Headers
|
|
||||||
#
|
|
||||||
# After the optional header should be the section headers.
|
|
||||||
# We know how many there should be from the file header...
|
|
||||||
#
|
|
||||||
offset += file_header.SizeOfOptionalHeader
|
|
||||||
|
|
||||||
num_sections = file_header.NumberOfSections
|
|
||||||
section_headers = self.class._parse_section_headers(
|
|
||||||
isource.read(offset, IMAGE_SIZEOF_SECTION_HEADER * num_sections)
|
|
||||||
)
|
|
||||||
|
|
||||||
#
|
|
||||||
# End of Headers
|
|
||||||
#
|
|
||||||
# After the section headers (which are padded to FileAlignment)
|
|
||||||
# we should find the section data, described by the section
|
|
||||||
# headers...
|
|
||||||
#
|
|
||||||
# So this is the end of our header data, lets store this
|
|
||||||
# in an image source for possible access later...
|
|
||||||
#
|
|
||||||
offset += IMAGE_SIZEOF_SECTION_HEADER * num_sections
|
|
||||||
offset = self.class._align_offset(offset, optional_header.FileAlignment)
|
|
||||||
|
|
||||||
header_section = Section.new(isource.subsource(0, offset), 0, nil)
|
|
||||||
|
|
||||||
#
|
|
||||||
# Sections
|
|
||||||
#
|
|
||||||
# So from here on out should be section data, and then any
|
|
||||||
# trailing data (like authenticode and stuff I think)
|
|
||||||
#
|
|
||||||
|
|
||||||
sections = [ ]
|
|
||||||
|
|
||||||
section_headers.each do |section_header|
|
|
||||||
|
|
||||||
rva = section_header.VirtualAddress
|
|
||||||
size = section_header.SizeOfRawData
|
|
||||||
file_offset = section_header.PointerToRawData
|
|
||||||
|
|
||||||
sections << Section.new(
|
|
||||||
isource.subsource(file_offset, size),
|
|
||||||
rva,
|
|
||||||
section_header
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# Save the stuffs!
|
|
||||||
#
|
|
||||||
# We have parsed enough to load the file up here, now we just
|
|
||||||
# save off all of the structures and data... We will
|
|
||||||
# save our fake header section, the real sections, etc.
|
|
||||||
#
|
|
||||||
|
|
||||||
#
|
|
||||||
# These should not be accessed directly
|
|
||||||
#
|
|
||||||
|
|
||||||
self._isource = isource
|
|
||||||
|
|
||||||
self._dos_header = dos_header
|
|
||||||
self._file_header = file_header
|
|
||||||
self._optional_header = optional_header
|
|
||||||
self._section_headers = section_headers
|
|
||||||
|
|
||||||
self.image_base = base
|
|
||||||
self.sections = sections
|
|
||||||
self.header_section = header_section
|
|
||||||
|
|
||||||
self._config_header = _parse_config_header()
|
|
||||||
self._tls_header = _parse_tls_header()
|
|
||||||
|
|
||||||
# These can be accessed directly
|
|
||||||
self.hdr = HeaderAccessor.new
|
|
||||||
self.hdr.dos = self._dos_header
|
|
||||||
self.hdr.file = self._file_header
|
|
||||||
self.hdr.opt = self._optional_header
|
|
||||||
self.hdr.sections = self._section_headers
|
|
||||||
self.hdr.config = self._config_header
|
|
||||||
self.hdr.tls = self._tls_header
|
|
||||||
self.hdr.exceptions = self._exception_header
|
|
||||||
|
|
||||||
# We load the exception directory last as it relies on hdr.file to be created above.
|
|
||||||
self._exception_header = _load_exception_directory()
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# Return everything that's going to be mapped in the process
|
|
||||||
# and accessable. This should include all of the sections
|
|
||||||
# and our "fake" section for the header data...
|
|
||||||
#
|
|
||||||
def all_sections
|
|
||||||
[ header_section ] + sections
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# Returns true if this binary is for a 64-bit architecture.
|
|
||||||
#
|
|
||||||
def ptr_64?
|
|
||||||
[
|
|
||||||
IMAGE_FILE_MACHINE_IA64,
|
|
||||||
IMAGE_FILE_MACHINE_ALPHA64,
|
|
||||||
IMAGE_FILE_MACHINE_AMD64
|
|
||||||
].include?(self._file_header.Machine)
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# Returns true if this binary is for a 32-bit architecture.
|
|
||||||
# This check does not take into account 16-bit binaries at the moment.
|
|
||||||
#
|
|
||||||
def ptr_32?
|
|
||||||
ptr_64? == false
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# Converts a virtual address to a string representation based on the
|
|
||||||
# underlying architecture.
|
|
||||||
#
|
|
||||||
def ptr_s(va)
|
|
||||||
(ptr_32?) ? ("0x%.8x" % va) : ("0x%.16x" % va)
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# Converts a file offset into a virtual address
|
|
||||||
#
|
|
||||||
def file_offset_to_va(offset)
|
|
||||||
image_base + file_offset_to_rva(offset)
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# Read raw bytes from the specified offset in the underlying file
|
|
||||||
#
|
|
||||||
# NOTE: You should pass raw file offsets into this, not offsets from
|
|
||||||
# the beginning of the section. If you need to read from within a
|
|
||||||
# section, add section.file_offset prior to passing the offset in.
|
|
||||||
#
|
|
||||||
def read(offset, len)
|
|
||||||
_isource.read(offset, len)
|
|
||||||
end
|
|
||||||
|
|
||||||
def size
|
|
||||||
_isource.size
|
|
||||||
end
|
|
||||||
def length
|
|
||||||
_isource.size
|
|
||||||
end
|
|
||||||
|
|
||||||
end end end
|
|
|
@ -1,61 +0,0 @@
|
||||||
# -*- coding: binary -*-
|
|
||||||
|
|
||||||
require 'rex/image_source'
|
|
||||||
require 'rex/peparsey/exceptions'
|
|
||||||
require 'rex/peparsey/pebase'
|
|
||||||
require 'rex/peparsey/section'
|
|
||||||
require 'rex/struct2'
|
|
||||||
|
|
||||||
#
|
|
||||||
# This class is for use with memdump.exe generated dump images. It basically
|
|
||||||
# just lies, gets the ImageBase from the file name, and generates 1 big
|
|
||||||
# header_section with all of the data in it...
|
|
||||||
#
|
|
||||||
|
|
||||||
module Rex
|
|
||||||
module PeParsey
|
|
||||||
class PeMemDump < Pe
|
|
||||||
|
|
||||||
def self.new_from_string(data)
|
|
||||||
raise NotImplementError
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.new_from_file(filename, disk_backed = false)
|
|
||||||
|
|
||||||
if filename[-4, 4] != '.rng'
|
|
||||||
raise "Not a .rng file: #{filename}"
|
|
||||||
end
|
|
||||||
|
|
||||||
if filename[-9, 9] == "index.rng"
|
|
||||||
raise SkipError
|
|
||||||
end
|
|
||||||
|
|
||||||
file = File.open(filename, 'rb')
|
|
||||||
|
|
||||||
if disk_backed
|
|
||||||
obj = ImageSource::Disk.new(file)
|
|
||||||
else
|
|
||||||
obj = ImageSource::Memory.new(file.read)
|
|
||||||
obj.close
|
|
||||||
end
|
|
||||||
|
|
||||||
return self.new(obj, filename.gsub(/.*[\/\\]/, '')[0,8].hex)
|
|
||||||
end
|
|
||||||
|
|
||||||
def initialize(isource, base)
|
|
||||||
self._isource = isource
|
|
||||||
self.header_section = Section.new(isource, base, nil)
|
|
||||||
self.sections = [ self.header_section ]
|
|
||||||
self.image_base = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
def all_sections
|
|
||||||
self.sections
|
|
||||||
end
|
|
||||||
|
|
||||||
# No 64-bit support
|
|
||||||
def ptr_64?
|
|
||||||
false
|
|
||||||
end
|
|
||||||
|
|
||||||
end end end
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,128 +0,0 @@
|
||||||
# -*- coding: binary -*-
|
|
||||||
|
|
||||||
require 'rex/peparsey/exceptions'
|
|
||||||
require 'rex/peparsey/pebase'
|
|
||||||
require 'rex/struct2'
|
|
||||||
|
|
||||||
module Rex
|
|
||||||
module PeParsey
|
|
||||||
class Section
|
|
||||||
attr_accessor :_section_header, :_isource
|
|
||||||
attr_accessor :base_rva
|
|
||||||
|
|
||||||
#
|
|
||||||
# Initialize a section.
|
|
||||||
#
|
|
||||||
# isource - The ImageSource class backing the image
|
|
||||||
# base_vma - The address of this section base
|
|
||||||
# section_header - The section header (struct2) although this is not
|
|
||||||
# required, which is why there is a base_vma. This can be nil.
|
|
||||||
#
|
|
||||||
def initialize(isource, base_rva, section_header = nil)
|
|
||||||
self._isource = isource
|
|
||||||
self.base_rva = base_rva
|
|
||||||
self._section_header = section_header
|
|
||||||
end
|
|
||||||
|
|
||||||
def file_offset
|
|
||||||
_isource.file_offset
|
|
||||||
end
|
|
||||||
|
|
||||||
def size
|
|
||||||
_isource.size
|
|
||||||
end
|
|
||||||
|
|
||||||
def name
|
|
||||||
# a section header is not required
|
|
||||||
return nil if !_section_header
|
|
||||||
|
|
||||||
# FIXME make this better...
|
|
||||||
_section_header.v['Name'].gsub(/\x00+$/n, '')
|
|
||||||
end
|
|
||||||
|
|
||||||
def flags
|
|
||||||
# a section header is not required
|
|
||||||
return nil if !_section_header
|
|
||||||
_section_header.v['Characteristics']
|
|
||||||
end
|
|
||||||
|
|
||||||
def vma
|
|
||||||
# a section header is not required
|
|
||||||
return nil if !_section_header
|
|
||||||
_section_header.v['VirtualAddress']
|
|
||||||
end
|
|
||||||
|
|
||||||
def raw_size
|
|
||||||
# a section header is not required
|
|
||||||
return nil if !_section_header
|
|
||||||
_section_header.v['SizeOfRawData']
|
|
||||||
end
|
|
||||||
|
|
||||||
def _check_offset(offset, len = 1)
|
|
||||||
if offset < 0 || offset+len > size
|
|
||||||
raise BoundsError, "Offset #{offset} outside of section", caller
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def read(offset, len)
|
|
||||||
_check_offset(offset, len)
|
|
||||||
return _isource.read(offset, len)
|
|
||||||
end
|
|
||||||
|
|
||||||
def read_rva(rva, len)
|
|
||||||
return read(rva_to_offset(rva), len)
|
|
||||||
end
|
|
||||||
|
|
||||||
def read_asciiz(offset)
|
|
||||||
_check_offset(offset)
|
|
||||||
return _isource.read_asciiz(offset)
|
|
||||||
end
|
|
||||||
|
|
||||||
def read_asciiz_rva(rva)
|
|
||||||
return read_asciiz(rva_to_offset(rva))
|
|
||||||
end
|
|
||||||
|
|
||||||
def index(*args)
|
|
||||||
_isource.index(*args)
|
|
||||||
end
|
|
||||||
|
|
||||||
def offset_to_rva(offset)
|
|
||||||
if !contains_offset?(offset)
|
|
||||||
raise BoundsError, "Offset #{offset} outside of section", caller
|
|
||||||
end
|
|
||||||
|
|
||||||
return offset + base_rva
|
|
||||||
end
|
|
||||||
|
|
||||||
def file_offset_to_rva(foffset)
|
|
||||||
return offset_to_rva(foffset - file_offset)
|
|
||||||
end
|
|
||||||
|
|
||||||
def rva_to_offset(rva)
|
|
||||||
offset = rva - base_rva
|
|
||||||
if !contains_offset?(offset)
|
|
||||||
raise BoundsError, "RVA #{rva} outside of section", caller
|
|
||||||
end
|
|
||||||
|
|
||||||
return offset
|
|
||||||
end
|
|
||||||
|
|
||||||
def rva_to_file_offset(rva)
|
|
||||||
return rva_to_offset(rva) + file_offset
|
|
||||||
end
|
|
||||||
|
|
||||||
def contains_offset?(offset)
|
|
||||||
offset >= 0 && offset < size
|
|
||||||
end
|
|
||||||
|
|
||||||
def contains_file_offset?(foffset)
|
|
||||||
contains_offset?(foffset - file_offset)
|
|
||||||
end
|
|
||||||
|
|
||||||
def contains_rva?(rva)
|
|
||||||
contains_offset?(rva - base_rva)
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end end
|
|
|
@ -1,11 +0,0 @@
|
||||||
# -*- coding: binary -*-
|
|
||||||
|
|
||||||
module Rex
|
|
||||||
module PeScan
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
require 'rex/pescan/analyze'
|
|
||||||
require 'rex/pescan/scanner'
|
|
||||||
require 'rex/pescan/search'
|
|
|
@ -1,366 +0,0 @@
|
||||||
# -*- coding: binary -*-
|
|
||||||
module Rex
|
|
||||||
module PeScan
|
|
||||||
module Analyze
|
|
||||||
|
|
||||||
require "rex/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"
|
|
||||||
|
|
||||||
# Get DllCharacteristics (in Integer)
|
|
||||||
dllcharacteristics = pe.hdr.opt.struct[23].value
|
|
||||||
|
|
||||||
if (dllcharacteristics > 0)
|
|
||||||
tbl = table("DllCharacteristics", ['Flag', 'Value'])
|
|
||||||
|
|
||||||
# http://msdn.microsoft.com/en-us/library/ms680339(v=vs.85).aspx
|
|
||||||
traits = {
|
|
||||||
:ASLR => 'False', #IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
|
|
||||||
:Integrity => 'False', #IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY
|
|
||||||
:NX => 'False', #IMAGE_DLLCHARACTERISTICS_NX_COMPAT
|
|
||||||
:Isolation => 'False', #IMAGE_DLLCHARACTERISTICS_NO_ISOLATION
|
|
||||||
:SEH => 'False', #IMAGE_DLLCHARACTERISTICS_NO_SEH
|
|
||||||
:Bind => 'False', #IMAGE_DLLCHARACTERISTICS_NO_BIND
|
|
||||||
:WDM => 'False', #IMAGE_DLLCHARACTERISTICS_WDM_DRIVER
|
|
||||||
:Terminal => 'False' #IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
|
|
||||||
}
|
|
||||||
|
|
||||||
# Convert integer to an bit array
|
|
||||||
c_bits = ("%32d" %dllcharacteristics.to_s(2)).split('').map { |e| e.to_i }.reverse
|
|
||||||
|
|
||||||
# Check characteristics
|
|
||||||
traits[:ASLR] = 'True' if c_bits[6] == 1 #0x0040
|
|
||||||
traits[:Integrity] = 'True' if c_bits[7] == 1 #0x0080
|
|
||||||
traits[:NX] = 'True' if c_bits[8] == 1 #0x0100
|
|
||||||
traits[:Isolation] = 'True' if c_bits[9] == 1 #0x0200
|
|
||||||
traits[:SEH] = 'True' if c_bits[10] == 1 #0x0400
|
|
||||||
traits[:Bind] = 'True' if c_bits[11] == 1 #0x0800
|
|
||||||
traits[:WDM] = 'True' if c_bits[13] == 1 #2000
|
|
||||||
traits[:Terminal] = 'True' if c_bits[15] == 1 #0x8000
|
|
||||||
|
|
||||||
# Putting results to table
|
|
||||||
traits.each do |trait_name, trait_value|
|
|
||||||
tbl << [trait_name, trait_value]
|
|
||||||
end
|
|
||||||
|
|
||||||
$stdout.puts tbl.to_s
|
|
||||||
$stdout.puts "\n\n"
|
|
||||||
end
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
# Rex::PeParsey::Pe doesn't seem to give us any offset information for each function,
|
|
||||||
# which makes it difficult to calculate the actual addresses for them. So instead we
|
|
||||||
# are using Metasm::COFF::ImportDirectory to do this task. The ability to see
|
|
||||||
# addresses is mainly for ROP.
|
|
||||||
if (pe.imports)
|
|
||||||
tbl = table("Imported Functions", ['Library', 'Address', 'Ordinal', 'Name'])
|
|
||||||
exefmt = Metasm::AutoExe.orshellcode{ Metasm.const_get('x86_64').new }
|
|
||||||
exe = exefmt.decode_file(pe._isource.file.path)
|
|
||||||
ibase = pe.image_base
|
|
||||||
exe_imports = exe.imports
|
|
||||||
exe_imports.each do |lib|
|
|
||||||
lib_name = lib.libname
|
|
||||||
ini_offset = lib.iat_p
|
|
||||||
func_table = lib.imports
|
|
||||||
offset = 0
|
|
||||||
func_table.each do |func|
|
|
||||||
func_addr = "0x%08x" %(ibase + ini_offset + offset)
|
|
||||||
tbl << [lib_name, func_addr, func.hint, func.name]
|
|
||||||
offset += 4
|
|
||||||
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::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
|
|
||||||
|
|
|
@ -1,230 +0,0 @@
|
||||||
# -*- coding: binary -*-
|
|
||||||
require 'metasm'
|
|
||||||
|
|
||||||
module Rex
|
|
||||||
module PeScan
|
|
||||||
module Scanner
|
|
||||||
|
|
||||||
class Generic
|
|
||||||
|
|
||||||
attr_accessor :pe, :regex
|
|
||||||
|
|
||||||
def initialize(pe)
|
|
||||||
self.pe = pe
|
|
||||||
end
|
|
||||||
|
|
||||||
def config(param)
|
|
||||||
end
|
|
||||||
|
|
||||||
def scan(param)
|
|
||||||
config(param)
|
|
||||||
|
|
||||||
$stdout.puts "[#{param['file']}]"
|
|
||||||
pe.all_sections.each do |section|
|
|
||||||
hits = scan_section(section, param)
|
|
||||||
hits.each do |hit|
|
|
||||||
vma = pe.rva_to_vma(hit[0])
|
|
||||||
|
|
||||||
next if (param['filteraddr'] and [vma].pack("V").reverse !~ /#{param['filteraddr']}/)
|
|
||||||
|
|
||||||
msg = hit[1].is_a?(Array) ? hit[1].join(" ") : hit[1]
|
|
||||||
$stdout.puts pe.ptr_s(vma) + " " + msg
|
|
||||||
if(param['disasm'])
|
|
||||||
#puts [msg].pack('H*').inspect
|
|
||||||
insns = []
|
|
||||||
|
|
||||||
msg.gsub!("; ", "\n")
|
|
||||||
if msg.include?("retn")
|
|
||||||
msg.gsub!("retn", "ret")
|
|
||||||
end
|
|
||||||
#puts msg
|
|
||||||
begin
|
|
||||||
d2 = Metasm::Shellcode.assemble(Metasm::Ia32.new, msg).disassemble
|
|
||||||
rescue Metasm::ParseError
|
|
||||||
d2 = Metasm::Shellcode.disassemble(Metasm::Ia32.new, [msg].pack('H*'))
|
|
||||||
end
|
|
||||||
addr = 0
|
|
||||||
while ((di = d2.disassemble_instruction(addr)))
|
|
||||||
insns << di.instruction
|
|
||||||
disasm = "0x%08x\t" % (vma + addr)
|
|
||||||
disasm << di.instruction.to_s
|
|
||||||
$stdout.puts disasm
|
|
||||||
addr = di.next_addr
|
|
||||||
end
|
|
||||||
# ::Rex::Assembly::Nasm.disassemble([msg].pack("H*")).split("\n").each do |line|
|
|
||||||
# $stdout.puts "\tnasm: #{line.strip}"
|
|
||||||
#end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def scan_section(section, param={})
|
|
||||||
[]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class JmpRegScanner < Generic
|
|
||||||
|
|
||||||
def config(param)
|
|
||||||
regnums = param['args']
|
|
||||||
|
|
||||||
# build a list of the call bytes
|
|
||||||
calls = _build_byte_list(0xd0, regnums - [4]) # note call esp's don't work..
|
|
||||||
jmps = _build_byte_list(0xe0, regnums)
|
|
||||||
pushs1 = _build_byte_list(0x50, regnums)
|
|
||||||
pushs2 = _build_byte_list(0xf0, regnums)
|
|
||||||
|
|
||||||
regexstr = '('
|
|
||||||
if !calls.empty?
|
|
||||||
regexstr += "\xff[#{calls}]|"
|
|
||||||
end
|
|
||||||
|
|
||||||
regexstr += "\xff[#{jmps}]|([#{pushs1}]|\xff[#{pushs2}])(\xc3|\xc2..))"
|
|
||||||
|
|
||||||
self.regex = Regexp.new(regexstr, nil, 'n')
|
|
||||||
end
|
|
||||||
|
|
||||||
# build a list for regex of the possible bytes, based on a base
|
|
||||||
# byte and a list of register numbers..
|
|
||||||
def _build_byte_list(base, regnums)
|
|
||||||
regnums.collect { |regnum| Regexp.escape((base | regnum).chr) }.join('')
|
|
||||||
end
|
|
||||||
|
|
||||||
def _ret_size(section, index)
|
|
||||||
d = section.read(index, 1)
|
|
||||||
case d
|
|
||||||
when "\xc3"
|
|
||||||
return 1
|
|
||||||
when "\xc2"
|
|
||||||
return 3
|
|
||||||
end
|
|
||||||
|
|
||||||
raise RuntimeError, "invalid return opcode"
|
|
||||||
end
|
|
||||||
|
|
||||||
def _parse_ret(data)
|
|
||||||
if data.length == 1
|
|
||||||
return "ret"
|
|
||||||
else
|
|
||||||
return "retn 0x%04x" % data[1, 2].unpack('v')[0]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
def scan_section(section, param={})
|
|
||||||
index = 0
|
|
||||||
|
|
||||||
hits = [ ]
|
|
||||||
|
|
||||||
while (index = section.index(regex, index)) != nil
|
|
||||||
rva = section.offset_to_rva(index)
|
|
||||||
message = ''
|
|
||||||
|
|
||||||
parse_ret = false
|
|
||||||
|
|
||||||
byte1 = section.read(index, 1).unpack("C*")[0]
|
|
||||||
|
|
||||||
if byte1 == 0xff
|
|
||||||
byte2 = section.read(index+1, 1).unpack("C*")[0]
|
|
||||||
regname = Rex::Arch::X86.reg_name32(byte2 & 0x7)
|
|
||||||
|
|
||||||
case byte2 & 0xf8
|
|
||||||
when 0xd0
|
|
||||||
message = "call #{regname}"
|
|
||||||
index += 2
|
|
||||||
when 0xe0
|
|
||||||
message = "jmp #{regname}"
|
|
||||||
index += 2
|
|
||||||
when 0xf0
|
|
||||||
retsize = _ret_size(section, index+2)
|
|
||||||
message = "push #{regname}; " + _parse_ret(section.read(index+2, retsize))
|
|
||||||
index += 2 + retsize
|
|
||||||
else
|
|
||||||
raise "wtf"
|
|
||||||
end
|
|
||||||
else
|
|
||||||
regname = Rex::Arch::X86.reg_name32(byte1 & 0x7)
|
|
||||||
retsize = _ret_size(section, index+1)
|
|
||||||
message = "push #{regname}; " + _parse_ret(section.read(index+1, retsize))
|
|
||||||
index += 1 + retsize
|
|
||||||
end
|
|
||||||
|
|
||||||
hits << [ rva, message ]
|
|
||||||
end
|
|
||||||
|
|
||||||
return hits
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class PopPopRetScanner < JmpRegScanner
|
|
||||||
|
|
||||||
def config(param)
|
|
||||||
pops = _build_byte_list(0x58, (0 .. 7).to_a - [4]) # we don't want pop esp's...
|
|
||||||
self.regex = Regexp.new("[#{pops}][#{pops}](\xc3|\xc2..)", nil, 'n')
|
|
||||||
end
|
|
||||||
|
|
||||||
def scan_section(section, param={})
|
|
||||||
|
|
||||||
index = 0
|
|
||||||
|
|
||||||
hits = [ ]
|
|
||||||
|
|
||||||
while index < section.size && (index = section.index(regex, index)) != nil
|
|
||||||
rva = section.offset_to_rva(index)
|
|
||||||
message = ''
|
|
||||||
|
|
||||||
pops = section.read(index, 2)
|
|
||||||
reg1 = Rex::Arch::X86.reg_name32(pops[0,1].unpack("C*")[0] & 0x7)
|
|
||||||
reg2 = Rex::Arch::X86.reg_name32(pops[1,1].unpack("C*")[0] & 0x7)
|
|
||||||
|
|
||||||
message = "pop #{reg1}; pop #{reg2}; "
|
|
||||||
|
|
||||||
retsize = _ret_size(section, index+2)
|
|
||||||
message += _parse_ret(section.read(index+2, retsize))
|
|
||||||
|
|
||||||
index += 2 + retsize
|
|
||||||
|
|
||||||
hits << [ rva, message ]
|
|
||||||
end
|
|
||||||
|
|
||||||
return hits
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class RegexScanner < Generic
|
|
||||||
|
|
||||||
def config(param)
|
|
||||||
self.regex = Regexp.new(param['args'], nil, 'n')
|
|
||||||
end
|
|
||||||
|
|
||||||
def scan_section(section, param={})
|
|
||||||
index = 0
|
|
||||||
|
|
||||||
hits = [ ]
|
|
||||||
|
|
||||||
while index < section.size && (index = section.index(regex, index)) != nil
|
|
||||||
|
|
||||||
idx = index
|
|
||||||
buf = ''
|
|
||||||
mat = nil
|
|
||||||
|
|
||||||
while (! (mat = buf.match(regex)))
|
|
||||||
buf << section.read(idx, 1)
|
|
||||||
idx += 1
|
|
||||||
end
|
|
||||||
|
|
||||||
rva = section.offset_to_rva(index)
|
|
||||||
|
|
||||||
hits << [ rva, buf.unpack("H*") ]
|
|
||||||
index += buf.length
|
|
||||||
end
|
|
||||||
|
|
||||||
return hits
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
|
@ -1,68 +0,0 @@
|
||||||
# -*- coding: binary -*-
|
|
||||||
module Rex
|
|
||||||
module PeScan
|
|
||||||
module Search
|
|
||||||
|
|
||||||
require "rex/assembly/nasm"
|
|
||||||
|
|
||||||
class DumpRVA
|
|
||||||
attr_accessor :pe
|
|
||||||
|
|
||||||
def initialize(pe)
|
|
||||||
self.pe = pe
|
|
||||||
end
|
|
||||||
|
|
||||||
def config(param)
|
|
||||||
@address = pe.vma_to_rva(param['args'])
|
|
||||||
end
|
|
||||||
|
|
||||||
def scan(param)
|
|
||||||
config(param)
|
|
||||||
|
|
||||||
$stdout.puts "[#{param['file']}]"
|
|
||||||
|
|
||||||
# Adjust based on -A and -B flags
|
|
||||||
pre = param['before'] || 0
|
|
||||||
suf = param['after'] || 16
|
|
||||||
|
|
||||||
@address -= pre
|
|
||||||
@address = 0 if (@address < 0 || ! @address)
|
|
||||||
|
|
||||||
begin
|
|
||||||
buf = pe.read_rva(@address, suf)
|
|
||||||
rescue ::Rex::PeParsey::PeParseyError
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
$stdout.puts pe.ptr_s(pe.rva_to_vma(@address)) + " " + buf.unpack("H*")[0]
|
|
||||||
if(param['disasm'])
|
|
||||||
insns = []
|
|
||||||
buf.gsub!("; ", "\n")
|
|
||||||
if buf.include?("retn")
|
|
||||||
buf.gsub!("retn", "ret")
|
|
||||||
end
|
|
||||||
d2 = Metasm::Shellcode.disassemble(Metasm::Ia32.new, buf)
|
|
||||||
addr = 0
|
|
||||||
while ((di = d2.disassemble_instruction(addr)))
|
|
||||||
insns << di.instruction
|
|
||||||
disasm = "0x%08x\t" % (pe.rva_to_vma(@address) + addr)
|
|
||||||
disasm << di.instruction.to_s
|
|
||||||
$stdout.puts disasm
|
|
||||||
addr = di.next_addr
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class DumpOffset < DumpRVA
|
|
||||||
def config(param)
|
|
||||||
begin
|
|
||||||
@address = pe.file_offset_to_rva(param['args'])
|
|
||||||
rescue Rex::PeParsey::BoundsError
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -30,12 +30,8 @@ Gem::Specification.new do |spec|
|
||||||
spec.bindir = '.'
|
spec.bindir = '.'
|
||||||
if ENV['CREATE_BINSTUBS']
|
if ENV['CREATE_BINSTUBS']
|
||||||
spec.executables = [
|
spec.executables = [
|
||||||
'msfbinscan',
|
|
||||||
'msfconsole',
|
'msfconsole',
|
||||||
'msfd',
|
'msfd',
|
||||||
'msfelfscan',
|
|
||||||
'msfmachscan',
|
|
||||||
'msfpescan',
|
|
||||||
'msfrop',
|
'msfrop',
|
||||||
'msfrpc',
|
'msfrpc',
|
||||||
'msfrpcd',
|
'msfrpcd',
|
||||||
|
@ -134,6 +130,8 @@ Gem::Specification.new do |spec|
|
||||||
spec.add_runtime_dependency 'rex-ole'
|
spec.add_runtime_dependency 'rex-ole'
|
||||||
# Library for creating and/or parsing MIME messages.
|
# Library for creating and/or parsing MIME messages.
|
||||||
spec.add_runtime_dependency 'rex-mime'
|
spec.add_runtime_dependency 'rex-mime'
|
||||||
|
# Library for parsing and manipulating executable binaries
|
||||||
|
spec.add_runtime_dependency 'rex-bin_tools'
|
||||||
|
|
||||||
# rb-readline doesn't work with Ruby Installer due to error with Fiddle:
|
# rb-readline doesn't work with Ruby Installer due to error with Fiddle:
|
||||||
# NoMethodError undefined method `dlopen' for Fiddle:Module
|
# NoMethodError undefined method `dlopen' for Fiddle:Module
|
||||||
|
|
300
msfbinscan
300
msfbinscan
|
@ -1,300 +0,0 @@
|
||||||
#!/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
|
|
||||||
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
|
|
135
msfelfscan
135
msfelfscan
|
@ -1,135 +0,0 @@
|
||||||
#!/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 'rex/elfparsey'
|
|
||||||
require 'rex/elfscan'
|
|
||||||
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 = {}
|
|
||||||
|
|
||||||
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::ElfScan::Scanner::JmpRegScanner
|
|
||||||
param['args'] = regnums
|
|
||||||
end
|
|
||||||
|
|
||||||
opt.on('-p', '--poppopret', 'Search for pop+pop+ret combinations') do |t|
|
|
||||||
worker = Rex::ElfScan::Scanner::PopPopRetScanner
|
|
||||||
param['args'] = t
|
|
||||||
end
|
|
||||||
|
|
||||||
opt.on('-r', '--regex [regex]', 'Search for regex match') do |t|
|
|
||||||
worker = Rex::ElfScan::Scanner::RegexScanner
|
|
||||||
param['args'] = t
|
|
||||||
end
|
|
||||||
|
|
||||||
opt.on('-a', '--analyze-address [address]', 'Display the code at the specified address') do |t|
|
|
||||||
worker = Rex::ElfScan::Search::DumpRVA
|
|
||||||
param['args'] = opt2i(t)
|
|
||||||
end
|
|
||||||
|
|
||||||
opt.on('-b', '--analyze-offset [offset]', 'Display the code at the specified offset') do |t|
|
|
||||||
worker = Rex::ElfScan::Search::DumpOffset
|
|
||||||
param['args'] = opt2i(t)
|
|
||||||
end
|
|
||||||
|
|
||||||
opt.separator('')
|
|
||||||
opt.separator('Options:')
|
|
||||||
|
|
||||||
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_tail("-h", "--help", "Show this message") do
|
|
||||||
puts opt
|
|
||||||
exit
|
|
||||||
end
|
|
||||||
|
|
||||||
begin
|
|
||||||
opt.parse!
|
|
||||||
rescue OptionParser::InvalidOption
|
|
||||||
puts "Invalid option, try -h for usage"
|
|
||||||
exit(1)
|
|
||||||
end
|
|
||||||
|
|
||||||
if (! worker)
|
|
||||||
puts opt
|
|
||||||
exit(1)
|
|
||||||
end
|
|
||||||
|
|
||||||
ARGV.each do |file|
|
|
||||||
|
|
||||||
param['file'] = file
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
o = worker.new(elf)
|
|
||||||
o.scan(param)
|
|
||||||
|
|
||||||
elf.close
|
|
||||||
|
|
||||||
end
|
|
116
msfmachscan
116
msfmachscan
|
@ -1,116 +0,0 @@
|
||||||
#!/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 'rex/machparsey'
|
|
||||||
require 'rex/machscan'
|
|
||||||
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 = {}
|
|
||||||
|
|
||||||
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::MachScan::Scanner::JmpRegScanner
|
|
||||||
param['args'] = regnums
|
|
||||||
end
|
|
||||||
|
|
||||||
opt.on('-p', '--poppopret', 'Search for pop+pop+ret combinations') do |t|
|
|
||||||
worker = Rex::MachScan::Scanner::PopPopRetScanner
|
|
||||||
param['args'] = t
|
|
||||||
end
|
|
||||||
|
|
||||||
opt.on('-r', '--regex [regex]', 'Search for regex match') do |t|
|
|
||||||
worker = Rex::MachScan::Scanner::RegexScanner
|
|
||||||
param['args'] = t
|
|
||||||
end
|
|
||||||
|
|
||||||
opt.separator('')
|
|
||||||
opt.separator('Options:')
|
|
||||||
|
|
||||||
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('-I', '--image-base [address]', 'Specify an alternate ImageBase') do |t|
|
|
||||||
param['imagebase'] = opt2i(t)
|
|
||||||
end
|
|
||||||
|
|
||||||
opt.on_tail("-h", "--help", "Show this message") do
|
|
||||||
puts opt
|
|
||||||
exit
|
|
||||||
end
|
|
||||||
|
|
||||||
begin
|
|
||||||
opt.parse!
|
|
||||||
rescue OptionParser::InvalidOption
|
|
||||||
puts "Invalid option, try -h for usage"
|
|
||||||
exit(1)
|
|
||||||
end
|
|
||||||
|
|
||||||
if (! worker)
|
|
||||||
puts opt
|
|
||||||
exit(1)
|
|
||||||
end
|
|
||||||
|
|
||||||
ARGV.each do |file|
|
|
||||||
|
|
||||||
param['file'] = file
|
|
||||||
|
|
||||||
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")
|
|
||||||
fat = Rex::MachParsey::Fat.new_from_file(file, true)
|
|
||||||
o = worker.new(fat)
|
|
||||||
o.scan(param)
|
|
||||||
fat.close
|
|
||||||
rescue Errno::ENOENT
|
|
||||||
$stderr.puts("File does not exist: #{file}")
|
|
||||||
next
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
199
msfpescan
199
msfpescan
|
@ -1,199 +0,0 @@
|
||||||
#!/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 '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
|
|
||||||
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 ""
|
|
|
@ -1,60 +0,0 @@
|
||||||
require 'rex/file'
|
|
||||||
|
|
||||||
RSpec.describe Rex::FileUtils do
|
|
||||||
context "Class methods" do
|
|
||||||
|
|
||||||
context ".normalize_win_path" do
|
|
||||||
it "should convert an absolute path as an array into Windows format" do
|
|
||||||
expect(described_class.normalize_win_path('C:\\', 'hello', 'world')).to eq("C:\\hello\\world")
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should convert an absolute path as a string into Windows format" do
|
|
||||||
expect(described_class.normalize_win_path('C:\\hello\\world')).to eq("C:\\hello\\world")
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should convert a relative path" do
|
|
||||||
expect(described_class.normalize_win_path('/', 'test', 'me')).to eq("\\test\\me")
|
|
||||||
expect(described_class.normalize_win_path('\\temp')).to eq("\\temp")
|
|
||||||
expect(described_class.normalize_win_path('temp')).to eq("temp")
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should keep the trailing slash if exists" do
|
|
||||||
expect(described_class.normalize_win_path('/', 'test', 'me\\')).to eq("\\test\\me\\")
|
|
||||||
expect(described_class.normalize_win_path('\\temp\\')).to eq("\\temp\\")
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should convert a path without reserved characters" do
|
|
||||||
expect(described_class.normalize_win_path('C:\\', 'Windows:')).to eq("C:\\Windows")
|
|
||||||
expect(described_class.normalize_win_path('C:\\Windows???\\test')).to eq("C:\\Windows\\test")
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should convert a path without double slashes" do
|
|
||||||
expect(described_class.normalize_win_path('C:\\\\\\', 'Windows')).to eq("C:\\Windows")
|
|
||||||
expect(described_class.normalize_win_path('C:\\\\\\Hello World\\\\whatever.txt')).to eq("C:\\Hello World\\whatever.txt")
|
|
||||||
expect(described_class.normalize_win_path('C:\\\\')).to eq("C:\\")
|
|
||||||
expect(described_class.normalize_win_path('\\test\\\\test\\\\')).to eq("\\test\\test\\")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context ".normalize_unix_path" do
|
|
||||||
it "should convert an absolute path as an array into Unix format" do
|
|
||||||
expect(described_class.normalize_unix_path('/etc', '/passwd')).to eq("/etc/passwd")
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should convert an absolute path as a string into Unix format" do
|
|
||||||
expect(described_class.normalize_unix_path('/etc/passwd')).to eq('/etc/passwd')
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should still give me a trailing slash if I have it" do
|
|
||||||
expect(described_class.normalize_unix_path('/etc/folder/')).to eq("/etc/folder/")
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should convert a path without double slashes" do
|
|
||||||
expect(described_class.normalize_unix_path('//etc////passwd')).to eq("/etc/passwd")
|
|
||||||
expect(described_class.normalize_unix_path('/etc////', 'passwd')).to eq('/etc/passwd')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
Loading…
Reference in New Issue