heapinfo 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +130 -0
- data/lib/heapinfo.rb +75 -0
- data/lib/heapinfo/arena.rb +193 -0
- data/lib/heapinfo/chunk.rb +96 -0
- data/lib/heapinfo/chunks.rb +20 -0
- data/lib/heapinfo/dumper.rb +204 -0
- data/lib/heapinfo/ext/string.rb +29 -0
- data/lib/heapinfo/helper.rb +123 -0
- data/lib/heapinfo/libc.rb +46 -0
- data/lib/heapinfo/nil.rb +27 -0
- data/lib/heapinfo/process.rb +205 -0
- data/lib/heapinfo/segment.rb +34 -0
- data/lib/heapinfo/tools/get_arena.c +29 -0
- data/lib/heapinfo/version.rb +3 -0
- data/spec/chunk_spec.rb +40 -0
- data/spec/chunks_spec.rb +25 -0
- data/spec/dumper_spec.rb +79 -0
- data/spec/files/32bit_maps +23 -0
- data/spec/files/64bit_maps +29 -0
- data/spec/files/victim.cpp +32 -0
- data/spec/helper_spec.rb +73 -0
- data/spec/nil_spec.rb +15 -0
- data/spec/process_spec.rb +157 -0
- data/spec/spec_helper.rb +98 -0
- data/spec/string_spec.rb +18 -0
- metadata +81 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1ba7cc3de8a173a616d298c30836fa0bb5a2800a
|
4
|
+
data.tar.gz: 792feedd7e5a6deea7b956082150cb7b4609d887
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d97610801e28487dd52e3e100558e746e8d23784a7925be2f99e4e35dd101c9976cbb526e1f18c18105b4a6bef2b00a305139f5406bd02021cedb75df6394749
|
7
|
+
data.tar.gz: 434774059d8ff5f5fcebc1fb81788a8fd9e78856f295c5bbfd3a045ae1b8b28cfa5237d4bd5cd330f71b319e1333efb65cfaffc557263721836a90f48687480f
|
data/README.md
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
[![Build Status](https://travis-ci.org/david942j/heapinfo.svg?branch=master)](https://travis-ci.org/david942j/heapinfo)
|
2
|
+
[![Code Climate](https://codeclimate.com/github/david942j/heapinfo/badges/gpa.svg)](https://codeclimate.com/github/david942j/heapinfo)
|
3
|
+
[![Issue Count](https://codeclimate.com/github/david942j/heapinfo/badges/issue_count.svg)](https://codeclimate.com/github/david942j/heapinfo)
|
4
|
+
[![Test Coverage](https://codeclimate.com/github/david942j/heapinfo/badges/coverage.svg)](https://codeclimate.com/github/david942j/heapinfo/coverage)
|
5
|
+
[![Inline docs](https://inch-ci.org/github/david942j/heapinfo.svg?branch=master)](https://inch-ci.org/github/david942j/heapinfo)
|
6
|
+
[![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](http://choosealicense.com/licenses/mit/)
|
7
|
+
|
8
|
+
## HeapInfo
|
9
|
+
As pwn lovers, while playing CTF with heap exploitation, we always need a debugger (e.g. gdb) for tracking memory layout. But we don't really need a debugger if we just want to see whether the heap layout same as our imagine or not. Hope this small tool helps us exploit easier ;).
|
10
|
+
|
11
|
+
Implement with ruby because I love ruby :P. But might also implement with Python (if no others did) in the future.
|
12
|
+
|
13
|
+
If you prefer [pwntools](https://github.com/Gallopsled/pwntools) for exploiting, you can still use **HeapInfo** in irb/pry as a small debugger.
|
14
|
+
|
15
|
+
Any suggestion of features or bug issues is welcome.
|
16
|
+
|
17
|
+
Relation works are [pwntools-ruby](https://github.com/peter50216/pwntools-ruby) and [gdbpwn](https://github.com/scwuaptx/Pwngdb).
|
18
|
+
|
19
|
+
## Install
|
20
|
+
**HeapInfo** is still under developing for more features, so the version might change frequently :p
|
21
|
+
|
22
|
+
```
|
23
|
+
$ gem install heapinfo
|
24
|
+
```
|
25
|
+
|
26
|
+
## Features
|
27
|
+
* Can use in your ruby exploit script or in irb/pry
|
28
|
+
* **HeapInfo** works when the `victim` is being traced! i.e. you can use ltrace/strace/gdb and **HeapInfo** simultaneously!
|
29
|
+
* `dump` - dump arbitrarily address memory.
|
30
|
+
* `layouts` - show the current bin layouts, very useful for heap exploitation.
|
31
|
+
* `x` - Provide gdb-like commands.
|
32
|
+
* `find` - Provide gdb-like commands.
|
33
|
+
* More features and details can be found in [RDoc](http://www.rubydoc.info/github/david942j/heapinfo/master/)
|
34
|
+
|
35
|
+
## Usage
|
36
|
+
|
37
|
+
#### Load
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
require 'heapinfo'
|
41
|
+
# ./victim is running
|
42
|
+
h = heapinfo('victim')
|
43
|
+
# or use h = heapinfo(20568) to prevent multi processes exist
|
44
|
+
|
45
|
+
# will present simple info when loading:
|
46
|
+
# Program: /home/heapinfo/victim PID: 20568
|
47
|
+
# victim base @ 0x400000
|
48
|
+
# [heap] base @ 0x11cc000
|
49
|
+
# [stack] base @ 0x7fff2b244000
|
50
|
+
# libc-2.19.so base @ 0x7f892a63a000
|
51
|
+
# ld-2.19.so base @ 0x7f892bee6000
|
52
|
+
|
53
|
+
# query segments' info
|
54
|
+
"%#x" % h.libc.base
|
55
|
+
# => "0x7f892a63a000"
|
56
|
+
h.libc.name
|
57
|
+
# => "/lib/x86_64-linux-gnu/libc-2.19.so"
|
58
|
+
"%#x" % h.elf.base
|
59
|
+
# => "0x400000"
|
60
|
+
"%#x" % h.heap.base
|
61
|
+
# => "0x11cc000"
|
62
|
+
```
|
63
|
+
|
64
|
+
NOTICE: While the process is not found, most methods will return `nil`. One way to prevent some error happend is to wrapper methods within `debug`, the block will be ignored while doing remote exploitation.
|
65
|
+
|
66
|
+
```ruby
|
67
|
+
h = heapinfo('remote')
|
68
|
+
# Process not found
|
69
|
+
h.pid # nil
|
70
|
+
h.debug {
|
71
|
+
fail unless leak_libc_base == h.libc.base
|
72
|
+
# wrapper with `debug` so that no error will be raised when pwning remote service
|
73
|
+
}
|
74
|
+
```
|
75
|
+
|
76
|
+
#### Dump
|
77
|
+
Query content of specific address.
|
78
|
+
|
79
|
+
NOTICE: you MUST have permission of attaching a program, otherwise dump will fail.
|
80
|
+
|
81
|
+
i.e. `/proc/sys/kernel/yama/ptrace_scope` set to 0 or run as root.
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
h.debug {
|
85
|
+
p h.dump(:libc, 8)
|
86
|
+
# => "\x7FELF\x02\x01\x01\x00"
|
87
|
+
p h.dump(:heap, 16)
|
88
|
+
# => "\x00\x00\x00\x00\x00\x00\x00\x00\x31\x00\x00\x00\x00\x00\x00\x00"
|
89
|
+
p h.dump('heap+0x30, 16') # support offset!
|
90
|
+
# => "\x00\x00\x00\x00\x00\x00\x00\x00\x81\x00\x00\x00\x00\x00\x00\x00"
|
91
|
+
p h.dump(:elf, 8)
|
92
|
+
# => "\x7FELF\x02\x01\x01\x00"
|
93
|
+
p h.dump(0x400000, 8) # or simply give addr
|
94
|
+
# => "\x7FELF\x02\x01\x01\x00"
|
95
|
+
}
|
96
|
+
# invalid examples:
|
97
|
+
# h.dump('meow') # no such segment
|
98
|
+
# h.dump('heap-1, 64') # not support `-`
|
99
|
+
```
|
100
|
+
|
101
|
+
#### layouts
|
102
|
+
```ruby
|
103
|
+
h.layouts :fastbin
|
104
|
+
```
|
105
|
+
![fastbin layouts](https://github.com/david942j/heapinfo/blob/master/examples/fastbin_layouts.png?raw=true)
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
h.layouts :unsorted_bin, :smallbin
|
109
|
+
```
|
110
|
+
![smallbin layouts](https://github.com/david942j/heapinfo/blob/master/examples/unsorted_smallbin_layouts.png?raw=true)
|
111
|
+
|
112
|
+
#### x - gdb-like command
|
113
|
+
```ruby
|
114
|
+
h.x 8, :heap
|
115
|
+
```
|
116
|
+
![x/8gx](https://github.com/david942j/heapinfo/blob/master/examples/x8_heap.png?raw=true)
|
117
|
+
|
118
|
+
#### find - gdb-like command
|
119
|
+
Provide a searcher of memory, easier to use than in (naive) gdb.
|
120
|
+
|
121
|
+
Support search integer, string, and even regular expression.
|
122
|
+
|
123
|
+
```ruby
|
124
|
+
h.find(/E.F/, 0x400000, 4)
|
125
|
+
# => 4194305 # 0x400001
|
126
|
+
h.find(/E.F/, 0x400000, 3)
|
127
|
+
# => nil
|
128
|
+
sh_offset = h.find('/bin/sh', :libc) - h.libc.base
|
129
|
+
# => 1559771 # 0x17ccdb
|
130
|
+
```
|
data/lib/heapinfo.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
# Basic requirements from standard library
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
# HeapInfo - an interactive debugger for heap exploitation
|
5
|
+
#
|
6
|
+
# HeapInfo makes pwning life easier with ruby style memory dumper.
|
7
|
+
# Easy to show bin(s) layouts, or dump memory for checking whether exploit (will) works.
|
8
|
+
# HeapInfo can be used with ltrace/strace/gdb simultaneously since it not use any ptrace.
|
9
|
+
#
|
10
|
+
# @author david942j
|
11
|
+
module HeapInfo
|
12
|
+
# Directory for writing some tmp files when working,
|
13
|
+
# make sure /tmp is writable
|
14
|
+
TMP_DIR = '/tmp/.heapinfo'
|
15
|
+
|
16
|
+
# Directory for caching files.
|
17
|
+
# e.g. HeapInfo will record main_arena_offset for glibc(s)
|
18
|
+
CACHE_DIR = '~/.cache/heapinfo'
|
19
|
+
|
20
|
+
FileUtils.mkdir_p(TMP_DIR)
|
21
|
+
FileUtils.mkdir_p(CACHE_DIR)
|
22
|
+
|
23
|
+
# Entry point for using HeapInfo.
|
24
|
+
# Show segments info of the process after loaded
|
25
|
+
# @param [String, Fixnum] prog The program name of victim. If a number is given, seem as pid (useful when multi-processes exist)
|
26
|
+
# @param [Hash] options Give library's file name.
|
27
|
+
# @option options [String, Regexp] :libc file name of glibc, default is <tt>/libc[^\w]/</tt>
|
28
|
+
# @option options [String, Regexp] :ld file name of dynamic linker/loader, default is <tt>/\/ld-.+\.so/</tt>
|
29
|
+
# @return [HeapInfo::Process] The object for further usage
|
30
|
+
# @example
|
31
|
+
# h = heapinfo './victim'
|
32
|
+
# # outputs:
|
33
|
+
# # Program: /home/heapinfo/victim PID: 20568
|
34
|
+
# # victim base @ 0x400000
|
35
|
+
# # [heap] base @ 0x11cc000
|
36
|
+
# # [stack] base @ 0x7fff2b244000
|
37
|
+
# # libc-2.19.so base @ 0x7f892a63a000
|
38
|
+
# # ld-2.19.so base @ 0x7f892bee6000
|
39
|
+
# p h.libc.name
|
40
|
+
# # => "/lib/x86_64-linux-gnu/libc-2.19.so"
|
41
|
+
# p h.ld.name
|
42
|
+
# # => "/lib/x86_64-linux-gnu/ld-2.19.so"
|
43
|
+
#
|
44
|
+
# @example
|
45
|
+
# h = heapinfo(27605, libc: 'libc.so.6', ld: 'ld-linux-x86-64.so.2')
|
46
|
+
# # pid 27605 is run by custom loader
|
47
|
+
# p h.libc.name
|
48
|
+
# # => "/home/heapinfo/libc.so.6"
|
49
|
+
# p h.ld.name
|
50
|
+
# # => "/home/heapinfo/ld-linux-x86-64.so.2"
|
51
|
+
def self.heapinfo(prog, options = {})
|
52
|
+
h = HeapInfo::Process.new(prog, options)
|
53
|
+
puts h
|
54
|
+
h
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Alias method of #HeapInfo::heapinfo for global usage
|
59
|
+
# @return [HeapInfo::Process]
|
60
|
+
# @param [Mixed] args see #HeapInfo::heapinfo for more information
|
61
|
+
def heapinfo(*args)
|
62
|
+
::HeapInfo::heapinfo(*args)
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
require 'heapinfo/helper'
|
67
|
+
require 'heapinfo/nil'
|
68
|
+
require 'heapinfo/process'
|
69
|
+
require 'heapinfo/segment'
|
70
|
+
require 'heapinfo/libc'
|
71
|
+
require 'heapinfo/chunk'
|
72
|
+
require 'heapinfo/chunks'
|
73
|
+
require 'heapinfo/arena'
|
74
|
+
require 'heapinfo/dumper'
|
75
|
+
require 'heapinfo/ext/string.rb'
|
@@ -0,0 +1,193 @@
|
|
1
|
+
module HeapInfo
|
2
|
+
class Arena
|
3
|
+
attr_reader :fastbin, :unsorted_bin, :smallbin, :top_chunk
|
4
|
+
# attr_reader :largebin, :last_remainder
|
5
|
+
|
6
|
+
# Instantiate a <tt>HeapInfo::Arena</tt> object
|
7
|
+
#
|
8
|
+
# @param [Integer] base Base address of arena.
|
9
|
+
# @param [Integer] bits Either 64 or 32
|
10
|
+
# @param [Proc] dumper For dump more data
|
11
|
+
def initialize(base, bits, dumper)
|
12
|
+
@base, @dumper = base, dumper
|
13
|
+
@size_t = bits / 8
|
14
|
+
reload
|
15
|
+
end
|
16
|
+
|
17
|
+
# Refresh all attributes
|
18
|
+
# Retrive data using <tt>@dumper</tt>, load bins, top chunk etc.
|
19
|
+
# @return [HeapInfo::Arena] self
|
20
|
+
def reload
|
21
|
+
top_ptr = Helper.unpack(size_t, @dumper.call(@base + 8 + size_t * 10, size_t))
|
22
|
+
@fastbin = []
|
23
|
+
return self if top_ptr == 0 # arena not init yet
|
24
|
+
@top_chunk = Chunk.new size_t, top_ptr, @dumper
|
25
|
+
@fastbin = Array.new(7) do |idx|
|
26
|
+
f = Fastbin.new(size_t, @base + 8 - size_t * 2 + size_t * idx, @dumper, head: true)
|
27
|
+
f.index = idx
|
28
|
+
f
|
29
|
+
end
|
30
|
+
@unsorted_bin = UnsortedBin.new(size_t, @base + 8 + size_t * 10, @dumper, head: true)
|
31
|
+
@smallbin = Array.new(55) do |idx|
|
32
|
+
s = Smallbin.new(size_t, @base + 8 + size_t * (26 + 2 * idx), @dumper, head: true)
|
33
|
+
s.index = idx
|
34
|
+
s
|
35
|
+
end
|
36
|
+
self
|
37
|
+
end
|
38
|
+
|
39
|
+
# Pretty dump of bins layouts.
|
40
|
+
#
|
41
|
+
# @param [Symbol] args Bin type(s) you want to see.
|
42
|
+
# @return [String] Bin layouts that wrapper with color codes.
|
43
|
+
# @example
|
44
|
+
# puts h.libc.main_arena.layouts :fastbin, :unsorted_bin, :smallbin
|
45
|
+
def layouts(*args)
|
46
|
+
res = ''
|
47
|
+
res += fastbin.map(&:inspect).join if args.include? :fastbin
|
48
|
+
res += unsorted_bin.inspect if args.include? :unsorted_bin
|
49
|
+
res += smallbin.map(&:inspect).join if args.include? :smallbin
|
50
|
+
res
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
attr_reader :size_t
|
55
|
+
end
|
56
|
+
|
57
|
+
class Fastbin < Chunk
|
58
|
+
attr_reader :fd
|
59
|
+
attr_accessor :index
|
60
|
+
|
61
|
+
# Instantiate a <tt>HeapInfo::Fastbin</tt> object
|
62
|
+
#
|
63
|
+
# @param [Mixed] args See <tt>HeapInfo::Chunk</tt> for more information.
|
64
|
+
def initialize(*args)
|
65
|
+
super
|
66
|
+
@fd = Helper.unpack(size_t, @data[0, @size_t])
|
67
|
+
end
|
68
|
+
|
69
|
+
# Mapping index of fastbin to chunk size
|
70
|
+
# @return [Integer] size
|
71
|
+
def idx_to_size
|
72
|
+
index * size_t * 2 + size_t * 4
|
73
|
+
end
|
74
|
+
|
75
|
+
# For pretty inspect
|
76
|
+
# @return [String] Title with color codes
|
77
|
+
def title
|
78
|
+
"%s%s: " % [Helper.color(Helper.class_name(self), sev: :bin), index.nil? ? nil : "[#{Helper.color("%#x" % idx_to_size)}]"]
|
79
|
+
end
|
80
|
+
|
81
|
+
def inspect
|
82
|
+
title + list.map do |ptr|
|
83
|
+
next "(#{ptr})\n" if ptr.is_a? Symbol
|
84
|
+
next " => (nil)\n" if ptr.nil?
|
85
|
+
" => %s" % Helper.color("%#x" % ptr)
|
86
|
+
end.join
|
87
|
+
end
|
88
|
+
|
89
|
+
# @return [Array<Integer, Symbol, NilClass>] single link list of <tt>fd</tt> chain.
|
90
|
+
# Last element will be:
|
91
|
+
# - <tt>:loop</tt> if loop detectded
|
92
|
+
# - <tt>:invalid</tt> invalid address detected
|
93
|
+
# - <tt>nil</tt> end with zero address (normal case)
|
94
|
+
def list
|
95
|
+
dup = {}
|
96
|
+
ptr = @fd
|
97
|
+
ret = []
|
98
|
+
while ptr != 0
|
99
|
+
ret << ptr
|
100
|
+
return ret << :loop if dup[ptr]
|
101
|
+
dup[ptr] = true
|
102
|
+
ptr = fd_of(ptr)
|
103
|
+
return ret << :invalid if ptr.nil?
|
104
|
+
end
|
105
|
+
ret << nil
|
106
|
+
end
|
107
|
+
|
108
|
+
def fd_of(ptr)
|
109
|
+
addr_of(ptr, 2)
|
110
|
+
end
|
111
|
+
|
112
|
+
def bk_of(ptr)
|
113
|
+
addr_of(ptr, 3)
|
114
|
+
end
|
115
|
+
|
116
|
+
private
|
117
|
+
def addr_of(ptr, offset)
|
118
|
+
t = dump(ptr + size_t * offset, size_t)
|
119
|
+
return nil if t.nil?
|
120
|
+
Helper.unpack(size_t, t)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
class UnsortedBin < Fastbin
|
125
|
+
attr_reader :bk
|
126
|
+
def initialize(*args)
|
127
|
+
super
|
128
|
+
@bk = Helper.unpack(size_t, @data[@size_t, @size_t])
|
129
|
+
end
|
130
|
+
|
131
|
+
# size: at most extend size
|
132
|
+
# size=2: bk, bk, bin, fd, fd
|
133
|
+
def inspect(size: 2)
|
134
|
+
list = link_list(size)
|
135
|
+
return '' if list.size <= 1 and Helper.class_name(self) != 'UnsortedBin' # bad..
|
136
|
+
title + pretty_list(list) + "\n"
|
137
|
+
end
|
138
|
+
|
139
|
+
def pretty_list(list)
|
140
|
+
center = nil
|
141
|
+
list.map.with_index do |c, idx|
|
142
|
+
next center = Helper.color("[self]", sev: :bin) if c == @base
|
143
|
+
fwd = fd_of(c)
|
144
|
+
next "%s(invalid)" % Helper.color("%#x" % c) if fwd.nil? # invalid c
|
145
|
+
bck = bk_of(c)
|
146
|
+
if center.nil? # bk side
|
147
|
+
next Helper.color("%s%s" % [
|
148
|
+
Helper.color("%#x" % c),
|
149
|
+
fwd == list[idx+1] ? nil : "(%#x)" % fwd,
|
150
|
+
])
|
151
|
+
else #fd side
|
152
|
+
next Helper.color("%s%s" % [
|
153
|
+
bck == list[idx-1] ? nil : "(%#x)" % bck,
|
154
|
+
Helper.color("%#x" % c),
|
155
|
+
])
|
156
|
+
end
|
157
|
+
end.join(" === ")
|
158
|
+
end
|
159
|
+
|
160
|
+
def link_list(expand_size)
|
161
|
+
list = [@base]
|
162
|
+
# fd
|
163
|
+
work = Proc.new do |ptr, nxt, append|
|
164
|
+
sz = 0
|
165
|
+
dup = {}
|
166
|
+
while ptr != @base and sz < expand_size
|
167
|
+
append.call ptr
|
168
|
+
break if ptr.nil? # invalid pointer
|
169
|
+
break if dup[ptr] # looped
|
170
|
+
dup[ptr] = true
|
171
|
+
ptr = self.send(nxt, ptr)
|
172
|
+
sz += 1
|
173
|
+
end
|
174
|
+
end
|
175
|
+
work.call(@fd, :fd_of, lambda{|ptr| list << ptr})
|
176
|
+
work.call(@bk, :bk_of, lambda{|ptr| list.unshift ptr})
|
177
|
+
list
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
class Smallbin < UnsortedBin
|
182
|
+
|
183
|
+
# Mapping index of smallbin to chunk size
|
184
|
+
# @return [Integer] size
|
185
|
+
def idx_to_size
|
186
|
+
index * size_t * 2 + size_t * 18
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
# class Largebin < Smallbin
|
191
|
+
# attr_accessor :fd_nextsize, :bk_nextsize
|
192
|
+
# end
|
193
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module HeapInfo
|
2
|
+
# The object of a heap chunk
|
3
|
+
class Chunk
|
4
|
+
# @return [Integer] 4 or 8 according to 32bit or 64bit, respectively.
|
5
|
+
attr_reader :size_t
|
6
|
+
# @return [Integer] previous chunk size
|
7
|
+
attr_reader :prev_size
|
8
|
+
# @return [String] chunk data
|
9
|
+
attr_reader :data
|
10
|
+
# @return [Integer] Base address of this chunk
|
11
|
+
attr_reader :base
|
12
|
+
|
13
|
+
# Instantiate a <tt>HeapInfo::Chunk</tt> object
|
14
|
+
#
|
15
|
+
# @param [Integer] size_t 4 or 8
|
16
|
+
# @param [Integer] base Start address of this chunk
|
17
|
+
# @param [Proc] dumper For dump more information of this chunk
|
18
|
+
# @param [Boolean] head For specific if is fake chunk in <tt>arena</tt>. If <tt>head</tt> is <tt>true</tt>, will not load <tt>size</tt> and <tt>prev_size</tt> (since it's meaningless)
|
19
|
+
# @example
|
20
|
+
# HeapInfo::Chunk.new 8, 0x602000, lambda{|addr, len| [0,0x21, 0xda4a].pack("Q*")[addr-0x602000, len]}
|
21
|
+
# # create a chunk with chunk size 0x21
|
22
|
+
def initialize(size_t, base, dumper, head: false)
|
23
|
+
fail unless [4, 8].include? size_t
|
24
|
+
self.class.send(:define_method, :dump){|*args| dumper.call(*args)}
|
25
|
+
@size_t = size_t
|
26
|
+
@base = base
|
27
|
+
sz = dump(@base, size_t * 2)
|
28
|
+
if head # no need to read size if is bin
|
29
|
+
return @data = dump(@base + size_t * 2, size_t * 4)
|
30
|
+
end
|
31
|
+
@prev_size = Helper.unpack(size_t, sz[0, size_t])
|
32
|
+
@size = Helper.unpack(size_t, sz[size_t..-1])
|
33
|
+
r_size = [size - size_t * 2, size_t * 4].min # don't read too much data
|
34
|
+
r_size = [r_size, 0].max # prevent negative size
|
35
|
+
@data = dump(@base + size_t * 2, r_size)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Hook <tt>#to_s</tt> for pretty printing
|
39
|
+
# @return [String]
|
40
|
+
def to_s
|
41
|
+
ret = Helper.color("#<%s:%#x>\n" % [self.class.to_s, @base], sev: :klass) +
|
42
|
+
"flags = [#{flags.map{|f|Helper.color(":#{f}", sev: :sym)}.join(',')}]\n" +
|
43
|
+
"size = #{Helper.color "%#x" % size} (#{bintype})\n"
|
44
|
+
ret += "prev_size = #{Helper.color "%#x" % @prev_size}\n" unless flags.include? :prev_inuse
|
45
|
+
ret += "data = #{Helper.color @data.inspect}#{'...' if @data.length < size - size_t * 2}\n"
|
46
|
+
ret
|
47
|
+
end
|
48
|
+
|
49
|
+
# The chunk flags record in low three bits of size
|
50
|
+
# @return [Array<Symbol>] flags of chunk
|
51
|
+
# @example
|
52
|
+
# c = [0, 0x25].pack("Q*").to_chunk
|
53
|
+
# c.flags
|
54
|
+
# # [:non_main_arena, :prev_inuse]
|
55
|
+
def flags
|
56
|
+
mask = @size - size
|
57
|
+
flag = []
|
58
|
+
flag << :non_main_arena unless mask & 4 == 0
|
59
|
+
flag << :mmapped unless mask & 2 == 0
|
60
|
+
flag << :prev_inuse unless mask & 1 == 0
|
61
|
+
flag
|
62
|
+
end
|
63
|
+
|
64
|
+
# Size of chunk
|
65
|
+
# @return [Integer] The chunk size without flag masks
|
66
|
+
def size
|
67
|
+
@size & -8
|
68
|
+
end
|
69
|
+
|
70
|
+
# Bin type of this chunk
|
71
|
+
# @return [Symbol] Bin type is simply determined according to <tt>#size</tt>
|
72
|
+
# @example
|
73
|
+
# [c.size, c.size_t]
|
74
|
+
# # => [80, 8]
|
75
|
+
# c.bintype
|
76
|
+
# # => :fast
|
77
|
+
# @example
|
78
|
+
# [c.size, c.size_t]
|
79
|
+
# # => [80, 4]
|
80
|
+
# c.bintype
|
81
|
+
# # => :small
|
82
|
+
# @example
|
83
|
+
# c.size
|
84
|
+
# # => 135168
|
85
|
+
# c.bintype
|
86
|
+
# # => :mmap
|
87
|
+
def bintype
|
88
|
+
sz = size
|
89
|
+
return :unknown if sz < @size_t * 4
|
90
|
+
return :fast if sz <= @size_t * 16
|
91
|
+
return :small if sz <= @size_t * 0x7e
|
92
|
+
return :large if sz <= @size_t * 0x3ffe # is this correct?
|
93
|
+
return :mmap
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|