metasm/disassemble_api.rb in metasm-1.0.3 vs metasm/disassemble_api.rb in metasm-1.0.4
- old
+ new
@@ -128,15 +128,10 @@
def block_head?
self == @block.list.first
end
end
-class CPU
- # compat alias, for scripts using older version of metasm
- def get_backtrace_binding(di) backtrace_binding(di) end
-end
-
class Disassembler
# access the default value for @@backtrace_maxblocks for newly created Disassemblers
def self.backtrace_maxblocks ; @@backtrace_maxblocks ; end
def self.backtrace_maxblocks=(b) ; @@backtrace_maxblocks = b ; end
@@ -208,12 +203,12 @@
ret
end
alias instructionblocks each_instructionblock
# return a backtrace_binding reversed (akin to code emulation) (but not really)
- def get_fwdemu_binding(di, pc=nil)
- @cpu.get_fwdemu_binding(di, pc)
+ def get_fwdemu_binding(di, pc=nil, dbg_ctx=nil)
+ @cpu.get_fwdemu_binding(di, pc, dbg_ctx)
end
# reads len raw bytes from the mmaped address space
def read_raw_data(addr, len)
if e = get_section_at(addr)
@@ -274,29 +269,36 @@
from_addr = from_addr.address if from_addr.kind_of? DecodedInstruction
from_addr = normalize(from_addr)
if b = block_at(from_addr)
b.add_to_normal(addr)
end
- @addrs_todo << [addr, from_addr]
+ @addrs_todo << { :addr => addr, :from => from_addr }
disassemble
end
# returns the label associated to an addr, or nil if none exist
def get_label_at(addr)
e = get_edata_at(addr, false)
e.inv_export[e.ptr] if e
end
+ # return the array of all labels associated to an addr
+ def get_all_labels_at(addr)
+ addr = normalize(addr)
+ label_alias[addr].to_a
+ end
+
# sets the label for the specified address
# returns nil if the address is not mapped
# memcheck is passed to get_section_at to validate that the address is mapped
# keep existing label if 'overwrite' is false
def set_label_at(addr, name, memcheck=true, overwrite=true)
addr = Expression[addr].reduce
e, b = get_section_at(addr, memcheck)
if not e
elsif not l = e.inv_export[e.ptr] or (!overwrite and l != name)
+ split_block(addr)
l = @program.new_label(name)
e.add_export l, e.ptr
@label_alias_cache = nil
@old_prog_binding[l] = @prog_binding[l] = b + e.ptr
elsif l != name
@@ -681,23 +683,28 @@
# returns an array of instructions/label that, once parsed and assembled, should
# give something equivalent to the code accessible from the (list of) entrypoints given
# from the @decoded dasm graph
# assume all jump targets have a matching label in @prog_binding
- # may add inconditionnal jumps in the listing to preserve the code flow
+ # may add inconditional jumps in the listing to preserve the code flow
def flatten_graph(entry, include_subfunc=true)
ret = []
entry = [entry] if not entry.kind_of? Array
todo = entry.map { |a| normalize(a) }
done = []
inv_binding = @prog_binding.invert
while addr = todo.pop
- next if done.include? addr or not di_at(addr)
+ next if done.include?(addr)
done << addr
- b = @decoded[addr].block
ret << Label.new(inv_binding[addr]) if inv_binding[addr]
+ if not di_at(addr)
+ ret << @cpu.instr_jump_stop
+ next
+ end
+
+ b = @decoded[addr].block
ret.concat b.list.map { |di| di.instruction }
b.each_to_otherfunc(self) { |to|
to = normalize to
todo.unshift to if include_subfunc
@@ -707,22 +714,24 @@
todo << to
}
if not di = b.list[-1-@cpu.delay_slot] or not di.opcode.props[:stopexec] or di.opcode.props[:saveip]
to = b.list.last.next_addr
- if todo.include? to
- if done.include? to or not di_at(to)
+ if todo.include?(to) and di_at(to)
+ if done.include?(to)
if not to_l = inv_binding[to]
to_l = auto_label_at(to, 'loc')
if done.include? to and idx = ret.index(@decoded[to].block.list.first.instruction)
ret.insert(idx, Label.new(to_l))
end
end
ret << @cpu.instr_uncond_jump_to(to_l)
else
todo << to # ensure it's next in the listing
end
+ else
+ ret << @cpu.instr_jump_stop
end
end
end
ret
@@ -975,11 +984,11 @@
"#{o} #{type} #{l}"
}
end
# loads a map file (addr => symbol)
- # off is an optionnal offset to add to every address found (for eg rebased binaries)
+ # off is an optional offset to add to every address found (for eg rebased binaries)
# understands:
# standard map files (eg linux-kernel.map: <addr> <type> <name>, e.g. 'c01001ba t setup_idt')
# ida map files (<sectionidx>:<sectionoffset> <name>)
# arg is either the map itself or the filename of the map (if it contains no newline)
def load_map(str, off=0)
@@ -994,11 +1003,11 @@
seen[addr] = true
when /^([0-9A-F]+):([0-9A-F]+)\s+([a-z_]\w+)/i # IDA style
# we do not have section load order, let's just hope that the addresses are sorted (and sortable..)
# could check the 1st part of the file, with section sizes, but it is not very convenient
# the regexp is so that we skip the 1st part with section descriptions
- # in the file, section 1 is the 1st section ; we have an additionnal section (exe header) which fixes the 0-index
+ # in the file, section 1 is the 1st section ; we have an additional section (exe header) which fixes the 0-index
# XXX this is PE-specific, TODO fix it for ELF (ida references sections, we reference segments...)
addr = sks[$1.to_i(16)] + $2.to_i(16) + off
set_label_at(addr, $3, false, !seen[addr])
seen[addr] = true
end
@@ -1135,11 +1144,15 @@
data = data[info.length, data.length]
pp.feed!(info)
addr = Expression.parse(pp).reduce
len = Expression.parse(pp).reduce
edata = EncodedData.new(data.unpack('m*').first, :virtsize => len)
- add_section(addr, edata)
+ # check for an existing section, eg from binarypath
+ existing_section = get_section_at(addr)
+ if not existing_section or existing_section[0].data.to_str != edata.data.to_str
+ add_section(addr, edata)
+ end
when 'map'
load_map data
when 'decoded'
data.each_line { |l|
begin
@@ -1739,10 +1752,10 @@
instance_eval File.read(plugin_filename)
end
# same as load_plugin, but hides the @gui attribute while loading, preventing the plugin do popup stuff
- # this is useful when you want to load a plugin from another plugin to enhance the plugin's functionnality
+ # this is useful when you want to load a plugin from another plugin to enhance the plugin's functionality
# XXX this also prevents setting up kbd_callbacks etc..
def load_plugin_nogui(plugin_filename)
oldgui = gui
@gui = nil
load_plugin(plugin_filename)