samples/lindebug.rb in metasm-1.0.1 vs samples/lindebug.rb in metasm-1.0.2

- old
+ new

@@ -45,18 +45,19 @@ def self.get_terminal_size s = ''.ljust(8) $stdin.ioctl(TIOCGWINSZ, s) >= 0 ? s.unpack('SS') : [80, 25] end def self.set_term_canon(bool) - tty = ''.ljust(256) - $stdin.ioctl(TCGETS, tty) + ttys = ''.ljust(256) + $stdin.ioctl(TCGETS, ttys) + tty = ttys.unpack('C*') if bool tty[12] &= ~(ECHO|CANON) else tty[12] |= ECHO|CANON end - $stdin.ioctl(TCSETS, tty) + $stdin.ioctl(TCSETS, tty.pack('C*')) end ESC_SEQ = {'A' => :up, 'B' => :down, 'C' => :right, 'D' => :left, '1~' => :home, '2~' => :inser, '3~' => :suppr, '4~' => :end, '5~' => :pgup, '6~' => :pgdown, @@ -83,53 +84,15 @@ end ESC_SEQ[seq] || seq end end -class Indirect < Metasm::ExpressionType - attr_accessor :ptr, :sz - UNPACK_STR = {1 => 'C', 2 => 'S', 4 => 'L'} - def initialize(ptr, sz) @ptr, @sz = ptr, sz end - def bind(bd) - raw = bd['tracer_memory'][@ptr.bind(bd).reduce, @sz] - Metasm::Expression[raw.unpack(UNPACK_STR[@sz]).first] - end - def externals ; @ptr.externals end -end - -class ExprParser < Metasm::Expression - def self.parse_intfloat(lex, tok) - case tok.raw - when 'byte', 'word', 'dword' - nil while ntok = lex.readtok and ntok.type == :space - nil while ntok = lex.readtok and ntok.type == :space if ntok and ntok.raw == 'ptr' - if ntok and ntok.raw == '[' - tok.value = Indirect.new(parse(lex), {'byte' => 1, 'word' => 2, 'dword' => 4}[tok.raw]) - nil while ntok = lex.readtok and ntok.type == :space - nil while ntok = lex.readtok and ntok.type == :space if ntok and ntok.raw == ']' - lex.unreadtok ntok - end - else super(lex, tok) - end - end - def self.parse_value(lex) - nil while tok = lex.readtok and tok.type == :space - lex.unreadtok tok - if tok and tok.type == :punct and tok.raw == '[' - tt = tok.dup - tt.type = :string - tt.raw = 'dword' - lex.unreadtok tt - end - super(lex) - end -end - class LinDebug - attr_accessor :win_data_height, :win_code_height, :win_prpt_height + attr_accessor :win_reg_height, :win_data_height, :win_code_height, :win_prpt_height def init_screen Ansi.set_term_canon(true) + @win_reg_height = 2 @win_data_height = 20 @win_code_height = 20 resize end @@ -137,11 +100,11 @@ Ansi.set_term_canon(false) $stdout.write Ansi.color(:normal, :reset) $stdout.flush end - def win_data_start; 2 end + def win_data_start; @win_reg_height end def win_code_start; win_data_start+win_data_height end def win_prpt_start; win_code_start+win_code_height end Color = {:changed => Ansi.color(:cyan, :bold), :border => Ansi.color(:green), :normal => Ansi.color(:white, :black, :normal), :hilight => Ansi.color(:blue, :white, :normal), @@ -162,32 +125,32 @@ end attr_accessor :dataptr, :codeptr, :rs, :promptlog, :command def initialize(rs) @rs = rs - @rs.logger = self + @rs.set_log_proc { |l| add_log l } @datafmt = 'db' - @watch = nil @prompthistlen = 20 @prompthistory = [] @promptloglen = 200 @promptlog = [] @promptbuf = '' @promptpos = 0 @log_off = 0 @console_width = 80 + @oldregs = {} @running = false @focus = :prompt @command = {} load_commands trap('WINCH') { resize } end def init_rs - @codeptr = @dataptr = @rs.regs_cache['eip'] # avoid initial faults + @codeptr = @dataptr = @rs.pc # avoid initial faults end def main_loop begin begin @@ -199,13 +162,13 @@ ensure fini_screen $stdout.print Ansi.set_cursor_pos(@console_height, 1) end rescue - $stdout.puts $!, $!.backtrace + puts $!, $!.backtrace end - $stdout.puts @promptlog.last + puts @promptlog.last end # optimize string to display to stdout # currently only skips unchanged lines # could also match end of lines (spaces), but would need to check for color codes etc @@ -216,11 +179,11 @@ # screenlines is a big text buffer with 1 line per tobeshown screen line (e.g. no Ansi cursor pos) # screenlines must be screen wide def display_screen(screenlines, cursx, cursy) @oldscreenbuf ||= [] - lines = screenlines.to_a + lines = screenlines.lines oldlines = @oldscreenbuf @oldscreenbuf = lines screenlines = lines.zip(oldlines).map { |l, ol| l == ol ? "\n" : l }.join while screenlines[-1] == ?\n @@ -245,69 +208,70 @@ def updateregs once(:updateregs, "\n\n") { _updateregs } end def _updateregs - text = '' - text << ' ' - x = 1 - %w[eax ebx ecx edx eip].each { |r| - text << Color[:changed] if @rs.regs_cache[r] != @rs.oldregs[r] - text << r << ?= - text << ('%08X' % @rs.regs_cache[r]) - text << Color[:normal] if @rs.regs_cache[r] != @rs.oldregs[r] - text << ' ' - x += r.length + 11 + pvrsz = 0 + words = @rs.register_list.map { |r| + rs = r.to_s.rjust(pvrsz) + pvrsz = rs.length + rv = @rs[r] + ["#{rs}=%0#{@rs.register_size[r]/4}X " % rv, + (@oldregs[r] != rv)] + } + @rs.flag_list.map { |fl| + fv = @rs.get_flag(fl) + [fv ? fl.to_s.upcase : fl.to_s.downcase, + (@oldregs[fl] != fv)] } - text << (' '*([@console_width-x, 0].max)) << "\n" << ' ' - x = 1 - %w[esi edi ebp esp].each { |r| - text << Color[:changed] if @rs.regs_cache[r] != @rs.oldregs[r] - text << r << ?= - text << ('%08X' % @rs.regs_cache[r]) - text << Color[:normal] if @rs.regs_cache[r] != @rs.oldregs[r] - text << ' ' - x += r.length + 11 - } - Rubstop::EFLAGS.sort.each { |off, flag| - val = @rs.regs_cache['eflags'] & (1<<off) - flag = flag.upcase if val != 0 - if val != @rs.oldregs['eflags'] & (1 << off) - text << Color[:changed] - text << flag - text << Color[:normal] - else - text << flag + + text = ' ' + linelen = 1 # line length w/o ansi colors + + owr = @win_reg_height + @win_reg_height = 1 + words.each { |w, changed| + if linelen + w.length >= @console_width - 1 + text << (' '*([@console_width-linelen, 0].max)) << "\n " + linelen = 1 + @win_reg_height += 1 end + + text << Color[:changed] if changed + text << w + text << Color[:normal] if changed text << ' ' - x += 2 + + linelen += w.length+1 } - text << (' '*([@console_width-x, 0].max)) << "\n" + resize if owr != @win_reg_height + text << (' '*([@console_width-linelen, 0].max)) << "\n" end def updatecode once(:updatecode, "...\n"*@win_code_height) { _updatecode } end def _updatecode if @codeptr addr = @codeptr - elsif @rs.oldregs['eip'] and @rs.oldregs['eip'] < @rs.regs_cache['eip'] and @rs.oldregs['eip'] + 8 >= @rs.regs_cache['eip'] - addr = @rs.oldregs['eip'] + elsif @oldregs[@rs.register_pc] and @oldregs[@rs.register_pc] < @rs.pc and @oldregs[@rs.register_pc] + 8 >= @rs.pc + addr = @oldregs[@rs.register_pc] else - addr = @rs.regs_cache['eip'] + addr = @rs.pc end @codeptr = addr - if @rs.findfilemap(addr) == '???' - base = addr & 0xffff_f000 + addrsz = @rs.register_size[@rs.register_pc] + addrfmt = "%0#{addrsz/4}X" + if not @rs.addr2module(addr) and @rs.shortname !~ /remote/ + base = addr & ((1 << addrsz) - 0x1000) @noelfsig ||= {} # cache elfmagic notfound - if not @noelfsig[base] and base < 0xc000_0000 - self.statusline = " scanning for elf header at #{'%08X' % base}" + if not @noelfsig[base] and base < ((1 << addrsz) - 0x1_0000) + self.statusline = " scanning for elf header at #{addrfmt % base}" 128.times { - @statusline = " scanning for elf header at #{'%08X' % base}" - if not @noelfsig[base] and @rs[base, 4] == Metasm::ELF::MAGIC + @statusline = " scanning for elf header at #{addrfmt % base}" + if not @noelfsig[base] and @rs[base, Metasm::ELF::MAGIC.length] == Metasm::ELF::MAGIC @rs.loadsyms(base, base.to_s(16)) break else @noelfsig[base] = true # XXX an elf may be mmaped here later.. end @@ -318,40 +282,47 @@ end end text = '' text << Color[:border] - title = @rs.findsymbol(addr) + title = @rs.addrname(addr) pre = [@console_width-100, 6].max post = @console_width - (pre + title.length + 2) text << Ansi.hline(pre) << ' ' << title << ' ' << Ansi.hline(post) << Color[:normal] << "\n" + seg = '' + seg = ('%04X' % @rs['cs']) << ':' if @rs.cpu.shortname =~ /ia32|x64/ + cnt = @win_code_height while (cnt -= 1) > 0 if @rs.symbols[addr] text << (' ' << @rs.symbols[addr] << ?:) << Ansi::ClearLineAfter << "\n" break if (cnt -= 1) <= 0 end - text << Color[:hilight] if addr == @rs.regs_cache['eip'] - text << ('%04X' % @rs.regs_cache['cs']) << ':' - text << ('%08X' % addr) - di = @rs.mnemonic_di(addr) - di = nil if di and addr < @rs.regs_cache['eip'] and addr+di.bin_length > @rs.regs_cache['eip'] + text << Color[:hilight] if addr == @rs.pc + text << seg + if @rs.shortname =~ /remote/ and @rs.realmode + text << (addrfmt % (addr - 16*@rs['cs'])) + else + text << (addrfmt % addr) + end + di = @rs.di_at(addr) + di = nil if di and addr < @rs.pc and addr+di.bin_length > @rs.pc len = (di ? di.bin_length : 1) text << ' ' - text << @rs[addr, [len, 10].min].unpack('C*').map { |c| '%02X' % c }.join.ljust(22) + text << @rs.memory[addr, [len, 10].min].to_s.unpack('C*').map { |c| '%02X' % c }.join.ljust(22) if di text << - if addr == @rs.regs_cache['eip'] - "*#{di.instruction}".ljust([@console_width-37, 0].max) + if addr == @rs.pc + "*#{di.instruction}".ljust([@console_width-(addrsz/4+seg.length+24), 0].max) else " #{di.instruction}" << Ansi::ClearLineAfter end else text << ' <unk>' << Ansi::ClearLineAfter end - text << Color[:normal] if addr == @rs.regs_cache['eip'] + text << Color[:normal] if addr == @rs.pc addr += len text << "\n" end text end @@ -359,31 +330,37 @@ def updatedata once(:updatedata, "...\n"*@win_data_height) { _updatedata } end def _updatedata - @dataptr &= 0xffff_ffff + addrsz = @rs.register_size[@rs.register_pc] + addrfmt = "%0#{addrsz/4}X" + + @dataptr &= ((1 << addrsz) - 1) addr = @dataptr text = '' text << Color[:border] - title = @rs.findsymbol(addr) + title = @rs.addrname(addr) pre = [@console_width-100, 6].max post = [@console_width - (pre + title.length + 2), 0].max text << Ansi.hline(pre) << ' ' << title << ' ' << Ansi.hline(post) << Color[:normal] << "\n" + seg = '' + seg = ('%04X' % @rs['ds']) << ':' if @rs.cpu.shortname =~ /^ia32/ + cnt = @win_data_height while (cnt -= 1) > 0 - raw = @rs[addr, 16].to_s - text << ('%04X' % @rs.regs_cache['ds']) << ':' << ('%08X' % addr) << ' ' + raw = @rs.memory[addr, 16].to_s + text << seg << (addrfmt % addr) << ' ' case @datafmt when 'db'; text << raw[0,8].unpack('C*').map { |c| '%02x ' % c }.join << ' ' << raw[8,8].to_s.unpack('C*').map { |c| '%02x ' % c }.join when 'dw'; text << raw.unpack('S*').map { |c| '%04x ' % c }.join when 'dd'; text << raw.unpack('L*').map { |c| '%08x ' % c }.join end - text << ' ' << raw.unpack('C*').map { |c| (0x20..0x7e).include?(c) ? c : ?. }.pack('C*') + text << ' ' << raw.unpack('C*').map { |c| (0x20..0x7e).include?(c) ? c : 0x2e }.pack('C*') text << Ansi::ClearLineAfter << "\n" addr += 16 end text end @@ -418,149 +395,120 @@ def resize @console_height, @console_width = Ansi.get_terminal_size @win_data_height = 1 if @win_data_height < 1 @win_code_height = 1 if @win_code_height < 1 - if @win_data_height + @win_code_height + 5 > @console_height + if @win_data_height + @win_code_height + @win_reg_height + 3 > @console_height @win_data_height = @console_height/2 - 4 @win_code_height = @console_height/2 - 4 end - @win_prpt_height = @console_height-(@win_data_height+@win_code_height+2) - 1 + @win_prpt_height = @console_height-(@win_data_height+@win_code_height+@win_reg_height) - 1 @oldscreenbuf = [] update end def log(*strs) - strs.each { |str| + strs.each { |str| raise str.inspect if not str.kind_of? ::String str = str.chomp if str.length > @console_width # word wrap str.scan(/.{0,#@console_width}/) { |str_| log str_ } return end @promptlog << str @promptlog.shift if @promptlog.length > @promptloglen - } + } end - def puts(*s) - s.each { |s_| log s_.to_s } - super(*s) if not @running - update rescue super(*s) + def add_log(l) + log l + puts l if not @running + update rescue puts l end - def mem_binding(expr) - b = @rs.regs_cache.dup - ext = expr.externals - (ext - @rs.regs_cache.keys).each { |ex| - if not s = @rs.symbols.index(ex) - near = @rs.symbols.values.grep(/#{ex}/i) - if near.length > 1 - log "#{ex.inspect} is ambiguous: #{near.inspect}" - return {} - elsif near.empty? - log "unknown value #{ex.inspect}" - return {} - else - log "using #{near.first.inspect} for #{ex.inspect}" - s = @rs.symbols.index(near.first) - end - end - b[ex] = s - } - b['tracer_memory'] = @rs - b - end - def exec_prompt @log_off = 0 log ':'+@promptbuf return if @promptbuf == '' - lex = Metasm::Preprocessor.new.feed @promptbuf + str = @promptbuf @prompthistory << @promptbuf @prompthistory.shift if @prompthistory.length > @prompthistlen @promptbuf = '' @promptpos = @promptbuf.length - argint = lambda { - begin - raise if not e = ExprParser.parse(lex) - rescue - log 'syntax error' - return - end - e = e.bind(mem_binding(e)).reduce - if e.kind_of? Integer; e - else log "could not resolve #{e.inspect}" ; nil - end - } - cmd = lex.readtok - cmd = cmd.raw if cmd - nil while ntok = lex.readtok and ntok.type == :space - lex.unreadtok ntok + cmd, str = str.split(/\s+/, 2) if @command.has_key? cmd - @command[cmd].call(lex, argint) + @command[cmd].call(str.to_s) else if cmd and (poss = @command.keys.find_all { |c| c[0, cmd.length] == cmd }).length == 1 - @command[poss.first].call(lex, argint) + @command[poss.first].call(str.to_s) else log 'unknown command' end end end + def preupdate + @rs.register_list.each { |r| @oldregs[r] = @rs[r] } + @rs.flag_list.each { |fl| @oldregs[fl] = @rs.get_flag(fl) } + end + def updatecodeptr - @codeptr ||= @rs.regs_cache['eip'] - if @codeptr > @rs.regs_cache['eip'] or @codeptr < @rs.regs_cache['eip'] - 6*@win_code_height - @codeptr = @rs.regs_cache['eip'] - elsif @codeptr != @rs.regs_cache['eip'] + @codeptr ||= @rs.pc + if @codeptr > @rs.pc or @codeptr < @rs.pc - 6*@win_code_height + @codeptr = @rs.pc + elsif @codeptr != @rs.pc addr = @codeptr addrs = [] - while addr < @rs.regs_cache['eip'] + while addr < @rs.pc addrs << addr - o = ((di = @rs.mnemonic_di(addr)) ? di.bin_length : 0) + o = ((di = @rs.di_at(addr)) ? di.bin_length : 0) addr += ((o == 0) ? 1 : o) end if addrs.length > @win_code_height-4 @codeptr = addrs[-(@win_code_height-4)] end end updatedataptr end def updatedataptr - @dataptr = @watch.bind(mem_binding(@watch)).reduce if @watch end def singlestep self.statusline = ' target singlestepping...' - @rs.singlestep + preupdate + @rs.singlestep_wait updatecodeptr @statusline = nil end def stepover self.statusline = ' target running...' - @rs.stepover + preupdate + @rs.stepover_wait updatecodeptr @statusline = nil end def cont(*a) self.statusline = ' target running...' - @rs.cont(*a) + preupdate + @rs.continue_wait(*a) updatecodeptr @statusline = nil end def stepout self.statusline = ' target running...' - @rs.stepout + preupdate + @rs.stepout_wait updatecodeptr @statusline = nil end def syscall self.statusline = ' target running to next syscall...' - @rs.syscall + preupdate + @rs.syscall_wait updatecodeptr @statusline = nil end def main_loop_inner @@ -576,21 +524,18 @@ break end end break if handle_keypress(Ansi.getkey) end - @rs.checkbp end def handle_keypress(k) case k - when 4; log 'exiting'; return true # eof - when ?\e; focus = :prompt + when ?\4; log 'exiting'; return true # eof + when ?\e; @focus = :prompt when :f5; cont - when :f6 - syscall - log @rs.syscallnr.index(@rs.regs_cache['orig_eax']) || @rs.regs_cache['orig_eax'].to_s + when :f6; syscall when :f10; stepover when :f11; singlestep when :f12; stepout when :up case @focus @@ -605,13 +550,13 @@ @promptbuf = @prompthistory[-@histptr].dup @promptpos = @promptbuf.length when :data @dataptr -= 16 when :code - @codeptr ||= @rs.regs_cache['eip'] + @codeptr ||= @rs.pc @codeptr -= (1..10).find { |off| - di = @rs.mnemonic_di(@codeptr-off) + di = @rs.di_at(@codeptr-off) di.bin_length == off if di } || 10 end when :down case @focus @@ -626,40 +571,40 @@ @promptbuf = @prompthistory[-@histptr].dup @promptpos = @promptbuf.length when :data @dataptr += 16 when :code - @codeptr ||= @rs.regs_cache['eip'] - di = @rs.mnemonic_di(@codeptr) + @codeptr ||= @rs.pc + di = @rs.di_at(@codeptr) @codeptr += (di ? (di.bin_length || 1) : 1) end when :left; @promptpos -= 1 if @promptpos > 0 when :right; @promptpos += 1 if @promptpos < @promptbuf.length when :home; @promptpos = 0 when :end; @promptpos = @promptbuf.length - when :backspace, 0x7f; @promptbuf[@promptpos-=1, 1] = '' if @promptpos > 0 + when :backspace, ?\x7f; @promptbuf[@promptpos-=1, 1] = '' if @promptpos > 0 when :suppr; @promptbuf[@promptpos, 1] = '' if @promptpos < @promptbuf.length when :pgup case @focus when :prompt; @log_off += @win_prpt_height-3 when :data; @dataptr -= 16*(@win_data_height-1) when :code - @codeptr ||= @rs.regs_cache['eip'] + @codeptr ||= @rs.pc (@win_code_height-1).times { @codeptr -= (1..10).find { |off| - di = @rs.mnemonic_di(@codeptr-off) + di = @rs.di_at(@codeptr-off) di.bin_length == off if di } || 10 } end when :pgdown case @focus when :prompt; @log_off -= @win_prpt_height-3 when :data; @dataptr += 16*(@win_data_height-1) when :code - @codeptr ||= @rs.regs_cache['eip'] - (@win_code_height-1).times { @codeptr += ((o = @rs.mnemonic_di(@codeptr)) ? [o.bin_length, 1].max : 1) } + @codeptr ||= @rs.pc + (@win_code_height-1).times { @codeptr += ((o = @rs.di_at(@codeptr)) ? [o.bin_length, 1].max : 1) } end when ?\t if not @promptbuf[0, @promptpos].include? ' ' poss = @command.keys.find_all { |c| c[0, @promptpos] == @promptbuf[0, @promptpos] } if poss.length > 1 @@ -674,251 +619,100 @@ begin exec_prompt rescue Exception log "error: #$!", *$!.backtrace end - when 0x20..0x7e + when ?\ ..?~ @promptbuf[@promptpos, 0] = k.chr @promptpos += 1 else log "unknown key pressed #{k.inspect}" end nil end def load_commands - ntok = nil - @command['kill'] = lambda { |lex, int| + @command['kill'] = lambda { |str| @rs.kill @running = false log 'killed' } - @command['quit'] = @command['detach'] = @command['exit'] = lambda { |lex, int| + @command['quit'] = @command['detach'] = @command['exit'] = lambda { |str| @rs.detach @running = false } - @command['closeui'] = lambda { |lex, int| - @rs.logger = nil + @command['closeui'] = lambda { |str| @running = false } - @command['bpx'] = lambda { |lex, int| - addr = int[] - @rs.bpx addr + @command['bpx'] = lambda { |str| + @rs.bpx @rs.resolve(str) } - @command['bphw'] = lambda { |lex, int| - type = lex.readtok.raw - addr = int[] - @rs.set_hwbp type, addr + @command['bphw'] = @command['hwbp'] = lambda { |str| + type, str = str.split(/\s+/, 2) + @rs.hwbp @rs.resolve(str.to_s), type } - @command['bl'] = lambda { |lex, int| - log "bpx at #{@rs.findsymbol(@rs.wantbp)}" if @rs.wantbp.kind_of? ::Integer - @rs.breakpoints.sort.each { |addr, oct| - log "bpx at #{@rs.findsymbol(addr)}" - } - (0..3).each { |dr| - if @rs.regs_cache['dr7'] & (1 << (2*dr)) != 0 - log "bphw #{{0=>'x', 1=>'w', 2=>'?', 3=>'r'}[(@rs.regs_cache['dr7'] >> (16+4*dr)) & 3]} at #{@rs.findsymbol(@rs.regs_cache["dr#{dr}"])}" - end - } - } - @command['bc'] = lambda { |lex, int| - @rs.clearbreaks - } - @command['bt'] = lambda { |lex, int| @rs.backtrace { |t| puts t } } - @command['d'] = lambda { |lex, int| @dataptr = int[] || return } - @command['db'] = lambda { |lex, int| @datafmt = 'db' ; @dataptr = int[] || return } - @command['dw'] = lambda { |lex, int| @datafmt = 'dw' ; @dataptr = int[] || return } - @command['dd'] = lambda { |lex, int| @datafmt = 'dd' ; @dataptr = int[] || return } - @command['r'] = lambda { |lex, int| - r = lex.readtok.raw - nil while ntok = lex.readtok and ntok.type == :space + @command['bt'] = lambda { |str| @rs.stacktrace { |a,t| add_log "#{'%x' % a} #{t}" } } + @command['d'] = lambda { |str| @dataptr = @rs.resolve(str) if str.length > 0 } + @command['db'] = lambda { |str| @datafmt = 'db' ; @dataptr = @rs.resolve(str) if str.length > 0 } + @command['dw'] = lambda { |str| @datafmt = 'dw' ; @dataptr = @rs.resolve(str) if str.length > 0 } + @command['dd'] = lambda { |str| @datafmt = 'dd' ; @dataptr = @rs.resolve(str) if str.length > 0 } + @command['r'] = lambda { |str| + r, str = str.split(/\s+/, 2) if r == 'fl' - flag = ntok.raw - if i = Rubstop::EFLAGS.index(flag) - @rs.eflags ^= 1 << i - @rs.readregs - else - log "bad flag #{flag}" - end - elsif not @rs.regs_cache[r] + @rs.toggle_flag(str.to_sym) + elsif not @rs[r] log "bad reg #{r}" - elsif ntok - lex.unreadtok ntok - newval = int[] - if newval and newval.kind_of? ::Integer - @rs.send r+'=', newval - @rs.readregs - end + elsif str and str.length > 0 + @rs[r] = @rs.resolve(str) else - log "#{r} = #{@rs.regs_cache[r]}" + log "#{r} = #{@rs[r]}" end } - @command['run'] = @command['cont'] = lambda { |lex, int| - if tok = lex.readtok - lex.unreadtok tok - cont int[] - else cont - end + @command['g'] = lambda { |str| + @rs.go @rs.resolve(str) } - @command['syscall'] = lambda { |lex, int| syscall } - @command['singlestep'] = lambda { |lex, int| singlestep } - @command['stepover'] = lambda { |lex, int| stepover } - @command['stepout'] = lambda { |lex, int| stepout } - @command['g'] = lambda { |lex, int| - target = int[] - @rs.singlestep if @rs.regs_cache['eip'] == target - @rs.bpx target, true - cont - } - @command['u'] = lambda { |lex, int| @codeptr = int[] || break } - @command['has_pax'] = lambda { |lex, int| - if tok = lex.readtok - lex.unreadtok tok - if (int[] == 0) - @rs.set_pax false - else - @rs.set_pax true - end - else @rs.set_pax !@rs.has_pax - end - log "has_pax now #{@rs.has_pax}" - } - @command['loadsyms'] = lambda { |lex, int| - mapfile = '' - mapfile << ntok.raw while ntok = lex.readtok - if mapfile != '' - @rs.loadmap mapfile - else - @rs.loadallsyms - end - } - @command['scansyms'] = lambda { |lex, int| @rs.scansyms } - @command['sym'] = lambda { |lex, int| - sym = '' - sym << ntok.raw while ntok = lex.readtok - s = [] - @rs.symbols.each { |k, v| - s << k if v =~ /#{sym}/ - } - if s.empty? - log "unknown symbol #{sym}" - else - s.sort.each { |s_| log "#{'%08x' % s_} #{@rs.symbols_len[s_].to_s.ljust 6} #{@rs.findsymbol(s_)}" } - end - } - @command['delsym'] = lambda { |lex, int| - addr = int[] - log "deleted #{@rs.symbols.delete addr}" - @rs.symbols_len.delete addr - } - @command['addsym'] = lambda { |lex, int| - name = lex.readtok.raw - addr = int[] - if t = lex.readtok - lex.unreadtok t - @rs.symbols_len[addr] = int[] - else - @rs.symbols_len[addr] = 1 - end - @rs.symbols[addr] = name - } - @command['help'] = lambda { |lex, int| - log 'commands: (addr/values are things like dword ptr [ebp+(4*byte [eax])] ), type <tab> to see all commands' - log ' bpx <addr>' - log ' bphw [r|w|x] <addr>: debug register breakpoint' - log ' bl: list breakpoints' - log ' bc: clear breakpoints' - log ' cont [<signr>]: continue the target sending a signal' - log ' d/db/dw/dd [<addr>]: change data type/address' - log ' g <addr>: set a bp at <addr> and run' - log ' has_pax [0|1]: set has_pax flag' - log ' loadsyms: load symbol information from mapped files (from /proc and disk)' - log ' ma <addr> <ascii>: write memory' - log ' mx <addr> <hex>: write memory' - log ' maps: list maps' - log ' r <reg> [<value>]: show/change register' - log ' r fl <flag>: toggle eflags bit' - log ' scansyms: scan memory for ELF headers' - log ' sym <symbol regex>: show symbol information' - log ' addsym <name> <addr> [<size>]' - log ' delsym <addr>' - log ' u <addr>: disassemble addr' - log ' reload: reload lindebug source' - log ' ruby <ruby code>: instance_evals ruby code in current instance' - log ' closeui: detach from the underlying RubStop' - log 'keys:' - log ' F5: continue' - log ' F6: syscall' - log ' F10: step over' - log ' F11: single step' - log ' F12: step out (til next ret)' - log ' pgup/pgdown: move command history' - } - @command['reload'] = lambda { |lex, int| load $0 ; load_commands } - @command['ruby'] = lambda { |lex, int| - str = '' - str << ntok.raw while ntok = lex.readtok - instance_eval str - } - @command['maps'] = lambda { |lex, int| - @rs.filemap.sort_by { |f, (b, e)| b }.each { |f, (b, e)| - log "#{f.ljust 20} #{'%08x' % b} - #{'%08x' % e}" - } - } - @command['ma'] = lambda { |lex, int| - addr = int[] - str = '' - str << ntok.raw while ntok = lex.readtok - @rs[addr, str.length] = str - } - @command['mx'] = lambda { |lex, int| - addr = int[] - data = [lex.readtok.raw].pack('H*') - @rs[addr, data.length] = data - } - @command['resize'] = lambda { |lex, int| resize } - @command['watch'] = lambda { |lex, int| @watch = ExprParser.parse(lex) ; updatedataptr } - @command['wd'] = lambda { |lex, int| + @command['u'] = lambda { |str| @codeptr = @rs.resolve(str) } + @command['ruby'] = lambda { |str| instance_eval str } + @command['wd'] = lambda { |str| @focus = :data - if tok = lex.readtok - lex.unreadtok tok - @win_data_height = int[] || return + if str.length > 0 + @win_data_height = @rs.resolve(str) resize end } - @command['wc'] = lambda { |lex, int| + @command['wc'] = lambda { |str| @focus = :code - if tok = lex.readtok - lex.unreadtok tok - @win_code_height = int[] || return + if str.length > 0 + @win_code_height = @rs.resolve(str) resize end } - @command['wp'] = lambda { |lex, int| @focus = :prompt } - @command['?'] = lambda { |lex, int| - val = int[] + @command['wp'] = lambda { |str| @focus = :prompt } + @command['?'] = lambda { |str| + val = @rs.resolve(str) log "#{val} 0x#{val.to_s(16)} #{[val].pack('L').inspect}" } - @command['.'] = lambda { |lex, int| @codeptr = nil } + @command['syscall'] = lambda { |str| + @rs.syscall_wait(str) + } end end if $0 == __FILE__ require 'optparse' - filemap = nil + opts = { :sc_cpu => 'Ia32' } OptionParser.new { |opt| - opt.on('-m map', '--map filemap') { |f| filemap = f } + opt.on('-m map', '--map filemap') { |f| opts[:filemap] = f } + opt.on('--cpu cpu') { |c| opts[:sc_cpu] = c } }.parse!(ARGV) - if not defined? Rubstop - if ARGV.first =~ /:/ - stub = 'gdbclient' - else - stub = 'rubstop' - end - require File.join(File.dirname(__FILE__), stub) + case ARGV.first + when /^(tcp:|udp:)?..+:/, /^ser:/ + opts[:sc_cpu] = eval(opts[:sc_cpu]) if opts[:sc_cpu] =~ /[.(\s:]/ + opts[:sc_cpu] = opts[:sc_cpu].new if opts[:sc_cpu].kind_of?(::Class) + rs = Metasm::GdbRemoteDebugger.new(ARGV.first, opts[:sc_cpu]) + else + rs = Metasm::LinDebugger.new(ARGV.join(' ')) end - - rs = Rubstop.new(ARGV.join(' ')) - rs.loadmap(filemap) if filemap + rs.load_map(opts[:filemap]) if opts[:filemap] LinDebug.new(rs).main_loop end