class Metasm::Debugger

this class implements a high-level debugging API (abstract superclass)

Attributes

breakpoint[RW]

per-process data

breakpoint_cause[RW]

per-thread data

breakpoint_memory[RW]

per-process data

breakpoint_thread[RW]

per-thread data

callback_bpm[RW]

global debugger callbacks, called whenever such event occurs

callback_bpx[RW]

global debugger callbacks, called whenever such event occurs

callback_endprocess[RW]

global debugger callbacks, called whenever such event occurs

callback_endthread[RW]

global debugger callbacks, called whenever such event occurs

callback_exception[RW]

global debugger callbacks, called whenever such event occurs

callback_hwbp[RW]

global debugger callbacks, called whenever such event occurs

callback_loadlibrary[RW]

global debugger callbacks, called whenever such event occurs

callback_newprocess[RW]

global debugger callbacks, called whenever such event occurs

callback_newthread[RW]

global debugger callbacks, called whenever such event occurs

callback_singlestep[RW]

global debugger callbacks, called whenever such event occurs

cpu[RW]

per-process data

disassembler[RW]

per-process data

gui[RW]

link to the user-interface object if available

ignore_endthread[RW]

global switches, specify wether to break on exception/thread event

can be a Proc that is evaluated (arg = info parameter of the evt_func)

#trace_children is a bool to tell if we should debug subprocesses spawned

by the target
ignore_newthread[RW]

global switches, specify wether to break on exception/thread event

can be a Proc that is evaluated (arg = info parameter of the evt_func)

#trace_children is a bool to tell if we should debug subprocesses spawned

by the target
info[RW]

per-thread data

memory[RW]

per-process data

modulemap[RW]

per-process data

pass_all_exceptions[RW]

global switches, specify wether to break on exception/thread event

can be a Proc that is evaluated (arg = info parameter of the evt_func)

#trace_children is a bool to tell if we should debug subprocesses spawned

by the target
pid[R]
pid_stuff[RW]

which/where per-process/thread stuff is stored

pid_stuff_list[RW]

which/where per-process/thread stuff is stored

run_args[RW]

per-thread data

run_method[RW]

per-thread data

singlestep_cb[RW]

per-thread data

state[RW]

per-thread data

symbols[RW]

per-process data

symbols_len[RW]

per-process data

tid[R]
tid_stuff[RW]

which/where per-process/thread stuff is stored

tid_stuff_list[RW]

which/where per-process/thread stuff is stored

trace_children[RW]

global switches, specify wether to break on exception/thread event

can be a Proc that is evaluated (arg = info parameter of the evt_func)

#trace_children is a bool to tell if we should debug subprocesses spawned

by the target

Public Class Methods

new() click to toggle source

initializes the disassembler internal data - subclasses should call super()

# File metasm/debug.rb, line 145
def initialize
        @pid_stuff = {}
        @tid_stuff = {}
        @log_proc = nil
        @state = :dead
        @info = ''
        # stuff saved when we switch pids
        @pid_stuff_list = [:memory, :cpu, :disassembler, :symbols, :symbols_len,
                :modulemap, :breakpoint, :breakpoint_memory, :tid, :tid_stuff,
                :dead_process]
        @tid_stuff_list = [:state, :info, :breakpoint_thread, :singlestep_cb,
                :run_method, :run_args, :breakpoint_cause, :dead_thread]
        @callback_loadlibrary = lambda { |h| loadsyms(h[:address]) ; continue }
        @callback_newprocess = lambda { |h| log "process #{@pid} attached" }
        @callback_endprocess = lambda { |h| log "process #{@pid} died" }
        initialize_newpid
        initialize_newtid
end

Public Instance Methods

[](arg0, arg1=nil) click to toggle source

accepts a range or begin/end address to read memory, or a register name

# File metasm/debug.rb, line 1289
def [](arg0, arg1=nil)
        if arg1
                arg0 = resolve_expr(arg0) if not arg0.kind_of? ::Integer
                arg1 = resolve_expr(arg1) if not arg1.kind_of? ::Integer
                @memory[arg0, arg1].to_str
        elsif arg0.kind_of? ::Range
                arg0.begin = resolve_expr(arg0.begin) if not arg0.begin.kind_of? ::Integer   # cannot happen, invalid ruby Range
                arg0.end = resolve_expr(arg0.end) if not arg0.end.kind_of? ::Integer
                @memory[arg0].to_str
        else
                get_reg_value(arg0)
        end
end
[]=(arg0, arg1, val=nil) click to toggle source

accepts a range or begin/end address to write memory, or a register name

# File metasm/debug.rb, line 1304
def []=(arg0, arg1, val=nil)
        arg1, val = val, arg1 if not val
        if arg1
                arg0 = resolve_expr(arg0) if not arg0.kind_of? ::Integer
                arg1 = resolve_expr(arg1) if not arg1.kind_of? ::Integer
                @memory[arg0, arg1] = val
        elsif arg0.kind_of? ::Range
                arg0.begin = resolve_expr(arg0.begin) if not arg0.begin.kind_of? ::Integer   # cannot happen, invalid ruby Range
                arg0.end = resolve_expr(arg0.end) if not arg0.end.kind_of? ::Integer
                @memory[arg0] = val
        else
                set_reg_value(arg0, val)
        end
end
add_bp(addr, info={}) click to toggle source

create a thread/process breakpoint addr can be a numeric address, an Expression that is resolved, or

a String that is parsed+resolved

info's keys are set to the breakpoint standard keys are :type, :oneshot, :condition, :action returns the Breakpoint object

# File metasm/debug.rb, line 360
def add_bp(addr, info={})
        info[:pid] ||= @pid
        # dont define :tid for bpx, otherwise on del_bp we may switch_context to this thread that may not be stopped -> cant ptrace_write
        info[:tid] ||= @tid if info[:pid] == @pid and info[:type] == :hwbp

        b = Breakpoint.new
        info.each { |k, v|
                b.send("#{k}=", v)
        }

        switch_context(b) {
                addr = resolve_expr(addr) if not addr.kind_of? ::Integer
                b.address = addr

                b.hash_owner ||= case b.type
                        when :bpm;  @breakpoint_memory
                        when :hwbp; @breakpoint_thread
                        when :bpx;  @breakpoint
                        end
                # XXX bpm may hash_share with an :active, but be larger and still need enable()
                b.add

                enable_bp(b) if not info[:state]
        }

        b
end
addr2module(addr) click to toggle source

returns the name of the module containing addr or nil

