Class: MemoryIO::Process

Inherits:
Object
  • Object
show all
Defined in:
lib/memory_io/process.rb

Overview

Records information of a process.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(pid) ⇒ Process

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

TODO:

Support MacOS

TODO:

Support Windows

Note:

This class only supports procfs-based system. i.e. /proc is mounted and readable.

Create process object from pid.

Raises:

  • (Errno::ENOENT)

22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/memory_io/process.rb', line 22

def initialize(pid)
  @pid = pid
  @mem = "/proc/#{pid}/mem"
  # check permission of '/proc/pid/mem'
  @perm = MemoryIO::Util.file_permission(@mem)
  # TODO: raise custom exception
  raise Errno::ENOENT, @mem if perm.nil?
  # FIXME: use logger
  warn("You have no permission to read/write this process.\n\nCheck the setting of /proc/sys/kernel/yama/ptrace_scope, or try\nagain as the root user.\n\nTo enable attach another process, do:\n\n$ echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope\n".strip) unless perm.readable? || perm.writable?
end

Instance Attribute Details

#perm#readable?, #writable? (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.


6
7
8
# File 'lib/memory_io/process.rb', line 6

def perm
  @perm
end

Instance Method Details

#bases{Symbol => Integer}

Parse /proc//maps to get all bases.

Examples:

process = Process.new(`pidof victim`.to_i)
puts process.bases.map { |key, val| format('%s: 0x%016x', key, val) }
# vsyscall: 0xffffffffff600000
# vdso: 0x00007ffd5b565000
# vvar: 0x00007ffd5b563000
# stack: 0x00007ffd5ad21000
# ld: 0x00007f339a69b000
# libc: 0x00007f33996f1000
# heap: 0x00005571994a1000
# victim: 0x0000557198bcb000
#=> nil

59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/memory_io/process.rb', line 59

def bases
  file = "/proc/#{@pid}/maps"
  stat = MemoryIO::Util.file_permission(file)
  return {} unless stat && stat.readable?
  maps = ::IO.binread(file).split("\n").map do |line|
    # 7f76515cf000-7f76515da000 r-xp 00000000 fd:01 29360257  /lib/x86_64-linux-gnu/libnss_files-2.24.so
    addr, _perm, _offset, _dev, _inode, pathname = line.strip.split(' ', 6)
    next nil if pathname.nil?
    addr = addr.to_i(16)
    pathname = pathname[1..-2] if pathname =~ /^\[.+\]$/
    pathname = ::File.basename(pathname)
    [MemoryIO::Util.trim_libname(pathname).to_sym, addr]
  end
  maps.compact.reverse.to_h
end

#read(addr, num_elements, **options) ⇒ String, ...

Read from process's memory.

This method has almost same arguements and return types as IO#read. The only difference is this method needs parameter addr (which will be passed to paramter from in IO#read).

Examples:

process = MemoryIO.attach(`pidof victim`.to_i)
puts process.read('heap', 4, as: :u64).map { |c| '0x%016x' % c }
# 0x0000000000000000
# 0x0000000000000021
# 0x00000000deadbeef
# 0x0000000000000000
#=> nil
process.read('heap+0x10', 4, as: :u8).map { |c| '0x%x' % c }
#=> ['0xef', '0xbe', '0xad', '0xde']

process.read('libc', 4)
#=> "\x7fELF"

See Also:


106
107
108
# File 'lib/memory_io/process.rb', line 106

def read(addr, num_elements, **options)
  mem_io(:read) { |io| io.read(num_elements, from: MemoryIO::Util.safe_eval(addr, bases), **options) }
end

#write(addr, objects, **options) ⇒ void

This method returns an undefined value.

Write objects at addr.

This method has almost same arguments as IO#write.

Examples:

process = MemoryIO.attach('self')
s = 'A' * 16
process.write(s.object_id * 2 + 16, 'BBBBCCCC')
s
#=> 'BBBBCCCCAAAAAAAA'

See Also:


130
131
132
# File 'lib/memory_io/process.rb', line 130

def write(addr, objects, **options)
  mem_io(:write) { |io| io.write(objects, from: MemoryIO::Util.safe_eval(addr, bases), **options) }
end