metasploit-framework/lib/metasm/samples/dasm-plugins/patch_file.rb

96 lines
2.5 KiB
Ruby

# This file is part of Metasm, the Ruby assembly manipulation suite
# Copyright (C) 2006-2009 Yoann GUILLOT
#
# Licence is LGPL, see LICENCE in the top-level directory
# metasm dasm plugin: allow patching the file from the dasm interface
# use P to assemble a new instruction at the current address
# backup the executable file
def backup_program_file
f = @program.filename
if File.exist?(f) and not File.exist?(f + '.bak')
File.open(f + '.bak', 'wb') { |wfd|
File.open(f, 'rb') { |rfd|
while buf = rfd.read(1024*1024)
wfd.write buf
end
}
}
end
end
# create a backup and reopen the backend VirtualFile RW
def reopen_rw(addr=nil, edata=nil)
if not edata
sections.each { |k, v| reopen_rw(k, v) }
return true
end
return if not File.writable?(@program.filename)
backup_program_file
if not edata.data.kind_of? VirtualFile
# section too small, loaded as real String
# force reopen as VFile (allow hexediting in gui)
return if not off = addr_to_fileoff(addr)
len = edata.data.length
edata.data = VirtualFile.read(@program.filename, 'rb+').dup(off, len)
else
edata.data.fd.reopen @program.filename, 'rb+'
end
end
raise "cant find original file" if not @program.filename or not File.exist? @program.filename
reopen_rw
def patch_instrs(addr, asmsrc)
sc = Metasm::Shellcode.new(cpu, addr) # pfx needed for autorequire
sc.assemble(asmsrc, cpu)
sc.encoded.fixup! prog_binding # allow references to dasm labels in the shellcode
raw = sc.encode_string
if s = get_section_at(addr) and s[0].data.kind_of? VirtualFile
s[0][s[0].ptr, raw.length] = raw
elsif o = addr_to_fileoff(addr) # section too small, not loaded as a VirtFile
backup_program_file
File.open(@program.filename, 'rb+') { |fd|
fd.pos = o
fd.write raw
}
s[0][s[0].ptr, raw.length] = raw if s
else
return
end
b = split_block(addr)
# clear what we had in the rewritten space
raw.length.times { |rawoff|
next if not di = di_at(addr+rawoff)
di.block.list.each { |ldi| @decoded.delete ldi.address }
}
disassemble_fast(addr) if b
if b and @decoded[addr]
nb = @decoded[addr].block
nb.from_normal = b.from_normal
nb.from_subfuncret = b.from_subfuncret
nb.from_indirect = b.from_indirect
end
true
end
if gui
gui.keyboard_callback[?P] = lambda { |k|
addr = gui.curaddr
gui.inputbox('new instructions') { |src|
src = src.gsub(/;\s+/, "\n")
patch_instrs(addr, src)
gui.gui_update
}
true
}
end