heapinfo 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
[](https://travis-ci.org/david942j/heapinfo)
|
2
|
+
[](https://codeclimate.com/github/david942j/heapinfo)
|
3
|
+
[](https://codeclimate.com/github/david942j/heapinfo)
|
4
|
+
[](https://codeclimate.com/github/david942j/heapinfo/coverage)
|
5
|
+
[](https://inch-ci.org/github/david942j/heapinfo)
|
6
|
+
[](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
|
+

|
106
|
+
|
107
|
+
```ruby
|
108
|
+
h.layouts :unsorted_bin, :smallbin
|
109
|
+
```
|
110
|
+

|
111
|
+
|
112
|
+
#### x - gdb-like command
|
113
|
+
```ruby
|
114
|
+
h.x 8, :heap
|
115
|
+
```
|
116
|
+

|
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
|