# File metasm/debug.rb, line 1101
def addr2module(addr)
        @modulemap.keys.find { |k| @modulemap[k][0] <= addr and @modulemap[k][1] > addr }
end
addrname(addr) click to toggle source

returns a string describing addr in term of symbol (eg 'libc.so.6!printf+2f')

# File metasm/debug.rb, line 1106
def addrname(addr)
        (addr2module(addr) || '???') + '!' +
        if s = @symbols[addr] ? addr : @symbols_len.keys.find { |s_| s_ < addr and s_ + @symbols_len[s_] > addr }
                @symbols[s] + (addr == s ? '' : ('+%x' % (addr-s)))
        else '%08x' % addr
        end
end
addrname!(addr) click to toggle source

same as addrname, but scan preceding addresses if no symbol matches

# File metasm/debug.rb, line 1115
def addrname!(addr)
        (addr2module(addr) || '???') + '!' +
        if s = @symbols[addr] ? addr :
                        @symbols_len.keys.find { |s_| s_ < addr and s_ + @symbols_len[s_] > addr } ||
                        @symbols.keys.sort.find_all { |s_| s_ < addr and s_ + 0x10000 > addr }.max
                @symbols[s] + (addr == s ? '' : ('+%x' % (addr-s)))
        else '%08x' % addr
        end
end
all_breakpoints(addr=nil) click to toggle source

return all breakpoints set on a specific address (or all bp)

# File metasm/debug.rb, line 580
def all_breakpoints(addr=nil)
        ret = []
        if addr
                if b = @breakpoint[addr]
                        ret |= b.hash_shared
                end
        else
                @breakpoint.each_value { |bb| ret |= bb.hash_shared }
        end

        @breakpoint_thread.each_value { |bb|
                next if addr and bb.address != addr
                ret |= bb.hash_shared
        }

        @breakpoint_memory.each_value { |bb|
                next if addr and (bb.address+bb.internal[:len] <= addr or bb.address > addr)
                ret |= bb.hash_shared
        }

        ret
end
bpm(addr, mtype=:r, mlen=4096, oneshot=false, cond=nil, &action) click to toggle source

sets a memory breakpoint mtype is :r :w :rw or :x mlen is the size of the memory zone to cover

# File metasm/debug.rb, line 542
def bpm(addr, mtype=:r, mlen=4096, oneshot=false, cond=nil, &action)
        h = { :type => :bpm }
        addr = resolve_expr(addr) if not addr.kind_of? ::Integer
        h[:hash_key] = addr & -4096   # XXX actually referenced at addr, addr+4096, ... addr+len
        h[:internal] = { :type => mtype, :len => mlen }
        h[:oneshot] = true if oneshot
        h[:condition] = cond if cond
        h[:action] = action if action
        add_bp(addr, h)
end
bpx(addr, oneshot=false, cond=nil, &action) click to toggle source

sets a breakpoint on execution

# File metasm/debug.rb, line 514
def bpx(addr, oneshot=false, cond=nil, &action)
        h = { :type => :bpx }
        h[:oneshot] = true if oneshot
        h[:condition] = cond if cond
        h[:action] = action if action
        add_bp(addr, h)
end
check_bpm_range(b, info) click to toggle source

returns true if the fault described in info is valid to trigger b

# File metasm/debug.rb, line 726
def check_bpm_range(b, info)
        return if b.address+b.internal[:len] <= info[:fault_addr]
        return if b.address >= info[:fault_addr] + info[:fault_len]
        case b.internal[:type]
        when :r; info[:fault_access] == :r    # or info[:fault_access] == :x
        when :w; info[:fault_access] == :w
        when :x; info[:fault_access] == :x    # XXX non-NX cpu => check pc is in bpm range ?
        when :rw; true
        end
end
check_breakpoint_cause() click to toggle source

checks if @breakpoint_cause is valid, or was obsoleted by the user changing pc

# File metasm/debug.rb, line 896
def check_breakpoint_cause
        if bp = @breakpoint_cause and
                        (bp.type == :bpx or (bp.type == :hwbp and bp.internal[:type] == :x)) and
                        pc != bp.address
                bp = @breakpoint_cause = nil
        end
        bp
end
check_pid(pid) click to toggle source

check if pid is valid

# File metasm/debug.rb, line 1403
def check_pid(pid)
        list_processes.find { |p| p.pid == pid }
end
check_pre_run(run_m, *run_a) click to toggle source

to be called right before resuming execution of the target run_m is the method that should be called if the execution is stopped due to a side-effect of the debugger (bpx with wrong condition etc) returns nil if the execution should be avoided (just deleted the dead thread/process)

# File metasm/debug.rb, line 614
def check_pre_run(run_m, *run_a)
        if @dead_process
                del_pid
                return
        elsif @dead_thread
                del_tid
                return
        elsif @state == :running
                return
        end
        @cpu.dbg_check_pre_run(self) if @cpu.respond_to?(:dbg_check_pre_run)
        @breakpoint_cause = nil
        @run_method = run_m
        @run_args = run_a
        @info = nil
        true
end
check_target() click to toggle source

checks if the running target has stopped (nonblocking) returns false if no debug event happened

# File metasm/debug.rb, line 907
def check_target
        do_check_target
end
check_tid(tid) click to toggle source

check if tid is valid for the current process

# File metasm/debug.rb, line 1417
def check_tid(tid)
        list_threads.include?(tid)
end
continue() click to toggle source

resume execution of the target bypasses a software breakpoint on pc if needed thread breakpoints must be manually disabled before calling continue

# File metasm/debug.rb, line 919
def continue
        if b = check_breakpoint_cause and b.hash_shared.find { |bb| bb.state == :active }
                singlestep_bp(b) {
                        next if not check_pre_run(:continue)
                        do_continue
                }
        else
                return if not check_pre_run(:continue)
                do_continue
        end
end
Also aliased as: run
continue_wait() click to toggle source

continue ; #wait_target

# File metasm/debug.rb, line 933
def continue_wait
        continue
        wait_target
end
dasm() click to toggle source
# File metasm/debug.rb, line 164
def dasm; disassembler; end
dasm_invalidate() click to toggle source

invalidates the EncodedData backend for the dasm sections

# File metasm/debug.rb, line 575
def dasm_invalidate
        disassembler.sections.each_value { |s| s.data.invalidate if s.data.respond_to?(:invalidate) } if disassembler
end
del_all_breakpoints() click to toggle source

delete all breakpoints for the current process and all its threads

