heapinfo 0.1.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +45 -23
- data/lib/heapinfo.rb +2 -2
- data/lib/heapinfo/arena.rb +6 -5
- data/lib/heapinfo/cache.rb +57 -49
- data/lib/heapinfo/dumper.rb +28 -66
- data/lib/heapinfo/glibc/free.rb +2 -2
- data/lib/heapinfo/helper.rb +144 -108
- data/lib/heapinfo/libc.rb +7 -5
- data/lib/heapinfo/process.rb +66 -39
- data/lib/heapinfo/process_info.rb +14 -5
- data/lib/heapinfo/version.rb +1 -1
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 22b8757e3a48390494b019943a52754a154051c3
|
4
|
+
data.tar.gz: 4b4af3bf4d67e0ca35b9f5f8feb537499565f56e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3872db0f32aaa2fbbedcca004163cb0b5181495cc81605a6587ef5c6dfcebf60f43209146aeda541c716cbdb6bca2bd2770927643cf3bfe50ade2cf7b709a282
|
7
|
+
data.tar.gz: 82a57d0254a650ba531b5f00db676acc3ef4635cd0dbcdc34877f5a09ea8b369667377d96456e466e0a20ad2a6fb3dcb3737cb05966c95cd2483315bb24b7a8c
|
data/README.md
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
[![Build Status](https://travis-ci.org/david942j/heapinfo.svg?branch=master)](https://travis-ci.org/david942j/heapinfo)
|
2
|
-
![](http://ruby-gem-downloads-badge.herokuapp.com/heapinfo?type=total)
|
2
|
+
[![Downloads](http://ruby-gem-downloads-badge.herokuapp.com/heapinfo?type=total)](https://rubygems.org/gems/heapinfo)
|
3
3
|
[![Code Climate](https://codeclimate.com/github/david942j/heapinfo/badges/gpa.svg)](https://codeclimate.com/github/david942j/heapinfo)
|
4
4
|
[![Issue Count](https://codeclimate.com/github/david942j/heapinfo/badges/issue_count.svg)](https://codeclimate.com/github/david942j/heapinfo)
|
5
5
|
[![Test Coverage](https://codeclimate.com/github/david942j/heapinfo/badges/coverage.svg)](https://codeclimate.com/github/david942j/heapinfo/coverage)
|
@@ -7,7 +7,7 @@
|
|
7
7
|
[![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](http://choosealicense.com/licenses/mit/)
|
8
8
|
|
9
9
|
## HeapInfo
|
10
|
-
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
|
10
|
+
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 gdb if we want to see whether the heap layout same as our imagine or not. Hope this small tool helps us exploit easier ;).
|
11
11
|
|
12
12
|
Implement with ruby because I love ruby :P. But might also implement with Python (if no others did) in the future.
|
13
13
|
|
@@ -15,7 +15,7 @@ If you prefer [pwntools](https://github.com/Gallopsled/pwntools) for exploiting,
|
|
15
15
|
|
16
16
|
Any suggestion of features or bug issues is welcome.
|
17
17
|
|
18
|
-
|
18
|
+
Related works are [pwntools-ruby](https://github.com/peter50216/pwntools-ruby) and [gdbpwn](https://github.com/scwuaptx/Pwngdb).
|
19
19
|
|
20
20
|
## Install
|
21
21
|
**HeapInfo** is still under developing for more features, so the version might change frequently :p
|
@@ -25,14 +25,18 @@ $ gem install heapinfo
|
|
25
25
|
```
|
26
26
|
|
27
27
|
## Features
|
28
|
-
* Can use in your ruby exploit script or in irb/pry
|
29
|
-
* **HeapInfo** works when
|
30
|
-
* `dump` -
|
31
|
-
* `layouts` -
|
28
|
+
* Can use in your ruby exploit script or in irb/pry.
|
29
|
+
* **HeapInfo** works when `victim` is being traced! i.e. you can use ltrace/strace/gdb and **HeapInfo** simultaneously!
|
30
|
+
* `dump` - Dump arbitrarily address memory.
|
31
|
+
* `layouts` - Show the current bin layouts, very useful for heap exploitation.
|
32
|
+
* `offset` - Show the offset between given address and segment. Very useful for calculating relative offset.
|
32
33
|
* `x` - Provide gdb-like commands.
|
33
34
|
* `find` - Provide gdb-like commands.
|
34
35
|
* More features and details can be found in [RDoc](http://www.rubydoc.info/github/david942j/heapinfo/master/)
|
35
36
|
|
37
|
+
### Under developing
|
38
|
+
* `free` - Show what will happend when `free` an address.
|
39
|
+
|
36
40
|
## Usage
|
37
41
|
|
38
42
|
#### Load
|
@@ -68,48 +72,60 @@ NOTICE: While the process is not found, most methods will return `nil`. One way
|
|
68
72
|
h = heapinfo('remote')
|
69
73
|
# Process not found
|
70
74
|
h.pid # nil
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
}
|
75
|
+
|
76
|
+
# wrapper with `debug` so that no error will be raised when pwning remote service
|
77
|
+
h.debug { fail unless leak_libc_base == h.libc.base }
|
75
78
|
```
|
76
79
|
|
77
80
|
#### Dump
|
78
81
|
Query content of specific address.
|
79
82
|
|
80
|
-
NOTICE:
|
83
|
+
NOTICE: You MUST have permission of attaching a program, otherwise dump will fail.
|
81
84
|
|
82
85
|
i.e. `/proc/sys/kernel/yama/ptrace_scope` set to 0 or run as root.
|
83
86
|
|
84
87
|
```ruby
|
85
|
-
h.debug
|
88
|
+
h.debug do
|
86
89
|
p h.dump(:libc, 8)
|
87
90
|
# => "\x7FELF\x02\x01\x01\x00"
|
88
91
|
p h.dump(:heap, 16)
|
89
92
|
# => "\x00\x00\x00\x00\x00\x00\x00\x00\x31\x00\x00\x00\x00\x00\x00\x00"
|
90
|
-
p h.dump('heap+0x30, 16
|
93
|
+
p h.dump('heap+0x30', 16) # support offset!
|
91
94
|
# => "\x00\x00\x00\x00\x00\x00\x00\x00\x81\x00\x00\x00\x00\x00\x00\x00"
|
92
|
-
p h.dump(
|
95
|
+
p h.dump('heap+0x30 * 3 + 0x8', 16) # and even complex formula!
|
96
|
+
# => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
97
|
+
p h.dump(:program, 8)
|
93
98
|
# => "\x7FELF\x02\x01\x01\x00"
|
94
|
-
p h.dump(0x400000, 8) # or simply give
|
99
|
+
p h.dump(0x400000, 8) # or simply give address
|
95
100
|
# => "\x7FELF\x02\x01\x01\x00"
|
96
|
-
|
97
|
-
|
101
|
+
end
|
102
|
+
|
103
|
+
# invalid example:
|
98
104
|
# h.dump('meow') # no such segment
|
99
|
-
# h.dump('heap-1, 64') # not support `-`
|
100
105
|
```
|
101
106
|
|
102
107
|
#### layouts
|
103
108
|
```ruby
|
104
|
-
h.layouts :
|
109
|
+
h.layouts :fast
|
105
110
|
```
|
106
111
|
![fastbin layouts](https://github.com/david942j/heapinfo/blob/master/examples/fastbin_layouts.png?raw=true)
|
107
112
|
|
108
113
|
```ruby
|
109
|
-
h.layouts :
|
114
|
+
h.layouts :unsorted, :small
|
110
115
|
```
|
111
116
|
![smallbin layouts](https://github.com/david942j/heapinfo/blob/master/examples/unsorted_smallbin_layouts.png?raw=true)
|
112
117
|
|
118
|
+
#### offset
|
119
|
+
```ruby
|
120
|
+
h.offset(0x7fda86fe8670)
|
121
|
+
# 0xf6670 after libc
|
122
|
+
h.offset(0x1839cd0, :heap) # specific segment name
|
123
|
+
# 0x20cd0 after heap
|
124
|
+
h.offset(0x1839cd0)
|
125
|
+
# 0x20cd0 after heap
|
126
|
+
```
|
127
|
+
![offset](https://github.com/david942j/heapinfo/blob/master/examples/offset.png?raw=true)
|
128
|
+
|
113
129
|
#### x - gdb-like command
|
114
130
|
```ruby
|
115
131
|
h.x 8, :heap
|
@@ -128,6 +144,12 @@ h.find(/E.F/, 0x400000, 4)
|
|
128
144
|
# => 4194305 # 0x400001
|
129
145
|
h.find(/E.F/, 0x400000, 3)
|
130
146
|
# => nil
|
131
|
-
|
132
|
-
#
|
147
|
+
h.offset(h.find('/bin/sh', :libc))
|
148
|
+
# 0x18c177 after libc
|
133
149
|
```
|
150
|
+
|
151
|
+
## Tests
|
152
|
+
**HeapInfo** currently only run tests on ubuntu, followings are tested glibc versions:
|
153
|
+
* libc-2.19
|
154
|
+
* libc-2.23
|
155
|
+
* libc-2.24
|
data/lib/heapinfo.rb
CHANGED
@@ -19,8 +19,8 @@ module HeapInfo
|
|
19
19
|
# @param [String, Fixnum] prog
|
20
20
|
# The program name of victim. If a number is given, seem as pid (useful when multi-processes exist).
|
21
21
|
# @param [Hash] options Give library's file name.
|
22
|
-
# @option options [String, Regexp] :libc file name of glibc, default is
|
23
|
-
# @option options [String, Regexp] :ld file name of dynamic linker/loader, default is
|
22
|
+
# @option options [String, Regexp] :libc file name of glibc, default is +/bc[^a-z]*\.so/+.
|
23
|
+
# @option options [String, Regexp] :ld file name of dynamic linker/loader, default is +/\/ld-.+\.so/+.
|
24
24
|
# @return [HeapInfo::Process] The object for further usage
|
25
25
|
# @example
|
26
26
|
# h = heapinfo './victim'
|
data/lib/heapinfo/arena.rb
CHANGED
@@ -13,7 +13,7 @@ module HeapInfo
|
|
13
13
|
attr_reader :smallbin
|
14
14
|
# @return [Integer] The +system_mem+ in arena.
|
15
15
|
attr_reader :system_mem
|
16
|
-
# attr_reader :largebin
|
16
|
+
# attr_reader :largebin
|
17
17
|
|
18
18
|
# Instantiate a {HeapInfo::Arena} object.
|
19
19
|
#
|
@@ -60,12 +60,13 @@ module HeapInfo
|
|
60
60
|
# @param [Symbol] args Bin type(s) you want to see.
|
61
61
|
# @return [String] Bin layouts that wrapper with color codes.
|
62
62
|
# @example
|
63
|
-
# puts h.libc.main_arena.layouts
|
63
|
+
# puts h.libc.main_arena.layouts(:fast, :unsorted, :small)
|
64
64
|
def layouts(*args)
|
65
|
+
args = args.map(&:to_s).join('|')
|
65
66
|
res = ''
|
66
|
-
res += fastbin.map(&:inspect).join if args.include?
|
67
|
-
res += unsorted_bin.inspect if args.include?
|
68
|
-
res += smallbin.map(&:inspect).join if args.include?
|
67
|
+
res += fastbin.map(&:inspect).join if args.include?('fast')
|
68
|
+
res += unsorted_bin.inspect if args.include?('unsort')
|
69
|
+
res += smallbin.map(&:inspect).join if args.include?('small')
|
69
70
|
res
|
70
71
|
end
|
71
72
|
|
data/lib/heapinfo/cache.rb
CHANGED
@@ -2,65 +2,73 @@ require 'digest'
|
|
2
2
|
module HeapInfo
|
3
3
|
# Self implment file-base cache manager.
|
4
4
|
#
|
5
|
-
# Values are recorded in files based on
|
5
|
+
# Values are recorded in files based on +Marshal+.
|
6
6
|
module Cache
|
7
7
|
# Directory for caching files.
|
8
|
-
# e.g. HeapInfo will record main_arena_offset for glibc(s)
|
8
|
+
# e.g. HeapInfo will record main_arena_offset for glibc(s).
|
9
9
|
CACHE_DIR = File.join(ENV['HOME'], '.cache/heapinfo')
|
10
10
|
|
11
|
-
#
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
11
|
+
# Define class methods.
|
12
|
+
module ClassMethods
|
13
|
+
# Get the key for store libc offsets.
|
14
|
+
#
|
15
|
+
# @param [String] libc_path The realpath to libc file.
|
16
|
+
# @return [String] The key for cache read/write.
|
17
|
+
def key_libc_offset(libc_path)
|
18
|
+
File.join('libc', Digest::MD5.hexdigest(IO.binread(libc_path)), 'offset')
|
19
|
+
end
|
18
20
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
21
|
+
# Write cache to file.
|
22
|
+
#
|
23
|
+
# @param [String] key In file path format, only accept +[\w/]+ to prevent horrible things.
|
24
|
+
# @param [Object] value +value+ will be stored with +Marshal#dump+.
|
25
|
+
# @return [Boolean] true
|
26
|
+
def write(key, value)
|
27
|
+
filepath = realpath(key)
|
28
|
+
FileUtils.mkdir_p(File.dirname(filepath))
|
29
|
+
IO.binwrite(filepath, Marshal.dump(value))
|
30
|
+
true
|
31
|
+
end
|
30
32
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
33
|
+
# Read cache from file.
|
34
|
+
#
|
35
|
+
# @param [String] key In file path format, only accept +[\w\/]+ to prevent horrible things.
|
36
|
+
# @return [Object, NilClass] Value that recorded, return +nil+ when cache miss.
|
37
|
+
def read(key)
|
38
|
+
filepath = realpath(key)
|
39
|
+
return unless File.file?(filepath)
|
40
|
+
Marshal.load(IO.binread(filepath))
|
41
|
+
rescue
|
42
|
+
nil # handle if file content invalid
|
43
|
+
end
|
42
44
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
end
|
45
|
+
# Clear the cache directory.
|
46
|
+
# @return [void]
|
47
|
+
def clear_all
|
48
|
+
FileUtils.rm_rf(CACHE_DIR)
|
49
|
+
end
|
49
50
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
51
|
+
private
|
52
|
+
|
53
|
+
# @return [void]
|
54
|
+
def init
|
55
|
+
FileUtils.mkdir_p(CACHE_DIR)
|
56
|
+
rescue
|
57
|
+
# To prevent ~/ is not writable.
|
58
|
+
send(:remove_const, :CACHE_DIR)
|
59
|
+
const_set(:CACHE_DIR, File.join(HeapInfo::TMP_DIR, '.cache/heapinfo'))
|
60
|
+
FileUtils.mkdir_p(CACHE_DIR)
|
61
|
+
end
|
58
62
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
+
# @param [String] key
|
64
|
+
# @return [String] Prepend with {HeapInfo::Cache::CACHE_DIR}.
|
65
|
+
def realpath(key)
|
66
|
+
raise ArgumentError, 'Invalid key(file path)' if key =~ %r{[^\w/]}
|
67
|
+
File.join(CACHE_DIR, key)
|
68
|
+
end
|
63
69
|
end
|
70
|
+
|
71
|
+
extend ClassMethods
|
64
72
|
init
|
65
73
|
end
|
66
74
|
end
|
data/lib/heapinfo/dumper.rb
CHANGED
@@ -48,22 +48,21 @@ module HeapInfo
|
|
48
48
|
#
|
49
49
|
# Details are in {HeapInfo:Process#x}.
|
50
50
|
# @param [Integer] count The number of result need to dump.
|
51
|
-
# @param [
|
52
|
-
# @
|
53
|
-
# @return [NilClass] The return value of +io.puts+.
|
51
|
+
# @param [Symbol, String, Integer] address The base address to be dumped.
|
52
|
+
# @return [void]
|
54
53
|
# @example
|
55
54
|
# x 3, 0x400000
|
56
55
|
# # 0x400000: 0x00010102464c457f 0x0000000000000000
|
57
56
|
# # 0x400010: 0x00000001003e0002
|
58
|
-
def x(count,
|
59
|
-
commands
|
57
|
+
def x(count, address)
|
58
|
+
commands = [address, count * size_t]
|
60
59
|
base = base_of(*commands)
|
61
60
|
res = dump(*commands).unpack(size_t == 4 ? 'L*' : 'Q*')
|
62
61
|
str = res.group_by.with_index { |_, i| i / (16 / size_t) }.map do |round, values|
|
63
|
-
|
62
|
+
Helper.hex(base + round * 16) + ":\t" +
|
64
63
|
values.map { |v| Helper.color(format("0x%0#{size_t * 2}x", v)) }.join("\t")
|
65
64
|
end.join("\n")
|
66
|
-
|
65
|
+
puts str
|
67
66
|
end
|
68
67
|
|
69
68
|
# Search a specific value/string/regexp in memory.
|
@@ -92,57 +91,6 @@ module HeapInfo
|
|
92
91
|
end
|
93
92
|
end
|
94
93
|
|
95
|
-
# Parse the dump command into +[base, offset, length]+
|
96
|
-
# @param [Array] args The command, see examples for more information
|
97
|
-
# @return [Array<Symbol, Integer>]
|
98
|
-
# +[base, offset, length]+, while +base+ can be a +Symbol+ or an +Integer+.
|
99
|
-
# +length+ has default value equals to +8+.
|
100
|
-
# @example
|
101
|
-
# HeapInfo::Dumper.parse_cmd([:heap, 32, 10])
|
102
|
-
# # [:heap, 32, 10]
|
103
|
-
# HeapInfo::Dumper.parse_cmd(['heap+0x10, 10'])
|
104
|
-
# # [:heap, 16, 10]
|
105
|
-
# HeapInfo::Dumper.parse_cmd(['heap+0x10'])
|
106
|
-
# # [:heap, 16, 8]
|
107
|
-
# HeapInfo::Dumper.parse_cmd([0x400000, 4])
|
108
|
-
# # [0x400000, 0, 4]
|
109
|
-
def self.parse_cmd(args)
|
110
|
-
args = split_cmd args
|
111
|
-
return :fail unless args.size == 3
|
112
|
-
len = args[2].nil? ? DUMP_BYTES : Integer(args[2])
|
113
|
-
offset = Integer(args[1])
|
114
|
-
base = args[0]
|
115
|
-
base = Helper.integer?(base) ? Integer(base) : base.delete(':').to_sym
|
116
|
-
[base, offset, len]
|
117
|
-
end
|
118
|
-
|
119
|
-
# Helper for +#parse_cmd+.
|
120
|
-
#
|
121
|
-
# Split commands to exactly three parts: +[base, offset, length]+.
|
122
|
-
# +length+ is +nil+ if not present.
|
123
|
-
# @param [Array] args
|
124
|
-
# @return [Array<String>] +[base, offset, length]+ in string expression.
|
125
|
-
# @example
|
126
|
-
# HeapInfo::Dumper.split_cmd([:heap, 32, 10])
|
127
|
-
# # ['heap', '32', '10']
|
128
|
-
# HeapInfo::Dumper.split_cmd([':heap+0x10, 10'])
|
129
|
-
# # [':heap', '0x10', '10']
|
130
|
-
# HeapInfo::Dumper.split_cmd([':heap+0x10'])
|
131
|
-
# # [':heap', '0x10', nil]
|
132
|
-
# HeapInfo::Dumper.split_cmd([0x400000, 4])
|
133
|
-
# # ['4194304', 0, '4']
|
134
|
-
def self.split_cmd(args)
|
135
|
-
args = args.join(',').delete(' ').split(',').reject(&:empty?) # 'heap, 100', 32 => 'heap', '100', '32'
|
136
|
-
return [] if args.empty?
|
137
|
-
if args[0].include? '+' # 'heap+0x1'
|
138
|
-
args.unshift(*args.shift.split('+', 2))
|
139
|
-
elsif args.size <= 2 # no offset given
|
140
|
-
args.insert(1, 0)
|
141
|
-
end
|
142
|
-
args << nil if args.size <= 2 # no length given
|
143
|
-
args[0, 3]
|
144
|
-
end
|
145
|
-
|
146
94
|
private
|
147
95
|
|
148
96
|
def need_permission
|
@@ -169,14 +117,28 @@ module HeapInfo
|
|
169
117
|
File.open(@filename)
|
170
118
|
end
|
171
119
|
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
120
|
+
# Get the base address and length.
|
121
|
+
#
|
122
|
+
# @param [Integer, Symbol, String] arg The base address, see examples.
|
123
|
+
# @param [Integer] len An integer.
|
124
|
+
# @example
|
125
|
+
# base_len_of(123, 321) #=> [123, 321]
|
126
|
+
# base_len_of(123) #=> [123, DUMP_BYTES]
|
127
|
+
# base_len_of(:heap, 10) #=> [0x603000, 10] # assume heap base @ 0x603000
|
128
|
+
# base_len_of('heap+0x30', 10) #=> [0x603030, 10]
|
129
|
+
# base_len_of('elf+0x3*2-1') #=> [0x400005, DUMP_BYTES]
|
130
|
+
def base_len_of(arg, len = DUMP_BYTES)
|
131
|
+
values = HeapInfo::ProcessInfo::EXPORT.map do |seg|
|
132
|
+
segment = @info.respond_to?(seg) && @info.send(seg)
|
133
|
+
[seg, segment.base] if segment.is_a?(Segment)
|
134
|
+
end.compact.to_h
|
135
|
+
base = case arg
|
136
|
+
when Integer then arg
|
137
|
+
when Symbol then values[arg]
|
138
|
+
when String then Helper.evaluate(arg, store: values)
|
139
|
+
end
|
140
|
+
raise ArgumentError, "Invalid base: #{arg.inspect}" unless base.is_a?(Integer) # invalid usage
|
141
|
+
[base, len]
|
180
142
|
end
|
181
143
|
|
182
144
|
def base_of(*args)
|
data/lib/heapinfo/glibc/free.rb
CHANGED
@@ -37,8 +37,8 @@ module HeapInfo
|
|
37
37
|
int_free_fast(av, ptr, size)
|
38
38
|
elsif !chunk_is_mmapped(ptr) # Though this has been checked in #libc_free
|
39
39
|
int_free_small(av, ptr, size)
|
40
|
-
|
41
|
-
munmap_chunk(ptr)
|
40
|
+
# else # never reached block, redundant code in glibc.
|
41
|
+
# munmap_chunk(ptr)
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
data/lib/heapinfo/helper.rb
CHANGED
@@ -1,19 +1,9 @@
|
|
1
|
+
require 'dentaku'
|
2
|
+
|
1
3
|
module HeapInfo
|
2
4
|
# Some helper functions
|
3
5
|
module Helper
|
4
|
-
#
|
5
|
-
# @param [String] prog The request process name
|
6
|
-
# @return [Fixnum] process id
|
7
|
-
def self.pidof(prog)
|
8
|
-
# plz, don't cmd injection your self :p
|
9
|
-
pid = `pidof #{prog}`.strip.to_i
|
10
|
-
return nil if pid.zero? # process not exists yet
|
11
|
-
throw "pidof #{prog} fail" unless pid.between?(2, 65_535)
|
12
|
-
pid
|
13
|
-
# TODO: handle when multi processes exists
|
14
|
-
end
|
15
|
-
|
16
|
-
# Create read <tt>/proc/[pid]/*</tt> methods
|
6
|
+
# Create read +/proc/[pid]/*+ methods
|
17
7
|
%w(exe maps).each do |method|
|
18
8
|
define_singleton_method("#{method}_of".to_sym) do |pid|
|
19
9
|
begin
|
@@ -24,106 +14,152 @@ module HeapInfo
|
|
24
14
|
end
|
25
15
|
end
|
26
16
|
|
27
|
-
#
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
# # [0x7f2788315000, 0x7f2788316000, 'r--p', '/lib/x86_64-linux-gnu/ld-2.19.so']]
|
41
|
-
def self.parse_maps(content)
|
42
|
-
lines = content.split("\n")
|
43
|
-
lines.map do |line|
|
44
|
-
s = line.scan(%r{^([0-9a-f]+)-([0-9a-f]+)\s([rwxp-]{4})[^/|\[]*([/|\[].+)$})[0]
|
45
|
-
next nil if s.nil?
|
46
|
-
s[0], s[1] = s[0, 2].map { |h| h.to_i(16) }
|
47
|
-
s
|
48
|
-
end.compact
|
49
|
-
end
|
17
|
+
# Define class methods here.
|
18
|
+
module ClassMethods
|
19
|
+
# Get the process id of a process
|
20
|
+
# @param [String] prog The request process name
|
21
|
+
# @return [Fixnum] process id
|
22
|
+
def pidof(prog)
|
23
|
+
# plz, don't cmd injection your self :p
|
24
|
+
pid = `pidof #{prog}`.strip.to_i
|
25
|
+
return nil if pid.zero? # process not exists yet
|
26
|
+
throw "pidof #{prog} fail" unless pid.between?(2, 65_535)
|
27
|
+
pid
|
28
|
+
# TODO: handle when multi processes exists
|
29
|
+
end
|
50
30
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
end
|
31
|
+
# Parse the contents of <tt>/proc/[pid]/maps</tt>.
|
32
|
+
#
|
33
|
+
# @param [String] content The file content of <tt>/proc/[pid]/maps</tt>
|
34
|
+
# @return [Array] In form of <tt>[[start, end, permission, name], ...]</tt>. See examples.
|
35
|
+
# @example
|
36
|
+
# HeapInfo::Helper.parse_maps(<<EOS
|
37
|
+
# 00400000-0040b000 r-xp 00000000 ca:01 271708 /bin/cat
|
38
|
+
# 00bc4000-00be5000 rw-p 00000000 00:00 0 [heap]
|
39
|
+
# 7f2788315000-7f2788316000 r--p 00022000 ca:01 402319 /lib/x86_64-linux-gnu/ld-2.19.so
|
40
|
+
# EOS
|
41
|
+
# )
|
42
|
+
# # [[0x400000, 0x40b000, 'r-xp', '/bin/cat'],
|
43
|
+
# # [0xbc4000, 0xbe5000, 'rw-p', '[heap]'],
|
44
|
+
# # [0x7f2788315000, 0x7f2788316000, 'r--p', '/lib/x86_64-linux-gnu/ld-2.19.so']]
|
45
|
+
def parse_maps(content)
|
46
|
+
lines = content.split("\n")
|
47
|
+
lines.map do |line|
|
48
|
+
s = line.scan(%r{^([0-9a-f]+)-([0-9a-f]+)\s([rwxp-]{4})[^/|\[]*([/|\[].+)$})[0]
|
49
|
+
next nil if s.nil?
|
50
|
+
s[0], s[1] = s[0, 2].map { |h| h.to_i(16) }
|
51
|
+
s
|
52
|
+
end.compact
|
53
|
+
end
|
75
54
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
# @return [Integer] Unpacked result.
|
83
|
-
# @example
|
84
|
-
# HeapInfo::Helper.unpack(4, "\x12\x34\x56\x78")
|
85
|
-
# # 0x78563412
|
86
|
-
# HeapInfo::Helper.unpack(8, "\x12\x34\x56\x78\xfe\xeb\x90\x90")
|
87
|
-
# # 0x9090ebfe78563412
|
88
|
-
def self.unpack(size_t, data)
|
89
|
-
data.unpack(size_t == 4 ? 'L*' : 'Q*')[0]
|
90
|
-
end
|
55
|
+
# enable / disable the color function.
|
56
|
+
# @param [Boolean] on Enable or not.
|
57
|
+
# @return [void]
|
58
|
+
def toggle_color(on: false)
|
59
|
+
@disable_color = !on
|
60
|
+
end
|
91
61
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
62
|
+
# Color codes for pretty print
|
63
|
+
COLOR_CODE = {
|
64
|
+
esc_m: "\e[0m",
|
65
|
+
normal_s: "\e[38;5;1m", # red
|
66
|
+
integer: "\e[38;5;12m", # light blue
|
67
|
+
fatal: "\e[38;5;197m", # dark red
|
68
|
+
bin: "\e[38;5;120m", # light green
|
69
|
+
klass: "\e[38;5;155m", # pry like
|
70
|
+
sym: "\e[38;5;229m", # pry like
|
71
|
+
}.freeze
|
72
|
+
# Wrapper color codes for pretty inspect.
|
73
|
+
# @param [String] s Contents for wrapper
|
74
|
+
# @param [Symbol?] sev Specific which kind of color want to use, valid symbols are defined in +#COLOR_CODE+.
|
75
|
+
# If this argument is not present, will detect according to the content of +s+.
|
76
|
+
# @return [String] wrapper with color codes.
|
77
|
+
def color(s, sev: nil)
|
78
|
+
s = s.to_s
|
79
|
+
return s if @disable_color
|
80
|
+
cc = COLOR_CODE
|
81
|
+
color = if cc.key?(sev) then cc[sev]
|
82
|
+
elsif integer?(s) then cc[:integer] # integers
|
83
|
+
else cc[:normal_s] # normal string
|
84
|
+
end
|
85
|
+
"#{color}#{s.sub(cc[:esc_m], color)}#{cc[:esc_m]}"
|
86
|
+
end
|
101
87
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
88
|
+
# Unpack strings to integer.
|
89
|
+
#
|
90
|
+
# Like the <tt>p32</tt> and <tt>p64</tt> in pwntools.
|
91
|
+
#
|
92
|
+
# @param [Integer] size_t Either 4 or 8.
|
93
|
+
# @param [String] data String to be unpack.
|
94
|
+
# @return [Integer] Unpacked result.
|
95
|
+
# @example
|
96
|
+
# HeapInfo::Helper.unpack(4, "\x12\x34\x56\x78")
|
97
|
+
# # 0x78563412
|
98
|
+
# HeapInfo::Helper.unpack(8, "\x12\x34\x56\x78\xfe\xeb\x90\x90")
|
99
|
+
# # 0x9090ebfe78563412
|
100
|
+
def unpack(size_t, data)
|
101
|
+
data.unpack(size_t == 4 ? 'L*' : 'Q*')[0]
|
102
|
+
end
|
103
|
+
|
104
|
+
# Convert number in hex format
|
105
|
+
#
|
106
|
+
# @param [Integer] num Non-negative integer.
|
107
|
+
# @return [String] number in hex format.
|
108
|
+
# @example
|
109
|
+
# HeapInfo::Helper.hex(1000) # => '0x3e8'
|
110
|
+
def hex(num)
|
111
|
+
return format('0x%x', num) if num >= 0
|
112
|
+
format('-0x%x', -num)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Retrieve pure class name(without module) of an object
|
116
|
+
# @param [Object] obj Any instance
|
117
|
+
# @return [String] Class name of <tt>obj</tt>
|
118
|
+
# @example
|
119
|
+
# # suppose obj is an instance of HeapInfo::Chunk
|
120
|
+
# Helper.class_name(obj)
|
121
|
+
# # => 'Chunk'
|
122
|
+
def class_name(obj)
|
123
|
+
obj.class.name.split('::').last || obj.class.name
|
124
|
+
end
|
125
|
+
|
126
|
+
# For checking a string is actually an integer.
|
127
|
+
# @param [String] str String to be checked.
|
128
|
+
# @return [Boolean] If +str+ can be converted into integer.
|
129
|
+
# @example
|
130
|
+
# Helper.integer? '1234'
|
131
|
+
# # => true
|
132
|
+
# Helper.integer? '0x1234'
|
133
|
+
# # => true
|
134
|
+
# Helper.integer? '0xheapoverflow'
|
135
|
+
# # => false
|
136
|
+
def integer?(str)
|
137
|
+
true if Integer(str)
|
138
|
+
rescue ArgumentError, TypeError
|
139
|
+
false
|
140
|
+
end
|
141
|
+
|
142
|
+
# Safe-eval using dentaku.
|
143
|
+
# @param [String] formula Formula to be eval.
|
144
|
+
# @param [Hash{Symbol => Integer}] store Predefined values.
|
145
|
+
# @return [Integer] Evaluate result.
|
146
|
+
def evaluate(formula, store: {})
|
147
|
+
calc = Dentaku::Calculator.new
|
148
|
+
formula = formula.delete(':')
|
149
|
+
formula.gsub!(/0x[\da-z]+/) do |s|
|
150
|
+
s.to_i(16).to_s
|
151
|
+
end
|
152
|
+
calc.store(store).evaluate(formula)
|
153
|
+
end
|
112
154
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
# Helper.integer? '0x1234'
|
120
|
-
# # => true
|
121
|
-
# Helper.integer? '0xheapoverflow'
|
122
|
-
# # => false
|
123
|
-
def self.integer?(str)
|
124
|
-
true if Integer(str)
|
125
|
-
rescue ArgumentError, TypeError
|
126
|
-
false
|
155
|
+
# Get temp filename.
|
156
|
+
# @param [String] name Filename.
|
157
|
+
# @return [String] Temp filename.
|
158
|
+
def tempfile(name)
|
159
|
+
Dir::Tmpname.create(name, HeapInfo::TMP_DIR) {}
|
160
|
+
end
|
127
161
|
end
|
162
|
+
|
163
|
+
extend ClassMethods
|
128
164
|
end
|
129
165
|
end
|
data/lib/heapinfo/libc.rb
CHANGED
@@ -60,15 +60,17 @@ module HeapInfo
|
|
60
60
|
end
|
61
61
|
|
62
62
|
def resolve_main_arena_offset
|
63
|
-
|
64
|
-
|
65
|
-
|
63
|
+
dir = HeapInfo::Helper.tempfile('')
|
64
|
+
FileUtils.mkdir(dir)
|
65
|
+
tmp_elf = File.join(dir, 'get_arena')
|
66
|
+
libc_file = File.join(dir, 'libc.so.6')
|
67
|
+
ld_file = File.join(dir, 'ld.so')
|
66
68
|
flags = "-w #{size_t == 4 ? '-m32' : ''}"
|
67
69
|
`cp #{name} #{libc_file} && \
|
68
70
|
cp #{ld_name} #{ld_file} && \
|
69
71
|
gcc #{flags} #{File.expand_path('../tools/get_arena.c', __FILE__)} -o #{tmp_elf} 2>&1 > /dev/null && \
|
70
|
-
#{ld_file} --library-path #{
|
71
|
-
rm
|
72
|
+
#{ld_file} --library-path #{dir} #{tmp_elf} && \
|
73
|
+
rm -fr #{dir}`.to_i(16)
|
72
74
|
end
|
73
75
|
end
|
74
76
|
end
|
data/lib/heapinfo/process.rb
CHANGED
@@ -2,18 +2,18 @@
|
|
2
2
|
module HeapInfo
|
3
3
|
# Main class of heapinfo.
|
4
4
|
class Process
|
5
|
-
# The default options of
|
6
|
-
# use for matching glibc and ld segments in
|
5
|
+
# The default options of libraries,
|
6
|
+
# use for matching glibc and ld segments in +/proc/[pid]/maps+.
|
7
7
|
DEFAULT_LIB = {
|
8
8
|
libc: /bc[^a-z]*\.so/,
|
9
9
|
ld: %r{/ld-.+\.so}
|
10
10
|
}.freeze
|
11
|
-
# @return [Fixnum, NilClass] return the pid of process,
|
11
|
+
# @return [Fixnum, NilClass] return the pid of process, +nil+ if no such process found
|
12
12
|
attr_reader :pid
|
13
13
|
|
14
|
-
# Instantiate a
|
15
|
-
# @param [String, Fixnum] prog Process name or pid, see
|
16
|
-
# @param [Hash] options libraries' filename, see
|
14
|
+
# Instantiate a {HeapInfo::Process} object.
|
15
|
+
# @param [String, Fixnum] prog Process name or pid, see {HeapInfo::heapinfo} for more information
|
16
|
+
# @param [Hash{Symbol => RegExp, String}] options libraries' filename, see {HeapInfo::heapinfo} for more information
|
17
17
|
def initialize(prog, options = {})
|
18
18
|
@prog = prog
|
19
19
|
@options = DEFAULT_LIB.merge options
|
@@ -23,7 +23,7 @@ module HeapInfo
|
|
23
23
|
|
24
24
|
# Reload a new process with same program name
|
25
25
|
#
|
26
|
-
# @return [HeapInfo::Process] return
|
26
|
+
# @return [HeapInfo::Process] return +self+ for chainable.
|
27
27
|
# @example
|
28
28
|
# puts h.reload!
|
29
29
|
def reload!
|
@@ -34,9 +34,9 @@ module HeapInfo
|
|
34
34
|
|
35
35
|
# Use this method to wrapper all HeapInfo methods.
|
36
36
|
#
|
37
|
-
# Since
|
37
|
+
# Since {HeapInfo} is a tool(debugger) for local usage,
|
38
38
|
# while exploiting remote service, all methods will not work properly.
|
39
|
-
# So I suggest to wrapper all methods inside
|
39
|
+
# So I suggest to wrapper all methods inside {#debug},
|
40
40
|
# which will ignore the block while the victim process is not found.
|
41
41
|
#
|
42
42
|
# @example
|
@@ -57,49 +57,77 @@ module HeapInfo
|
|
57
57
|
# Note: This method require you have permission of attaching another process.
|
58
58
|
# If not, a warning message will present.
|
59
59
|
#
|
60
|
-
# @param [Mixed] args Will be parsed into
|
60
|
+
# @param [Mixed] args Will be parsed into +[base, length]+, see Examples for more information.
|
61
61
|
# @return [String, HeapInfo::Nil]
|
62
62
|
# The content needed. When the request address is not readable or the process not exists,
|
63
|
-
#
|
63
|
+
# instance of {HeapInfo::Nil} is returned.
|
64
64
|
#
|
65
65
|
# @example
|
66
66
|
# h = heapinfo('victim')
|
67
|
-
# h.dump(:heap) #
|
68
|
-
# h.dump(:heap, 64) #
|
69
|
-
# h.dump(
|
70
|
-
# h.dump('heap+
|
71
|
-
# h.dump('heap+0x100', 64) #
|
67
|
+
# h.dump(:heap) # heap[0, 8]
|
68
|
+
# h.dump(:heap, 64) # heap[0, 64]
|
69
|
+
# h.dump('heap+256', 64) # heap[256, 64]
|
70
|
+
# h.dump('heap+0x100', 64) # heap[256, 64]
|
71
|
+
# h.dump('heap+0x100 * 2 + 0x300', 64) # heap[1024, 64]
|
72
72
|
# h.dump(<segment>, 8) # semgent can be [heap, stack, (program|elf), libc, ld]
|
73
73
|
# h.dump(addr, 64) # addr[0, 64]
|
74
74
|
#
|
75
75
|
# # Invalid usage
|
76
76
|
# dump(:meow) # no such segment
|
77
|
-
# dump('heap-1, 64') # not support '-'
|
78
77
|
def dump(*args)
|
79
78
|
return Nil.new unless load?
|
80
79
|
dumper.dump(*args)
|
81
80
|
end
|
82
81
|
|
83
82
|
# Return the dump result as chunks.
|
84
|
-
# see
|
83
|
+
# see {HeapInfo::Dumper#dump_chunks} for more information.
|
85
84
|
#
|
86
85
|
# @return [HeapInfo::Chunks, HeapInfo::Nil] An array of chunk(s).
|
87
|
-
# @param [Mixed] args Same as arguments of
|
86
|
+
# @param [Mixed] args Same as arguments of {#dump}.
|
88
87
|
def dump_chunks(*args)
|
89
88
|
return Nil.new unless load?
|
90
89
|
dumper.dump_chunks(*args)
|
91
90
|
end
|
92
91
|
|
92
|
+
# Show the offset in pretty way between the segment.
|
93
|
+
# Very useful in pwn when leak some address,
|
94
|
+
# see examples for more details.
|
95
|
+
# @param [Integer] addr The leaked address.
|
96
|
+
# @param [Symbol] sym
|
97
|
+
# The segement symbol to be calculated offset.
|
98
|
+
# If this parameter not given, will loop segments
|
99
|
+
# and find the most close one. See examples for more details.
|
100
|
+
# @return [void] Offset will show to stdout.
|
101
|
+
# @example
|
102
|
+
# h.offset(0x7f11f6ae1670, :libc)
|
103
|
+
# #=> 0xf6670 after libc
|
104
|
+
# h.offset(0x5559edc057a0, :heap)
|
105
|
+
# #=> 0x9637a0 after heap
|
106
|
+
# h.offset(0x7f11f6ae1670)
|
107
|
+
# #=> 0xf6670 after :libc
|
108
|
+
# h.offset(0x5559edc057a0)
|
109
|
+
# #=> 0x9637a0 after :heap
|
110
|
+
def offset(addr, sym = nil)
|
111
|
+
return unless load?
|
112
|
+
segment = @info.send(sym) if HeapInfo::ProcessInfo::EXPORT.include?(sym)
|
113
|
+
segment = nil unless segment.is_a?(HeapInfo::Segment)
|
114
|
+
if segment.nil?
|
115
|
+
sym, segment = @info.segments.select { |_, seg| seg.base <= addr }.min_by { |_, seg| addr - seg.base }
|
116
|
+
end
|
117
|
+
return puts "Invalid address #{Helper.hex(addr)}" if segment.nil?
|
118
|
+
puts Helper.color(Helper.hex(addr - segment.base)) + ' after ' + Helper.color(sym, sev: :sym)
|
119
|
+
end
|
120
|
+
|
93
121
|
# Gdb-like command
|
94
122
|
#
|
95
|
-
# Show dump results like
|
96
|
-
#
|
123
|
+
# Show dump results like gdb's command +x+.
|
124
|
+
# While will auto detect the current elf class to decide using +gx+ or +wx+.
|
97
125
|
#
|
98
|
-
# The dump results wrapper with color codes and nice typesetting will output to
|
99
|
-
# @param [Integer] count The number of result need to dump, see examples for more information
|
100
|
-
# @param [
|
101
|
-
#
|
102
|
-
# @return [
|
126
|
+
# The dump results wrapper with color codes and nice typesetting will output to +stdout+.
|
127
|
+
# @param [Integer] count The number of result need to dump, see examples for more information.
|
128
|
+
# @param [String, Symbol, Integer] address The base address to be dumped.
|
129
|
+
# Same format as {#dump(*args)}, see {#dump} for more information.
|
130
|
+
# @return [void]
|
103
131
|
# @example
|
104
132
|
# h.x 8, :heap
|
105
133
|
# # 0x1f0d000: 0x0000000000000000 0x0000000000002011
|
@@ -110,23 +138,23 @@ module HeapInfo
|
|
110
138
|
# h.x 3, 0x400000
|
111
139
|
# # 0x400000: 0x00010102464c457f 0x0000000000000000
|
112
140
|
# # 0x400010: 0x00000001003e0002
|
113
|
-
def x(count,
|
114
|
-
return unless load?
|
115
|
-
dumper.x(count,
|
141
|
+
def x(count, address)
|
142
|
+
return unless load?
|
143
|
+
dumper.x(count, address)
|
116
144
|
end
|
117
145
|
|
118
146
|
# Gdb-like command.
|
119
147
|
#
|
120
148
|
# Search a specific value/string/regexp in memory.
|
121
149
|
# @param [Integer, String, Regexp] pattern
|
122
|
-
# The desired search pattern, can be value(
|
150
|
+
# The desired search pattern, can be value(+Integer+), string, or regular expression.
|
123
151
|
# @param [Integer, String, Symbol] from
|
124
|
-
# Start address for searching, can be segment(
|
152
|
+
# Start address for searching, can be segment(+Symbol+) or segments with offset.
|
125
153
|
# See examples for more information.
|
126
154
|
# @param [Integer] length
|
127
155
|
# The search length limit, default is unlimited,
|
128
156
|
# which will search until pattern found or reach unreadable memory.
|
129
|
-
# @return [Integer, NilClass] The first matched address,
|
157
|
+
# @return [Integer, NilClass] The first matched address, +nil+ is returned when no such pattern found.
|
130
158
|
# @example
|
131
159
|
# h.find(0xdeadbeef, 'heap+0x10', 0x1000)
|
132
160
|
# # => 6299664 # 0x602010
|
@@ -147,15 +175,14 @@ module HeapInfo
|
|
147
175
|
|
148
176
|
# Pretty dump of bins layouts.
|
149
177
|
#
|
150
|
-
# The request layouts will output to
|
178
|
+
# The request layouts will output to +stdout+.
|
151
179
|
# @param [Array<Symbol>] args Bin type(s) you want to see.
|
152
|
-
# @
|
153
|
-
# @return [NilClass] The return value of <tt>io.puts</tt>.
|
180
|
+
# @return [void]
|
154
181
|
# @example
|
155
|
-
# h.layouts
|
156
|
-
def layouts(*args
|
157
|
-
return unless load?
|
158
|
-
|
182
|
+
# h.layouts(:fast, :unsorted, :small)
|
183
|
+
def layouts(*args)
|
184
|
+
return unless load?
|
185
|
+
puts libc.main_arena.layouts(*args)
|
159
186
|
end
|
160
187
|
|
161
188
|
# Show simple information of target process.
|
@@ -6,7 +6,7 @@ module HeapInfo
|
|
6
6
|
class ProcessInfo
|
7
7
|
# Methods to be transparent to +process+.
|
8
8
|
# e.g. +process.libc+ alias to +process.info.libc+.
|
9
|
-
EXPORT = %i(libc ld heap elf
|
9
|
+
EXPORT = %i(libc ld heap program elf stack bits).freeze
|
10
10
|
|
11
11
|
# @return [Integer] 32 or 64.
|
12
12
|
attr_reader :bits
|
@@ -20,14 +20,14 @@ module HeapInfo
|
|
20
20
|
attr_reader :ld
|
21
21
|
alias elf program
|
22
22
|
|
23
|
-
# Instantiate a {ProcessInfo} object
|
23
|
+
# Instantiate a {ProcessInfo} object.
|
24
24
|
#
|
25
25
|
# @param [HeapInfo::Process] process Load information from maps/memory for +process+.
|
26
26
|
def initialize(process)
|
27
27
|
@pid = process.pid
|
28
28
|
options = process.instance_variable_get(:@options)
|
29
29
|
maps!
|
30
|
-
@bits = bits_of
|
30
|
+
@bits = bits_of(Helper.exe_of(@pid))
|
31
31
|
@elf = @program = Segment.find(maps, File.readlink("/proc/#{@pid}/exe"))
|
32
32
|
@stack = Segment.find(maps, '[stack]')
|
33
33
|
# well.. stack is a strange case because it will grow in runtime..
|
@@ -39,18 +39,27 @@ module HeapInfo
|
|
39
39
|
# Heap will not be mmapped if the process not use heap yet, so create a lazy loading method.
|
40
40
|
# Will re-read maps when heap segment not found yet.
|
41
41
|
#
|
42
|
-
# @return [HeapInfo::Segment] The {Segment} of heap
|
42
|
+
# @return [HeapInfo::Segment] The {Segment} of heap.
|
43
43
|
def heap # special handle because heap might not be initialized in the beginning
|
44
44
|
@heap ||= Segment.find(maps!, '[heap]')
|
45
45
|
end
|
46
46
|
|
47
|
+
# Return segemnts load currently.
|
48
|
+
# @return [Hash{Symbol => Segment}] The segments in hash format.
|
49
|
+
def segments
|
50
|
+
EXPORT.map do |sym|
|
51
|
+
seg = send(sym)
|
52
|
+
[sym, seg] if seg.is_a?(Segment)
|
53
|
+
end.compact.to_h
|
54
|
+
end
|
55
|
+
|
47
56
|
private
|
48
57
|
|
49
58
|
attr_reader :maps
|
50
59
|
|
51
60
|
# force reload maps
|
52
61
|
def maps!
|
53
|
-
@maps = Helper.parse_maps
|
62
|
+
@maps = Helper.parse_maps(Helper.maps_of(@pid))
|
54
63
|
end
|
55
64
|
|
56
65
|
def bits_of(elf)
|
data/lib/heapinfo/version.rb
CHANGED
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: heapinfo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- david942j
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-03-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: dentaku
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: rspec
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|