class Object
searches an object in the attributes of another anyobj.scan_for() => “anyobj.someattr['blabla']”
Constants
- TRACE_BUF_SZ
size of the eip buffer (in dwords)
Public Class Methods
computes the difference beetween two ruby objects walks accessors, arrays and hashes
# File misc/objdiff.rb, line 10 def Object.diff(o1, o2) if o1.class == o2.class h = {} case o1 when Array, Hash if o1.kind_of? Array keys = (0...[o1.length, o2.length].max).to_a else keys = o1.keys | o2.keys end keys.each { |k| d = diff(o1[k], o2[k]) h["[#{k.inspect}]"] = d if not d.empty? } else a = ($diff_accessor_cache ||= {})[o1.class] ||= ( im = o1.class.public_instance_methods.map { |m| m.to_s }.grep(/^[a-z]/) (im & im.map { |m| m+'=' }).map { |m| m.chop }.find_all { |m| o1.instance_variable_get('@'+m) } ) if a.empty? return o1 == o2 ? h : [o1, o2] end a.each { |k| d = diff(o1.send(k), o2.send(k)) h['.' + k] = d if not d.empty? } end # simplify tree h.keys.each { |k| if h[k].kind_of? Hash and h[k].length == 1 v = h.delete k h[k + v.keys.first] = v.values.first end } h else [o1, o2] end end
Public Instance Methods
# File samples/scan_pt_gnu_stack.rb, line 18 def _printadv(a) $stderr.print a.to_s.ljust(60)[-60, 60] + "\r" end
# File samples/scan_pt_gnu_stack.rb, line 15 def _puts(a) puts a.to_s.ljust(60) end
metasm dasm plugin walks all disassembled instructions referencing an address if the address is a label, update the instruction to use the label esp. useful after a disassemble_fast, with a .map file
# File samples/dasm-plugins/imm2off.rb, line 12 def addrtolabel bp = prog_binding.invert @decoded.each_value { |di| next if not di.kind_of?(DecodedInstruction) di.each_expr { |e| next unless e.kind_of?(Expression) if l = bp[e.lexpr] add_xref(e.lexpr, Xref.new(:addr, di.address)) e.lexpr = Expression[l] end if l = bp[e.rexpr] add_xref(e.rexpr, Xref.new(:addr, di.address)) e.rexpr = (e.lexpr ? Expression[l] : l) end } } nil end
get in interactive assembler mode
# File samples/metasm-shell.rb, line 65 def asm puts "[+] Metasm assembly shell" puts "type help for usage..\n\n" Readline.completion_proc = lambda { |line| %w[help exit quit].find_all { |w| line.downcase == w[0, line.length] } } Readline.completion_append_character = ' ' while line = Readline.readline('asm> ', true) case line when /^help(\W|$)/ puts "", "Type in opcodes to see their binary form", "You can use ';' to type multi-line stuff", "e.g. 'nop nop' will display \"\\x90\\x90\"", "", "exit/quit Quit the console", "help Show this screen", "" when /^(quit|exit)(\W|$)/ break else begin data = line.gsub(';', "\n") next if data.strip.empty? e_data = data.encode puts '"' + e_data.unpack('C*').map { |c| '\\x%02x' % c }.join + '"' rescue Metasm::Exception => e puts "Error: #{e.class} #{e.message}" end end end puts end
backup the executable file
# File samples/dasm-plugins/patch_file.rb, line 11 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
# File samples/dasm-plugins/bookmark.rb, line 97 def bookmark_addrs(list, color) al = [list].flatten.uniq gui.session_append("dasm.bookmark_addrs(#{list.inspect}, #{color.inspect})") @bookmarklist |= [al.min] al.each { |a| @bookmarkcolor[a] = color } gui.gui_update end
# File samples/dasm-plugins/bookmark.rb, line 109 def bookmark_delete(list) @bookmarklist -= list list.each { |a| @bookmarkcolor.delete a } end
# File samples/dasm-plugins/bookmark.rb, line 113 def bookmark_delete_color(col) @bookmarkcolor.delete_if { |k, v| if v == col ; @bookmarklist.delete k ; true end } end
# File samples/dasm-plugins/bookmark.rb, line 104 def bookmark_delete_function(addr) return if not fa = find_function_start(addr) list = function_blocks(fa).map { |k, v| block_at(k).list.map { |di| di.address } }.flatten bookmark_delete list end
an api to bookmark a function
# File samples/dasm-plugins/bookmark.rb, line 92 def bookmark_function(addr, color) return if not fa = find_function_start(addr) list = function_blocks(fa).map { |k, v| block_at(k).list.map { |di| di.address } }.flatten bookmark_addrs list, color end
# File misc/tcp_proxy_hex.rb, line 14 def bouncepkt(clt, srv, timeout=nil) s2c = '' c2s = '' loop do break if not IO.select([clt, srv], nil, nil, timeout) while srv and s2c.length < 1024*16 and IO.select([srv], nil, nil, 0) str = (srv.read(1) rescue nil) if not str or str.empty? srv = false else s2c << str end end while clt and c2s.length < 1024*16 and IO.select([clt], nil, nil, 0) str = (clt.read(1) rescue nil) if not str or str.empty? clt = false else c2s << str end end if clt and s2c.length > 0 and IO.select(nil, [clt], nil, 0) puts Time.now.strftime('s -> c %H:%M:%S') s2c.hexdump(:fmt => ['c', 'a']) clt.write s2c s2c.replace '' end if srv and c2s.length > 0 and IO.select(nil, [srv], nil, 0) puts Time.now.strftime('c -> s %H:%M:%S') c2s.hexdump(:fmt => ['c', 'a']) srv.write c2s c2s.replace '' end break if not clt or not srv end end
# File misc/lint.rb, line 42 def compile_warn(tg) r, w = IO.pipe('binary') if !fork r.close $stderr.reopen w $stdout.reopen '/dev/null' exec 'ruby', '-v', '-c', tg exit! else w.close end r end
read a binary file, print a signature for all symbols found
# File samples/generate_libsigs.rb, line 24 def create_sig(exe) func = case exe when COFFArchive; :create_sig_arch when PE, COFF; :create_sig_coff when ELF; :create_sig_elf else raise 'unsupported file format' end send(func, exe) { |sym, edata| sig = edata.data.unpack('H*').first edata.reloc.each { |o, r| # TODO if the reloc points to a known func (eg WinMain), keep the info sz = r.length sig[2*o, 2*sz] = '.' * sz * 2 } next if sig.gsub('.', '').length < 2*$min_sigbytes puts sym sig.scan(/.{1,78}/) { |s| puts ' ' + s } } end
handle coff archives
# File samples/generate_libsigs.rb, line 47 def create_sig_arch(exe) exe.members.each { |m| next if m.name == '/' or m.name == '//' obj = m.exe rescue next create_sig(obj) } end
scan a pe/coff file
# File samples/generate_libsigs.rb, line 80 def create_sig_coff(coff) if coff.kind_of? PE # dll # dll # TODO else coff.symbols.to_a.compact.each { |sym| next if sym.type != 'FUNCTION' next if not sym.sec_nr.kind_of? Integer data = coff.sections[sym.sec_nr-1].encoded off = sym.value len = data.export.find_all { |k, o| o > off and k !~ /_uuid/ }.transpose[1].to_a.min || data.length yield sym.name, data[off, len] } end end
scan an elf file
# File samples/generate_libsigs.rb, line 56 def create_sig_elf(elf) elf.symbols.each { |sym| next if sym.type != 'FUNC' or sym.shndx == 'UNDEF' if elf.header.type == 'REL' next if not data = elf.sections[sym.shndx].encoded off = sym.value else next if not seg = elf.segments.find { |s| s.type == 'LOAD' and sym.value >= s.vaddr and sym.value < s.vaddr+s.memsz } next if not data = seg.encoded off = sym.value - seg.vaddr end len = sym.size if len == 0 len = data.export.find_all { |k, o| o > off and k !~ /_uuid/ }.transpose[1].to_a.min || data.length len -= off len = 256 if len > 256 end yield sym.name, data[off, len] } end
metasm dasm plugin: retrieve a section section, and disassemble everything it can, skipping existing code and nops usage: load the plugin, then call (ruby snipped): dasm.dasm_all_section '.text'
# File samples/dasm-plugins/dasm_all.rb, line 9 def dasm_all(addrstart, length, method=:disassemble_fast_deep) s = get_section_at(addrstart) return if not s s = s[0] boff = s.ptr off = 0 while off < length if di = di_at(addrstart + off) off += di.bin_length elsif @decoded[addrstart+off] off += 1 else s.ptr = boff+off maydi = cpu.decode_instruction(s, 0) if not maydi off += 1 elsif maydi.instruction.to_s =~ /nop|lea (.*), \[\1(?:\+0)?\]|mov (.*), \2|int 3/ off += maydi.bin_length else puts "dasm_all: found #{Expression[addrstart+off]}" if $VERBOSE send(method, addrstart+off) end end Gui.main_iter if gui and off & 15 == 0 end count = 0 off = 0 while off < length addr = addrstart+off if di = di_at(addr) if di.block_head? b = di.block if not @function[addr] and b.from_subfuncret.to_a.empty? and b.from_normal.to_a.empty? l = auto_label_at(addr, 'sub_orph') puts "dasm_all: found orphan function #{l}" @function[addrstart+off] = DecodedFunction.new @function[addrstart+off].finalized = true detect_function_thunk(addr) count += 1 end end off += di.bin_length else off += 1 end Gui.main_iter if gui and off & 15 == 0 end puts "found #{count} orphan functions" if $VERBOSE gui.gui_update if gui end
# File samples/dasm-plugins/dasm_all.rb, line 63 def dasm_all_section(name, method=:disassemble_fast_deep) section_info.each { |n, a, l, i| if name == n dasm_all(Expression[a].reduce, l, method) end } true end
metasm dasm plugin: try to demangle all labels as c++ names, add them as comment if successful
# File samples/dasm-plugins/demangle_cpp.rb, line 10 def demangle_all_cppnames cnt = 0 prog_binding.each { |name, addr| cname = name.sub(/^thunk_/, '') if dname = demangle_cppname(cname) cnt += 1 add_comment(addr, dname) each_xref(addr, :x) { |xr| if di = di_at(xr.origin) di.add_comment dname di.comment.delete "x:#{name}" end } end } cnt end
# File misc/ppc_pdf2oplist.rb, line 100 def epilog puts "\n\t@field_shift = {" puts $field_shift.sort_by { |k, v| k.to_s }.enum_slice(6).map { |slc| "\t\t" + slc.map { |k, v| "#{k.inspect} => #{v}" }.join(', ') }.join(",\n") puts "\t}" puts "\n\t@field_mask = {" puts $field_mask.sort_by { |k, v| k.to_s }.enum_slice(6).map { |slc| "\t\t" + slc.map { |k, v| "#{k.inspect} => #{v > 1000 ? '0x%X' % v : v}" }.join(', ') }.join(",\n") puts "\t}" end
parse asm to a regexp, return the list of addresses matching
# File samples/dasm-plugins/findgadget.rb, line 47 def findgadget_asm(asm) pattern_scan(findgadget_asm_to_regex(asm)) end
metasm dasm plugin scan for a given asm instruction sequence (all encodings) add the G dasm-gui shortcut, the input change ';' for line splits
# File samples/dasm-plugins/findgadget.rb, line 11 def findgadget_asm_to_regex(asm) fullre = '' asm = asm.gsub(';', "\n") sc = Shellcode.new(@cpu) sc.parse asm sc.source.each { |i| case i when Data opts_edata = i.encode(@cpu.endianness) when Instruction opts_edata = @cpu.encode_instruction(sc, i) else raise "cant scan for #{i}" end opts_edata = [opts_edata] if opts_edata.kind_of?(EncodedData) opts_re = opts_edata.map { |ed| # Regexp.escape ed.data, with relocs replaced with '.' re = '' off = 0 ed.reloc.sort.each { |o, rel| re << Regexp.escape(ed.data[off...o]) re << ('.' * rel.length) off = o + rel.length } re << Regexp.escape(ed.data[off..-1]) } fullre << '(' << opts_re.join('|') << ')' } Regexp.new(fullre, Regexp::MULTILINE, 'n') end
# File samples/dasm-plugins/findgadget.rb, line 51 def findgadget_prompt gui.inputbox("source for the gadget - separate with ;") { |asm| lst = findgadget_asm(asm) list = [['address', 'section']] sections = section_info list += lst.map { |addr| # [name, addr, len, misc] if s = sections.find { |s_| s_[1] <= addr and s_[1] + s_[2] > addr } s = s[0] else s = '?' end [Expression[addr], s] } gui.listwindow("gadgetscan for #{asm}", list) { |args| gui.focus_addr(args[0]) } } end
shows the preprocessor path to find a specific line usage: ruby chdr-find.rb 'regex pattern' list of files.h
# File misc/cheader-findpppath.rb, line 11 def gets l = $ungets $ungets = nil l || super() end
# File samples/dasm-plugins/export_graph_svg.rb, line 13 def graph_to_svg gw = gui.curview.dup class << gw attr_accessor :svgbuf, :svgcol def draw_color(col) col = @default_color_association.fetch(col, col) col = BasicColor.fetch(col, col) @svgcol = "##{col}" end def draw_line(x1, y1, x2, y2) bb(x1, y1, x2, y2) svgbuf << %Q{<line x1="#{x1}" y1="#{y1}" x2="#{x2}" y2="#{y2}" stroke="#{@svgcol}" />\n} end def draw_rectangle(x, y, w, h) bb(x, y, x+w, y+h) svgbuf << %Q{<rect x="#{x}" y="#{y}" width="#{w}" height="#{h}" fill="#{@svgcol}" />\n} end def draw_string(x, y, str) bb(x, y, x+str.length*@font_width, y+@font_height) stre = str.gsub('<', '<').gsub('>', '>') svgbuf << %Q{<text x="#{(0...str.length).map { |i| x+i*@font_width }.join(',')}" y="#{y+@font_height*0.7}" stroke="#{@svgcol}">#{stre}</text>\n} end def draw_rectangle_color(c, *a) draw_color(c) draw_rectangle(*a) end def draw_line_color(c, *a) draw_color(c) draw_line(*a) end def draw_string_color(c, *a) draw_color(c) draw_string(*a) end def focus?; false; end def view_x; @svgvx ||= @curcontext.boundingbox[0]-20; end def view_y; @svgvy ||= @curcontext.boundingbox[1]-20; end def width; @svgvw ||= (@curcontext ? (@curcontext.boundingbox[2]-@curcontext.boundingbox[0])*@zoom+20 : 800); end def height; @svgvh ||= (@curcontext ? (@curcontext.boundingbox[3]-@curcontext.boundingbox[1])*@zoom+20 : 600); end def svgcuraddr; @curcontext ? @curcontext.root_addrs.first : current_address; end # drawing bounding box (for the background rectangle) attr_accessor :bbx, :bby, :bbxm, :bbym def bb(x1, y1, x2, y2) @bbx = [x1, x2, @bbx].compact.min @bbxm = [x1, x2, @bbxm].compact.max @bby = [y1, y2, @bby].compact.min @bbym = [y1, y2, @bbym].compact.max end end ret = gw.svgbuf = '' gw.paint ret[0, 0] = <<EOS <?xml version="1.0" standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"> <svg xmlns="http://www.w3.org/2000/svg" font-family="courier, monospace"> <desc>Graph of #{get_label_at(gw.svgcuraddr) || Expression[gw.svgcuraddr]}</desc> <rect x="#{gw.bbx-10}" y="#{gw.bby-10}" width="#{gw.bbxm-gw.bbx+20}" height="#{gw.bbym-gw.bby+20}" fill="#{gw.draw_color(:background)}" />" EOS ret << %Q{</svg>} end
# File samples/dbg-plugins/heapscan.rb, line 166 def gui_show_array(addr) head = resolve(addr) e = @heap.xrchunksto[head].to_a.find { |ee| @heap.arrays[ee] and @heap.arrays[ee][head] } return if not e lst = @heap.arrays[e][head] if not st = @heap.chunk_struct[head] st = Metasm::C::Struct.new st.name = "array_#{'%x' % head}" st.members = [] (@heap.chunks[head] / 4).times { |i| n = "u#{i}" v = @memory[head+4*i, 4].unpack('L').first if @heap.chunks[v] t = Metasm::C::Pointer.new(Metasm::C::BaseType.new(:void)) else t = Metasm::C::BaseType.new(:int) end st.members << Metasm::C::Variable.new(n, t) } @heap.cp.toplevel.struct[st.name] ||= st end @heap.chunk_struct[head] = st $ghw.addr_struct = { head => @heap.cp.decode_c_struct(st.name, @memory, head) } if not st = lst.map { |l| @heap.chunk_struct[l] }.compact.first e = lst.first st = Metasm::C::Struct.new st.name = "elem_#{'%x' % head}" st.members = [] (@heap.chunks[e] / 4).times { |i| n = "u#{i}" v = @memory[e+4*i, 4].unpack('L').first if @heap.chunks[v] t = Metasm::C::Pointer.new(Metasm::C::BaseType.new(:void)) else t = Metasm::C::BaseType.new(:int) end st.members << Metasm::C::Variable.new(n, t) } @heap.cp.toplevel.struct[st.name] ||= st end lst.each { |l| @heap.chunk_struct[l] = st } lst.each { |aa| $ghw.addr_struct[aa] = @heap.cp.decode_c_struct(st.name, @memory, aa) } gui.parent_widget.mem.focus_addr(head, :graphheap) end
# File samples/dbg-plugins/heapscan.rb, line 135 def gui_show_list(addr) a = resolve(addr) #@heap.cp.parse("struct ptr { void *ptr; };") if not @heap.cp.toplevel.struct['ptr'] h = @heap.linkedlists[a] off = h.keys.first lst = h[off] if not st = lst.map { |l| @heap.chunk_struct[l] }.compact.first st = Metasm::C::Struct.new st.name = "list_#{'%x' % lst.first}" st.members = [] (@heap.chunks[lst.first] / 4).times { |i| n = "u#{i}" t = Metasm::C::BaseType.new(:int) if i == off/4 n = "next" t = Metasm::C::Pointer.new(st) end st.members << Metasm::C::Variable.new(n, t) } @heap.cp.toplevel.struct[st.name] = st end lst.each { |l| @heap.chunk_struct[l] = st } $ghw.addr_struct = {} lst.each { |aa| $ghw.addr_struct[aa] = @heap.cp.decode_c_struct(st.name, @memory, aa) } gui.parent_widget.mem.focus_addr(lst.first, :graphheap) end
# File samples/shellcode-dynlink.rb, line 104 def hash_name(sym) hash = 0 sym.each_byte { |char| hash = (((hash >> 0xd) | (hash << (32-0xd))) + char) & 0xffff_ffff } hash end
# File samples/dbg-plugins/heapscan.rb, line 49 def heap; @heap ; end
# File samples/dbg-plugins/heapscan.rb, line 50 def heap=(h) ; @heap = h ; end
# File samples/dbg-plugins/heapscan.rb, line 129 def heapscan_graph heapscan_time @heap.dump_graph heapscan_time 'graph.gv' end
# File samples/dbg-plugins/heapscan.rb, line 117 def heapscan_kernels heapscan_time @heap.find_kernels heapscan_time "#{@heap.kernels.length} kernels" end
# File samples/dbg-plugins/heapscan.rb, line 123 def heapscan_roots heapscan_time @heap.find_roots heapscan_time "#{@heap.roots.length} roots" end
# File samples/dbg-plugins/heapscan.rb, line 52 def heapscan_scan(xr=true) heaps = [] mmaps = [] libc = nil pr = os_process pr.mappings.each { |a, l, p, f| case f.to_s when /heap/ heaps << [a, l] when /libc[^a-zA-Z]/ libc ||= a if p == 'r-xp' when '' mmaps << [a, l] end } heapscan_time '' @disassembler.parse_c '' if pr and OS.current.name =~ /winos/i if OS.current.version[0] == 5 @heap = WindowsHeap.new(self) @heap.cp = @disassembler.c_parser @heap.cp.parse_file File.join($heapscan_dir, 'winheap.h') unless @heap.cp.toplevel.struct['_HEAP'] else @heap = Windows7Heap.new(self) @heap.cp = @disassembler.c_parser @heap.cp.parse_file File.join($heapscan_dir, 'winheap7.h') unless @heap.cp.toplevel.struct['_HEAP'] end @heap.heaps = heaps else @heap = LinuxHeap.new(self) @heap.cp = @disassembler.c_parser @heap.mmaps = mmaps @heap.scan_libc(libc) heapscan_time "libc!main_arena #{'%x' % @heap.main_arena_ptr}" end hsz = 0 (heaps + mmaps).each { |a, l| hsz += l @heap.range.update a => l } log "#{hsz/1024/1024}M heap" @heap.scan_chunks heapscan_time "#{@heap.chunks.length} chunks" return if not xr @heap.scan_chunks_xr heapscan_time "#{@heap.xrchunksto.length} src, #{@heap.xrchunksfrom.length} dst" end
# File samples/dbg-plugins/heapscan.rb, line 105 def heapscan_structs heapscan_time @heap.bucketize heapscan_time "#{@heap.buckets.length} buckets" @heap.find_arrays heapscan_time "#{@heap.allarrays.length} arrays (#{@heap.allarrays.flatten.length} elems)" @heap.find_linkedlists heapscan_time "#{@heap.alllists.length} lists (#{@heap.alllists.flatten.length} elems)" end
# File samples/dbg-plugins/heapscan.rb, line 41 def heapscan_time(s='') @heapscan_time ||= nil t = Time.now log s + ' %.2fs' % (t-@heapscan_time) if @heapscan_time and s != '' @heapscan_time = t Gui.main_iter if gui end
find immediate exprs in the instruction at addr, yield them
# File samples/dasm-plugins/c_constants.rb, line 11 def imm_to_const(addr) return if not di = di_at(addr) # TODO enter into memrefs ? di.instruction.args.grep(Expression).each { |a| i = a.reduce next if not i.kind_of? Integer next if not cstbase = yield(i) if c = imm_to_const_decompose(i, cstbase) di.add_comment c end } end
find the bitwise decomposition of imm into constants whose name include cstbase
# File samples/dasm-plugins/c_constants.rb, line 25 def imm_to_const_decompose(imm, cstbase) cstbase = /#{cstbase}/i if not cstbase.kind_of? Regexp dict = {} c_parser.lexer.definition.keys.grep(cstbase).each { |cst| if i = c_parser.macro_numeric(cst) dict[cst] = i end } c_parser.toplevel.symbol.each { |k, v| dict[k] = v if v.kind_of? Integer and k =~ cstbase } dict.delete_if { |k, v| imm & v != v } if cst = dict.index(imm) cst else # a => 1, b => 2, c => 4, all => 7: discard abc, keep 'all' dict.delete_if { |k, v| dict.find { |kk, vv| vv > v and vv & v == v } } dict.keys.join(' | ') if not dict.empty? end end
# File samples/shellcode-dynlink.rb, line 112 def lib_name(sym) raise "unknown libname for #{sym}" if not lib = Metasm::WindowsExports::EXPORT[sym] n = lib.downcase[0, 4].unpack('C*') n[0] + (n[1]<<8) + (n[2] << 16) + (n[3] << 24) end
this is a ruby code cleaner tool it passes its argument to ruby -v -c, which displays warnings (eg unused variable) it shows the incriminated line along the warning, to help identify false positives probably linux-only, and need ruby-1.9.1 or newer
# File misc/lint.rb, line 13 def lint(tg) if File.symlink?(tg) # nothing elsif File.directory?(tg) Dir.entries(tg).each { |ent| next if ent == '.' or ent == '..' ent = File.join(tg, ent) lint(ent) if File.directory?(ent) or ent =~ /\.rb$/ } else lint_file(tg) end end
# File misc/lint.rb, line 27 def lint_file(tg) flines = nil compile_warn(tg).each_line { |line| file, lineno, warn = line.split(/\s*:\s*/, 3) if file == tg if not flines puts "#{tg}:" flines = File.readlines(file) #File.open(file, 'rb') { |fd| fd.readlines } end puts " l.#{lineno}: #{warn.strip}: #{flines[lineno.to_i-1].strip.inspect}" end } puts if flines end
# File samples/r0trace.rb, line 245 def loadmod(mod=$drv) sh = DynLdr.openscmanagera(0, 0, DynLdr::SC_MANAGER_ALL_ACCESS) raise "cannot openscm" if (sh == 0) rh = DynLdr.createservicea(sh, mod, mod, DynLdr::SERVICE_ALL_ACCESS, DynLdr::SERVICE_KERNEL_DRIVER, DynLdr::SERVICE_DEMAND_START, DynLdr::SERVICE_ERROR_NORMAL, File.expand_path(mod), 0, 0, 0, 0, 0) if (DynLdr.startservicea(rh, 0, 0) == 0) raise "cannot start service" end DynLdr.CloseServiceHandle(rh) DynLdr.CloseServiceHandle(sh) end
A script to help finding performance bottlenecks:
$ ruby-prof myscript.rb
=> String#+ gets called 50k times and takes 30s
$ LOGCALLER='String#+' ruby -r bottleneck myscript.rb
=> String#+ called 40k times from: stuff.rb:42 in Myclass#uglymethod from stuff.rb:32 in Myclass#initialize
now you know what to rewrite
# File misc/bottleneck.rb, line 20 def log_caller(cls, meth, singleton=false, histlen=nil) histlen ||= ENV.fetch('LOGCALLER_MAXHIST', 16).to_i dec_meth = 'm_' + meth.to_s.gsub(/[^\w]/) { |c| c.unpack('H*')[0] } malias = dec_meth + '_log_caller' mcntr = '$' + dec_meth + '_counter' eval <<EOS #{cls.kind_of?(Class) ? 'class' : 'module'} #{cls} #{'class << self' if singleton} alias #{malias} #{meth} def #{meth}(*a, &b) #{mcntr}[caller[0, #{histlen}]] += 1 #{malias}(*a, &b) end #{'end' if singleton} end #{mcntr} = Hash.new(0) at_exit { total = #{mcntr}.inject(0) { |a, (k, v)| a+v } puts "\#{total} callers of #{cls} #{meth}:" #{mcntr}.sort_by { |k, v| -v }[0, 4].each { |k, v| puts " \#{'%.2f%%' % (100.0*v/total)} - \#{v} times from", k, '' } } EOS end
handle instruction aliases NOT WORKING should be implemented in the parser/displayer instead of opcode list manual work needed for eg conditionnal jumps
# File misc/ppc_pdf2oplist.rb, line 85 def make_alias(newop, newargs, oldop, oldargs) raise "unknown alias #{newop} => #{oldop}" if not op = $opcodes.reverse.find { |op_| op_[0] == oldop } op2 = op.dup op2[0] = newop oldargs.each_with_index { |oa, i| # XXX bcctr 4, 6 -> bcctr 4, 6, 0 => not the work if oa =~ /^[0-9]+$/ or oa =~ /^0x[0-9a-f]+$/i fld = op[2][i] op2[1] |= Integer(oa) << $field_shift[fld] end } puts "#\talias #{newop} #{newargs.join(', ')} -> #{oldop} #{oldargs.join(', ')}".downcase end
# File misc/ppc_pdf2oplist.rb, line 14 def make_instr(bins, bits, text) # calc bitfields length from their offset last = 32 bitlen = [] bits.reverse_each { |bit| bitlen.unshift last-bit last = bit } # the opcode binary value (w/o fields) bin = 0 fields = [] # parse the data bins.zip(bits, bitlen).each { |val, off, len| off = 32-(off+len) msk = (1 << len) - 1 case val when '/', '//', '///' # reserved field, value unspecified when /^\d+$/; bin |= val.to_i << off # constant field when /^[A-Za-z]+$/ fld = val.downcase.to_sym fld = "#{fld}_".to_sym while $field_mask[fld] and ($field_mask[fld] != msk or $field_shift[fld] != off) fields << fld $field_mask[fld] ||= msk $field_shift[fld] ||= off end } text.each { |txt| # fnabs FRT,FRB (Rc=0) curbin = bin curfields = fields.dup txt.sub!(' Rc=1)', ' (Rc=1)') if txt.include? 'fdiv.' # typo: fdiv. has no '(' if txt =~ /(.*\S)\s*\((\w+=.*)\)/ txt = $1 $2.split.each { |e| raise e if e !~ /(\w+)=(\d+)/ name, val = $1.downcase, $2.to_i raise "bad bit #{name} in #{txt}" if not fld = curfields.find { |fld_| fld_.to_s.delete('_') == name } curfields.delete fld curbin |= val << $field_shift[fld] } end opname, args = txt.split(/\s+/, 2) args = args.to_s.downcase.split(/\s*,\s*/).map { |arg| fld = curfields.find { |fld_| fld_.to_s.delete('_') == arg } ; curfields.delete fld ; fld } if args.include? nil and curfields.length == 2 and (curfields - [:ra, :d]).empty? args[args.index(nil)] = :ra_i16 curfields.clear elsif args.include? nil and curfields.length == 2 and (curfields - [:ra, :ds]).empty? args[args.index(nil)] = :ra_i16s curfields.clear elsif args.include? nil and curfields.length == 2 and (curfields - [:ra, :dq]).empty? args[args.index(nil)] = :ra_i16q curfields.clear elsif args.include? nil and curfields.length == 1 args[args.index(nil)] = curfields.shift end raise "bad args #{args.inspect} (#{curfields.inspect}) in #{txt}" if args.include? nil $opcodes << [opname, curbin, args] n = (opname.inspect << ',').ljust(10) + '0x%08X' % curbin n << ', ' if not args.empty? puts "\taddop " + n + args.map { |e| e.inspect }.join(', ') } end
# File samples/dasm-plugins/match_libsigs.rb, line 82 def match_libsigs(sigfile) ls = LibSignature.new(sigfile) count = 0 @sections.each { |b, s| count += ls.match(s.data) { |off, sym| set_label_at(b+off, sym) } } count end
# File misc/cheader-findpppath.rb, line 17 def parse(root=false) want = false ret = [] while l = gets case l = l.strip when /^#if/ ret << l r = parse(true) if r.empty? ret.pop else want = true rr = r.pop ret.concat r.map { |l_| (l_[0,3] == '#el' ? ' ' : ' ') << l_ } ret << rr end when /^#el/ if not root $ungets = l break end ret << l r = parse want = true if not r.empty? ret.concat r when /^#endif/ if not root $ungets = l break end ret << l break when /#$srch/ #, /^#include/ want = true ret << l end end want ? ret : [] end
# File misc/ppc_pdf2oplist.rb, line 114 def parse_page(lines) # all instr defining pages include this return unless lines.find { |l| l.str =~ /Special Registers Altered|Memory Barrier Instructions|Data Cache Instructions/ } # sync L/dcbt ilist = [] # line buffer extended = false # concat lines with same y lines = lines.sort_by { |l| [-l.y, l.x] } lastline = nil lines.delete_if { |l| if lastline and lastline.y == l.y and ([lastline.fontx, lastline.fonty] == [l.fontx, l.fonty] or l.str =~ /^\s*$/) lastline.str << ' ' << l.str true else lastline = l false end } lines.each { |l| # search for the bit indices list if l.fonty < 7 and l.str =~ /^0 [\d ]+ 31\s*$/ and (ilist.last.str.split.length == l.str.split.length or ilist.last.str.split.length == l.str.split.length-1) $foundop = true bitindices = l.str.split.map { |i| i.to_i } # previous line is the binary encoding encoding = ilist.pop.str.split bitindices.pop if encoding.length < bitindices.length # previous line is the instruction text format ilist.pop if ilist.last.str =~ /\[POWER2? mnemonics?: (.*)\]/ text = [] text.unshift l while l = ilist.pop and l = l.str and (l =~ /,|\)$/ or text.empty?) ilist = [] make_instr(encoding, bitindices, text) elsif l.str.include? 'Special Registers Altered' if not $foundop puts ilist.map { |l_| "(#{l_.y}) #{l_.str}" } puts lines.map { |l_| "(#{l_.y}) #{l_.str}" } if ilist.empty? raise 'nofoundop' else $foundop = false end elsif l.str =~ /Extended:\s+Equivalent to:/ extended = true elsif extended if l.str.include? ',' and l.str =~ /^(\S+)\s+(\S+)\s+(\S+)\s+(.*)/ and $opcodes.find { |op| op[0] == $3 } newop, newargs, exop, exargs = $1, $2, $3, $4 make_alias(newop, newargs.split(','), exop, exargs.split(',')) else extended = false end else ilist << l end } end
# File samples/dasm-plugins/patch_file.rb, line 48 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
create a backup and reopen the backend VirtualFile RW
# File samples/dasm-plugins/patch_file.rb, line 25 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
dumps to stdout the path to find some targets ( array of objects to match with == )
# File misc/objscan.rb, line 22 def scan_for(targets, path='', done={}) done[object_id] = self if done.empty? if t = targets.find { |t_| self == t_ } puts "found #{t} at #{path}" end scan_iter { |v, p| case v when Integer, Symbol; next end p = path+p if done[v.object_id] puts "loop #{p} -> #{done[v.object_id]}" if $VERBOSE else done[v.object_id] = p v.scan_for(targets, p, done) end } end
# File misc/objscan.rb, line 10 def scan_iter case self when ::Array length.times { |i| yield self[i], "[#{i}]" } when ::Hash each { |k, v| yield v, "[#{k.inspect}]" ; yield k, "(key)" } else instance_variables.each { |i| yield instance_variable_get(i), ".#{i[1..-1]}" } end end
metasm dasm plugin: scan the memory for a 'ret' which could indicate the beginning of the current function (x86 only)
# File samples/dasm-plugins/scanfuncstart.rb, line 9 def scanfuncstart(addr) if o = (1..16).find { |off| @decoded[addr-off].kind_of? DecodedInstruction } and @decoded[addr-o].bin_length == o addr -= o end if @decoded[addr].kind_of? DecodedInstruction fs = find_function_start(addr) return fs if fs != addr end edata = get_edata_at(addr) if o = (1..1000).find { |off| @decoded[addr-off-1] or edata.data[edata.ptr-off-1] == ?\xcc or edata.data[edata.ptr-off-1] == ?\xc3 or edata.data[edata.ptr-off-3] == ?\xc2 } o -= @decoded[addr-o-1].bin_length-1 if @decoded[addr-o-1].kind_of? DecodedInstruction addr-o end end
metasm dasm plugin: scan for xrefs to the target address, incl. relative offsets (eg near call/jmp)
# File samples/dasm-plugins/scanxrefs.rb, line 8 def scanxrefs(target) ans = [] msk = (1 << cpu.size) - 1 sections.sort.each { |s_addr, edata| raw = edata.data.to_str (0..raw.length-4).each { |off| r = raw[off, 4].unpack('V').first ans << (s_addr + off) if (r + off+4 + s_addr) & msk == target or r == target } } ans end
prints the string in 80 cols with the first column filled with
pfx
# File misc/hexdiff.rb, line 19 def show(pfx, str) loop do if str.length > 79 len = 79 - str[0...79][/\S+$/].to_s.length len = 79 if len == 0 puts pfx + str[0...len] str = str[len..-1] else puts pfx + str break end end end
# File samples/dasm-plugins/cppobj_funcall.rb, line 14 def solve_indirect_call_set_struct(ptr, struct) struct = @c_parser.toplevel.struct[struct] if struct.kind_of? String raise 'no such struct' if not struct @indirect_call_struct[ptr] = struct end
# File samples/dasm-plugins/cppobj_funcall.rb, line 20 def solve_indirect_calls @decoded.values.grep(DecodedInstruction).each { |di| next if not di.opcode.props[:saveip] # only calls fptr = get_xrefs_x(di) next if fptr.to_a.length != 1 fptr = Expression[fptr.first].reduce_rec next if not fptr.kind_of? Indirection next if not fptr.pointer.lexpr.kind_of? Symbol next if not fptr.pointer.rexpr.kind_of? Integer obj = backtrace(fptr.pointer.lexpr, di.address) obj.delete Expression::Unknown next if obj.length != 1 obj = obj.first obj = Expression[obj].reduce_rec next if not obj.kind_of? Indirection obj = obj.pointer # vtable ptr -> object ptr if not struct = @indirect_call_struct[obj] struct = yield obj if block_given? solve_indirect_call_set_struct(obj, struct || :none) end if struct.kind_of? C::Struct and fld = struct.members.find { |m| struct.offsetof(c_parser, m) == fptr.pointer.rexpr } and fld.name di.add_comment "#{struct.name || obj}->#{fld.name}" di.comment.delete 'x:unknown' end } end
metasm dasm plugin walks all disassembled instructions referencing an address if this address points a C string, show that in the instruction comments esp. useful after a disassemble_fast
# File samples/dasm-plugins/stringsxrefs.rb, line 12 def stringsxrefs(maxsz = 32) @decoded.each_value { |di| next if not di.kind_of?(DecodedInstruction) di.instruction.args.grep(Expression).each { |e| if str = decode_strz(e) and str.length >= 4 and str =~ /^[\x20-\x7e]*$/ di.add_comment str[0, maxsz].inspect add_xref(normalize(e), Xref.new(:r, di.address, 1)) end } } nil end
# File tests/graph_layout.rb, line 34 def test_all test_layout <<EOS line -> 2 -> 3 -> 4 -> 5 -> 6 -> 7; EOS test_layout <<EOS sep1 -> 1; sep2 -> 2; sep3 -> 3; sep4 -> 4; sep5 -> 5; EOS test_layout <<EOS fork -> 2 -> 3; 2 -> 4; EOS test_layout <<EOS diamond -> 2 -> 3 -> 5; 2 -> 4 -> 5; EOS test_layout <<EOS ifthen -> 2 -> 3 -> 4; 2 -> 4; EOS test_layout <<EOS ufork -> 2 -> 3; 1 -> 2; EOS test_layout <<EOS multidiamond -> 2 -> 31 -> 32 -> 34 -> 5 -> 6 -> 8; 2 -> 41 -> 42 -> 44 -> 5 -> 7 -> 8; 41 -> 43 -> 44; 31 -> 33 -> 34; EOS test_layout <<EOS dmdout -> 2 -> 3a -> 4; 2 -> 3b -> 4; 3a -> 4a; 3b -> 4b; EOS test_layout <<EOS ifthenthen -> 2 -> 8; 2 -> 3 -> 8; 2 -> 4 -> 5 -> 8; 2 -> 6 -> 7 -> 8; EOS test_layout <<EOS multipod -> 2 -> 3; 2 -> 4; 2 -> 5; 2 -> 6; 2 -> 7; 2 -> 8; EOS test_layout <<EOS mplarge -> 1 -> 2; 1 -> 3333333333333333333333333333333333; EOS test_layout <<EOS multif -> 1 1 -> a2 -> a3 a2 -> a222222222 -> a3 1 -> b2 -> b3 b2 -> b222222222 -> b3 EOS test_layout <<EOS ifx -> 1 -> 2 -> 3 -> 4 -> 5 4 -> eeeeeeeeeeee -> 5 EOS test_layout <<EOS llll -> 1 -> 22222222222222222222222222 -> e 1 -> 33333333333333333333333333 -> e 1 -> 4444444444444444444444 -> e 1 -> 5 -> e 5 -> 5t -> e 1 -> 6 -> e 6 -> 6t -> e 1 -> 7 -> e 7 -> 7t -> e EOS test_layout <<EOS dangling -> 2 -> 11 -> 12 -> 13 -> 4; 2 -> 21 -> 22 -> 23 -> 4; 2 -> 31 -> 32 -> 33 -> 4; 21 -> 2z; 31 -> 3z; EOS test_layout <<EOS dangin -> 2 -> 11 -> 12 -> 13; 2 -> 21 -> 22 -> 13; 2 -> 31 -> 32 -> 33; 22 -> 33; 21 -> z; EOS test_layout <<EOS cascadeclean -> 2 -> 3 -> 4 -> 5 -> 6 -> 62 -> 52 -> 42 -> 32 -> 22 -> e; 2 -> 21 -> 22; 3 -> 31 -> 32; 4 -> 41 -> 42; 5 -> 51 -> 52; 6 -> 61 -> 62; EOS test_layout <<EOS cascade -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> e; 2 -> 21 -> e; 3 -> 31 -> e; 4 -> 41 -> e; 5 -> 51 -> e; 6 -> 61 -> e; EOS test_layout <<EOS rstair -> 2 -> 3 -> 4 -> 5 -> 6; 2 -> 4; 2 -> 5; 2 -> 6; EOS test_layout <<EOS drstair -> 2a -> 3 -> 4 -> 5 -> 6; drstair -> 2b -> 4; 2a -> 4; 2a -> 5; 2a -> 6; 2b -> 4; 2b -> 5; 2b -> 6; EOS test_layout <<EOS mrstair -> 2a -> 3a -> 4a -> 5a -> 6a; mrstair -> 2b -> 4a; 2a -> 4a; 2a -> 5a; 2a -> 6a; 2b -> 4a; 2b -> 5a; 2b -> 6a; 2a -> 3b -> 4b -> 5b -> 6b; 2a -> 4b; 2a -> 5b; 2a -> 6b; 2b -> 3b; 2b -> 4b; 2b -> 5b; 2b -> 6b; EOS test_layout <<EOS loop -> 2 -> 3 -> 4; 3 -> 2; EOS test_layout <<EOS loopbreak -> 2 -> 3 -> e; 2 -> 4 -> 5 -> 6 -> 8 -> e; 5 -> 7 -> 4; EOS test_layout <<EOS loopbreak2 -> 2 -> 3 -> e; 2 -> 4 -> 5 -> 6 -> 8 -> e; 5 -> 7 -> 4; 7 -> 8; EOS test_layout <<EOS unbalance -> 2 -> 3 -> 4 -> 5 -> e; 2 -> 6 -> 7 -> 8 -> 9 -> 10 -> 11 -> 12 -> e; EOS test_layout <<EOS unbalance2 -> 2 -> 3 -> e; 2 -> 4 -> e; 2 -> 5 -> e; 2 -> 6 -> 7 -> 8 -> 9 -> 10 -> 11 -> 12 -> e; EOS test_layout <<EOS unbalance3 -> 2 -> 3 -> e; 2 -> 4 -> e; 2 -> 5 -> e; 2 -> 6 -> e; 8 -> 9 -> e; 2 -> 7 -> e; EOS test_layout <<EOS disjoint -> 1 -> 2 -> 3 -> 4 -> 5 -> 6; l1 -> l2; l1 -> l3; l1 -> l4; l1 -> l5; l1 -> l6; EOS test_layout <<EOS lol -> 2 -> 31 -> 41 -> 5; 2 -> 32 -> 42 -> 5; 31 -> 42; 41 -> 32; EOS test_layout <<EOS nestloop -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> e; 6 -> 4; 7 -> 3; EOS test_layout <<EOS escape -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8; 2 -> 21; 4 -> 6; EOS test_layout <<EOS loophead -> 1 -> loophead; 2 -> 3 -> 2; 3 -> 4; 1 -> 4; EOS test_layout <<EOS 1 -> e1; l00pz -> 1 -> l00pz; l2 -> 2 -> l2; 2 -> e1; 2 -> e2; l3 -> 3 -> l3; 3 -> e2; EOS test_layout <<EOS 3loop -> 1 -> 3loop; 1 -> 2 -> 3 -> 2; 0 -> 00 -> 0 -> 2; EOS test_layout <<EOS foo -> 0 -> 1 0 -> 2 -> 3 -> 4 -> 5 4 -> 6 4 -> 7 -> 5 4 -> 8 -> 6 2 -> 1 -> 7 3 -> 1 -> 8 EOS test_layout <<EOS dang -> 2 -> 3 -> 4 -> 5 -> 6 -> 4; 2 -> 9; 5 -> 9; EOS test_layout <<EOS dang2 -> 2 -> 3 -> 4 -> 5 -> 6 -> 4 2 -> 9 5 -> 9 9 -> a -> 9 EOS test_layout <<EOS onlyloop -> onlyloop EOS rescue Interrupt end
# File tests/graph_layout.rb, line 13 def test_layout(lo) $cur ||= 0 $cur += 1 if $target.to_i != 0 return if $cur != $target else return if not lo.include? $target end if $target puts $cur, lo, '' if $VERBOSE w = Gui::Window.new ww = w.widget = Gui::GraphViewWidget.new(nil, nil) ww.grab_focus Gui.idle_add { ww.load_dot(lo) ww.curcontext.auto_arrange_boxes ww.zoom_all false } Gui.main end
start tracing now, and stop only when @trace_terminate is set
# File samples/dbg-plugins/trace_func.rb, line 31 def trace @trace_subfuncs = true @trace_terminate = false id = [pc, 0, 0] trace_func_newtrace(id) trace_func_block(id) continue end
setup the initial breakpoint at func start
# File samples/dbg-plugins/trace_func.rb, line 13 def trace_func(addr, oneshot = false) @trace_terminate = false # distinguish different hits on the same function entry counter = 0 bp = bpx(addr, oneshot) { counter += 1 id = [disassembler.normalize(addr), counter, func_retaddr] trace_func_newtrace(id) trace_func_block(id) continue } if addr == pc del_bp bp if oneshot bp.action.call end end
a new block is added to a trace
# File samples/dbg-plugins/trace_func.rb, line 151 def trace_func_add_block(id, blockaddr) @trace_func_counter[id] += 1 if di = disassembler.di_at(blockaddr) di.add_comment "functrace #{@trace_func_counter[id]}" end end
we hit the beginning of a block we want to trace
# File samples/dbg-plugins/trace_func.rb, line 41 def trace_func_block(id) blockaddr = pc if b = trace_get_block(blockaddr) trace_func_add_block(id, blockaddr) if b.list.length == 1 trace_func_blockend(id, blockaddr) else bpx(b.list.last.address, true) { finished = trace_func_blockend(id, blockaddr) continue if not finished } end else # invalid opcode ? trace_func_blockend(id, blockaddr) end end
we are at the end of a traced block, find whats next
# File samples/dbg-plugins/trace_func.rb, line 60 def trace_func_blockend(id, blockaddr) if di = disassembler.di_at(pc) if end_stepout(di) and trace_func_istraceend(id, di) # trace ends there trace_func_finish(id) return true elsif di.opcode.props[:saveip] and not trace_func_entersubfunc(id, di) # call to a subfunction bpx(di.next_addr, true) { trace_func_block(id) continue } continue else singlestep { newaddr = pc trace_func_block(id) trace_func_linkdasm(di.address, newaddr) continue } end else # XXX should link in the dasm somehow singlestep { trace_func_block(id) continue } end false end
the tracer is on a subfunction call instruction, should it trace into or stepover ?
# File samples/dbg-plugins/trace_func.rb, line 182 def trace_func_entersubfunc(id, di) if trace_subfuncs @trace_func_subfunccache ||= {} if not target = @trace_func_subfunccache[di.address] # even if the target is dynamic, its module should be static if target = disassembler.get_xrefs_x(di)[0] @trace_func_subfunccache[di.address] = target = resolve(disassembler.normalize(target)) end end # check if the target subfunction is in the same module as the main # XXX should check against the list of loaded modules etc # XXX call thunk_foo -> jmp [other_module] true if target.kind_of? Integer and target & 0xffc0_0000 == id[0] & 0xffc0_0000 else false end end
the trace is finished
# File samples/dbg-plugins/trace_func.rb, line 159 def trace_func_finish(id) puts "finished tracing #{Expression[id[0]]}" end
the tracer is on a end-of-func instruction, should the trace end ?
# File samples/dbg-plugins/trace_func.rb, line 167 def trace_func_istraceend(id, di) if trace_subfuncs if @trace_terminate true elsif id[2] == 0 # trace VS func_trace elsif target = disassembler.get_xrefs_x(di)[0] # check the current return address against the one saved at trace start resolve(disassembler.normalize(target)) == id[2] end else true end end
update the blocks links in the disassembler
# File samples/dbg-plugins/trace_func.rb, line 102 def trace_func_linkdasm(from_addr, new_addr) di = disassembler.di_at(from_addr) ndi = disassembler.di_at(new_addr) return if not di # is it a subfunction return ? if end_stepout(di) and cdi = (1..8).map { |i| disassembler.di_at(new_addr - i) }.compact.find { |cdi_| cdi_.opcode.props[:saveip] and cdi_.next_addr == new_addr } cdi.block.add_to_subfuncret new_addr ndi.block.add_from_subfuncret cdi.address if ndi cdi.block.each_to_normal { |f| disassembler.function[f] ||= DecodedFunction.new if disassembler.di_at(f) } else di.block.add_to_normal new_addr ndi.block.add_from_normal from_addr if ndi end end
a new trace is about to begin
# File samples/dbg-plugins/trace_func.rb, line 129 def trace_func_newtrace(id) @trace_func_counter ||= {} @trace_func_counter[id] = 0 puts "start tracing #{Expression[id[0]]}" # setup a bg_color_callback on the disassembler if not defined? @trace_func_dasmcolor @trace_func_dasmcolor = true return if not disassembler.gui oldcb = disassembler.gui.bg_color_callback disassembler.gui.bg_color_callback = lambda { |addr| if oldcb and c = oldcb[addr] c elsif di = disassembler.di_at(addr) and di.block.list.first.comment.to_s =~ /functrace/ 'ff0' end } end end
retrieve an instructionblock, disassemble if needed
# File samples/dbg-plugins/trace_func.rb, line 93 def trace_get_block(addr) # TODO trace all blocks from addr for which we know the target, stop on call / jmp [foo] disassembler.disassemble_fast_block(addr) if di = disassembler.di_at(addr) di.block end end
# File samples/dbg-plugins/trace_func.rb, line 164 def trace_subfuncs; @trace_subfuncs ||= false end
# File samples/dbg-plugins/trace_func.rb, line 163 def trace_subfuncs=(v) @trace_subfuncs = v end
# File samples/r0trace.rb, line 256 def unloadmod(mod=$drv) sh = DynLdr.openscmanagera(0, 0, DynLdr::SC_MANAGER_ALL_ACCESS) raise "cannot openscm" if (sh == 0) rh = DynLdr.openservicea(sh, mod, DynLdr::SERVICE_ALL_ACCESS) DynLdr.controlservice(rh, DynLdr::SERVICE_CONTROL_STOP, 0.chr*4*32) DynLdr.deleteservice(rh) DynLdr.CloseServiceHandle(rh) DynLdr.CloseServiceHandle(sh) end