class Metasm::PTrace
Constants
- BTS
- BTS_O
block trace
- COMMAND
linux/ptrace.h
- ERRNO
include/asm-generic/errno-base.h
- OPTIONS
- REGS_I386
- REGS_X86_64
- SIGINFO
- SIGINFO_C
- SIGNAL
- SYSCALLNR_I386
- SYSCALLNR_X86_64
- WAIT_EXTENDEDRESULT
Attributes
buf[RW]
host_intsize[RW]
host_syscallnr[RW]
intsize[RW]
packint[RW]
packuint[RW]
pid[RW]
reg_off[RW]
syscallnr[RW]
syscallreg[RW]
tgcpu[RW]
Public Class Methods
new(target, do_attach=true, &b)
click to toggle source
creates a ptraced process (target = path) or opens a running process (target = pid) values for do_attach:
:create => always fork+traceme+exec+wait :attach => always attach false/nil => same as attach, without actually calling PT_ATTACH (useful when the ruby process is already tracing pid) default/anything else: try to attach if pid is numeric, else create
# File metasm/os/linux.rb, line 36 def initialize(target, do_attach=true, &b) case do_attach when :create init_create(target, &b) when :attach init_attach(target) when :dup raise ArgumentError unless target.kind_of?(PTrace) @pid = target.pid tweak_for_pid(@pid, target.tgcpu) # avoid re-parsing /proc/self/exe when nil, false @pid = Integer(target) tweak_for_pid(@pid) else t = begin; Integer(target) rescue ArgumentError, TypeError end t ? init_attach(t) : init_create(target, &b) end end
open(target) { |ptrace| ... }
click to toggle source
# File metasm/os/linux.rb, line 14 def self.open(target) ptrace = new(target) return ptrace if not block_given? begin yield ptrace ensure ptrace.detach end end
traceme()
click to toggle source
calls PTRACE_TRACEME on the current (ruby) process
# File metasm/os/linux.rb, line 25 def self.traceme new(::Process.pid, false).traceme end
Public Instance Methods
attach()
click to toggle source
# File metasm/os/linux.rb, line 568 def attach sys_ptrace(COMMAND[:ATTACH], @pid, 0, 0) end
bufval()
click to toggle source
interpret the value turned as an unsigned long
# File metasm/os/linux.rb, line 176 def bufval @buf.unpack(@packint).first end
cont(sig = nil)
click to toggle source
# File metasm/os/linux.rb, line 544 def cont(sig = nil) sig ||= 0 sys_ptrace(COMMAND[:CONT], @pid, 0, sig) end
cp()
click to toggle source
# File metasm/os/linux.rb, line 587 def cp @cp ||= @tgcpu.new_cparser end
detach()
click to toggle source
# File metasm/os/linux.rb, line 572 def detach sys_ptrace(COMMAND[:DETACH], @pid, 0, 0) end
dup()
click to toggle source
# File metasm/os/linux.rb, line 167 def dup self.class.new(self, :dup) end
get_thread_area(addr)
click to toggle source
# File metasm/os/linux.rb, line 532 def get_thread_area(addr) sys_ptrace(COMMAND[:GET_THREAD_AREA], @pid, addr, @buf) bufval end
geteventmsg()
click to toggle source
retrieve pid of cld for EVENT_CLONE/FORK, exitcode for EVENT_EXIT
# File metasm/os/linux.rb, line 582 def geteventmsg sys_ptrace(COMMAND[:GETEVENTMSG], @pid, 0, @buf) bufval end
getfpregs(buf=nil)
click to toggle source
# File metasm/os/linux.rb, line 510 def getfpregs(buf=nil) buf = buf.str if buf.respond_to?(:str) buf ||= [0].pack('C')*1024 sys_ptrace(COMMAND[:GETFPREGS], @pid, 0, buf) buf end
getfpxregs(buf=nil)
click to toggle source
# File metasm/os/linux.rb, line 521 def getfpxregs(buf=nil) buf = buf.str if buf.respond_to?(:str) buf ||= [0].pack('C')*512 sys_ptrace(COMMAND[:GETFPXREGS], @pid, 0, buf) buf end
getregs(buf=nil)
click to toggle source
# File metasm/os/linux.rb, line 499 def getregs(buf=nil) buf = buf.str if buf.respond_to?(:str) # AllocCStruct buf ||= [0].pack('C')*512 sys_ptrace(COMMAND[:GETREGS], @pid, 0, buf) buf end
getsiginfo()
click to toggle source
# File metasm/os/linux.rb, line 598 def getsiginfo sys_ptrace(COMMAND[:GETSIGINFO], @pid, 0, siginfo.str) siginfo end
host_csn()
click to toggle source
# File metasm/os/linux.rb, line 165 def host_csn; @@host_csn end
init_attach(target)
click to toggle source
# File metasm/os/linux.rb, line 57 def init_attach(target) @pid = Integer(target) tweak_for_pid(@pid) attach wait puts "Ptrace: attached to #@pid" if $DEBUG end
init_create(target, &b)
click to toggle source
# File metasm/os/linux.rb, line 65 def init_create(target, &b) if not @pid = ::Process.fork tweak_for_pid(::Process.pid) traceme b.call if b ::Process.exec(*target) exit!(0) end wait raise "could not exec #{target}" if $?.exited? tweak_for_pid(@pid) puts "Ptrace: attached to new #@pid" if $DEBUG end
kill()
click to toggle source
# File metasm/os/linux.rb, line 549 def kill sys_ptrace(COMMAND[:KILL], @pid, 0, 0) end
peekdata(addr)
click to toggle source
# File metasm/os/linux.rb, line 476 def peekdata(addr) sys_ptrace(COMMAND[:PEEKDATA], @pid, addr, @buf) @buf end
peektext(addr)
click to toggle source
# File metasm/os/linux.rb, line 471 def peektext(addr) sys_ptrace(COMMAND[:PEEKTEXT], @pid, addr, @buf) @buf end
peekusr(addr)
click to toggle source
# File metasm/os/linux.rb, line 481 def peekusr(addr) sys_ptrace(COMMAND[:PEEKUSR], @pid, @host_intsize*addr, @buf) @peekmask ||= (1 << ([@host_intsize, @intsize].min*8)) - 1 bufval & @peekmask end
pokedata(addr, data)
click to toggle source
# File metasm/os/linux.rb, line 491 def pokedata(addr, data) sys_ptrace(COMMAND[:POKEDATA], @pid, addr, data.unpack(@packint).first) end
poketext(addr, data)
click to toggle source
# File metasm/os/linux.rb, line 487 def poketext(addr, data) sys_ptrace(COMMAND[:POKETEXT], @pid, addr, data.unpack(@packint).first) end
pokeusr(addr, data)
click to toggle source
# File metasm/os/linux.rb, line 495 def pokeusr(addr, data) sys_ptrace(COMMAND[:POKEUSR], @pid, @host_intsize*addr, data) end
prctl(addr, data)
click to toggle source
# File metasm/os/linux.rb, line 540 def prctl(addr, data) sys_ptrace(COMMAND[:ARCH_PRCTL], @pid, addr, data) end
readmem(off, len)
click to toggle source
reads a memory range
# File metasm/os/linux.rb, line 181 def readmem(off, len) decal = off % @host_intsize buf = '' if decal > 0 off -= decal peekdata(off) off += @host_intsize buf << @buf[decal...@host_intsize] end offend = off + len while off < offend peekdata(off) buf << @buf[0, @host_intsize] off += @host_intsize end buf[0, len] end
set_thread_area(addr, data)
click to toggle source
# File metasm/os/linux.rb, line 536 def set_thread_area(addr, data) sys_ptrace(COMMAND[:SET_THREAD_AREA], @pid, addr, data) end
setfpregs(buf)
click to toggle source
# File metasm/os/linux.rb, line 516 def setfpregs(buf) buf = buf.str if buf.respond_to?(:str) sys_ptrace(COMMAND[:SETFPREGS], @pid, 0, buf) end
setfpxregs(buf)
click to toggle source
# File metasm/os/linux.rb, line 527 def setfpxregs(buf) buf = buf.str if buf.respond_to?(:str) sys_ptrace(COMMAND[:SETFPXREGS], @pid, 0, buf) end
setoptions(*opt)
click to toggle source
# File metasm/os/linux.rb, line 576 def setoptions(*opt) opt = opt.inject(0) { |b, o| b |= o.kind_of?(Integer) ? o : OPTIONS[o] } sys_ptrace(COMMAND[:SETOPTIONS], @pid, 0, opt) end
setregs(buf)
click to toggle source
# File metasm/os/linux.rb, line 505 def setregs(buf) buf = buf.str if buf.respond_to?(:str) sys_ptrace(COMMAND[:SETREGS], @pid, 0, buf) end
setsiginfo(si=siginfo)
click to toggle source
# File metasm/os/linux.rb, line 603 def setsiginfo(si=siginfo) si = si.str if si.respond_to?(:str) sys_ptrace(COMMAND[:SETSIGINFO], @pid, 0, si) end
setup_sys_ptrace(sysnr)
click to toggle source
# File metasm/os/linux.rb, line 128 def setup_sys_ptrace(sysnr) moo = Class.new(DynLdr) case @@host_csn when 'ia32' # XXX compat lin2.4 ? asm = <<EOS #define off 3*4 push ebx push esi mov eax, #{sysnr} mov ebx, [esp+off] mov ecx, [esp+off+4] mov edx, [esp+off+8] mov esi, [esp+off+12] call gs:[10h] pop esi pop ebx ret EOS when 'x64' asm = <<EOS #define off 3*8 mov rax, #{sysnr} //mov rdi, rdi //mov rsi, rdi //mov rdx, rdx mov r10, rcx syscall ret EOS else raise 'unsupported target architecture' end moo.new_func_asm 'long ptrace(unsigned long, unsigned long, unsigned long, unsigned long)', asm moo end
siginfo()
click to toggle source
# File metasm/os/linux.rb, line 591 def siginfo @siginfo ||= ( cp.parse SIGINFO_C if not cp.toplevel.struct['siginfo'] cp.alloc_c_struct('siginfo') ) end
singleblock(sig = nil)
click to toggle source
# File metasm/os/linux.rb, line 558 def singleblock(sig = nil) sig ||= 0 sys_ptrace(COMMAND[:SINGLEBLOCK], @pid, 0, sig) end
singlestep(sig = nil)
click to toggle source
# File metasm/os/linux.rb, line 553 def singlestep(sig = nil) sig ||= 0 sys_ptrace(COMMAND[:SINGLESTEP], @pid, 0, sig) end
str_ptr(str)
click to toggle source
# File metasm/os/linux.rb, line 171 def str_ptr(str) [str].pack('P').unpack(@packint).first end
sys_ptrace(req, pid, addr, data)
click to toggle source
# File metasm/os/linux.rb, line 459 def sys_ptrace(req, pid, addr, data) ret = @sys_ptrace.ptrace(req, pid, addr, data) if ret < 0 and ret > -256 raise SystemCallError.new("ptrace #{COMMAND.index(req) || req}", -ret) end ret end
syscall(sig = nil)
click to toggle source
# File metasm/os/linux.rb, line 563 def syscall(sig = nil) sig ||= 0 sys_ptrace(COMMAND[:SYSCALL], @pid, 0, sig) end
traceme()
click to toggle source
# File metasm/os/linux.rb, line 467 def traceme sys_ptrace(COMMAND[:TRACEME], 0, 0, 0) end
tweak_for_pid(pid=@pid, tgcpu=nil)
click to toggle source
setup variables according to the target (ptrace interface, syscall nrs, …)
# File metasm/os/linux.rb, line 89 def tweak_for_pid(pid=@pid, tgcpu=nil) # use these for our syscalls PTRACE @@host_csn ||= LinOS.open_process(::Process.pid).cpu.shortname case @@host_csn when 'ia32' @packint = 'l' @packuint = 'L' @host_intsize = 4 @host_syscallnr = SYSCALLNR_I386 @reg_off = REGS_I386 when 'x64' @packint = 'q' @packuint = 'Q' @host_intsize = 8 @host_syscallnr = SYSCALLNR_X86_64 @reg_off = REGS_X86_64 else raise 'unsupported architecture' end @tgcpu = tgcpu || LinOS.open_process(pid).cpu # use these to interpret the child state case @tgcpu.shortname when 'ia32' @syscallreg = 'ORIG_EAX' @syscallnr = SYSCALLNR_I386 @intsize = 4 when 'x64' @syscallreg = 'ORIG_RAX' @syscallnr = SYSCALLNR_X86_64 @intsize = 8 else raise 'unsupported target architecture' end # buffer used in ptrace syscalls @buf = [0].pack(@packint) @sys_ptrace = @@sys_ptrace[@host_syscallnr['ptrace']] ||= setup_sys_ptrace(@host_syscallnr['ptrace']) end
wait()
click to toggle source
# File metasm/os/linux.rb, line 79 def wait ::Process.wait(@pid, ::Process::WALL) end
writemem(off, str)
click to toggle source
# File metasm/os/linux.rb, line 199 def writemem(off, str) str.force_encoding('binary') if str.respond_to?(:force_encoding) decal = off % @host_intsize if decal > 0 off -= decal peekdata(off) str = @buf[0...decal] + str end decal = str.length % @host_intsize if decal > 0 peekdata(off+str.length-decal) str += @buf[decal...@host_intsize] end i = 0 while i < str.length pokedata(off+i, str[i, @host_intsize]) i += @host_intsize end true end