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