# File metasm/debug.rb, line 427
def del_all_breakpoints
        each_tid { del_all_breakpoints_thread }
        @breakpoint.values.map { |b| b.hash_shared }.flatten.uniq.each { |b| del_bp(b) }
        @breakpoint_memory.values.uniq.map { |b| b.hash_shared }.flatten.uniq.each { |b| del_bp(b) }
end
del_all_breakpoints_thread() click to toggle source

delete all breakpoints defined in the current thread

# File metasm/debug.rb, line 422
def del_all_breakpoints_thread
        @breakpoint_thread.values.map { |b| b.hash_shared }.flatten.uniq.each { |b| del_bp(b) }
end
del_bp(b) click to toggle source

remove a breakpoint

# File metasm/debug.rb, line 389
def del_bp(b)
        disable_bp(b)
        b.del
end
del_pid() click to toggle source

delete references to the current pid switch to another pid, set @state = :dead if none available

# File metasm/debug.rb, line 271
def del_pid
        @pid_stuff.delete @pid
        if @pid = @pid_stuff.keys.first
                swapin_pid
        else
                @state = :dead
                @info = ''
                @tid = nil
        end
end
del_tid() click to toggle source

delete references to the current thread

# File metasm/debug.rb, line 283
def del_tid
        @tid_stuff.delete @tid
        if @tid = @tid_stuff.keys.first
                swapin_tid
        else
                del_tid_notid
        end
end
del_tid_notid() click to toggle source

wipe the whole process when no TID is left XXX we may have a pending evt_newthread…

# File metasm/debug.rb, line 294
def del_tid_notid
        del_pid
end
di_at(addr) click to toggle source

decode the Instruction at the address, use the @disassembler cache if available

# File metasm/debug.rb, line 1014
def di_at(addr)
        @disassembler.di_at(addr) || @disassembler.disassemble_instruction(addr)
end
disable_bp(b, newstate = :inactive) click to toggle source

deactivate an active breakpoint

# File metasm/debug.rb, line 411
def disable_bp(b, newstate = :inactive)
        return if b.state != :active
        b.state = newstate
        return if b.hash_shared.find { |bb| bb.state == :active }
        switch_context(b) {
                do_disable_bp(b)
        }
end
do_disable_bp(b) click to toggle source

calls do_disable_bpm for bpms, or @cpu.dbg_disable_bp

# File metasm/debug.rb, line 441
def do_disable_bp(b)
        if b.type == :bpm; do_disable_bpm(b)
        else @cpu.dbg_disable_bp(self, b)
        end
end
do_enable_bp(b) click to toggle source

calls do_enable_bpm for bpms, or @cpu.dbg_enable_bp

# File metasm/debug.rb, line 434
def do_enable_bp(b)
        if b.type == :bpm; do_enable_bpm(b)
        else @cpu.dbg_enable_bp(self, b)
        end
end
each_pid(&b) click to toggle source

iterate over all pids, yield in the context of this pid

# File metasm/debug.rb, line 326
def each_pid(&b)
        # ensure @pid is last, so that we finish in the current context
        lst = @pid_stuff.keys - [@pid]
        lst << @pid
        return lst if not b
        lst.each { |p|
                set_pid p
                b.call
        }
end
each_pid_tid(&b) click to toggle source

iterate over all tids of all pids, yield in their context

# File metasm/debug.rb, line 349
def each_pid_tid(&b)
        each_pid { each_tid { b.call } }
end
each_tid(&b) click to toggle source

iterate over all tids of the current process, yield in its context

# File metasm/debug.rb, line 338
def each_tid(&b)
        lst = @tid_stuff.keys - [@tid]
        lst << @tid
        return lst if not b
        lst.each { |t|
                set_tid t rescue next
                b.call
        }
end
emulinstr_resv(e) click to toggle source
# File metasm/debug.rb, line 499
def emulinstr_resv(e)
        r = e
        flags = Expression[r].externals.uniq.find_all { |f| f.to_s =~ /flags?_(.+)/ }
        if flags.first
                bd = {}
                flags.each { |f|
                        f.to_s =~ /flags?_(.+)/
                        bd[f] = get_flag_value($1.downcase.to_sym)
                }
                r = r.bind(bd)
        end
        resolve(r)
end
enable_bp(b) click to toggle source

activate an inactive breakpoint

# File metasm/debug.rb, line 395
def enable_bp(b)
        return if b.state == :active
        if not b.hash_shared.find { |bb| bb.state == :active }
                switch_context(b) {
                        if not b.internal
                                init_bpx(b) if b.type == :bpx
                                b.internal ||= {}
                                b.hash_shared.each { |bb| bb.internal ||= b.internal }
                        end
                        do_enable_bp(b)
                }
        end
        b.state = :active
end
end_stepout(di = di_at(pc)) click to toggle source

checks if an instruction should stop the stepout() (eg it is a return instruction)

# File metasm/debug.rb, line 983
def end_stepout(di = di_at(pc))
        di and @cpu.dbg_end_stepout(self, di.address, di)
end
evt_bpm(b) click to toggle source

called when the target stops due to a memory exception caused by a memory bp called by #evt_exception

# File metasm/debug.rb, line 711
def evt_bpm(b)
        @state = :stopped
        @info = 'bpm'

        callback_bpm[b] if callback_bpm

        post_evt_bp(b)
end
evt_bpx(b=nil) click to toggle source

called when the target stops due to a soft breakpoint exception

# File metasm/debug.rb, line 657
def evt_bpx(b=nil)
        b ||= find_bp_bpx
        # TODO handle race:
        # bpx foo ; thread hits foo ; we bc foo ; os notify us of bp hit but we already cleared everything related to 'bpx foo' -> unhandled bp exception
        return evt_exception(:type => 'breakpoint') if not b

        @state = :stopped
        @info = 'breakpoint'
        @cpu.dbg_evt_bpx(self, b) if @cpu.respond_to?(:dbg_evt_bpx)

        callback_bpx[b] if callback_bpx

        post_evt_bp(b)
end
evt_endprocess(info={}) click to toggle source
# File metasm/debug.rb, line 838
def evt_endprocess(info={})
        @state = :stopped
        @info = 'end process'
        @dead_process = true

        callback_endprocess[info] if callback_endprocess
end
evt_endthread(info={}) click to toggle source
# File metasm/debug.rb, line 816
def evt_endthread(info={})
        @state = :stopped
        @info = 'end thread'
        # mark the thread as to be deleted on next check_pre_run
        @dead_thread = true

        callback_endthread[info] if callback_endthread

        ign = ignore_endthread
        ign = ign[info] if ign.kind_of? Proc
        if ign
                continue
        end
