class Ptrace::Target

# ———————————————————————– A target process managed by ptrace.

Usage:

tgt = Target.attach(pid)
loop do
  begin
    tgt.step
    puts tgt.regs.read.inspect
  rescue Ptrace::InvalidProcessError
    break
  end
end

tgt = Target.launch(cmd)
loop do
  begin
    state = tgt.syscall_state
    puts state.inspect
  rescue Ptrace::InvalidProcessError
    break
  end
end

Attributes

data[RW]

Data segment for process.

fpregs[RW]

FPU registers for process.

options[RW]

Ptrace options.

pid[RW]

PID of target process.

regs[RW]

General (CPU) registers for process.

text[RW]

Text (code) segment for process.

user[RW]

Target 'user' (task) area.

Public Class Methods

attach(pid) click to toggle source

PT_ATTACH : Attach to running process 'pid' and return a Ptrace::Target object. Raises an exception if the attach fails.

# File lib/Ptrace.rb, line 395
def self.attach(pid)
  tgt = Target.new(pid)
  begin
    Ptrace::Debugger.send_cmd( Ptrace::Debugger.commands[:attach], pid, nil)
    Process.waitpid(pid)
  rescue RuntimeError => e
    case e.message
      when 'PTRACE: Operation not permitted'
        raise OperationNotPermittedError.new(e.message)
      when 'PTRACE: No such process'
        raise InvalidProcessError.new(e.message)
      else
        raise
    end
  end
  return tgt
end
launch(cmd) click to toggle source

PT_TRACE_ME : Launch command 'cmd' and return a Ptrace::Target object for controlling it. Raises an exception if the command cannot be launched; returns nil if the command cannot be traced.

# File lib/Ptrace.rb, line 419
def self.launch(cmd)
  pid = fork
  if ! pid
    begin
      Ptrace::Debugger.send_cmd(Ptrace::Debugger.commands[:traceme], nil, 
                                nil)
      exec(cmd)
    rescue RuntimeError => e
      case e.message
        when 'PTRACE: Operation not permitted'
          raise OperationNotPermittedError.new(e.message)
        when 'PTRACE: No such process'
          raise InvalidProcessError.new(e.message)
        else
          raise
      end
    end

  elsif pid == -1
    return nil

  else
    Process.waitpid(pid)
    tgt = Target.new(pid)
    return tgt
  end
end
new(pid) click to toggle source

Create a Ptrace::Target object for process 'pid'. The process is assumed to have been launched or attached to by ptrace, e.g. using Target.launch or Target.attach.

# File lib/Ptrace.rb, line 379
def initialize(pid)
  @pid = pid
  @text = MemArea.new(MemArea::MEM_TEXT, pid)
  @data = MemArea.new(MemArea::MEM_DATA, pid)
  @user = MemArea.new(MemArea::MEM_USER, pid)
  @regs = RegSet.new(RegSet::GEN, pid)
  @fpregs = RegSet.new(RegSet::FP, pid)
  @fpxregs = RegSet.new(RegSet::Ext, pid)
  @options = Options.new(pid)
  @valid = true
end

Public Instance Methods

cont() click to toggle source

PT_CONTINUE: Continue execution of target.

# File lib/Ptrace.rb, line 529
def cont
  ptrace_send( :cont )
  Process.waitpid(@pid)
end
detach() click to toggle source

PT_DETACH : Detach from the process. Note: This makes the Ptrace::Target object invalid.

# File lib/Ptrace.rb, line 477
def detach
  ptrace_send( :detach )
  Process.waitpid(@pid)
  @valid = false
end
event_msg() click to toggle source
# File lib/Ptrace.rb, line 457
def event_msg
  # send signal
  # For PTRACE_EVENT_EXIT this is the child's exit status. For PTRACE_EVENT_FORK, PTRACE_EVENT_VFORK and PTRACE_EVENT_CLONE this is the PID of the new process.
  # Debugger.event_msg
end
kill() click to toggle source

PT_KILL : Terminate the process. Note: This makes the Ptrace::Target object invalid.

# File lib/Ptrace.rb, line 467
def kill
  ptrace_send( :kill )
  Process.waitpid(@pid)
  @valid = false
end
options=() click to toggle source
# File lib/Ptrace.rb, line 551
def options=
  # TODO
  # ptrace_send_data( :setoptions,  )
end
signal(sig=nil) click to toggle source
# File lib/Ptrace.rb, line 449
def signal(sig=nil)
  # if sig, send
  # else:
  Signal.read(pid)
end
step() click to toggle source

PT_STEP : Step a single instruction.

# File lib/Ptrace.rb, line 486
def step
  ptrace_send( :singlestep )
  Process.waitpid(@pid)
end
syscall() click to toggle source

PT_SYSCALL : Execute until the start or end of the next system call.

Usage:

tgt.syscall
in_regs = tgt.regs.read
tgt.syscall
out_regs = tgt.regs.read
# File lib/Ptrace.rb, line 500
def syscall
  ptrace_send( :syscall )
  Process.waitpid(@pid)
end
syscall_state() click to toggle source

Wrapper for recording syscalls. This issues a PT_SYSCALL to stop the target at the next syscall, records the 'in' register set, issues a PT_SYSCALL to stop the target after the syscall returns, records the 'out' register set, and returns a Hash { :in, :out } of the register sets. The target is stopped on return from this syscall.

# File lib/Ptrace.rb, line 512
def syscall_state
  begin
    state = {}
    syscall
    state[:in] = @regs.read
    syscall
    state[:out] = @regs.read
    state
  rescue InvalidProcessError
    # Program exited without a syscall
    return state
  end
end
sysemu() click to toggle source

PT_SYSEMU

# File lib/Ptrace.rb, line 537
def sysemu
  ptrace_send( :sysemu )
end
sysemu_step() click to toggle source

PT_SYSEMU_SINGLESTEP

# File lib/Ptrace.rb, line 544
def sysemu_step
  ptrace_send( :sysemu_singlestep )
end

Private Instance Methods

ptrace_send( cmd, arg=nil ) click to toggle source
# File lib/Ptrace.rb, line 558
def ptrace_send( cmd, arg=nil )
  begin
    raise OperationNotSupportedError if (not PTRACE_COMMANDS.include? cmd)
    raise OperationNotPermittedError if (not @valid)

    Debugger.send_cmd( PTRACE_COMMANDS[cmd], @pid, arg )
  rescue RuntimeError => e
    case e.message
      when 'PTRACE: Operation not permitted'
        raise OperationNotPermittedError.new(e.message)
      when 'PTRACE: No such process'
        raise InvalidProcessError.new(e.message)
      else
        raise
    end
  end
end