heapinfo 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/heapinfo/arena.rb +15 -9
- data/lib/heapinfo/chunk.rb +21 -0
- data/lib/heapinfo/glibc/error.rb +11 -0
- data/lib/heapinfo/glibc/free.rb +77 -0
- data/lib/heapinfo/glibc/glibc.rb +11 -0
- data/lib/heapinfo/glibc/helper.rb +44 -0
- data/lib/heapinfo/helper.rb +10 -0
- data/lib/heapinfo/libc.rb +12 -8
- data/lib/heapinfo/process.rb +19 -1
- data/lib/heapinfo/process_info.rb +1 -1
- data/lib/heapinfo/version.rb +1 -1
- data/lib/heapinfo.rb +1 -0
- data/spec/libc_spec.rb +54 -0
- data/spec/process_spec.rb +18 -1
- metadata +15 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e90a4db825f5e6b53989edc82441a0800d13aaa7
|
4
|
+
data.tar.gz: f638662fb165a7c884c5427b378cfac091ebe83a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f2356a124a5a9480e2e170f6abb2ee782f671734d4e7edbe2f15f47f690609a40580ac1d3d8f027342d03a82901c996b32d9dc21dccb4473105f027814149503
|
7
|
+
data.tar.gz: 3e3df55aca650bf6c8277185afdf56bb3f9c4e3ded9ec444af64f6159302aa9eb5d001da0f3f7dac82d174348c5e8af5b4340abfa1a5cca6f205a7936fa640b0
|
data/lib/heapinfo/arena.rb
CHANGED
@@ -3,22 +3,25 @@ module HeapInfo
|
|
3
3
|
class Arena
|
4
4
|
# @return [Array<HeapInfo::Fastbin>] Fastbins in an array.
|
5
5
|
attr_reader :fastbin
|
6
|
-
# @return [
|
6
|
+
# @return [HeapInfo::Chunk] Current top chunk.
|
7
|
+
attr_reader :top_chunk
|
8
|
+
# @return [HeapInfo::Chunk] Current last remainder.
|
9
|
+
attr_reader :last_remainder
|
10
|
+
# @return [Array<HeapInfo::UnsortedBin>] The unsorted bin (array size will always be one).
|
7
11
|
attr_reader :unsorted_bin
|
8
12
|
# @return [Array<HeapInfo::Smallbin>] Smallbins in an array.
|
9
13
|
attr_reader :smallbin
|
10
|
-
# @return [
|
11
|
-
attr_reader :
|
14
|
+
# @return [Integer] The <tt>system_mem</tt>
|
15
|
+
attr_reader :system_mem
|
12
16
|
# attr_reader :largebin, :last_remainder
|
13
17
|
|
14
18
|
# Instantiate a <tt>HeapInfo::Arena</tt> object.
|
15
19
|
#
|
16
20
|
# @param [Integer] base Base address of arena.
|
17
|
-
# @param [Integer]
|
21
|
+
# @param [Integer] size_t Either 8 or 4
|
18
22
|
# @param [Proc] dumper For dump more data
|
19
|
-
def initialize(base,
|
20
|
-
@base, @dumper = base, dumper
|
21
|
-
@size_t = bits / 8
|
23
|
+
def initialize(base, size_t, dumper)
|
24
|
+
@base, @size_t, @dumper = base, size_t, dumper
|
22
25
|
reload!
|
23
26
|
end
|
24
27
|
|
@@ -26,16 +29,19 @@ module HeapInfo
|
|
26
29
|
# Retrive data using <tt>@dumper</tt>, load bins, top chunk etc.
|
27
30
|
# @return [HeapInfo::Arena] self
|
28
31
|
def reload!
|
29
|
-
|
32
|
+
top_ptr_offset = @base + 8 + size_t * 10
|
33
|
+
top_ptr = Helper.unpack(size_t, @dumper.call(top_ptr_offset, size_t))
|
30
34
|
@fastbin = []
|
31
35
|
return self if top_ptr == 0 # arena not init yet
|
32
36
|
@top_chunk = Chunk.new size_t, top_ptr, @dumper
|
37
|
+
@last_remainder = Chunk.new size_t, top_ptr_offset + 8, @dumper
|
38
|
+
@system_mem = Helper.unpack(size_t, @dumper.call(top_ptr_offset + 258 * size_t + 16, size_t))
|
33
39
|
@fastbin = Array.new(7) do |idx|
|
34
40
|
f = Fastbin.new(size_t, @base + 8 - size_t * 2 + size_t * idx, @dumper, head: true)
|
35
41
|
f.index = idx
|
36
42
|
f
|
37
43
|
end
|
38
|
-
@unsorted_bin = UnsortedBin.new(size_t,
|
44
|
+
@unsorted_bin = UnsortedBin.new(size_t, top_ptr_offset, @dumper, head: true)
|
39
45
|
@smallbin = Array.new(55) do |idx|
|
40
46
|
s = Smallbin.new(size_t, @base + 8 + size_t * (26 + 2 * idx), @dumper, head: true)
|
41
47
|
s.index = idx
|
data/lib/heapinfo/chunk.rb
CHANGED
@@ -61,6 +61,27 @@ module HeapInfo
|
|
61
61
|
flag
|
62
62
|
end
|
63
63
|
|
64
|
+
# Ask if chunk not belongs to main arena.
|
65
|
+
#
|
66
|
+
# @return [Boolean] <tt>true|false</tt> if chunk not belongs to main arena
|
67
|
+
def non_main_arena?
|
68
|
+
flags.include? :non_main_arena
|
69
|
+
end
|
70
|
+
|
71
|
+
# Ask if chunk is mmapped.
|
72
|
+
#
|
73
|
+
# @return [Boolean] <tt>true|false</tt> if chunk is mmapped
|
74
|
+
def mmapped?
|
75
|
+
flags.include? :mmapped
|
76
|
+
end
|
77
|
+
|
78
|
+
# Ask if chunk has set the prev-inuse bit.
|
79
|
+
#
|
80
|
+
# @return [Boolean] <tt>true|false</tt> if the <tt>prev_inuse</tt> bit has been set
|
81
|
+
def prev_inuse?
|
82
|
+
flags.include? :prev_inuse
|
83
|
+
end
|
84
|
+
|
64
85
|
# Size of chunk
|
65
86
|
# @return [Integer] The chunk size without flag masks
|
66
87
|
def size
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module HeapInfo
|
2
|
+
module Glibc
|
3
|
+
# @abstract Exceptions raised by HeapInfo inherit from Error
|
4
|
+
class Error < StandardError; end
|
5
|
+
# Exception raised in malloc.c(malloc, free, etc.) methods.
|
6
|
+
class MallocError < Error; end
|
7
|
+
def malloc_assert(condition)
|
8
|
+
raise MallocError.new(yield) unless condition
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# Implment glibc's free-related functions
|
2
|
+
# [Reference](https://code.woboq.org/userspace/glibc/malloc/malloc.c.html)
|
3
|
+
module HeapInfo
|
4
|
+
module Glibc
|
5
|
+
# Implmentation of <tt>void __libc_free(void *mem)</tt>.
|
6
|
+
# [Source](https://code.woboq.org/userspace/glibc/malloc/malloc.c.html#__libc_free)
|
7
|
+
# @param [Integer] mem Memory address to be free.
|
8
|
+
def libc_free(mem)
|
9
|
+
# TODO: free_hook
|
10
|
+
mem = ulong mem
|
11
|
+
return if mem == 0 # free(0) has no effect
|
12
|
+
ptr = mem2chunk(mem)
|
13
|
+
return munmap_chunk(ptr) if chunk_is_mmapped(ptr)
|
14
|
+
av = arena_for_chunk(ptr)
|
15
|
+
int_free(av, ptr)
|
16
|
+
end
|
17
|
+
alias :free :libc_free
|
18
|
+
|
19
|
+
private
|
20
|
+
# Implmentation of <tt>void _int_free (mstate av, mchunkptr p, [int have_lock])</tt>.
|
21
|
+
# [Source](https://code.woboq.org/userspace/glibc/malloc/malloc.c.html#_int_free)
|
22
|
+
#
|
23
|
+
# The original method in C is too long, split to multiple methods to match ruby convention.
|
24
|
+
# @param [HeapInfo::Arena] av
|
25
|
+
# @param [Integer] ptr Use <tt>ptr</tt> instead of <tt>p</tt> to prevent confusing with ruby native method.
|
26
|
+
def int_free(av, ptr) # is have_lock important?
|
27
|
+
chunk = dumper.call(ptr, size_t * 2).to_chunk
|
28
|
+
size = ulong chunk.size
|
29
|
+
invalid_pointer(ptr, size)
|
30
|
+
invalid_size(size)
|
31
|
+
# check_inuse_chunk # off
|
32
|
+
if size <= get_max_fast
|
33
|
+
int_free_fast(av, ptr, size)
|
34
|
+
elsif !chunk_is_mmapped(ptr) # Though this has been checked in #libc_free
|
35
|
+
int_free_small(av, ptr, size)
|
36
|
+
else
|
37
|
+
munmap_chunk(ptr)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def int_free_fast(av, ptr, size)
|
42
|
+
invalid_next_size(:fast, av, ptr, size)
|
43
|
+
true
|
44
|
+
end
|
45
|
+
|
46
|
+
def int_free_small(av, ptr, size)
|
47
|
+
true
|
48
|
+
end
|
49
|
+
|
50
|
+
def munmap_chunk(ptr)
|
51
|
+
# TODO: check page alignment and... page exists?
|
52
|
+
true
|
53
|
+
end
|
54
|
+
|
55
|
+
# Start of checkers
|
56
|
+
|
57
|
+
def invalid_pointer(ptr, size)
|
58
|
+
errmsg = "free(): invalid pointer\n"
|
59
|
+
# unsigned compare
|
60
|
+
malloc_assert(ptr <= ulong(-size)) { errmsg + "ptr(0x%x) > -size(0x%x)" % [ptr, ulong(-size)] }
|
61
|
+
malloc_assert(ptr % (size_t * 2) == 0) { errmsg + "ptr(0x%x) %% %d != 0" % [ptr, size_t * 2] }
|
62
|
+
end
|
63
|
+
|
64
|
+
def invalid_size(size)
|
65
|
+
errmsg = "free(): invalid size\n"
|
66
|
+
malloc_assert(size >= min_chunk_size) { errmsg + "size(0x%x) < min_chunk_size(0x%x)" % [size, min_chunk_size] }
|
67
|
+
malloc_assert(aligned_ok size) { errmsg + "alignment error: size(0x%x) %% 0x%x != 0" % [size, size_t * 2] }
|
68
|
+
end
|
69
|
+
|
70
|
+
def invalid_next_size(type, av, ptr, size)
|
71
|
+
errmsg = "free(): invalid next size (#{type})\n"
|
72
|
+
nxt_chk = dumper.call(ptr + size, size_t * 2).to_chunk(base: ptr + size)
|
73
|
+
malloc_assert(nxt_chk.size > 2 * size_t) { errmsg + "next chunk(0x%x) has size(#{nxt_chk.size}) <= 2 * #{size_t}" % nxt_chk.base }
|
74
|
+
malloc_assert(nxt_chk.size < av.system_mem) { errmsg + "next chunk(0x%x) has size(0x%x) >= av.system_mem(0x%x)" % [nxt_chk.base, nxt_chk.size, av.system_mem] }
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module HeapInfo
|
2
|
+
module Glibc
|
3
|
+
private
|
4
|
+
def mem2chunk(mem)
|
5
|
+
ulong(mem - 2 * size_t)
|
6
|
+
end
|
7
|
+
|
8
|
+
# @return [Boolean]
|
9
|
+
def chunk_is_mmapped(ptr)
|
10
|
+
# TODO: handle memory not accessible
|
11
|
+
dumper.call(ptr, size_t * 2).to_chunk.mmapped?
|
12
|
+
end
|
13
|
+
|
14
|
+
def get_max_fast
|
15
|
+
size_t * 16
|
16
|
+
end
|
17
|
+
|
18
|
+
# The minimal chunk size.
|
19
|
+
# Not the real implmentation, maybe wrong some day?
|
20
|
+
# @return [Integer] The size.
|
21
|
+
def min_chunk_size
|
22
|
+
size_t * 4
|
23
|
+
end
|
24
|
+
|
25
|
+
# Not the real implmentation, maybe wrong some day?
|
26
|
+
# @return [Boolean]
|
27
|
+
def aligned_ok(size)
|
28
|
+
size & (2 * size_t - 1) == 0
|
29
|
+
end
|
30
|
+
|
31
|
+
# @return [Integer]
|
32
|
+
def ulong(n)
|
33
|
+
n % 2 ** (size_t * 8)
|
34
|
+
end
|
35
|
+
|
36
|
+
# @return [HeapInfo::Arena]
|
37
|
+
def arena_for_chunk(ptr)
|
38
|
+
# not support arena other than initial main_arena
|
39
|
+
return if dumper.call(ptr, size_t * 2).to_chunk.non_main_arena?
|
40
|
+
main_arena
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
data/lib/heapinfo/helper.rb
CHANGED
@@ -93,6 +93,16 @@ module HeapInfo
|
|
93
93
|
data.unpack(size_t == 4 ? 'L*' : 'Q*')[0]
|
94
94
|
end
|
95
95
|
|
96
|
+
# Convert number in hex format
|
97
|
+
#
|
98
|
+
# @param [Integer] num Non-negative integer.
|
99
|
+
# @return [String] number in hex format.
|
100
|
+
# @example
|
101
|
+
# HeapInfo::Helper.hex(1000) # => '0x3e8'
|
102
|
+
def self.hex(num)
|
103
|
+
'0x' + num.to_s(16)
|
104
|
+
end
|
105
|
+
|
96
106
|
# Retrieve pure class name(without module) of an object
|
97
107
|
# @param [Object] obj Any instance
|
98
108
|
# @return [String] Class name of <tt>obj</tt>
|
data/lib/heapinfo/libc.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module HeapInfo
|
2
2
|
# Record libc's base, name, and offsets.
|
3
3
|
class Libc < Segment
|
4
|
-
|
4
|
+
include HeapInfo::Glibc
|
5
5
|
# Instantiate a <tt>HeapInfo::Libc</tt> object.
|
6
6
|
#
|
7
7
|
# @param [Mixed] args See <tt>#HeapInfo::Segment.initialize</tt> for more information.
|
@@ -24,21 +24,25 @@ module HeapInfo
|
|
24
24
|
return @main_arena.reload! if @main_arena
|
25
25
|
off = main_arena_offset
|
26
26
|
return if off.nil?
|
27
|
-
@main_arena = Arena.new(off + self.base,
|
27
|
+
@main_arena = Arena.new(off + self.base, size_t, dumper)
|
28
28
|
end
|
29
29
|
|
30
30
|
# @param [Array] maps See <tt>#HeapInfo::Segment.find</tt> for more information.
|
31
31
|
# @param [String] name See <tt>#HeapInfo::Segment.find</tt> for more information.
|
32
|
-
# @param [
|
32
|
+
# @param [Integer] bits Either 64 or 32.
|
33
|
+
# @param [String] ld_name The loader's realpath, will be used for running subprocesses.
|
34
|
+
# @param [Proc] dumper The memory dumper for fetch more information.
|
33
35
|
# @return [HeapInfo::Libc] libc segment found in maps.
|
34
|
-
def self.find(maps, name,
|
36
|
+
def self.find(maps, name, bits, ld_name, dumper)
|
35
37
|
obj = super(maps, name)
|
36
|
-
obj.
|
38
|
+
obj.size_t = bits / 8
|
39
|
+
obj.send(:ld_name=, ld_name)
|
40
|
+
obj.send(:dumper=, dumper)
|
37
41
|
obj
|
38
42
|
end
|
39
43
|
|
40
44
|
private
|
41
|
-
attr_accessor :
|
45
|
+
attr_accessor :ld_name
|
42
46
|
# only for searching offset of main_arena now
|
43
47
|
def exhaust_search(symbol)
|
44
48
|
return false if symbol != :main_arena
|
@@ -58,9 +62,9 @@ module HeapInfo
|
|
58
62
|
tmp_elf = HeapInfo::TMP_DIR + "/get_arena"
|
59
63
|
libc_file = HeapInfo::TMP_DIR + "/libc.so.6"
|
60
64
|
ld_file = HeapInfo::TMP_DIR + "/ld.so"
|
61
|
-
flags = "-w #{
|
65
|
+
flags = "-w #{size_t == 4 ? '-m32' : ''}"
|
62
66
|
%x(cp #{self.name} #{libc_file} && \
|
63
|
-
cp #{
|
67
|
+
cp #{ld_name} #{ld_file} && \
|
64
68
|
gcc #{flags} #{File.expand_path('../tools/get_arena.c', __FILE__)} -o #{tmp_elf} 2>&1 > /dev/null && \
|
65
69
|
#{ld_file} --library-path #{HeapInfo::TMP_DIR} #{tmp_elf} && \
|
66
70
|
rm #{tmp_elf} #{libc_file} #{ld_file}).to_i(16)
|
data/lib/heapinfo/process.rb
CHANGED
@@ -21,6 +21,17 @@ module HeapInfo
|
|
21
21
|
return unless load?
|
22
22
|
end
|
23
23
|
|
24
|
+
# Reload a new process with same program name
|
25
|
+
#
|
26
|
+
# @return [HeapInfo::Process] return <tt>self</tt> for chainable.
|
27
|
+
# @example
|
28
|
+
# puts h.reload!
|
29
|
+
def reload!
|
30
|
+
@pid = nil
|
31
|
+
load!
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
24
35
|
# Use this method to wrapper all HeapInfo methods.
|
25
36
|
#
|
26
37
|
# Since <tt>::HeapInfo</tt> is a tool(debugger) for local usage,
|
@@ -164,7 +175,7 @@ module HeapInfo
|
|
164
175
|
def load! # try to load
|
165
176
|
return true if @pid
|
166
177
|
@pid = fetch_pid
|
167
|
-
return
|
178
|
+
return clear_process if @pid.nil? # still can't load
|
168
179
|
load_info!
|
169
180
|
true
|
170
181
|
end
|
@@ -179,6 +190,13 @@ module HeapInfo
|
|
179
190
|
pid
|
180
191
|
end
|
181
192
|
|
193
|
+
def clear_process
|
194
|
+
ProcessInfo::EXPORT.each do |m|
|
195
|
+
self.class.send(:define_method, m) {Nil.new}
|
196
|
+
end
|
197
|
+
false
|
198
|
+
end
|
199
|
+
|
182
200
|
def load_info!
|
183
201
|
@info = ProcessInfo.new(self)
|
184
202
|
ProcessInfo::EXPORT.each do |m|
|
@@ -32,8 +32,8 @@ module HeapInfo
|
|
32
32
|
@stack = Segment.find(maps, '[stack]')
|
33
33
|
# well.. stack is a strange case because it will grow in runtime..
|
34
34
|
# should i detect stack base growing..?
|
35
|
-
@libc = Libc.find(maps, match_maps(maps, options[:libc]), process)
|
36
35
|
@ld = Segment.find(maps, match_maps(maps, options[:ld]))
|
36
|
+
@libc = Libc.find(maps, match_maps(maps, options[:libc]), @bits, @ld.name, ->(*args){ process.dump(*args) })
|
37
37
|
end
|
38
38
|
|
39
39
|
# Heap will not be mmapped if the process not use heap yet, so create a lazy loading method.
|
data/lib/heapinfo/version.rb
CHANGED
data/lib/heapinfo.rb
CHANGED
data/spec/libc_spec.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
require 'heapinfo'
|
3
|
+
describe HeapInfo::Libc do
|
4
|
+
describe 'free' do
|
5
|
+
before(:all) do
|
6
|
+
HeapInfo::Cache.send :clear_all # force cache miss, to make sure coverage
|
7
|
+
@victim = HeapInfo::TMP_DIR + '/victim'
|
8
|
+
%x(g++ #{File.expand_path('../files/victim.cpp', __FILE__)} -o #{@victim} 2>&1 > /dev/null)
|
9
|
+
pid = fork
|
10
|
+
# run without ASLR
|
11
|
+
exec "setarch `uname -m` -R /bin/sh -c #{@victim}" if pid.nil?
|
12
|
+
loop until `pidof #{@victim}` != ''
|
13
|
+
@h = HeapInfo::Process.new(@victim, ld: '/ld')
|
14
|
+
@fake_mem = 0x13371000
|
15
|
+
@set_memory = ->(str) do
|
16
|
+
@h.libc.send(:dumper=, ->(ptr, len){
|
17
|
+
if ptr.between?(@fake_mem, @fake_mem + 0x1000)
|
18
|
+
str[ptr - @fake_mem, len]
|
19
|
+
else
|
20
|
+
@h.dump(ptr, len)
|
21
|
+
end
|
22
|
+
})
|
23
|
+
end
|
24
|
+
end
|
25
|
+
after(:all) do
|
26
|
+
`killall #{@victim}`
|
27
|
+
FileUtils.rm(@victim)
|
28
|
+
end
|
29
|
+
|
30
|
+
describe 'invalid' do
|
31
|
+
it 'invalid pointer' do
|
32
|
+
@set_memory.call [0, 0x21, 0x21, 0x0, 0x0].pack("Q*")
|
33
|
+
expect {@h.libc.free(@fake_mem + 24)}.to raise_error "free(): invalid pointer\nptr(#{HeapInfo::Helper.hex(@fake_mem + 8)}) % 16 != 0"
|
34
|
+
expect {@h.libc.free(@fake_mem + 32)}.to raise_error "free(): invalid pointer\nptr(#{HeapInfo::Helper.hex(@fake_mem + 16)}) > -size(0x0)"
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'invalid size' do
|
38
|
+
@set_memory.call [0, 0x11].pack("Q*")
|
39
|
+
expect {@h.libc.free(@fake_mem + 16)}.to raise_error "free(): invalid size\nsize(0x10) < min_chunk_size(0x20)"
|
40
|
+
@set_memory.call [0, 0x38].pack("Q*")
|
41
|
+
expect {@h.libc.free(@fake_mem + 16)}.to raise_error "free(): invalid size\nalignment error: size(0x38) % 0x10 != 0"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe 'fast' do
|
46
|
+
it 'invalid next size' do
|
47
|
+
@set_memory.call [0, 0x21, 0, 0, 0, 0xf].pack("Q*")
|
48
|
+
expect {@h.libc.free(@fake_mem + 16)}.to raise_error "free(): invalid next size (fast)\nnext chunk(#{HeapInfo::Helper.hex(@fake_mem + 32)}) has size(8) <= 2 * 8"
|
49
|
+
@set_memory.call [0, 0x21, 0, 0, 0, 0x21000].pack("Q*")
|
50
|
+
expect {@h.libc.free(@fake_mem + 16)}.to raise_error "free(): invalid next size (fast)\nnext chunk(#{HeapInfo::Helper.hex(@fake_mem + 32)}) has size(0x21000) >= av.system_mem(0x21000)"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/spec/process_spec.rb
CHANGED
@@ -38,7 +38,7 @@ describe HeapInfo::Process do
|
|
38
38
|
@io = Cio.new
|
39
39
|
end
|
40
40
|
after(:all) do
|
41
|
-
|
41
|
+
`killall #{@victim}`
|
42
42
|
FileUtils.rm(@victim)
|
43
43
|
end
|
44
44
|
|
@@ -84,6 +84,19 @@ describe HeapInfo::Process do
|
|
84
84
|
end
|
85
85
|
end
|
86
86
|
|
87
|
+
describe 'reload' do
|
88
|
+
it 'monkey' do
|
89
|
+
prog = File.readlink('/proc/self/exe')
|
90
|
+
@h = HeapInfo::Process.new(prog)
|
91
|
+
expect(@h.pid.is_a? Integer).to be true
|
92
|
+
pid = @h.pid
|
93
|
+
@h.instance_variable_set(:@prog, 'NO_THIS')
|
94
|
+
expect(@h.reload!.pid).to be nil
|
95
|
+
@h.instance_variable_set(:@prog, prog)
|
96
|
+
expect(@h.reload!.pid).to be pid
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
87
100
|
describe 'fastbin' do
|
88
101
|
it 'normal' do
|
89
102
|
expect(@h.libc.main_arena.fastbin[0].list).to eq [0x602020, 0x602000, nil]
|
@@ -154,5 +167,9 @@ describe HeapInfo::Process do
|
|
154
167
|
it 'nil chain' do
|
155
168
|
expect(@h.dump(:heap).no_such_method.xdd.nil?).to be true
|
156
169
|
end
|
170
|
+
|
171
|
+
it 'info methods' do
|
172
|
+
expect(@h.libc.base.nil?).to be true
|
173
|
+
end
|
157
174
|
end
|
158
175
|
end
|
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: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- david942j
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-12-20 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: create an interactive memory info interface while pwn / exploiting
|
14
14
|
email:
|
@@ -25,6 +25,10 @@ files:
|
|
25
25
|
- lib/heapinfo/chunks.rb
|
26
26
|
- lib/heapinfo/dumper.rb
|
27
27
|
- lib/heapinfo/ext/string.rb
|
28
|
+
- lib/heapinfo/glibc/error.rb
|
29
|
+
- lib/heapinfo/glibc/free.rb
|
30
|
+
- lib/heapinfo/glibc/glibc.rb
|
31
|
+
- lib/heapinfo/glibc/helper.rb
|
28
32
|
- lib/heapinfo/helper.rb
|
29
33
|
- lib/heapinfo/libc.rb
|
30
34
|
- lib/heapinfo/nil.rb
|
@@ -41,6 +45,7 @@ files:
|
|
41
45
|
- spec/files/64bit_maps
|
42
46
|
- spec/files/victim.cpp
|
43
47
|
- spec/helper_spec.rb
|
48
|
+
- spec/libc_spec.rb
|
44
49
|
- spec/nil_spec.rb
|
45
50
|
- spec/process_spec.rb
|
46
51
|
- spec/spec_helper.rb
|
@@ -65,20 +70,21 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
65
70
|
version: '0'
|
66
71
|
requirements: []
|
67
72
|
rubyforge_project:
|
68
|
-
rubygems_version: 2.
|
73
|
+
rubygems_version: 2.5.2
|
69
74
|
signing_key:
|
70
75
|
specification_version: 4
|
71
76
|
summary: HeapInfo - interactive heap exploitation helper
|
72
77
|
test_files:
|
73
|
-
- spec/chunk_spec.rb
|
74
78
|
- spec/files/32bit_maps
|
75
|
-
- spec/files/64bit_maps
|
76
79
|
- spec/files/victim.cpp
|
77
|
-
- spec/
|
80
|
+
- spec/files/64bit_maps
|
78
81
|
- spec/cache_spec.rb
|
82
|
+
- spec/dumper_spec.rb
|
83
|
+
- spec/process_spec.rb
|
84
|
+
- spec/chunk_spec.rb
|
85
|
+
- spec/chunks_spec.rb
|
79
86
|
- spec/string_spec.rb
|
80
87
|
- spec/spec_helper.rb
|
88
|
+
- spec/helper_spec.rb
|
81
89
|
- spec/nil_spec.rb
|
82
|
-
- spec/
|
83
|
-
- spec/dumper_spec.rb
|
84
|
-
- spec/process_spec.rb
|
90
|
+
- spec/libc_spec.rb
|