end
evt_exception(info={}) click to toggle source

called whenever the target stops due to an exception type may be:

  • 'access violation', :fault_addr, :fault_len, :fault_access (:r/:w/:x)

anything else for other exceptions (access violation is special to handle bpm) …

# File metasm/debug.rb, line 783
def evt_exception(info={})
        if info[:type] == 'access violation' and b = find_bp_bpm(info)
                info[:fault_len] ||= 1
                b.internal.update info
                return evt_bpm(b)
        end

        @state = :stopped
        @info = "exception #{info[:type]}"

        callback_exception[info] if callback_exception

        pass = pass_all_exceptions
        pass = pass[info] if pass.kind_of? Proc
        if pass
                pass_current_exception
                resume_badbreak
        end
end
evt_hwbp(b=nil) click to toggle source

called when the target stops due to a hwbp exception

# File metasm/debug.rb, line 679
def evt_hwbp(b=nil)
        b ||= find_bp_hwbp
        return evt_exception(:type => 'hwbp') if not b

        @state = :stopped
        @info = 'hwbp'
        @cpu.dbg_evt_hwbp(self, b) if @cpu.respond_to?(:dbg_evt_hwbp)

        callback_hwbp[b] if callback_hwbp

        post_evt_bp(b)
end
evt_hwbp_singlestep() click to toggle source

called for archs where the same interrupt is generated for hwbp and singlestep checks if a hwbp matches, then call #evt_hwbp, else call #evt_singlestep (which will forward to #evt_exception if singlestep does not match either)

# File metasm/debug.rb, line 701
def evt_hwbp_singlestep
        if b = find_bp_hwbp
                evt_hwbp(b)
        else
                evt_singlestep
        end
end
evt_loadlibrary(info={}) click to toggle source
# File metasm/debug.rb, line 846
def evt_loadlibrary(info={})
        @state = :stopped
        @info = 'loadlibrary'

        callback_loadlibrary[info] if callback_loadlibrary
end
evt_newprocess(info={}) click to toggle source
# File metasm/debug.rb, line 831
def evt_newprocess(info={})
        @state = :stopped
        @info = 'new process'

        callback_newprocess[info] if callback_newprocess
end
evt_newthread(info={}) click to toggle source
# File metasm/debug.rb, line 803
def evt_newthread(info={})
        @state = :stopped
        @info = 'new thread'

        callback_newthread[info] if callback_newthread

        ign = ignore_newthread
        ign = ign[info] if ign.kind_of? Proc
        if ign
                continue
        end
end
evt_singlestep(b=nil) click to toggle source

called when the target stops due to a singlestep exception

# File metasm/debug.rb, line 634
def evt_singlestep(b=nil)
        b ||= find_singlestep
        return evt_exception(:type => 'singlestep') if not b

        @state = :stopped
        @info = 'singlestep'
        @cpu.dbg_evt_singlestep(self) if @cpu.respond_to?(:dbg_evt_singlestep)

        callback_singlestep[] if callback_singlestep

        if cb = @singlestep_cb
                @singlestep_cb = nil
                cb.call      # call last, as the cb may change singlestep_cb/state/etc
        end
end
find_bp_bpm(info) click to toggle source

return a bpm whose page coverage includes the fault described in info

# File metasm/debug.rb, line 721
def find_bp_bpm(info)
        @breakpoint_memory[info[:fault_addr] & -0x1000]
end
find_bp_bpx() click to toggle source

return the breakpoint that is responsible for the #evt_bpx

# File metasm/debug.rb, line 673
def find_bp_bpx
        return @cpu.dbg_find_bpx(self) if @cpu.respond_to?(:dbg_find_bpx)
        @breakpoint[pc]
end
find_bp_hwbp() click to toggle source

return the breakpoint that is responsible for the #evt_hwbp

# File metasm/debug.rb, line 693
def find_bp_hwbp
        return @cpu.dbg_find_hwbp(self) if @cpu.respond_to?(:dbg_find_hwbp)
        @breakpoint_thread.find { |b| b.address == pc }
end
find_breakpoint(addr=nil, &b) click to toggle source

return on of the breakpoints at address addr

# File metasm/debug.rb, line 604
def find_breakpoint(addr=nil, &b)
        return @breakpoint[addr] if @breakpoint[addr] and (not b or b.call(@breakpoint[addr]))
        all_breakpoints(addr).find { |bp| b.call bp }
end
find_singlestep() click to toggle source

returns true if the singlestep is due to us

# File metasm/debug.rb, line 651
def find_singlestep
        return @cpu.dbg_find_singlestep(self) if @cpu.respond_to?(:dbg_find_singlestep)
        @run_method == :singlestep
end
flag_list() click to toggle source

list of flags available in the flag register

# File metasm/debug.rb, line 1044
def flag_list
        @cpu.dbg_flag_list
end
func_arg(nr) click to toggle source

retrieve an argument (call at a function entrypoint)

# File metasm/debug.rb, line 1334
def func_arg(nr)
        @cpu.dbg_func_arg(self, nr)
end
func_arg_set(nr, val) click to toggle source
# File metasm/debug.rb, line 1337
def func_arg_set(nr, val)
        @cpu.dbg_func_arg_set(self, nr, val)
end
func_retaddr() click to toggle source

retrieve a function return address (call at func entry/exit)

# File metasm/debug.rb, line 1353
def func_retaddr
        @cpu.dbg_func_retaddr(self)
end
func_retaddr=(addr) click to toggle source
# File metasm/debug.rb, line 1359
def func_retaddr=(addr)
        @cpu.dbg_func_retaddr_set(self, addr)
end
func_retaddr_set(addr) click to toggle source
# File metasm/debug.rb, line 1356
def func_retaddr_set(addr)
        @cpu.dbg_func_retaddr_set(self, addr)
end
func_retval() click to toggle source

retrieve a function returned value (call at func exitpoint)

# File metasm/debug.rb, line 1342
def func_retval
        @cpu.dbg_func_retval(self)
end
func_retval=(val) click to toggle source
# File metasm/debug.rb, line 1348
def func_retval=(val)
        @cpu.dbg_func_retval_set(self, val)
end
func_retval_set(val) click to toggle source
# File metasm/debug.rb, line 1345
def func_retval_set(val)
        @cpu.dbg_func_retval_set(self, val)
end
get_flag(f) click to toggle source

retrieve the value of a flag (true/false)

# File metasm/debug.rb, line 1076
def get_flag(f)
        get_flag_value(f) != 0
