heapinfo 1.1.0 → 1.2.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 +13 -8
- data/lib/heapinfo.rb +4 -17
- data/lib/heapinfo/arena.rb +28 -30
- data/lib/heapinfo/cache.rb +7 -5
- data/lib/heapinfo/chunk.rb +3 -1
- data/lib/heapinfo/dumper.rb +42 -2
- data/lib/heapinfo/ext/string.rb +4 -1
- data/lib/heapinfo/helper.rb +1 -3
- data/lib/heapinfo/libc.rb +58 -32
- data/lib/heapinfo/nil.rb +2 -2
- data/lib/heapinfo/process.rb +64 -11
- data/lib/heapinfo/process_info.rb +25 -2
- data/lib/heapinfo/segment.rb +3 -0
- data/lib/heapinfo/tcache.rb +55 -0
- data/lib/heapinfo/tools/libc_info.c +51 -0
- data/lib/heapinfo/version.rb +1 -1
- metadata +21 -20
- data/lib/heapinfo/tools/get_arena.c +0 -28
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0fcd356865d018666cc16677f3c7ab5052f8a43d
|
4
|
+
data.tar.gz: f5125e3505ff162b159edacec8d7b00c81e50bb2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f0386440f6b738e43b882473eb913420b2fa92b3b9e5b186d96c680cb25d65a8e2608c2dd25866cd1eb0805f9f0b0ea2a068c533296cf027ae6b4d69bd42ffa7
|
7
|
+
data.tar.gz: e31e4a02718653ed6909ca9f644d7101ba58b5eabcb508485bb390a66ac076cb856d5bd8ab380a297e4d0e0b256a465f988e2cce518f379389a73e7700b61d47
|
data/README.md
CHANGED
@@ -10,10 +10,10 @@
|
|
10
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
|
### Why
|
13
|
-
**HeapInfo** is very helpful when binary has somehow anti-debugger limitations, e.g being ptraced.
|
13
|
+
**HeapInfo** is very helpful when binary has somehow anti-debugger limitations, e.g. being ptraced.
|
14
14
|
**HeapInfo** still works because it doesn't use ptrace.
|
15
15
|
|
16
|
-
Implement with
|
16
|
+
Implement with Ruby because I love Ruby :P. But might implement with Python (if no others did) in the future.
|
17
17
|
|
18
18
|
If you prefer [pwntools](https://github.com/Gallopsled/pwntools) for exploiting, you can still use **HeapInfo** in irb/pry as a small debugger.
|
19
19
|
|
@@ -33,13 +33,11 @@ $ gem install heapinfo
|
|
33
33
|
* `layouts` - Show the current bin layouts, very useful for heap exploitation.
|
34
34
|
* `offset` - Show the offset between given address and segment. Very useful for calculating relative offset.
|
35
35
|
* `canary` - Fetch the value of stack guard!
|
36
|
-
* `x` - Provide gdb-like
|
37
|
-
* `
|
36
|
+
* `x` - Provide gdb-like command.
|
37
|
+
* `s` - Provide gdb-like command.
|
38
|
+
* `find` - Provide gdb-like command.
|
38
39
|
* More features and details can be found in [RDoc](http://www.rubydoc.info/github/david942j/heapinfo/master/)
|
39
40
|
|
40
|
-
### Under developing
|
41
|
-
* `free` - Show what will happend when calling `glibc#free` an address.
|
42
|
-
|
43
41
|
## Usage
|
44
42
|
|
45
43
|
#### Load
|
@@ -48,7 +46,7 @@ $ gem install heapinfo
|
|
48
46
|
require 'heapinfo'
|
49
47
|
# ./victim is running
|
50
48
|
h = heapinfo('victim')
|
51
|
-
# or use h = heapinfo(20568) to
|
49
|
+
# or use h = heapinfo(20568) to specify a pid
|
52
50
|
|
53
51
|
# will present simple info when loading:
|
54
52
|
# Program: /home/heapinfo/victim PID: 20568
|
@@ -119,6 +117,11 @@ h.layouts :unsorted, :small
|
|
119
117
|
```
|
120
118
|
![smallbin layouts](https://github.com/david942j/heapinfo/blob/master/examples/unsorted_smallbin_layouts.png?raw=true)
|
121
119
|
|
120
|
+
```ruby
|
121
|
+
h.layouts :tcache
|
122
|
+
```
|
123
|
+
![tcache layouts](https://github.com/david942j/heapinfo/blob/master/examples/tcache_layouts.png?raw=true)
|
124
|
+
|
122
125
|
#### offset
|
123
126
|
```ruby
|
124
127
|
h.offset(0x7fda86fe8670)
|
@@ -169,3 +172,5 @@ h.offset(h.find('/bin/sh', :libc))
|
|
169
172
|
* libc-2.23
|
170
173
|
* libc-2.24
|
171
174
|
* libc-2.25
|
175
|
+
* libc-2.26
|
176
|
+
* libc-2.27
|
data/lib/heapinfo.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
# Basic requirements from standard library
|
2
2
|
require 'fileutils'
|
3
3
|
|
4
|
+
require 'heapinfo/ext/string.rb'
|
5
|
+
require 'heapinfo/process'
|
6
|
+
|
4
7
|
# HeapInfo - an interactive debugger for heap exploitation
|
5
8
|
#
|
6
9
|
# HeapInfo makes pwning life easier with ruby style memory dumper.
|
@@ -46,9 +49,7 @@ module HeapInfo
|
|
46
49
|
# p h.ld.name
|
47
50
|
# #=> "/home/heapinfo/ld-linux-x86-64.so.2"
|
48
51
|
def self.heapinfo(prog, options = {})
|
49
|
-
|
50
|
-
puts h
|
51
|
-
h
|
52
|
+
HeapInfo::Process.new(prog, options).tap { |h| $stdout.puts h }
|
52
53
|
end
|
53
54
|
end
|
54
55
|
|
@@ -58,17 +59,3 @@ end
|
|
58
59
|
def heapinfo(*args)
|
59
60
|
::HeapInfo.heapinfo(*args)
|
60
61
|
end
|
61
|
-
|
62
|
-
require 'heapinfo/helper'
|
63
|
-
require 'heapinfo/nil'
|
64
|
-
require 'heapinfo/cache'
|
65
|
-
require 'heapinfo/process_info'
|
66
|
-
require 'heapinfo/process'
|
67
|
-
require 'heapinfo/segment'
|
68
|
-
require 'heapinfo/glibc/glibc'
|
69
|
-
require 'heapinfo/libc'
|
70
|
-
require 'heapinfo/chunk'
|
71
|
-
require 'heapinfo/chunks'
|
72
|
-
require 'heapinfo/arena'
|
73
|
-
require 'heapinfo/dumper'
|
74
|
-
require 'heapinfo/ext/string.rb'
|
data/lib/heapinfo/arena.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
require 'heapinfo/chunk'
|
2
|
+
require 'heapinfo/helper'
|
3
|
+
|
1
4
|
module HeapInfo
|
2
5
|
# Records status of an arena, including bin(s) and top chunk.
|
3
6
|
class Arena
|
@@ -18,8 +21,8 @@ module HeapInfo
|
|
18
21
|
# Instantiate a {HeapInfo::Arena} object.
|
19
22
|
#
|
20
23
|
# @param [Integer] base Base address of arena.
|
21
|
-
# @param [Integer] size_t Either 8 or 4
|
22
|
-
# @param [Proc] dumper For
|
24
|
+
# @param [Integer] size_t Either 8 or 4.
|
25
|
+
# @param [Proc] dumper For dumping more data.
|
23
26
|
def initialize(base, size_t, dumper)
|
24
27
|
@base = base
|
25
28
|
@size_t = size_t
|
@@ -35,22 +38,18 @@ module HeapInfo
|
|
35
38
|
top_ptr = Helper.unpack(size_t, @dumper.call(top_ptr_offset, size_t))
|
36
39
|
@fastbin = []
|
37
40
|
return self if top_ptr.zero? # arena not init yet
|
38
|
-
@top_chunk = Chunk.new
|
39
|
-
@last_remainder = Chunk.new
|
41
|
+
@top_chunk = Chunk.new(size_t, top_ptr, @dumper)
|
42
|
+
@last_remainder = Chunk.new(size_t, top_ptr_offset + 8, @dumper)
|
40
43
|
# this offset diff after 2.23
|
41
44
|
@system_mem = Array.new(2) do |off|
|
42
45
|
Helper.unpack(size_t, @dumper.call(top_ptr_offset + 258 * size_t + 16 + off * size_t, size_t))
|
43
46
|
end.find { |val| val >= 0x21000 && (val & 0xfff).zero? }
|
44
47
|
@fastbin = Array.new(7) do |idx|
|
45
|
-
|
46
|
-
f.index = idx
|
47
|
-
f
|
48
|
+
Fastbin.new(size_t, @base + 8 - size_t * 2 + size_t * idx, @dumper, head: true).tap { |f| f.index = idx }
|
48
49
|
end
|
49
50
|
@unsorted_bin = UnsortedBin.new(size_t, top_ptr_offset, @dumper, head: true)
|
50
51
|
@smallbin = Array.new(62) do |idx|
|
51
|
-
|
52
|
-
s.index = idx
|
53
|
-
s
|
52
|
+
Smallbin.new(size_t, @base + 8 + size_t * (12 + 2 * idx), @dumper, head: true).tap { |s| s.index = idx }
|
54
53
|
end
|
55
54
|
self
|
56
55
|
end
|
@@ -86,10 +85,10 @@ module HeapInfo
|
|
86
85
|
|
87
86
|
# Instantiate a {HeapInfo::Fastbin} object.
|
88
87
|
#
|
89
|
-
# @
|
90
|
-
def initialize(*
|
88
|
+
# @see HeapInfo::Chunk
|
89
|
+
def initialize(_size_t, base, *)
|
91
90
|
super
|
92
|
-
@fd =
|
91
|
+
@fd = fd_of(base)
|
93
92
|
end
|
94
93
|
|
95
94
|
# Mapping index of fastbin to chunk size.
|
@@ -110,7 +109,7 @@ module HeapInfo
|
|
110
109
|
# @return [String] fastbin layouts wrapper with color codes.
|
111
110
|
def inspect
|
112
111
|
title + list.map do |ptr|
|
113
|
-
next "(#{ptr})\n" if ptr.is_a?
|
112
|
+
next "(#{ptr})\n" if ptr.is_a?(Symbol)
|
114
113
|
next " => (nil)\n" if ptr.nil?
|
115
114
|
format(' => %s', Helper.color(format('%#x', ptr)))
|
116
115
|
end.join
|
@@ -135,6 +134,14 @@ module HeapInfo
|
|
135
134
|
ret << nil
|
136
135
|
end
|
137
136
|
|
137
|
+
private
|
138
|
+
|
139
|
+
def addr_of(ptr, offset)
|
140
|
+
t = dump(ptr + size_t * offset, size_t)
|
141
|
+
return nil if t.nil?
|
142
|
+
Helper.unpack(size_t, t)
|
143
|
+
end
|
144
|
+
|
138
145
|
# @param [Integer] ptr Get the +fd+ value of chunk at +ptr+.
|
139
146
|
# @return [Integer] The +fd+.
|
140
147
|
def fd_of(ptr)
|
@@ -146,14 +153,6 @@ module HeapInfo
|
|
146
153
|
def bk_of(ptr)
|
147
154
|
addr_of(ptr, 3)
|
148
155
|
end
|
149
|
-
|
150
|
-
private
|
151
|
-
|
152
|
-
def addr_of(ptr, offset)
|
153
|
-
t = dump(ptr + size_t * offset, size_t)
|
154
|
-
return nil if t.nil?
|
155
|
-
Helper.unpack(size_t, t)
|
156
|
-
end
|
157
156
|
end
|
158
157
|
|
159
158
|
# Class for record unsorted bin type chunk.
|
@@ -178,7 +177,7 @@ module HeapInfo
|
|
178
177
|
title + pretty_list(list) + "\n"
|
179
178
|
end
|
180
179
|
|
181
|
-
# Wrapper the
|
180
|
+
# Wrapper the doubly linked list with color codes.
|
182
181
|
# @param [Array<Integer>] list The list from {#link_list}.
|
183
182
|
# @return [String] Wrapper with color codes.
|
184
183
|
def pretty_list(list)
|
@@ -190,9 +189,9 @@ module HeapInfo
|
|
190
189
|
next "#{color_c}(invalid)" if fwd.nil? # invalid c
|
191
190
|
bck = bk_of(c)
|
192
191
|
if center.nil? # bk side
|
193
|
-
|
192
|
+
format('%s%s', color_c, fwd == list[idx + 1] ? nil : Helper.color(format('(%#x)', fwd)))
|
194
193
|
else # fd side
|
195
|
-
|
194
|
+
format('%s%s', bck == list[idx - 1] ? nil : Helper.color(format('(%#x)', bck)), color_c)
|
196
195
|
end
|
197
196
|
end.join(' === ')
|
198
197
|
end
|
@@ -210,16 +209,15 @@ module HeapInfo
|
|
210
209
|
sz = 0
|
211
210
|
dup = {}
|
212
211
|
while ptr != @base && sz < expand_size
|
213
|
-
append.call
|
214
|
-
break if ptr.nil? # invalid pointer
|
215
|
-
break if dup[ptr] # looped
|
212
|
+
append.call(ptr)
|
213
|
+
break if ptr.nil? || dup[ptr] # invalid or duplicated pointer
|
216
214
|
dup[ptr] = true
|
217
|
-
ptr =
|
215
|
+
ptr = __send__(nxt, ptr)
|
218
216
|
sz += 1
|
219
217
|
end
|
220
218
|
end
|
221
219
|
work.call(@fd, :fd_of, ->(ptr) { list << ptr })
|
222
|
-
work.call(@bk, :bk_of, ->(ptr) { list.unshift
|
220
|
+
work.call(@bk, :bk_of, ->(ptr) { list.unshift(ptr) })
|
223
221
|
list
|
224
222
|
end
|
225
223
|
end
|
data/lib/heapinfo/cache.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'digest'
|
2
|
+
require 'fileutils'
|
3
|
+
|
2
4
|
module HeapInfo
|
3
5
|
# Self implment file-base cache manager.
|
4
6
|
#
|
@@ -10,12 +12,12 @@ module HeapInfo
|
|
10
12
|
|
11
13
|
# Define class methods.
|
12
14
|
module ClassMethods
|
13
|
-
# Get the key for
|
15
|
+
# Get the key for storing libc info.
|
14
16
|
#
|
15
17
|
# @param [String] libc_path The realpath to libc file.
|
16
|
-
# @return [String] The key for cache read/write.
|
17
|
-
def
|
18
|
-
File.join('libc', Digest::MD5.hexdigest(IO.binread(libc_path)), '
|
18
|
+
# @return [String] The key for cache to read/write.
|
19
|
+
def key_libc_info(libc_path)
|
20
|
+
File.join('libc', Digest::MD5.hexdigest(IO.binread(libc_path)), 'info')
|
19
21
|
end
|
20
22
|
|
21
23
|
# Write cache to file.
|
@@ -55,7 +57,7 @@ module HeapInfo
|
|
55
57
|
FileUtils.mkdir_p(CACHE_DIR)
|
56
58
|
rescue Errno::EACCES
|
57
59
|
# To prevent ~/ is not writable.
|
58
|
-
|
60
|
+
__send__(:remove_const, :CACHE_DIR)
|
59
61
|
const_set(:CACHE_DIR, File.join(HeapInfo::TMP_DIR, '.cache/heapinfo'))
|
60
62
|
FileUtils.mkdir_p(CACHE_DIR)
|
61
63
|
end
|
data/lib/heapinfo/chunk.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'heapinfo/helper'
|
2
|
+
|
1
3
|
module HeapInfo
|
2
4
|
# The object of a heap chunk
|
3
5
|
class Chunk
|
@@ -23,7 +25,7 @@ module HeapInfo
|
|
23
25
|
# # create a chunk with chunk size 0x21
|
24
26
|
def initialize(size_t, base, dumper, head: false)
|
25
27
|
raise ArgumentError, 'size_t can be either 4 or 8' unless [4, 8].include?(size_t)
|
26
|
-
self.class.
|
28
|
+
self.class.__send__(:define_method, :dump) { |*args| dumper.call(*args) }
|
27
29
|
@size_t = size_t
|
28
30
|
@base = base
|
29
31
|
sz = dump(@base, size_t * 2)
|
data/lib/heapinfo/dumper.rb
CHANGED
@@ -1,4 +1,11 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
|
3
|
+
require 'heapinfo/helper'
|
4
|
+
require 'heapinfo/nil'
|
5
|
+
|
1
6
|
module HeapInfo
|
7
|
+
# @api private
|
8
|
+
#
|
2
9
|
# Class for memory dump relation works
|
3
10
|
class Dumper
|
4
11
|
# Default dump length
|
@@ -67,8 +74,22 @@ module HeapInfo
|
|
67
74
|
puts str
|
68
75
|
end
|
69
76
|
|
70
|
-
#
|
77
|
+
# Dump data from +address+ until reach null-byte.
|
71
78
|
#
|
79
|
+
# @return [String]
|
80
|
+
def cstring(address)
|
81
|
+
base = base_of(address)
|
82
|
+
len = 1
|
83
|
+
cur = ''
|
84
|
+
loop do
|
85
|
+
cur << (dump(base + len - 1, len) || '')
|
86
|
+
break if cur.index("\x00")
|
87
|
+
len <<= 1
|
88
|
+
return cur if cur.size != len - 1 # reached undumpable memory
|
89
|
+
end
|
90
|
+
cur[0, cur.index("\x00")]
|
91
|
+
end
|
92
|
+
|
72
93
|
# Search a specific value/string/regexp in memory.
|
73
94
|
# +#find+ only return the first matched address.
|
74
95
|
# @param [Integer, String, Regexp] pattern
|
@@ -98,6 +119,20 @@ module HeapInfo
|
|
98
119
|
ret
|
99
120
|
end
|
100
121
|
|
122
|
+
# @return [Array<Integer>]
|
123
|
+
def scan(pattern, from, length)
|
124
|
+
from = base_of(from)
|
125
|
+
cur = from
|
126
|
+
result = []
|
127
|
+
loop do
|
128
|
+
addr = find(pattern, cur, length, false)
|
129
|
+
break if addr.nil?
|
130
|
+
result << addr - from
|
131
|
+
cur = addr + @match_length
|
132
|
+
end
|
133
|
+
result
|
134
|
+
end
|
135
|
+
|
101
136
|
private
|
102
137
|
|
103
138
|
def need_permission
|
@@ -151,15 +186,20 @@ module HeapInfo
|
|
151
186
|
end
|
152
187
|
|
153
188
|
def find_integer(value, from, length)
|
189
|
+
@match_length = size_t
|
154
190
|
find_string([value].pack(size_t == 4 ? 'L*' : 'Q*'), from, length)
|
155
191
|
end
|
156
192
|
|
157
193
|
def find_string(string, from, length)
|
194
|
+
@match_length = string.size
|
158
195
|
batch_dumper(from, length) { |str| str.index(string) }
|
159
196
|
end
|
160
197
|
|
161
198
|
def find_regexp(pattern, from, length)
|
162
|
-
batch_dumper(from, length)
|
199
|
+
batch_dumper(from, length) do |str|
|
200
|
+
str.match(pattern).tap { |m| @match_length = m[0].size if m }
|
201
|
+
str =~ pattern
|
202
|
+
end
|
163
203
|
end
|
164
204
|
|
165
205
|
def batch_dumper(from, remain_size)
|
data/lib/heapinfo/ext/string.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
require 'heapinfo/chunk'
|
2
|
+
require 'heapinfo/chunks'
|
3
|
+
|
1
4
|
module HeapInfo
|
2
5
|
# Define extensions of naive objects.
|
3
6
|
module Ext
|
@@ -36,4 +39,4 @@ module HeapInfo
|
|
36
39
|
end
|
37
40
|
end
|
38
41
|
|
39
|
-
::String.
|
42
|
+
::String.__send__(:include, HeapInfo::Ext::String::InstanceMethods)
|
data/lib/heapinfo/helper.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'dentaku'
|
2
2
|
require 'shellwords'
|
3
3
|
require 'time'
|
4
|
+
require 'tmpdir'
|
4
5
|
|
5
6
|
module HeapInfo
|
6
7
|
# Some helper functions.
|
@@ -151,9 +152,6 @@ module HeapInfo
|
|
151
152
|
def evaluate(formula, store: {})
|
152
153
|
calc = Dentaku::Calculator.new
|
153
154
|
formula = formula.delete(':')
|
154
|
-
formula.gsub!(/0x[\da-z]+/) do |s|
|
155
|
-
s.to_i(16).to_s
|
156
|
-
end
|
157
155
|
calc.store(store).evaluate(formula)
|
158
156
|
end
|
159
157
|
|
data/lib/heapinfo/libc.rb
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
require 'heapinfo/arena'
|
5
|
+
require 'heapinfo/cache'
|
6
|
+
require 'heapinfo/glibc/glibc'
|
7
|
+
require 'heapinfo/helper'
|
8
|
+
require 'heapinfo/segment'
|
9
|
+
require 'heapinfo/tcache'
|
10
|
+
|
1
11
|
module HeapInfo
|
2
12
|
# Record libc's base, name, and offsets.
|
3
13
|
class Libc < Segment
|
@@ -7,15 +17,12 @@ module HeapInfo
|
|
7
17
|
# @param [Mixed] args See {HeapInfo::Segment#initialize} for more information.
|
8
18
|
def initialize(*args)
|
9
19
|
super
|
10
|
-
@offset = {}
|
11
20
|
end
|
12
21
|
|
13
22
|
# Get the offset of +main_arena+ in libc.
|
14
23
|
# @return [Integer]
|
15
24
|
def main_arena_offset
|
16
|
-
|
17
|
-
return nil unless exhaust_search :main_arena
|
18
|
-
@offset[:main_arena]
|
25
|
+
info['main_arena_offset']
|
19
26
|
end
|
20
27
|
|
21
28
|
# Get the +main_arena+ of libc.
|
@@ -27,50 +34,69 @@ module HeapInfo
|
|
27
34
|
@main_arena = Arena.new(off + base, size_t, dumper)
|
28
35
|
end
|
29
36
|
|
37
|
+
# Does this glibc support tcache?
|
38
|
+
#
|
39
|
+
# @return [Boolean]
|
40
|
+
# +true+ or +false+.
|
41
|
+
def tcache?
|
42
|
+
info['tcache_enable']
|
43
|
+
end
|
44
|
+
|
45
|
+
# The tcache object.
|
46
|
+
#
|
47
|
+
# @return [HeapInfo::Tcache?]
|
48
|
+
# Returns +nil+ if this libc doesn't support tcache.
|
49
|
+
def tcache
|
50
|
+
return unless tcache?
|
51
|
+
@tcache ||= Tcache.new(tcache_base, size_t, dumper)
|
52
|
+
end
|
53
|
+
|
30
54
|
# @param [Array] maps See {HeapInfo::Segment#find} for more information.
|
31
55
|
# @param [String] name See {HeapInfo::Segment#find} for more information.
|
32
|
-
#
|
33
|
-
# @
|
34
|
-
# @
|
56
|
+
#
|
57
|
+
# @option options [Integer] bits Either 64 or 32.
|
58
|
+
# @option options [String] ld_name The loader's realpath, will be used for running subprocesses.
|
59
|
+
# @option options [Proc] dumper The memory dumper for fetch more information.
|
60
|
+
# @option options [Proc] method_heap Method for getting heap segment.
|
61
|
+
#
|
35
62
|
# @return [HeapInfo::Libc] libc segment found in maps.
|
36
|
-
def self.find(maps, name,
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
63
|
+
def self.find(maps, name, **options)
|
64
|
+
super(maps, name).tap do |obj|
|
65
|
+
obj.size_t = options[:bits] / 8
|
66
|
+
%i[ld_name dumper method_heap].each do |sym|
|
67
|
+
obj.__send__("#{sym}=", options[sym])
|
68
|
+
end
|
69
|
+
end
|
42
70
|
end
|
43
71
|
|
44
72
|
private
|
45
73
|
|
46
|
-
attr_accessor :ld_name
|
47
|
-
#
|
48
|
-
def
|
49
|
-
return
|
50
|
-
|
51
|
-
|
74
|
+
attr_accessor :ld_name, :method_heap
|
75
|
+
# Get libc's info.
|
76
|
+
def info
|
77
|
+
return @info if @info
|
78
|
+
# Try to fetch from cache first.
|
79
|
+
key = HeapInfo::Cache.key_libc_info(name)
|
80
|
+
@info = HeapInfo::Cache.read(key)
|
81
|
+
@info ||= execute_libc_info.tap { |i| HeapInfo::Cache.write(key, i) }
|
52
82
|
end
|
53
83
|
|
54
|
-
def
|
55
|
-
key = HeapInfo::Cache.key_libc_offset(name)
|
56
|
-
@offset = HeapInfo::Cache.read(key) || {}
|
57
|
-
return @offset[:main_arena] if @offset.key?(:main_arena)
|
58
|
-
@offset[:main_arena] = resolve_main_arena_offset
|
59
|
-
HeapInfo::Cache.write(key, @offset)
|
60
|
-
end
|
61
|
-
|
62
|
-
def resolve_main_arena_offset
|
84
|
+
def execute_libc_info
|
63
85
|
dir = HeapInfo::Helper.tempfile('')
|
64
86
|
FileUtils.mkdir(dir)
|
65
|
-
tmp_elf = File.join(dir, '
|
87
|
+
tmp_elf = File.join(dir, 'libc_info')
|
66
88
|
libc_file = File.join(dir, 'libc.so.6')
|
67
89
|
ld_file = File.join(dir, 'ld.so')
|
68
90
|
flags = "-w #{size_t == 4 ? '-m32' : ''}"
|
69
|
-
`cp #{name} #{libc_file} && \
|
91
|
+
JSON.parse(`cp #{name} #{libc_file} && \
|
70
92
|
cp #{ld_name} #{ld_file} && \
|
71
|
-
gcc #{flags} #{File.expand_path('
|
93
|
+
gcc #{flags} #{File.expand_path('tools/libc_info.c', __dir__)} -o #{tmp_elf} 2>&1 > /dev/null && \
|
72
94
|
#{ld_file} --library-path #{dir} #{tmp_elf} && \
|
73
|
-
rm -fr #{dir}
|
95
|
+
rm -fr #{dir}`)
|
96
|
+
end
|
97
|
+
|
98
|
+
def tcache_base
|
99
|
+
method_heap.call.base + 2 * size_t
|
74
100
|
end
|
75
101
|
end
|
76
102
|
end
|
data/lib/heapinfo/nil.rb
CHANGED
@@ -5,7 +5,7 @@ module HeapInfo
|
|
5
5
|
# to prevent use the return value for calculating accidentally while exploiting remote.
|
6
6
|
class Nil
|
7
7
|
%i[nil? inspect to_s].each do |method_sym|
|
8
|
-
define_method(method_sym) { |*args, &block| nil.
|
8
|
+
define_method(method_sym) { |*args, &block| nil.__send__(method_sym, *args, &block) }
|
9
9
|
end
|
10
10
|
|
11
11
|
# Hook all missing methods
|
@@ -15,7 +15,7 @@ module HeapInfo
|
|
15
15
|
# p h.dump(:heap)[8, 8].unpack('Q*')
|
16
16
|
# #=> nil
|
17
17
|
def method_missing(method_sym, *args, &block)
|
18
|
-
return nil.
|
18
|
+
return nil.__send__(method_sym, *args, &block) if nil.respond_to?(method_sym)
|
19
19
|
self || super
|
20
20
|
end
|
21
21
|
|
data/lib/heapinfo/process.rb
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
# encoding: ascii-8bit
|
2
2
|
|
3
|
+
require 'heapinfo/dumper'
|
4
|
+
require 'heapinfo/helper'
|
5
|
+
require 'heapinfo/nil'
|
6
|
+
require 'heapinfo/process_info'
|
7
|
+
|
3
8
|
module HeapInfo
|
4
9
|
# Main class of heapinfo.
|
5
10
|
class Process
|
@@ -110,15 +115,14 @@ module HeapInfo
|
|
110
115
|
# #=> 0x9637a0 after :heap
|
111
116
|
def offset(addr, sym = nil)
|
112
117
|
return unless load?
|
113
|
-
segment = @info.
|
114
|
-
segment = nil unless segment.is_a?(HeapInfo::Segment)
|
118
|
+
segment = @info.to_segment(sym)
|
115
119
|
if segment.nil?
|
116
120
|
sym, segment = @info.segments
|
117
121
|
.select { |_, seg| seg.base <= addr }
|
118
122
|
.min_by { |_, seg| addr - seg }
|
119
123
|
end
|
120
|
-
return puts "Invalid address #{Helper.hex(addr)}" if segment.nil?
|
121
|
-
puts Helper.color(Helper.hex(addr - segment)) + ' after ' + Helper.color(sym, sev: :sym)
|
124
|
+
return $stdout.puts "Invalid address #{Helper.hex(addr)}" if segment.nil?
|
125
|
+
$stdout.puts Helper.color(Helper.hex(addr - segment)) + ' after ' + Helper.color(sym, sev: :sym)
|
122
126
|
end
|
123
127
|
alias off offset
|
124
128
|
|
@@ -147,6 +151,19 @@ module HeapInfo
|
|
147
151
|
dumper.x(count, address)
|
148
152
|
end
|
149
153
|
|
154
|
+
# Gdb-like command
|
155
|
+
#
|
156
|
+
# Dump a string until reach the null-byte.
|
157
|
+
# @param [String, Symbol, Integer] address The base address to be dumped.
|
158
|
+
# See {#dump}.
|
159
|
+
#
|
160
|
+
# @return [String]
|
161
|
+
# The string *without* null-byte.
|
162
|
+
def s(address)
|
163
|
+
return Nil.new unless load?
|
164
|
+
dumper.cstring(address)
|
165
|
+
end
|
166
|
+
|
150
167
|
# Gdb-like command.
|
151
168
|
#
|
152
169
|
# Search a specific value/string/regexp in memory.
|
@@ -176,23 +193,51 @@ module HeapInfo
|
|
176
193
|
return Nil.new unless load?
|
177
194
|
dumper.find(pattern, from, length, rel)
|
178
195
|
end
|
179
|
-
|
180
|
-
# +search+ is more intutive to me
|
181
196
|
alias search find
|
182
197
|
|
183
|
-
#
|
198
|
+
# Find pattern in all segments with pretty output.
|
199
|
+
#
|
200
|
+
# @param [Integer, String, Regexp] pattern
|
201
|
+
# The desired search pattern, can be value(+Integer+), string, or regular expression.
|
202
|
+
# @param [Symbol, Array<Symbol>] segment
|
203
|
+
# Only find pattern in these symbols.
|
204
|
+
#
|
205
|
+
# @return [void]
|
206
|
+
def find_all(pattern, segment = :all)
|
207
|
+
return Nil.new unless load?
|
208
|
+
segments = segment == :all ? %i[elf heap libc ld stack] : Array(segment)
|
209
|
+
result = findall_raw(pattern, segments).reject { |(_, _, ary)| ary.empty? }
|
210
|
+
target = pattern.is_a?(Integer) ? Helper.hex(pattern) : pattern.inspect
|
211
|
+
str = ["Searching #{Helper.color(target)}:\n"]
|
212
|
+
str.concat(result.map do |(sym, base, ary)|
|
213
|
+
"In #{Helper.color(sym, sev: :bin)} (#{Helper.color(Helper.hex(base))}):\n" +
|
214
|
+
ary.map { |v| " #{Helper.color(sym, sev: :bin)}+#{Helper.color(Helper.hex(v))}\n" }.join
|
215
|
+
end)
|
216
|
+
$stdout.puts str
|
217
|
+
end
|
218
|
+
alias findall find_all
|
219
|
+
|
220
|
+
# Pretty dump of bins' layouts.
|
184
221
|
#
|
185
222
|
# The request layouts will output to +stdout+.
|
186
223
|
# @param [Array<Symbol>] args Bin type(s) you want to see.
|
187
224
|
# @return [void]
|
188
225
|
# @example
|
189
226
|
# h.layouts(:fast, :unsorted, :small)
|
227
|
+
# # ...
|
228
|
+
# h.layouts(:tcache)
|
229
|
+
# # ...
|
230
|
+
# h.layouts(:all) # show all bin(s), includes tcache
|
190
231
|
def layouts(*args)
|
191
232
|
return unless load?
|
192
|
-
|
233
|
+
str = ''
|
234
|
+
str << libc.tcache.layouts if libc.tcache? && (%w[all tcache] & args.map(&:to_s)).any?
|
235
|
+
str << libc.main_arena.layouts(*args)
|
236
|
+
$stdout.puts str
|
193
237
|
end
|
194
238
|
|
195
239
|
# Show simple information of target process.
|
240
|
+
#
|
196
241
|
# Contains program names, pid, and segments' info.
|
197
242
|
#
|
198
243
|
# @return [String]
|
@@ -257,7 +302,7 @@ module HeapInfo
|
|
257
302
|
|
258
303
|
def clear_process
|
259
304
|
ProcessInfo::EXPORT.each do |m|
|
260
|
-
self.class.
|
305
|
+
self.class.__send__(:define_method, m) { Nil.new }
|
261
306
|
end
|
262
307
|
false
|
263
308
|
end
|
@@ -265,13 +310,21 @@ module HeapInfo
|
|
265
310
|
def load_info! # :nodoc:
|
266
311
|
@info = ProcessInfo.new(self)
|
267
312
|
ProcessInfo::EXPORT.each do |m|
|
268
|
-
self.class.
|
313
|
+
self.class.__send__(:define_method, m) { @info.__send__(m) }
|
269
314
|
end
|
270
315
|
@dumper = Dumper.new(mem_filename) do |sym|
|
271
|
-
@info.
|
316
|
+
@info.__send__(sym) if @info.respond_to?(sym)
|
272
317
|
end
|
273
318
|
end
|
274
319
|
|
320
|
+
def findall_raw(pattern, segments)
|
321
|
+
segments.map do |sym|
|
322
|
+
seg = @info.to_segment(sym)
|
323
|
+
next unless seg
|
324
|
+
[sym, seg.base, dumper.scan(pattern, sym, :unlimited)]
|
325
|
+
end.compact
|
326
|
+
end
|
327
|
+
|
275
328
|
def mem_filename
|
276
329
|
"/proc/#{pid}/mem"
|
277
330
|
end
|
@@ -1,5 +1,11 @@
|
|
1
1
|
# encoding: ascii-8bit
|
2
2
|
|
3
|
+
require 'stringio'
|
4
|
+
|
5
|
+
require 'heapinfo/segment'
|
6
|
+
require 'heapinfo/libc'
|
7
|
+
require 'heapinfo/helper'
|
8
|
+
|
3
9
|
module HeapInfo
|
4
10
|
# For {Process} to record basic process information.
|
5
11
|
#
|
@@ -42,7 +48,13 @@ module HeapInfo
|
|
42
48
|
@auxv = parse_auxv(Helper.auxv_of(@pid))
|
43
49
|
ld_seg = maps.find { |m| m[0] == @auxv[:ld_base] } # nil if static-linked elf
|
44
50
|
@ld = ld_seg.nil? ? Nil.new : Segment.new(@auxv[:ld_base], ld_seg.last)
|
45
|
-
@libc = Libc.find(
|
51
|
+
@libc = Libc.find(
|
52
|
+
maps, match_maps(maps, options[:libc]),
|
53
|
+
bits: @bits,
|
54
|
+
ld_name: @ld.name,
|
55
|
+
dumper: ->(*args) { process.dump(*args) },
|
56
|
+
method_heap: method(:heap)
|
57
|
+
)
|
46
58
|
end
|
47
59
|
|
48
60
|
# Heap will not be mmapped if the process not use heap yet, so create a lazy loading method.
|
@@ -59,11 +71,22 @@ module HeapInfo
|
|
59
71
|
# @return [Hash{Symbol => Segment}] The segments in hash format.
|
60
72
|
def segments
|
61
73
|
EXPORT.map do |sym|
|
62
|
-
seg =
|
74
|
+
seg = __send__(sym)
|
63
75
|
[sym, seg] if seg.is_a?(Segment)
|
64
76
|
end.compact.to_h
|
65
77
|
end
|
66
78
|
|
79
|
+
# Convert symbol to segment.
|
80
|
+
#
|
81
|
+
# @return [HeapInfo::Segment?]
|
82
|
+
# The segment object.
|
83
|
+
def to_segment(sym)
|
84
|
+
return nil unless EXPORT.include?(sym)
|
85
|
+
seg = __send__(sym)
|
86
|
+
return nil unless seg.is_a?(Segment)
|
87
|
+
seg
|
88
|
+
end
|
89
|
+
|
67
90
|
private
|
68
91
|
|
69
92
|
def load_maps
|
data/lib/heapinfo/segment.rb
CHANGED
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'heapinfo/arena'
|
2
|
+
|
3
|
+
module HeapInfo
|
4
|
+
# Fetch tcache structure and show its content.
|
5
|
+
class Tcache
|
6
|
+
# #define TCACHE_MAX_BINS 64
|
7
|
+
MAX_BINS = 64
|
8
|
+
# Instantiate a {HeapInfo::Tcache} object.
|
9
|
+
#
|
10
|
+
# @param [Integer] base Base address of +tcache+.
|
11
|
+
# @param [Integer] size_t Either 8 or 4.
|
12
|
+
# @param [Proc] dumper For dumping more data.
|
13
|
+
def initialize(base, size_t, dumper)
|
14
|
+
@base = base
|
15
|
+
@size_t = size_t
|
16
|
+
@dumper = dumper
|
17
|
+
end
|
18
|
+
|
19
|
+
# Pretty dump of tcache entries.
|
20
|
+
#
|
21
|
+
# @return [String] Tcache entries that wrapper with color codes.
|
22
|
+
def layouts
|
23
|
+
entries.map(&:inspect).join
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def entries
|
29
|
+
Array.new(MAX_BINS) do |idx|
|
30
|
+
TcacheEntry.new(@size_t, @base + 64 + @size_t * idx, @dumper, head: true).tap { |f| f.index = idx }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# A tcache entry.
|
36
|
+
#
|
37
|
+
# Though this class inherited from {Chunk} ({Fastbin}), tcache entries are *not* chunks.
|
38
|
+
class TcacheEntry < Fastbin
|
39
|
+
# For pretty inspect.
|
40
|
+
#
|
41
|
+
# @return [String]
|
42
|
+
# Empty string is returned if this entry contains nothing.
|
43
|
+
def inspect
|
44
|
+
return '' if fd_of(@base).zero? # empty
|
45
|
+
super
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
# Hack +fd_of+ method here then everything works.
|
51
|
+
def fd_of(ptr)
|
52
|
+
addr_of(ptr, 0)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
/*
|
2
|
+
@author: david942j
|
3
|
+
Getting libc's information, such as:
|
4
|
+
- main_arena offset
|
5
|
+
- is tcache enabled?
|
6
|
+
Sample Usage
|
7
|
+
> ./libc_info
|
8
|
+
> LD_LIBRARY_PATH=. ./libc_info
|
9
|
+
> ./ld-linux.so.2 --library-path . ./libc_info
|
10
|
+
*/
|
11
|
+
#include <stddef.h>
|
12
|
+
#include <stdio.h>
|
13
|
+
#include <stdlib.h>
|
14
|
+
#include <string.h>
|
15
|
+
|
16
|
+
#define SZ sizeof(size_t)
|
17
|
+
#define PAGE_SIZE 0x1000
|
18
|
+
|
19
|
+
void *search_head(size_t e) {
|
20
|
+
e = (e >> 12) << 12;
|
21
|
+
while(strncmp((void*)e, "\177ELF", 4)) e -= PAGE_SIZE;
|
22
|
+
return (void*) e;
|
23
|
+
}
|
24
|
+
|
25
|
+
void* main_arena_offset() {
|
26
|
+
void **p = (void**)malloc(SZ * 128 * 2); // a large chunk
|
27
|
+
void *z = malloc(SZ); // prevent p merge with top chunk
|
28
|
+
*p = z; // prevent compiler optimize
|
29
|
+
free(p); // now *p must be the pointer of the (chunk_ptr) unsorted bin
|
30
|
+
z = (void*)((*p) - (4 + 4 + SZ * 10)); // mutex+flags+fastbin[]
|
31
|
+
void* a = search_head((size_t)__builtin_return_address(1));
|
32
|
+
return (void*)(z - a);
|
33
|
+
}
|
34
|
+
|
35
|
+
int tcache_enable() {
|
36
|
+
void **p = malloc(SZ * 32); // smallbin size
|
37
|
+
*p = (void*) 0xdeadbeefu;
|
38
|
+
// if tcache is enabled, this free will put p into tcache_entry;
|
39
|
+
// otherwise, either merge with top_chunk or put into unsorted_bin
|
40
|
+
free(p);
|
41
|
+
if(*p == 0) return 1; // tcache_entry, fd set as zero
|
42
|
+
return 0;
|
43
|
+
}
|
44
|
+
|
45
|
+
int main(int argc, char **argv) {
|
46
|
+
printf("{" \
|
47
|
+
"\"main_arena_offset\": %u," \
|
48
|
+
"\"tcache_enable\": %s" \
|
49
|
+
"}\n", main_arena_offset(), tcache_enable() ? "true" : "false");
|
50
|
+
return 0;
|
51
|
+
}
|
data/lib/heapinfo/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: heapinfo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- david942j
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-05-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dentaku
|
@@ -16,84 +16,84 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '3'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '3'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '12.3'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '12.3'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: rspec
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
47
|
+
version: '3.7'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
54
|
+
version: '3.7'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: rubocop
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
61
|
+
version: '0.54'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
68
|
+
version: '0.54'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
70
|
+
name: simplecov
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version:
|
75
|
+
version: 0.16.0
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version:
|
82
|
+
version: 0.16.0
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
84
|
+
name: yard
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
87
|
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: 0.
|
89
|
+
version: '0.9'
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: 0.
|
96
|
+
version: '0.9'
|
97
97
|
description: |
|
98
98
|
Create an interactive memory info interface while pwn / exploiting.
|
99
99
|
Useful for rubiers writing exploit scripts.
|
@@ -123,7 +123,8 @@ files:
|
|
123
123
|
- lib/heapinfo/process.rb
|
124
124
|
- lib/heapinfo/process_info.rb
|
125
125
|
- lib/heapinfo/segment.rb
|
126
|
-
- lib/heapinfo/
|
126
|
+
- lib/heapinfo/tcache.rb
|
127
|
+
- lib/heapinfo/tools/libc_info.c
|
127
128
|
- lib/heapinfo/version.rb
|
128
129
|
homepage: https://github.com/david942j/heapinfo
|
129
130
|
licenses:
|
@@ -1,28 +0,0 @@
|
|
1
|
-
/*
|
2
|
-
@author: david942j
|
3
|
-
use for get libc's main_arena offset
|
4
|
-
Sample Usage
|
5
|
-
> ./get_arena
|
6
|
-
> LD_LIBRARY_PATH=. ./get_arena
|
7
|
-
> ./ld-linux.so.2 --library-path . ./get_arena
|
8
|
-
*/
|
9
|
-
#include <stdlib.h>
|
10
|
-
#include <stdio.h>
|
11
|
-
#include <stddef.h>
|
12
|
-
#define SZ sizeof(size_t)
|
13
|
-
#define PAGE_SIZE 0x1000
|
14
|
-
void *search_head(size_t e) {
|
15
|
-
e = (e >> 12) << 12;
|
16
|
-
while(strncmp((void*)e, "\177ELF", 4)) e -= PAGE_SIZE;
|
17
|
-
return (void*) e;
|
18
|
-
}
|
19
|
-
int main() {
|
20
|
-
void **p = (void**)malloc(SZ*16); // small bin with chunk size SZ*18
|
21
|
-
void *z = malloc(SZ); // prevent p merge with top chunk
|
22
|
-
*p = z; // prevent compiler optimize
|
23
|
-
free(p); // now *p must be the pointer of the (chunk_ptr) unsorted bin
|
24
|
-
z = (void*)((*p) - (4 + 4 + SZ * 10 )); // mutex+flags+fastbin[]
|
25
|
-
void* a = search_head((size_t)__builtin_return_address(0));
|
26
|
-
printf("%p\n", z-a);
|
27
|
-
return 0;
|
28
|
-
}
|