Retab lib

This commit is contained in:
Tab Assassin 2013-08-30 16:28:33 -05:00
parent bd1fb98e50
commit 40d45d7a28
1 changed files with 217 additions and 217 deletions

434
rop.rb
View File

@ -9,263 +9,263 @@ module Rex
module RopBuilder module RopBuilder
class RopBase class RopBase
def initialize() def initialize()
@stdio = Rex::Ui::Text::Output::Stdio.new @stdio = Rex::Ui::Text::Output::Stdio.new
@gadgets = [] @gadgets = []
end end
def to_csv(gadgets = []) def to_csv(gadgets = [])
if gadgets.empty? and @gadgets.nil? or @gadgets.empty? if gadgets.empty? and @gadgets.nil? or @gadgets.empty?
@stdio.print_error("No gadgets collected to convert to CSV format.") @stdio.print_error("No gadgets collected to convert to CSV format.")
return return
end end
# allow the users to import gadget collections from multiple files # allow the users to import gadget collections from multiple files
if @gadgets.empty? or @gadgets.nil? if @gadgets.empty? or @gadgets.nil?
@gadgets = gadgets @gadgets = gadgets
end end
table = Rex::Ui::Text::Table.new( table = Rex::Ui::Text::Table.new(
'Header' => "#{@file} ROP Gadgets", 'Header' => "#{@file} ROP Gadgets",
'Indent' => 1, 'Indent' => 1,
'Columns' => 'Columns' =>
[ [
"Address", "Address",
"Raw", "Raw",
"Disassembly", "Disassembly",
]) ])
@gadgets.each do |gadget| @gadgets.each do |gadget|
table << [gadget[:address], gadget[:raw].unpack('H*')[0], gadget[:disasm].gsub(/\n/, ' | ')] table << [gadget[:address], gadget[:raw].unpack('H*')[0], gadget[:disasm].gsub(/\n/, ' | ')]
end end
return table.to_csv return table.to_csv
end end
def import(file) def import(file)
begin begin
data = File.new(file, 'r').read data = File.new(file, 'r').read
rescue rescue
@stdio.print_error("Error reading #{file}") @stdio.print_error("Error reading #{file}")
return [] return []
end end
if data.empty? or data.nil? if data.empty? or data.nil?
return [] return []
end end
data.gsub!(/\"/, '') data.gsub!(/\"/, '')
data.gsub!("Address,Raw,Disassembly\n", '') data.gsub!("Address,Raw,Disassembly\n", '')
@gadgets = [] @gadgets = []
data.each_line do |line| data.each_line do |line|
addr, raw, disasm = line.split(',', 3) addr, raw, disasm = line.split(',', 3)
if addr.nil? or raw.nil? or disasm.nil? if addr.nil? or raw.nil? or disasm.nil?
@stdio.print_error("Import file format corrupted") @stdio.print_error("Import file format corrupted")
return [] return []
end end
disasm.gsub!(/: /, ":\t") disasm.gsub!(/: /, ":\t")
disasm.gsub!(' | ', "\n") disasm.gsub!(' | ', "\n")
raw = [raw].pack('H*') raw = [raw].pack('H*')
@gadgets << {:file => file, :address => addr, :raw => raw, :disasm => disasm.chomp!} @gadgets << {:file => file, :address => addr, :raw => raw, :disasm => disasm.chomp!}
end end
@gadgets @gadgets
end end
def print_msg(msg, color=true) def print_msg(msg, color=true)
if not @stdio if not @stdio
@stdio = Rex::Ui::Text::Output::Stdio.new @stdio = Rex::Ui::Text::Output::Stdio.new
end end
if color == true if color == true
@stdio.auto_color @stdio.auto_color
else else
@stdio.disable_color @stdio.disable_color
end end
@stdio.print_raw(@stdio.substitute_colors(msg)) @stdio.print_raw(@stdio.substitute_colors(msg))
end end
end end
class RopCollect < RopBase class RopCollect < RopBase
def initialize(file="") def initialize(file="")
@stdio = Rex::Ui::Text::Output::Stdio.new @stdio = Rex::Ui::Text::Output::Stdio.new
@file = file if not file.empty? @file = file if not file.empty?
@bin = Metasm::AutoExe.decode_file(file) if not file.empty? @bin = Metasm::AutoExe.decode_file(file) if not file.empty?
@disassembler = @bin.disassembler if not @bin.nil? @disassembler = @bin.disassembler if not @bin.nil?
if @disassembler if @disassembler
@disassembler.cpu = Metasm::Ia32.new('386_common') @disassembler.cpu = Metasm::Ia32.new('386_common')
end end
super() super()
end end
def collect(depth, pattern) def collect(depth, pattern)
matches = [] matches = []
gadgets = [] gadgets = []
# find matches by scanning for the pattern # find matches by scanning for the pattern
matches = @disassembler.pattern_scan(pattern) matches = @disassembler.pattern_scan(pattern)
if @bin.kind_of?(Metasm::PE) if @bin.kind_of?(Metasm::PE)
@bin.sections.each do |section| @bin.sections.each do |section|
next if section.characteristics.include? 'MEM_EXECUTE' next if section.characteristics.include? 'MEM_EXECUTE'
# delete matches if the address is outside the virtual address space # delete matches if the address is outside the virtual address space
matches.delete_if do |ea| matches.delete_if do |ea|
va = section.virtaddr + @bin.optheader.image_base va = section.virtaddr + @bin.optheader.image_base
ea >= va and ea < va + section.virtsize ea >= va and ea < va + section.virtsize
end end
end end
elsif @bin.kind_of?(Metasm::ELF) elsif @bin.kind_of?(Metasm::ELF)
@bin.segments.each do |seg| @bin.segments.each do |seg|
next if seg.flags.include? 'X' next if seg.flags.include? 'X'
matches.delete_if do |ea| matches.delete_if do |ea|
ea >= seg.vaddr and ea < seg.vaddr + seg.memsz ea >= seg.vaddr and ea < seg.vaddr + seg.memsz
end end
end end
elsif @bin.kind_of?(Metasm::MachO) elsif @bin.kind_of?(Metasm::MachO)
@bin.segments.each do |seg| @bin.segments.each do |seg|
next if seg.initprot.include? 'EXECUTE' next if seg.initprot.include? 'EXECUTE'
matches.delete_if do |ea| matches.delete_if do |ea|
ea >= seg.virtaddr and ea < seg.virtaddr + seg.filesize ea >= seg.virtaddr and ea < seg.virtaddr + seg.filesize
end end
end end
end end
gadgets = process_gadgets(matches, depth) gadgets = process_gadgets(matches, depth)
gadgets.each do |gadget| gadgets.each do |gadget|
@gadgets << gadget @gadgets << gadget
end end
gadgets gadgets
end end
def pattern_search(pattern) def pattern_search(pattern)
p = Regexp.new("(" + pattern + ")") p = Regexp.new("(" + pattern + ")")
matches = [] matches = []
@gadgets.each do |gadget| @gadgets.each do |gadget|
disasm = "" disasm = ""
addrs = [] addrs = []
gadget[:disasm].each_line do |line| gadget[:disasm].each_line do |line|
addr, asm = line.split("\t", 2) addr, asm = line.split("\t", 2)
addrs << addr addrs << addr
disasm << asm disasm << asm
end end
if gadget[:raw] =~ p or gadget[:disasm] =~ p or disasm =~ p if gadget[:raw] =~ p or gadget[:disasm] =~ p or disasm =~ p
matches << {:gadget => gadget, :disasm => disasm, :addrs => addrs} matches << {:gadget => gadget, :disasm => disasm, :addrs => addrs}
end end
end end
matches.each do |match| matches.each do |match|
@stdio.print_status("gadget with address: %bld%cya#{match[:gadget][:address]}%clr matched") @stdio.print_status("gadget with address: %bld%cya#{match[:gadget][:address]}%clr matched")
color_pattern(match[:gadget], match[:disasm], match[:addrs], p) color_pattern(match[:gadget], match[:disasm], match[:addrs], p)
end end
matches matches
end end
def color_pattern(gadget, disasm, addrs, p) def color_pattern(gadget, disasm, addrs, p)
idx = disasm.index(p) idx = disasm.index(p)
if idx.nil? if idx.nil?
print_msg(gadget[:disasm]) print_msg(gadget[:disasm])
return return
end end
disasm = disasm.insert(idx, "%bld%grn") disasm = disasm.insert(idx, "%bld%grn")
asm = "" asm = ""
cnt = 0 cnt = 0
colors = false colors = false
disasm.each_line do |line| disasm.each_line do |line|
# if we find this then we are in the matching area # if we find this then we are in the matching area
if line.index(/\%bld\%grn/) if line.index(/\%bld\%grn/)
colors = true colors = true
end end
asm << "%clr" + addrs[cnt] + "\t" asm << "%clr" + addrs[cnt] + "\t"
# color the remaining parts of the gadget # color the remaining parts of the gadget
if colors and line.index("%bld%grn").nil? if colors and line.index("%bld%grn").nil?
asm << "%bld%grn" + line asm << "%bld%grn" + line
else else
asm << line asm << line
end end
cnt += 1 cnt += 1
end end
asm << "%clr\n" asm << "%clr\n"
print_msg(asm) print_msg(asm)
end end
def process_gadgets(rets, num) def process_gadgets(rets, num)
ret = {} ret = {}
gadgets = [] gadgets = []
tmp = [] tmp = []
rets.each do |ea| rets.each do |ea|
insn = @disassembler.disassemble_instruction(ea) insn = @disassembler.disassemble_instruction(ea)
next if not insn next if not insn
xtra = insn.bin_length xtra = insn.bin_length
num.downto(0) do |x| num.downto(0) do |x|
addr = ea - x addr = ea - x
# get the disassembled instruction at this address # get the disassembled instruction at this address
di = @disassembler.disassemble_instruction(addr) di = @disassembler.disassemble_instruction(addr)
# skip invalid instructions # skip invalid instructions
next if not di next if not di
next if di.opcode.props[:setip] next if di.opcode.props[:setip]
next if di.opcode.props[:stopexec] next if di.opcode.props[:stopexec]
# get raw bytes # get raw bytes
buf = @disassembler.read_raw_data(addr, x + xtra) buf = @disassembler.read_raw_data(addr, x + xtra)
# make sure disassembling forward leads to our instruction # make sure disassembling forward leads to our instruction
next if not ends_with_addr(buf, addr, ea) next if not ends_with_addr(buf, addr, ea)
dasm = "" dasm = ""
while addr <= ea while addr <= ea
di = @disassembler.disassemble_instruction(addr) di = @disassembler.disassemble_instruction(addr)
dasm << ("0x%08x:\t" % addr) + di.instruction.to_s + "\n" dasm << ("0x%08x:\t" % addr) + di.instruction.to_s + "\n"
addr = addr + di.bin_length addr = addr + di.bin_length
end end
if not tmp.include?(ea) if not tmp.include?(ea)
tmp << ea tmp << ea
else else
next next
end end
# otherwise, we create a new tailchunk and add it to the list # otherwise, we create a new tailchunk and add it to the list
ret = {:file => @file, :address => ("0x%08x" % (ea - x)), :raw => buf, :disasm => dasm} ret = {:file => @file, :address => ("0x%08x" % (ea - x)), :raw => buf, :disasm => dasm}
gadgets << ret gadgets << ret
end end
end end
gadgets gadgets
end end
private private
def ends_with_addr(raw, base, addr) def ends_with_addr(raw, base, addr)
dasm2 = Metasm::Shellcode.decode(raw, @disassembler.cpu).disassembler dasm2 = Metasm::Shellcode.decode(raw, @disassembler.cpu).disassembler
offset = 0 offset = 0
while ((di = dasm2.disassemble_instruction(offset))) while ((di = dasm2.disassemble_instruction(offset)))
return true if (base + offset) == addr return true if (base + offset) == addr
return false if di.opcode.props[:setip] return false if di.opcode.props[:setip]
return false if di.opcode.props[:stopexec] return false if di.opcode.props[:stopexec]
offset = di.next_addr offset = di.next_addr
end end
false false
end end
def raw_instructions(raw) def raw_instructions(raw)
insns = [] insns = []
d2 = Metasm::Shellcode.decode(raw, @disassembler.cpu).disassembler d2 = Metasm::Shellcode.decode(raw, @disassembler.cpu).disassembler
addr = 0 addr = 0
while ((di = d2.disassemble_instruction(addr))) while ((di = d2.disassemble_instruction(addr)))
insns << di.instruction insns << di.instruction
addr = di.next_addr addr = di.next_addr
end end
insns insns
end end
end end
end end
end end