end
get_flag_value(f) click to toggle source

retrieve the value of a flag (0/1)

# File metasm/debug.rb, line 1071
def get_flag_value(f)
        @cpu.dbg_get_flag(self, f)
end
go(target, cond=nil) click to toggle source

set a singleshot breakpoint, run the process, and wait

# File metasm/debug.rb, line 1003
def go(target, cond=nil)
        bpx(target, true, cond)
        continue_wait
end
has_emul_instr(bp) click to toggle source

checks if bp has an emul_instr do the lazy initialization if needed

# File metasm/debug.rb, line 469
def has_emul_instr(bp)
        if bp.emul_instr.kind_of?(DecodedInstruction)
                if di = bp.emul_instr and fdbd = @disassembler.get_fwdemu_binding(di, register_pc) and
                                fdbd.all? { |k, v| (k.kind_of?(Symbol) or k.kind_of?(Indirection)) and
                                        k != :incomplete_binding and v != Expression::Unknown }
                        # setup a lambda that will mimic, using the debugger primitives, the actual execution of the instruction
                        bp.emul_instr = lambda {
                                fdbd.map { |k, v|
                                        k = Indirection[emulinstr_resv(k.pointer), k.len] if k.kind_of?(Indirection)
                                        [k, emulinstr_resv(v)]
                                }.each { |k, v|
                                        if k.to_s =~ /flags?_(.+)/
                                                f = $1.downcase.to_sym
                                                set_flag_value(f, v)
                                        elsif k.kind_of?(Symbol)
                                                set_reg_value(k, v)
                                        elsif k.kind_of?(Indirection)
                                                memory_write_int(k.pointer, v, k.len)
                                        end
                                }
                        }
                        bp.hash_shared.each { |bb| bb.emul_instr = bp.emul_instr }
                else
                        bp.hash_shared.each { |bb| bb.emul_instr = nil }
                end
        end

        bp.emul_instr
end
hwbp(addr, mtype=:x, mlen=1, oneshot=false, cond=nil, &action) click to toggle source

sets a hardware breakpoint mtype in :r :w :x mlen is the size of the memory zone to cover mlen may be constrained by the architecture

# File metasm/debug.rb, line 526
def hwbp(addr, mtype=:x, mlen=1, oneshot=false, cond=nil, &action)
        h = { :type => :hwbp }
        h[:hash_owner] = @breakpoint_thread
        addr = resolve_expr(addr) if not addr.kind_of? ::Integer
        mtype = mtype.to_sym
        h[:hash_key] = [addr, mtype, mlen]
        h[:internal] = { :type => mtype, :len => mlen }
        h[:oneshot] = true if oneshot
        h[:condition] = cond if cond
        h[:action] = action if action
        add_bp(addr, h)
end
init_bpx(b) click to toggle source

called in the context of the target when a bpx is to be initialized may (lazily) initialize b.emul_instr for virtual singlestep

# File metasm/debug.rb, line 449
def init_bpx(b)
        # dont bother setting stuff up if it is never to be used
        return if b.oneshot and not b.condition

        # lazy setup of b.emul_instr: delay building emulating lambda to if/when actually needed
        # we still need to disassemble now and update @disassembler, before we patch the memory for the bpx
        di = init_bpx_disassemble(b.address)
        b.hash_shared.each { |bb| bb.emul_instr = di }
end
init_bpx_disassemble(addr) click to toggle source

retrieve the di at a given address, disassemble if needed TODO make it so this doesn't interfere with other 'real' disassembler later commands, eg disassemble() or disassemble_fast_deep() (right now, when they see the block already present they stop all processing)

# File metasm/debug.rb, line 462
def init_bpx_disassemble(addr)
        @disassembler.disassemble_fast_block(addr)
        @disassembler.di_at(addr)
end
initialize_disassembler() click to toggle source

initialize the disassembler from @cpu/@memory

# File metasm/debug.rb, line 221
def initialize_disassembler
        return if not @memory or not @cpu
        @disassembler = Shellcode.decode(@memory, @cpu).disassembler
        gui.swapin_pid if gui.respond_to?(:swapin_pid)
end
initialize_newpid() click to toggle source

creates stuff related to a new process being debugged includes disassembler, modulemap, symbols, breakpoints subclasses should check that @pid maps to a real process and raise() otherwise to be called with @pid/@tid set, calls initialize_memory+initialize_cpu

# File metasm/debug.rb, line 194
def initialize_newpid
        return if not pid
        @pid_stuff_list.each { |s| instance_variable_set("@#{s}", nil) }

        @symbols = {}
        @symbols_len = {}
        @modulemap = {}
        @breakpoint = {}
        @breakpoint_memory = {}
        @tid_stuff = {}
        initialize_cpu
        initialize_memory
        initialize_disassembler
end
initialize_newtid() click to toggle source

subclasses should check that @tid maps to a real thread and raise() otherwise

# File metasm/debug.rb, line 210
def initialize_newtid
        return if not tid
        @tid_stuff_list.each { |s| instance_variable_set("@#{s}", nil) }

        @state = :stopped
        @info = 'new'
        @breakpoint_thread = {}
        gui.swapin_tid if @disassembler and gui.respond_to?(:swapin_tid)
end
invalidate() click to toggle source

marks the current cache of memory/regs invalid

# File metasm/debug.rb, line 570
def invalidate
        @memory.invalidate if @memory
end
ip()
Alias for: pc
ip=(v)
Alias for: pc=
list_debug_pids() click to toggle source

list debugged pids

# File metasm/debug.rb, line 1392
def list_debug_pids
        @pid_stuff.keys | [@pid].compact
end
list_debug_tids() click to toggle source

list debugged tids

# File metasm/debug.rb, line 1408
def list_debug_tids
        @tid_stuff.keys | [@tid].compact
end
Also aliased as: list_threads
list_processes() click to toggle source

return a list of OS::Process listing all alive processes (incl not debugged) default version only includes current debugged pids

# File metasm/debug.rb, line 1398
def list_processes
        list_debug_pids.map { |p| OS::Process.new(p) }
end
list_threads()

list of thread ids existing in the current process (incl not debugged) default version only lists debugged tids

Alias for: list_debug_tids
load_map(str, off=0) click to toggle source

see Metasm::Disassembler#load_map

