Class: HeapInfo::Process

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

Overview

Main class of heapinfo.

Constant Summary collapse

DEFAULT_LIB =

The default options of libraries, use for matching glibc segments in +/proc//maps+.

{
  libc: /bc[^a-z]*\.so/
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(prog, options = {}) ⇒ Process

Instantiate a HeapInfo::Process object.

Parameters:

  • prog (String, Integer)

    Process name or pid, see heapinfo for more information.

  • options (Hash{Symbol => Regexp, String}) (defaults to: {})

    Libraries' filename, see heapinfo for more information.


18
19
20
21
22
# File 'lib/heapinfo/process.rb', line 18

def initialize(prog, options = {})
  @prog = prog
  @options = DEFAULT_LIB.merge options
  load!
end

Instance Attribute Details

#pidInteger? (readonly)

Returns The pid of process, nil if no such process found.

Returns:

  • (Integer, nil)

    The pid of process, nil if no such process found.


12
13
14
# File 'lib/heapinfo/process.rb', line 12

def pid
  @pid
end

Instance Method Details

#canaryInteger

Get the value of stack guard.

Examples:

h.canary
#=> 11342701118118205184 # 0x9d695e921adc9700

Returns:

  • (Integer)

218
219
220
221
222
# File 'lib/heapinfo/process.rb', line 218

def canary
  return Nil.new unless load?
  addr = @info.auxv[:random]
  Helper.unpack(bits / 8, @dumper.dump(addr, bits / 8)) & 0xffffffffffffff00
end

#debugObject

Use this method to wrapper all HeapInfo methods.

Since HeapInfo is a tool(debugger) for local usage, while exploiting remote service, all methods will not work properly. So I suggest to wrapper all methods inside #debug, which will ignore the block while the victim process is not found.

Examples:

h = heapinfo('./victim') # such process doesn't exist
libc_base = leak_libc_base_of_victim # normal exploit
h.debug {
  # for local to check if exploit correct
  fail('libc_base') unless libc_base == h.libc.base
}
# block of #debug will not execute if can't found process

51
52
53
54
# File 'lib/heapinfo/process.rb', line 51

def debug
  return unless load!
  yield if block_given?
end

#dump(*args) ⇒ String, HeapInfo::Nil

Dump the content of specific memory address.

Note: This method require you have permission of attaching another process. If not, a warning message will present.

Examples:

h = heapinfo('victim')
h.dump(:heap) # heap[0, 8]
h.dump(:heap, 64) # heap[0, 64]
h.dump('heap+256', 64)  # heap[256, 64]
h.dump('heap+0x100', 64) # heap[256, 64]
h.dump('heap+0x100 * 2 + 0x300', 64) # heap[1024, 64]
h.dump(<segment>, 8) # semgent can be [heap, stack, (program|elf), libc, ld]
h.dump(addr, 64) # addr[0, 64]

# Invalid usage
dump(:meow) # no such segment

Parameters:

  • args (Mixed)

    Will be parsed into [base, length], see Examples for more information.

Returns:

  • (String, HeapInfo::Nil)

    The content needed. When the request address is not readable or the process not exists, instance of Nil is returned.


78
79
80
81
# File 'lib/heapinfo/process.rb', line 78

def dump(*args)
  return Nil.new unless load?
  dumper.dump(*args)
end

#dump_chunks(*args) ⇒ HeapInfo::Chunks, HeapInfo::Nil

Return the dump result as chunks. see Dumper#dump_chunks for more information.

Parameters:

  • args (Mixed)

    Same as arguments of #dump.

Returns:


88
89
90
91
# File 'lib/heapinfo/process.rb', line 88

def dump_chunks(*args)
  return Nil.new unless load?
  dumper.dump_chunks(*args)
end

#find(pattern, from, length = :unlimited, rel: false) ⇒ Integer? Also known as: search

Gdb-like command.

Search a specific value/string/regexp in memory.

Examples:

h.find(0xdeadbeef, 'heap+0x10', 0x1000)
#=> 6299664 # 0x602010
h.find(/E.F/, 0x400000, 4)
#=> 4194305 # 0x400001
h.find(/E.F/, 0x400000, 3)
#=> nil
sh_offset = h.find('/bin/sh', :libc) - h.libc
#=> 1559771 # 0x17ccdb
h.find('/bin/sh', :libc, rel: true) == h.find('/bin/sh', :libc) - h.libc
#=> true

Parameters:

  • pattern (Integer, String, Regexp)

    The desired search pattern, can be value(Integer), string, or regular expression.

  • from (Integer, String, Symbol)

    Start address for searching, can be segment(Symbol) or segments with offset. See examples for more information.

  • length (Integer) (defaults to: :unlimited)

    The search length limit, default is unlimited, which will search until pattern found or reach unreadable memory.

  • rel (Boolean)

    To show relative offset of from or absolute address.

Returns:

  • (Integer, nil)

    The first matched address, nil is returned when no such pattern found.


175
176
177
178
# File 'lib/heapinfo/process.rb', line 175

def find(pattern, from, length = :unlimited, rel: false)
  return Nil.new unless load?
  dumper.find(pattern, from, length, rel)
end

#inspectString

Make pry not so verbose.

Returns:

  • (String)

227
228
229
# File 'lib/heapinfo/process.rb', line 227

def inspect
  format('#<HeapInfo::Process:0x%016x>', __id__)
end

#layouts(*args) ⇒ void

This method returns an undefined value.

Pretty dump of bins layouts.

The request layouts will output to stdout.

Examples:

h.layouts(:fast, :unsorted, :small)

Parameters:

  • args (Array<Symbol>)

    Bin type(s) you want to see.


190
191
192
193
# File 'lib/heapinfo/process.rb', line 190

def layouts(*args)
  return unless load?
  puts libc.main_arena.layouts(*args)
end

#offset(addr, sym = nil) ⇒ void Also known as: off

This method returns an undefined value.

Show the offset in pretty way between the segment. Very useful in pwn when leak some address, see examples for more details.

Examples:

h.offset(0x7f11f6ae1670, :libc)
#=> 0xf6670 after libc
h.offset(0x5559edc057a0, :heap)
#=> 0x9637a0 after heap
h.offset(0x7f11f6ae1670)
#=> 0xf6670 after :libc
h.offset(0x5559edc057a0)
#=> 0x9637a0 after :heap

Parameters:

  • addr (Integer)

    The leaked address.

  • sym (Symbol) (defaults to: nil)

    The segement symbol to be calculated offset. If this parameter not given, will loop segments and find the most close one. See examples for more details.


111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/heapinfo/process.rb', line 111

def offset(addr, sym = nil)
  return unless load?
  segment = @info.__send__(sym) if HeapInfo::ProcessInfo::EXPORT.include?(sym)
  segment = nil unless segment.is_a?(HeapInfo::Segment)
  if segment.nil?
    sym, segment = @info.segments
                        .select { |_, seg| seg.base <= addr }
                        .min_by { |_, seg| addr - seg }
  end
  return puts "Invalid address #{Helper.hex(addr)}" if segment.nil?
  puts Helper.color(Helper.hex(addr - segment)) + ' after ' + Helper.color(sym, sev: :sym)
end

#reload!HeapInfo::Process Also known as: reload

Reload a new process with same program name.

Examples:

puts h.reload!

Returns:


29
30
31
32
33
# File 'lib/heapinfo/process.rb', line 29

def reload!
  @pid = nil
  load!
  self
end

#to_sString

Show simple information of target process. Contains program names, pid, and segments' info.

Examples:

puts h

Returns:

  • (String)

201
202
203
204
205
206
207
208
209
210
# File 'lib/heapinfo/process.rb', line 201

def to_s
  return 'Process not found' unless load?
  "Program: #{Helper.color(program.name)} PID: #{Helper.color(pid)}\n" +
    program.to_s +
    heap.to_s +
    stack.to_s +
    libc.to_s +
    ld.to_s +
    format("%-28s\tvalue: #{Helper.color(format('%#x', canary), sev: :sym)}", Helper.color('canary', sev: :sym))
end

#x(count, address) ⇒ void

This method returns an undefined value.

Gdb-like command

Show dump results like gdb's command x. While will auto detect the current elf class to decide using gx or wx.

The dump results wrapper with color codes and nice typesetting will output to stdout.

Examples:

h.x 8, :heap
# 0x1f0d000:      0x0000000000000000      0x0000000000002011
# 0x1f0d010:      0x00007f892a9f87b8      0x00007f892a9f87b8
# 0x1f0d020:      0x0000000000000000      0x0000000000000000
# 0x1f0d030:      0x0000000000000000      0x0000000000000000
h.x 3, 0x400000
# 0x400000:       0x00010102464c457f      0x0000000000000000
# 0x400010:       0x00000001003e0002

Parameters:

  • count (Integer)

    The number of result need to dump, see examples for more information.

  • address (String, Symbol, Integer)

    The base address to be dumped. Same format as #dump, see #dump for more information.


145
146
147
148
# File 'lib/heapinfo/process.rb', line 145

def x(count, address)
  return unless load?
  dumper.x(count, address)
end