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)