# File metasm/debug.rb, line 1194
def load_map(str, off=0)
        str = File.read(str) if File.exist?(str)
        sks = @disassembler.sections.keys.sort
        str.each_line { |l|
                case l.strip
                when /^([0-9A-F]+)\s+(\w+)\s+(\w+)/    # kernel.map style
                        a = $1.to_i(16) + off
                        n = $3
                when /^([0-9A-F]+):([0-9A-F]+)\s+([a-z_]\w+)/  # IDA style
                        # see Disassembler for comments
                        a = sks[$1.to_i(16)] + $2.to_i(16) + off
                        n = $3
                else next
                end
                @disassembler.set_label_at(a, n, false)
                @symbols[a] = n
        }

end
load_plugin(plugin_filename) click to toggle source
# File metasm/debug.rb, line 1363
def load_plugin(plugin_filename)
        if not File.exist?(plugin_filename) and defined? Metasmdir
                # try autocomplete
                pf = File.join(Metasmdir, 'samples', 'dbg-plugins', plugin_filename)
                if File.exist?(pf)
                        plugin_filename = pf
                elsif File.exist?(pf + '.rb')
                        plugin_filename = pf + '.rb'
                end
        end
        if (not File.exist?(plugin_filename) or File.directory?(plugin_filename)) and File.exist?(plugin_filename + '.rb')
                plugin_filename += '.rb'
        end

        instance_eval File.read(plugin_filename)
end
loadallsyms(&b) click to toggle source

load symbols from all libraries found by the OS module

# File metasm/debug.rb, line 1186
def loadallsyms(&b)
        modules.each { |m|
                b.call(m.addr) if b
                loadsyms(m.addr, m.path)
        }
end
loadsyms(addr, name='%08x'%addr.to_i) click to toggle source

loads the symbols from a mapped module

# File metasm/debug.rb, line 1126
def loadsyms(addr, name='%08x'%addr.to_i)
        if addr.kind_of? String
                modules.each { |m|
                        if m.path =~ /#{addr}/
                                addr = m.addr
                                name = File.basename m.path
                                break
                        end
                }
                return if not addr.kind_of? Integer
        end
        return if not peek = @memory.get_page(addr, 4)
        if peek == "\x7fELF"
                cls = LoadedELF
        elsif peek[0, 2] == "MZ" and @memory[addr+@memory[addr+0x3c,4].unpack('V').first, 4] == "PE\00\\00""
                cls = LoadedPE
        else return
        end

        begin
                e = cls.load @memory[addr, 0x1000_0000]
                e.load_address = addr
                e.decode_header
                e.decode_exports
        rescue
                # cache the error so that we dont hit it every time
                @modulemap[addr.to_s(16)] ||= [addr, addr+0x1000]
                return
        end

        if n = e.module_name and n != name
                name = n
        end

        @modulemap[name] ||= [addr, addr+e.module_size]

        cnt = 0
        e.module_symbols.each { |n_, a, l|
                cnt += 1
                a += addr
                @disassembler.set_label_at(a, n_, false)
                @symbols[a] = n_     # XXX store "lib!sym" ?
                if l and l > 1; @symbols_len[a] = l
                else @symbols_len.delete a   # we may overwrite an existing symbol, keep len in sync
                end
        }
        log "loaded #{cnt} symbols from #{name}"

        true
end
log(*a) click to toggle source

show information to the user, uses log_proc if defined

# File metasm/debug.rb, line 560
def log(*a)
        if @log_proc
                a.each { |aa| @log_proc[aa] }
        else
                puts(*a) if $VERBOSE
        end
end
mappings() click to toggle source

return the list of memory mappings of the current process array of [start, len, perms, infos]

# File metasm/debug.rb, line 1382
def mappings
        [[0, @memory.length]]
end
memory_read_int(addr, sz=@cpu.size/8) click to toggle source

read an int from the target memory, int of sz bytes (defaults to cpu.size)

# File metasm/debug.rb, line 1321
def memory_read_int(addr, sz=@cpu.size/8)
        addr = resolve_expr(addr) if not addr.kind_of? ::Integer
        Expression.decode_imm(@memory, sz, @cpu, addr)
end
memory_write_int(addr, val, sz=@cpu.size/8) click to toggle source

write an int in the target memory

# File metasm/debug.rb, line 1327
def memory_write_int(addr, val, sz=@cpu.size/8)
        addr = resolve_expr(addr) if not addr.kind_of? ::Integer
        val = resolve_expr(val) if not val.kind_of? ::Integer
        @memory[addr, sz] = Expression.encode_imm(val, sz, @cpu)
end
modules() click to toggle source

return a list of Process::Modules (with a path, addr) for the current process

# File metasm/debug.rb, line 1387
def modules
        []
end
need_stepover(di = di_at(pc)) click to toggle source

tests if the specified instructions should be stepover() using singlestep or by putting a breakpoint at next_addr

# File metasm/debug.rb, line 961
def need_stepover(di = di_at(pc))
        di and @cpu.dbg_need_stepover(self, di.address, di)
end
parse_expr(arg) click to toggle source

parses the expression contained in arg

# File metasm/debug.rb, line 1215
def parse_expr(arg)
        parse_expr!(arg.dup)
end
parse_expr!(arg, &b) click to toggle source

parses the expression contained in arg, updates arg to point after the expr

# File metasm/debug.rb, line 1220
def parse_expr!(arg, &b)
        return if not e = IndExpression.parse_string!(arg) { |s|
                # handle 400000 -> 0x400000
                # XXX no way to override and force decimal interpretation..
                if s.length > 4 and not @disassembler.get_section_at(s.to_i) and @disassembler.get_section_at(s.to_i(16))
                        s.to_i(16)
                else
                        s.to_i
                end
        }

        # resolve ambiguous symbol names/hex values
        bd = {}
        e.externals.grep(::String).each { |ex|
                if not v = register_list.find { |r| ex.downcase == r.to_s.downcase } ||
                                        (b && b.call(ex)) || symbols.index(ex)
                        lst = symbols.values.find_all { |s| s.downcase.include? ex.downcase }
                        case lst.length
                        when 0
                                if ex =~ /^[0-9a-f]+$/ and @disassembler.get_section_at(ex.to_i(16))
                                        v = ex.to_i(16)
                                else
                                        raise "unknown symbol name #{ex}"
                                end
                        when 1
                                v = symbols.index(lst.first)
                                log "using #{lst.first} for #{ex}"
                        else
                                suggest = lst[0, 50].join(', ')
                                suggest = suggest[0, 125] + '...' if suggest.length > 128
                                raise "ambiguous symbol name #{ex}: #{suggest} ?"
                        end
                end
                bd[ex] = v
        }
        e = e.bind(bd)

        e
end
pattern_scan(pat, start=0, len=@memory.length-start, &b) click to toggle source

