From fd3b885d838be571ed596db6afb11180376d2966 Mon Sep 17 00:00:00 2001 From: David Maloney Date: Mon, 12 Sep 2016 16:06:53 -0500 Subject: [PATCH 1/2] replace msfrop with the rex-rop_builder gem moved all of this code into the new gem MS-1722 --- Gemfile.lock | 7 +- lib/rex/ropbuilder.rb | 8 -- lib/rex/ropbuilder/rop.rb | 271 ----------------------------------- metasploit-framework.gemspec | 3 +- msfrop | 173 ---------------------- 5 files changed, 8 insertions(+), 454 deletions(-) delete mode 100644 lib/rex/ropbuilder.rb delete mode 100644 lib/rex/ropbuilder/rop.rb delete mode 100755 msfrop diff --git a/Gemfile.lock b/Gemfile.lock index 59a3c29012..62f9ca6b11 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -43,6 +43,7 @@ PATH rex-powershell rex-random_identifier rex-registry + rex-rop_builder rex-socket rex-sslscan rex-struct2 @@ -256,13 +257,17 @@ GEM rex-random_identifier (0.1.0) rex-text rex-registry (0.1.0) + rex-rop_builder (0.1.0) + metasm + rex-core + rex-text rex-socket (0.1.0) rex-core rex-sslscan (0.1.0) rex-socket rex-text rex-struct2 (0.1.0) - rex-text (0.2.1) + rex-text (0.2.2) rex-zip (0.1.0) rex-text rkelly-remix (0.0.6) diff --git a/lib/rex/ropbuilder.rb b/lib/rex/ropbuilder.rb deleted file mode 100644 index 3b24886cd4..0000000000 --- a/lib/rex/ropbuilder.rb +++ /dev/null @@ -1,8 +0,0 @@ -# -*- coding: binary -*- -module Rex -module RopBuilder - -require 'rex/ropbuilder/rop' -require 'metasm' -end -end diff --git a/lib/rex/ropbuilder/rop.rb b/lib/rex/ropbuilder/rop.rb deleted file mode 100644 index 503ff967d9..0000000000 --- a/lib/rex/ropbuilder/rop.rb +++ /dev/null @@ -1,271 +0,0 @@ -# -*- coding: binary -*- -require 'metasm' -require 'rex/compat' -require 'rex/text/table' -require 'rex/ui/text/output/stdio' -require 'rex/text/color' - -module Rex -module RopBuilder - -class RopBase - def initialize() - @stdio = Rex::Ui::Text::Output::Stdio.new - @gadgets = [] - end - - def to_csv(gadgets = []) - if gadgets.empty? and @gadgets.nil? or @gadgets.empty? - @stdio.print_error("No gadgets collected to convert to CSV format.") - return - end - - # allow the users to import gadget collections from multiple files - if @gadgets.empty? or @gadgets.nil? - @gadgets = gadgets - end - - table = Rex::Text::Table.new( - 'Header' => "#{@file} ROP Gadgets", - 'Indent' => 1, - 'Columns' => - [ - "Address", - "Raw", - "Disassembly", - ]) - - @gadgets.each do |gadget| - table << [gadget[:address], gadget[:raw].unpack('H*')[0], gadget[:disasm].gsub(/\n/, ' | ')] - end - - return table.to_csv - end - - def import(file) - begin - data = File.new(file, 'r').read - rescue - @stdio.print_error("Error reading #{file}") - return [] - end - - if data.empty? or data.nil? - return [] - end - - data.gsub!(/\"/, '') - data.gsub!("Address,Raw,Disassembly\n", '') - - @gadgets = [] - - data.each_line do |line| - addr, raw, disasm = line.split(',', 3) - if addr.nil? or raw.nil? or disasm.nil? - @stdio.print_error("Import file format corrupted") - return [] - end - disasm.gsub!(/: /, ":\t") - disasm.gsub!(' | ', "\n") - raw = [raw].pack('H*') - @gadgets << {:file => file, :address => addr, :raw => raw, :disasm => disasm.chomp!} - end - @gadgets - end - - def print_msg(msg, color=true) - if not @stdio - @stdio = Rex::Ui::Text::Output::Stdio.new - end - - if color == true - @stdio.auto_color - else - @stdio.disable_color - end - @stdio.print_raw(@stdio.substitute_colors(msg)) - end -end - -class RopCollect < RopBase - def initialize(file="") - @stdio = Rex::Ui::Text::Output::Stdio.new - @file = file if not file.empty? - @bin = Metasm::AutoExe.decode_file(file) if not file.empty? - @disassembler = @bin.disassembler if not @bin.nil? - if @disassembler - @disassembler.cpu = Metasm::Ia32.new('386_common') - end - super() - end - - def collect(depth, pattern) - matches = [] - gadgets = [] - - # find matches by scanning for the pattern - matches = @disassembler.pattern_scan(pattern) - if @bin.kind_of?(Metasm::PE) - @bin.sections.each do |section| - next if section.characteristics.include? 'MEM_EXECUTE' - # delete matches if the address is outside the virtual address space - matches.delete_if do |ea| - va = section.virtaddr + @bin.optheader.image_base - ea >= va and ea < va + section.virtsize - end - end - elsif @bin.kind_of?(Metasm::ELF) - @bin.segments.each do |seg| - next if seg.flags.include? 'X' - matches.delete_if do |ea| - ea >= seg.vaddr and ea < seg.vaddr + seg.memsz - end - end - elsif @bin.kind_of?(Metasm::MachO) - @bin.segments.each do |seg| - next if seg.initprot.include? 'EXECUTE' - matches.delete_if do |ea| - ea >= seg.virtaddr and ea < seg.virtaddr + seg.filesize - end - end - end - - gadgets = process_gadgets(matches, depth) - gadgets.each do |gadget| - @gadgets << gadget - end - gadgets - end - - def pattern_search(pattern) - p = Regexp.new("(" + pattern + ")") - matches = [] - - @gadgets.each do |gadget| - disasm = "" - addrs = [] - - gadget[:disasm].each_line do |line| - addr, asm = line.split("\t", 2) - addrs << addr - disasm << asm - end - - if gadget[:raw] =~ p or gadget[:disasm] =~ p or disasm =~ p - matches << {:gadget => gadget, :disasm => disasm, :addrs => addrs} - end - end - matches.each do |match| - @stdio.print_status("gadget with address: %bld%cya#{match[:gadget][:address]}%clr matched") - color_pattern(match[:gadget], match[:disasm], match[:addrs], p) - end - matches - end - - def color_pattern(gadget, disasm, addrs, p) - idx = disasm.index(p) - if idx.nil? - print_msg(gadget[:disasm]) - return - end - - disasm = disasm.insert(idx, "%bld%grn") - - asm = "" - cnt = 0 - colors = false - disasm.each_line do |line| - # if we find this then we are in the matching area - if line.index(/\%bld\%grn/) - colors = true - end - asm << "%clr" + addrs[cnt] + "\t" - - # color the remaining parts of the gadget - if colors and line.index("%bld%grn").nil? - asm << "%bld%grn" + line - else - asm << line - end - - cnt += 1 - end - asm << "%clr\n" - print_msg(asm) - end - - def process_gadgets(rets, num) - ret = {} - gadgets = [] - tmp = [] - rets.each do |ea| - insn = @disassembler.disassemble_instruction(ea) - next if not insn - - xtra = insn.bin_length - - num.downto(0) do |x| - addr = ea - x - - # get the disassembled instruction at this address - di = @disassembler.disassemble_instruction(addr) - - # skip invalid instructions - next if not di - next if di.opcode.props[:setip] - next if di.opcode.props[:stopexec] - - # get raw bytes - buf = @disassembler.read_raw_data(addr, x + xtra) - - - # make sure disassembling forward leads to our instruction - next if not ends_with_addr(buf, addr, ea) - - dasm = "" - while addr <= ea - di = @disassembler.disassemble_instruction(addr) - dasm << ("0x%08x:\t" % addr) + di.instruction.to_s + "\n" - addr = addr + di.bin_length - end - - if not tmp.include?(ea) - tmp << ea - else - next - end - - # otherwise, we create a new tailchunk and add it to the list - ret = {:file => @file, :address => ("0x%08x" % (ea - x)), :raw => buf, :disasm => dasm} - gadgets << ret - end - end - gadgets - end - - private - def ends_with_addr(raw, base, addr) - dasm2 = Metasm::Shellcode.decode(raw, @disassembler.cpu).disassembler - offset = 0 - while ((di = dasm2.disassemble_instruction(offset))) - return true if (base + offset) == addr - return false if di.opcode.props[:setip] - return false if di.opcode.props[:stopexec] - offset = di.next_addr - end - false - end - - def raw_instructions(raw) - insns = [] - d2 = Metasm::Shellcode.decode(raw, @disassembler.cpu).disassembler - addr = 0 - while ((di = d2.disassemble_instruction(addr))) - insns << di.instruction - addr = di.next_addr - end - insns - end -end -end -end diff --git a/metasploit-framework.gemspec b/metasploit-framework.gemspec index 2a42d03d91..6d420cfa9a 100644 --- a/metasploit-framework.gemspec +++ b/metasploit-framework.gemspec @@ -32,7 +32,6 @@ Gem::Specification.new do |spec| spec.executables = [ 'msfconsole', 'msfd', - 'msfrop', 'msfrpc', 'msfrpcd', 'msfupdate', @@ -142,6 +141,8 @@ Gem::Specification.new do |spec| spec.add_runtime_dependency 'rex-socket' # Library for scanning a server's SSL/TLS capabilities spec.add_runtime_dependency 'rex-sslscan' + # Library and tool for finding ROP gadgets in a supplied binary + spec.add_runtime_dependency 'rex-rop_builder' # rb-readline doesn't work with Ruby Installer due to error with Fiddle: # NoMethodError undefined method `dlopen' for Fiddle:Module diff --git a/msfrop b/msfrop deleted file mode 100755 index f5ce75d75f..0000000000 --- a/msfrop +++ /dev/null @@ -1,173 +0,0 @@ -#!/usr/bin/env ruby -# -*- coding: binary -*- -# -# $Id$ -# -# This tool will collect, export, and import ROP gadgets -# from various file formats (PE, ELF, Macho) -# $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' -require 'rex/ropbuilder' -require 'rex/ui/text/output/stdio' -require 'rex/text/color' -require 'optparse' - -def opt2i(o) - o.index("0x")==0 ? o.hex : o.to_i -end - -opts = {} -color = true - -opt = OptionParser.new -opt.banner = "Usage #{$PROGRAM_NAME}