heapinfo 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +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
|