see EData#pattern_scan scans only mapped areas of @memory, using os_process.mappings

# File metasm/debug.rb, line 1423
def pattern_scan(pat, start=0, len=@memory.length-start, &b)
        ret = []
        mappings.each { |maddr, mlen, perm, *o_|
                next if perm !~ /r/
                mlen -= start-maddr if maddr < start
                maddr = start if maddr < start
                mlen = start+len-maddr if maddr+mlen > start+len
                next if mlen <= 0
                EncodedData.new(read_mapped_range(maddr, mlen)).pattern_scan(pat) { |off|
                        off += maddr
                        ret << off if not b or b.call(off)
                }
        }
        ret
end
pc() click to toggle source

retreive the value of the program counter register (eip)

# File metasm/debug.rb, line 1049
def pc
        get_reg_value(register_pc)
end
Also aliased as: ip
pc=(v) click to toggle source

change the value of pc

# File metasm/debug.rb, line 1055
def pc=(v)
        set_reg_value(register_pc, v)
end
Also aliased as: ip=
pid=(npid) click to toggle source

change pid and associated cached data this will also re-load the previously selected tid for this process

# File metasm/debug.rb, line 171
def pid=(npid)
        return if npid == pid
        raise "invalid pid" if not check_pid(npid)
        swapout_pid
        @pid = npid
        swapin_pid
end
Also aliased as: set_pid
post_evt_bp(b) click to toggle source

handles breakpoint conditions/callbacks etc

# File metasm/debug.rb, line 738
def post_evt_bp(b)
        @breakpoint_cause = b

        found_valid_active = false

        pre_callback_pc = pc

        # XXX may have many active bps with callback that continue/singlestep/singlestep{}...
        b.hash_shared.dup.find_all { |bb|
                # ignore inactive bps
                next if bb.state != :active

                # ignore out-of-range bpms
                next if bb.type == :bpm and not check_bpm_range(bb, b.internal)

                # check condition
                case bb.condition
                when nil; cd = 1
                when Proc; cd = bb.condition.call
                when String, Expression; cd = resolve_expr(bb.condition)
                else raise "unknown bp condition #{bb.condition.inspect}"
                end
                next if not cd or cd == 0

                found_valid_active = true

                # oneshot
                del_bp(bb) if bb.oneshot

                bb.action
        }.each { |bb| bb.action.call }

        # discard @breakpoint_cause if a bp callback did modify register_pc
        @breakpoint_cause = nil if pc != pre_callback_pc

        # we did break due to a bp whose condition is not true: resume
        # (unless a callback already resumed)
        resume_badbreak(b) if not found_valid_active and @state == :stopped
end
read_mapped_range(addr, len) click to toggle source
# File metasm/debug.rb, line 1439
def read_mapped_range(addr, len)
        # try to use a single get_page call
        s = @memory.get_page(addr, len) || ''
        s.length == len ? s : (s = @memory[addr, len] ? s.to_str : nil)
end
register_flags() click to toggle source

then name of the register holding the cpu flags

# File metasm/debug.rb, line 1039
def register_flags
        @cpu.dbg_register_flags
end
register_list() click to toggle source

list the general purpose register names available for the target

# File metasm/debug.rb, line 1019
def register_list
        @cpu.dbg_register_list
end
register_pc() click to toggle source

retrieves the name of the register holding the program counter (address of the next instruction)

# File metasm/debug.rb, line 1029
def register_pc
        @cpu.dbg_register_pc
end
register_size() click to toggle source

hash { register_name => register_size_in_bits }

# File metasm/debug.rb, line 1024
def register_size
        @cpu.dbg_register_size
end
register_sp() click to toggle source

retrieve the name of the register holding the stack pointer

# File metasm/debug.rb, line 1034
def register_sp
        @cpu.dbg_register_sp
end
resolve(e)
Alias for: resolve_expr
resolve_expr(e) click to toggle source

resolves an expression involving register values and/or memory indirection using the current context uses register_list, get_reg_value, @mem, @cpu :tid/:pid resolve to current thread

# File metasm/debug.rb, line 1263
def resolve_expr(e)
        e = parse_expr(e) if e.kind_of? ::String
        bd = { :tid => @tid, :pid => @pid }
        Expression[e].externals.each { |ex|
                next if bd[ex]
                case ex
                when ::Symbol; bd[ex] = get_reg_value(ex)
                when ::String; bd[ex] = @symbols.index(ex) || @disassembler.prog_binding[ex] || 0
                end
        }
        Expression[e].bind(bd).reduce { |i|
                if i.kind_of? Indirection and p = i.pointer.reduce and p.kind_of? ::Integer
                        i.len ||= @cpu.size/8
                        p &= (1 << @cpu.size) - 1 if p < 0
                        Expression.decode_imm(@memory, i.len, @cpu, p)
                end
        }
end
Also aliased as: resolve
resume_badbreak(b=nil) click to toggle source

called when we did break due to a breakpoint whose condition is invalid resume execution as if we never stopped disable offending bp + singlestep if needed

# File metasm/debug.rb, line 856
def resume_badbreak(b=nil)
        # ensure we didn't delete b
        if b and b.hash_shared.find { |bb| bb.state == :active }
                rm = @run_method
                if rm == :singlestep
                        singlestep_bp(b)
                else
                        ra = @run_args
                        singlestep_bp(b) { send rm, *ra }
                end
        else
                send @run_method, *@run_args
        end
end
run()
Alias for: continue
run_forever() click to toggle source

#continue_wait until @state == :dead

# File metasm/debug.rb, line 1009
def run_forever
        continue_wait until @state == :dead
end
scansyms(addr=0, max=@memory.length-0x1000-addr) click to toggle source

scan the target memory for loaded libraries, load their symbols

# File metasm/debug.rb, line 1178
def scansyms(addr=0, max=@memory.length-0x1000-addr)
        while addr <= max
                loadsyms(addr)
                addr += 0x1000
        end
end
set_context(npid, ntid=nil, &b)
Alias for: switch_context
set_flag(f) click to toggle source

set the value of the flag to true

# File metasm/debug.rb, line 1091
def set_flag(f)
        @cpu.dbg_set_flag(self, f)
end
set_flag_value(f, v) click to toggle source

change the value of a flag

# File metasm/debug.rb, line 1081
def set_flag_value(f, v)
        (v && v != 0) ? set_flag(f) : unset_flag(f)
end
set_log_proc(l=nil, &b) click to toggle source

define the lambda to use to log stuff

# File metasm/debug.rb, line 555
def set_log_proc(l=nil, &b)
        @log_proc = l || b
end
set_pid(npid)
Alias for: pid=
set_tid(ntid)
Alias for: tid=
shortname() click to toggle source
# File metasm/debug.rb, line 166
def shortname; self.class.name.split('::').last.downcase; end
singlestep(&b) click to toggle source

resume execution of the target one instruction at a time

# File metasm/debug.rb, line 939
def singlestep(&b)
        @singlestep_cb = b
        bp = check_breakpoint_cause
        return if not check_pre_run(:singlestep)
        if bp and bp.hash_shared.find { |bb| bb.state == :active } and has_emul_instr(bp)
                @state = :stopped
                bp.emul_instr.call
                invalidate
                evt_singlestep(true)
        else
                do_singlestep
        end
end
singlestep_bp(bp, &b) click to toggle source

singlesteps over an active breakpoint and run its block if the breakpoint provides an emulation stub, run that, otherwise disable the breakpoint, singlestep, and re-enable

# File metasm/debug.rb, line 874
def singlestep_bp(bp, &b)
        if has_emul_instr(bp)
                @state = :stopped
                bp.emul_instr.call
                b.call if b
        else
                bp.hash_shared.each { |bb|
                        disable_bp(bb, :temp_inactive) if bb.state == :active
                }
                # this *should* work with different bps stopping the current instr
                prev_sscb = @singlestep_cb
                singlestep {
                        bp.hash_shared.each { |bb|
                                enable_bp(bb) if bb.state == :temp_inactive
                        }
                        prev_sscb[] if prev_sscb
                        b.call if b
                }
        end
end
singlestep_wait(&b) click to toggle source

singlestep ; #wait_target

# File metasm/debug.rb, line 954
def singlestep_wait(&b)
        singlestep(&b)
        wait_target
end
sp() click to toggle source

retrieve the value of the stack pointer register

# File metasm/debug.rb, line 1061
def sp
        get_reg_value(register_sp)
end
sp=(v) click to toggle source

update the stack pointer

# File metasm/debug.rb, line 1066
def sp=(v)
        set_reg_value(register_sp, v)
end
stacktrace(maxdepth=500, &b) click to toggle source

return/yield an array of [addr, addr symbolic name] corresponding to the current stack trace

# File metasm/debug.rb, line 1284
def stacktrace(maxdepth=500, &b)
        @cpu.dbg_stacktrace(self, maxdepth, &b)
end
stepout() click to toggle source

stepover until finding the last instruction of the function

# File metasm/debug.rb, line 988
def stepout
        # TODO thread-local bps
        while not end_stepout
                stepover
                wait_target
        end
        do_singlestep
end
stepout_wait() click to toggle source
# File metasm/debug.rb, line 997
def stepout_wait
        stepout
        wait_target
end
stepover() click to toggle source

stepover: singlesteps, but do not enter in subfunctions

# File metasm/debug.rb, line 966
def stepover
        di = di_at(pc)
        if need_stepover(di)
                bpx di.next_addr, true, Expression[:tid, :==, @tid]
                continue
        else
                singlestep
        end
end
stepover_wait() click to toggle source

stepover ; #wait_target

# File metasm/debug.rb, line 977
def stepover_wait
        stepover
        wait_target
end
swapin_pid() click to toggle source

we're switching focus from one pid to another, load current pid data

# File metasm/debug.rb, line 249
def swapin_pid
        return initialize_newpid if not @pid_stuff[@pid]

        @pid_stuff_list.each { |fld|
                instance_variable_set("@#{fld}", @pid_stuff[@pid][fld])
        }
        swapin_tid
        gui.swapin_pid if gui.respond_to?(:swapin_pid)
end
swapin_tid() click to toggle source

we're switching focus from one tid to another, load current tid data

# File metasm/debug.rb, line 260
def swapin_tid
        return initialize_newtid if not @tid_stuff[@tid]

        @tid_stuff_list.each { |fld|
                instance_variable_set("@#{fld}", @tid_stuff[@tid][fld])
        }
        gui.swapin_tid if gui.respond_to?(:swapin_tid)
end
swapout_pid() click to toggle source

we're switching focus from one pid to another, save current pid data

# File metasm/debug.rb, line 228
def swapout_pid
        return if not pid
        swapout_tid
        gui.swapout_pid if gui.respond_to?(:swapout_pid)
        @pid_stuff[@pid] ||= {}
        @pid_stuff_list.each { |fld|
                @pid_stuff[@pid][fld] = instance_variable_get("@#{fld}")
        }
end
swapout_tid() click to toggle source

we're switching focus from one tid to another, save current tid data

# File metasm/debug.rb, line 239
def swapout_tid
        return if not tid
        gui.swapout_tid if gui.respond_to?(:swapout_tid)
        @tid_stuff[@tid] ||= {}
        @tid_stuff_list.each { |fld|
                @tid_stuff[@tid][fld] = instance_variable_get("@#{fld}")
        }
end
switch_context(npid, ntid=nil, &b) click to toggle source

change the debugger to a specific pid/tid if given a block, run the block and then restore the original pid/tid pid may be an object that respond to pid/#tid

# File metasm/debug.rb, line 302
def switch_context(npid, ntid=nil, &b)
        if npid.respond_to?(:pid)
                ntid ||= npid.tid
                npid = npid.pid
        end
        oldpid = pid
        oldtid = tid
        set_pid npid
        set_tid ntid if ntid
        if b
                # shortcut begin..ensure overhead
                return b.call if oldpid == pid and oldtid == tid

                begin
                        b.call
                ensure
                        set_pid oldpid
                        set_tid oldtid
                end
        end
end
Also aliased as: set_context
tid=(ntid) click to toggle source
# File metasm/debug.rb, line 181
def tid=(ntid)
        return if ntid == tid
        raise "invalid tid" if not check_tid(ntid)
        swapout_tid
        @tid = ntid
        swapin_tid
end
Also aliased as: set_tid
toggle_flag(f) click to toggle source

switch the value of a flag (true->false, false->true)

# File metasm/debug.rb, line 1086
def toggle_flag(f)
        set_flag_value(f, 1-get_flag_value(f))
end
unset_flag(f) click to toggle source

set the value of the flag to false

# File metasm/debug.rb, line 1096
def unset_flag(f)
        @cpu.dbg_unset_flag(self, f)
end
wait_target() click to toggle source

waits until the running target stops (due to a breakpoint, fault, etc)

# File metasm/debug.rb, line 912
def wait_target
        do_wait_target while @state == :running
end