heapinfo 0.0.5 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/heapinfo/libc.rb CHANGED
@@ -2,15 +2,15 @@ module HeapInfo
2
2
  # Record libc's base, name, and offsets.
3
3
  class Libc < Segment
4
4
  include HeapInfo::Glibc
5
- # Instantiate a <tt>HeapInfo::Libc</tt> object.
5
+ # Instantiate a {HeapInfo::Libc} object.
6
6
  #
7
- # @param [Mixed] args See <tt>#HeapInfo::Segment.initialize</tt> for more information.
7
+ # @param [Mixed] args See {HeapInfo::Segment#initialize} for more information.
8
8
  def initialize(*args)
9
9
  super
10
10
  @offset = {}
11
11
  end
12
12
 
13
- # Get the offset of <tt>main_arena</tt> in libc.
13
+ # Get the offset of +main_arena+ in libc.
14
14
  # @return [Integer]
15
15
  def main_arena_offset
16
16
  return @offset[:main_arena] if @offset[:main_arena]
@@ -18,17 +18,17 @@ module HeapInfo
18
18
  @offset[:main_arena]
19
19
  end
20
20
 
21
- # Get the <tt>main_arena</tt> of libc.
21
+ # Get the +main_arena+ of libc.
22
22
  # @return [HeapInfo::Arena]
23
23
  def main_arena
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, size_t, dumper)
27
+ @main_arena = Arena.new(off + base, size_t, dumper)
28
28
  end
29
29
 
30
- # @param [Array] maps See <tt>#HeapInfo::Segment.find</tt> for more information.
31
- # @param [String] name See <tt>#HeapInfo::Segment.find</tt> for more information.
30
+ # @param [Array] maps See {HeapInfo::Segment#find} for more information.
31
+ # @param [String] name See {HeapInfo::Segment#find} for more information.
32
32
  # @param [Integer] bits Either 64 or 32.
33
33
  # @param [String] ld_name The loader's realpath, will be used for running subprocesses.
34
34
  # @param [Proc] dumper The memory dumper for fetch more information.
@@ -41,7 +41,8 @@ module HeapInfo
41
41
  obj
42
42
  end
43
43
 
44
- private
44
+ private
45
+
45
46
  attr_accessor :ld_name
46
47
  # only for searching offset of main_arena now
47
48
  def exhaust_search(symbol)
@@ -51,23 +52,23 @@ module HeapInfo
51
52
  end
52
53
 
53
54
  def read_main_arena_offset
54
- key = HeapInfo::Cache::key_libc_offset(self.name)
55
- @offset = HeapInfo::Cache::read(key) || {}
56
- return @offset[:main_arena] if @offset.key? :main_arena
55
+ key = HeapInfo::Cache.key_libc_offset(name)
56
+ @offset = HeapInfo::Cache.read(key) || {}
57
+ return @offset[:main_arena] if @offset.key?(:main_arena)
57
58
  @offset[:main_arena] = resolve_main_arena_offset
58
- HeapInfo::Cache::write key, @offset
59
+ HeapInfo::Cache.write(key, @offset)
59
60
  end
60
61
 
61
62
  def resolve_main_arena_offset
62
- tmp_elf = HeapInfo::TMP_DIR + "/get_arena"
63
- libc_file = HeapInfo::TMP_DIR + "/libc.so.6"
64
- ld_file = HeapInfo::TMP_DIR + "/ld.so"
63
+ tmp_elf = HeapInfo::TMP_DIR + '/get_arena'
64
+ libc_file = HeapInfo::TMP_DIR + '/libc.so.6'
65
+ ld_file = HeapInfo::TMP_DIR + '/ld.so'
65
66
  flags = "-w #{size_t == 4 ? '-m32' : ''}"
66
- %x(cp #{self.name} #{libc_file} && \
67
+ `cp #{name} #{libc_file} && \
67
68
  cp #{ld_name} #{ld_file} && \
68
69
  gcc #{flags} #{File.expand_path('../tools/get_arena.c', __FILE__)} -o #{tmp_elf} 2>&1 > /dev/null && \
69
70
  #{ld_file} --library-path #{HeapInfo::TMP_DIR} #{tmp_elf} && \
70
- rm #{tmp_elf} #{libc_file} #{ld_file}).to_i(16)
71
+ rm #{tmp_elf} #{libc_file} #{ld_file}`.to_i(16)
71
72
  end
72
73
  end
73
74
  end
data/lib/heapinfo/nil.rb CHANGED
@@ -1,24 +1,30 @@
1
1
  module HeapInfo
2
- # Self defined <tt>nil</tt> like class.
2
+ # Self define a +nil+ like class.
3
3
  #
4
- # Be the return values of <tt>#dump</tt> or <tt>#dump_chunks</tt>, to prevent use the return value for calculating accidentally while exploiting remote.
4
+ # Can be the return value of {HeapInfo::Process#dump} and {HeapInfo::Process#dump_chunks},
5
+ # to prevent use the return value for calculating accidentally while exploiting remote.
5
6
  class Nil
6
7
  %i(nil? inspect to_s).each do |method_sym|
7
- define_method(method_sym){|*args, &block| nil.send(method_sym, *args, &block)}
8
+ define_method(method_sym) { |*args, &block| nil.send(method_sym, *args, &block) }
8
9
  end
9
10
 
10
11
  # Hook all missing methods
11
- # @return [HeapInfo::Nil] return <tt>self</tt> so that it can be a <tt>nil</tt> chain.
12
+ # @return [HeapInfo::Nil] return +self+ so that it can be a +nil+ chain.
12
13
  # @example
13
14
  # # h.dump would return Nil when process not found
14
- # p h.dump(:heap)[8,8].unpack("Q*)
15
+ # p h.dump(:heap)[8, 8].unpack('Q*')
15
16
  # # => nil
16
17
  def method_missing(method_sym, *args, &block)
17
- return nil.send(method_sym, *args, &block) if nil.respond_to? method_sym
18
- self
18
+ return nil.send(method_sym, *args, &block) if nil.respond_to?(method_sym)
19
+ self || super
19
20
  end
20
21
 
21
- # To prevent error raised when using <tt>puts Nil.new</tt>
22
+ # Yap
23
+ def respond_to_missing?(*)
24
+ super
25
+ end
26
+
27
+ # To prevent error raised when using +puts Nil.new+.
22
28
  # @return [Array] Empty array
23
29
  def to_ary
24
30
  []
@@ -1,13 +1,13 @@
1
- #encoding: ascii-8bit
1
+ # encoding: ascii-8bit
2
2
  module HeapInfo
3
3
  # Main class of heapinfo.
4
4
  class Process
5
5
  # The default options of libaries,
6
6
  # use for matching glibc and ld segments in <tt>/proc/[pid]/maps</tt>
7
7
  DEFAULT_LIB = {
8
- libc: /bc.*\.so/,
9
- ld: /\/ld-.+\.so/,
10
- }
8
+ libc: /bc[^a-z]*\.so/,
9
+ ld: %r{/ld-.+\.so}
10
+ }.freeze
11
11
  # @return [Fixnum, NilClass] return the pid of process, <tt>nil</tt> if no such process found
12
12
  attr_reader :pid
13
13
 
@@ -34,7 +34,7 @@ module HeapInfo
34
34
 
35
35
  # Use this method to wrapper all HeapInfo methods.
36
36
  #
37
- # Since <tt>::HeapInfo</tt> is a tool(debugger) for local usage,
37
+ # Since <tt>::HeapInfo</tt> is a tool(debugger) for local usage,
38
38
  # while exploiting remote service, all methods will not work properly.
39
39
  # So I suggest to wrapper all methods inside <tt>#debug</tt>,
40
40
  # which will ignore the block while the victim process is not found.
@@ -54,10 +54,13 @@ module HeapInfo
54
54
 
55
55
  # Dump the content of specific memory address.
56
56
  #
57
- # Note: This method require you have permission of attaching another process. If not, a warning message will present.
57
+ # Note: This method require you have permission of attaching another process.
58
+ # If not, a warning message will present.
58
59
  #
59
60
  # @param [Mixed] args Will be parsed into <tt>[base, offset, length]</tt>, see Examples for more information.
60
- # @return [String, HeapInfo::Nil] The content needed. When the request address is not readable or the process not exists, <tt>HeapInfo::Nil.new</tt> is returned.
61
+ # @return [String, HeapInfo::Nil]
62
+ # The content needed. When the request address is not readable or the process not exists,
63
+ # <tt>HeapInfo::Nil.new</tt> is returned.
61
64
  #
62
65
  # @example
63
66
  # h = heapinfo('victim')
@@ -102,22 +105,27 @@ module HeapInfo
102
105
  # # 0x1f0d000: 0x0000000000000000 0x0000000000002011
103
106
  # # 0x1f0d010: 0x00007f892a9f87b8 0x00007f892a9f87b8
104
107
  # # 0x1f0d020: 0x0000000000000000 0x0000000000000000
105
- # # 0x1f0d030: 0x0000000000000000 0x0000000000000000
108
+ # # 0x1f0d030: 0x0000000000000000 0x0000000000000000
106
109
  # @example
107
110
  # h.x 3, 0x400000
108
111
  # # 0x400000: 0x00010102464c457f 0x0000000000000000
109
112
  # # 0x400010: 0x00000001003e0002
110
113
  def x(count, *commands, io: $stdout)
111
- return unless load? and io.respond_to? :puts
114
+ return unless load? && io.respond_to?(:puts)
112
115
  dumper.x(count, *commands, io: io)
113
116
  end
114
117
 
115
118
  # Gdb-like command.
116
119
  #
117
120
  # Search a specific value/string/regexp in memory.
118
- # @param [Integer, String, Regexp] pattern The desired search pattern, can be value(<tt>Integer</tt>), string, or regular expression.
119
- # @param [Integer, String, Symbol] from Start address for searching, can be segment(<tt>Symbol</tt>) or segments with offset. See examples for more information.
120
- # @param [Integer] length The search length limit, default is unlimited, which will search until pattern found or reach unreadable memory.
121
+ # @param [Integer, String, Regexp] pattern
122
+ # The desired search pattern, can be value(<tt>Integer</tt>), string, or regular expression.
123
+ # @param [Integer, String, Symbol] from
124
+ # Start address for searching, can be segment(<tt>Symbol</tt>) or segments with offset.
125
+ # See examples for more information.
126
+ # @param [Integer] length
127
+ # The search length limit, default is unlimited,
128
+ # which will search until pattern found or reach unreadable memory.
121
129
  # @return [Integer, NilClass] The first matched address, <tt>nil</tt> is returned when no such pattern found.
122
130
  # @example
123
131
  # h.find(0xdeadbeef, 'heap+0x10', 0x1000)
@@ -134,8 +142,8 @@ module HeapInfo
134
142
  dumper.find(pattern, from, length)
135
143
  end
136
144
 
137
- # <tt>search</tt> is more intutive to me
138
- alias :search :find
145
+ # +search+ is more intutive to me
146
+ alias search find
139
147
 
140
148
  # Pretty dump of bins layouts.
141
149
  #
@@ -146,8 +154,8 @@ module HeapInfo
146
154
  # @example
147
155
  # h.layouts :fastbin, :unsorted_bin, :smallbin
148
156
  def layouts(*args, io: $stdout)
149
- return unless load? and io.respond_to? :puts
150
- io.puts self.libc.main_arena.layouts(*args)
157
+ return unless load? && io.respond_to?(:puts)
158
+ io.puts libc.main_arena.layouts(*args)
151
159
  end
152
160
 
153
161
  # Show simple information of target process.
@@ -157,16 +165,17 @@ module HeapInfo
157
165
  # @example
158
166
  # puts h
159
167
  def to_s
160
- return "Process not found" unless load?
168
+ return 'Process not found' unless load?
161
169
  "Program: #{Helper.color program.name} PID: #{Helper.color pid}\n" +
162
- program.to_s +
163
- heap.to_s +
164
- stack.to_s +
165
- libc.to_s +
166
- ld.to_s
170
+ program.to_s +
171
+ heap.to_s +
172
+ stack.to_s +
173
+ libc.to_s +
174
+ ld.to_s
167
175
  end
168
176
 
169
- private
177
+ private
178
+
170
179
  attr_accessor :dumper
171
180
  def load?
172
181
  @pid != nil
@@ -192,7 +201,7 @@ module HeapInfo
192
201
 
193
202
  def clear_process
194
203
  ProcessInfo::EXPORT.each do |m|
195
- self.class.send(:define_method, m) {Nil.new}
204
+ self.class.send(:define_method, m) { Nil.new }
196
205
  end
197
206
  false
198
207
  end
@@ -200,7 +209,7 @@ module HeapInfo
200
209
  def load_info! # :nodoc:
201
210
  @info = ProcessInfo.new(self)
202
211
  ProcessInfo::EXPORT.each do |m|
203
- self.class.send(:define_method, m) {@info.send(m)}
212
+ self.class.send(:define_method, m) { @info.send(m) }
204
213
  end
205
214
  @dumper = Dumper.new(@info, mem_filename)
206
215
  end
@@ -1,13 +1,13 @@
1
- #encoding: ascii-8bit
1
+ # encoding: ascii-8bit
2
2
  module HeapInfo
3
- # for <tt>Process</tt> to record current info(s)
4
- # <tt>Process</tt> has a <tt>process_info</tt> object iff the process found (pid not <tt>nil</tt>).
3
+ # for {Process} to record current info(s)
4
+ # {Process} has a +process_info+ object iff the process found (pid not +nil+).
5
5
  # Mainly records segments' base.
6
6
  class ProcessInfo
7
- # Methods to be transparent to <tt>process</tt>.
8
- # e.g. <tt>process.libc alias to process.info.libc</tt>
9
- EXPORT = %i(libc ld heap elf program stack bits)
10
-
7
+ # Methods to be transparent to +process+.
8
+ # e.g. +process.libc+ alias to +process.info.libc+.
9
+ EXPORT = %i(libc ld heap elf program stack bits).freeze
10
+
11
11
  # @return [Integer] 32 or 64.
12
12
  attr_reader :bits
13
13
  # @return [HeapInfo::Segment]
@@ -18,11 +18,11 @@ module HeapInfo
18
18
  attr_reader :libc
19
19
  # @return [HeapInfo::Segment]
20
20
  attr_reader :ld
21
- alias :elf :program
21
+ alias elf program
22
22
 
23
- # Instantiate a <tt>ProcessInfo</tt> object
23
+ # Instantiate a {ProcessInfo} object
24
24
  #
25
- # @param [HeapInfo::Process] process Load information from maps/memory for <tt>process</tt>
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)
@@ -33,18 +33,19 @@ module HeapInfo
33
33
  # well.. stack is a strange case because it will grow in runtime..
34
34
  # should i detect stack base growing..?
35
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) })
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.
40
40
  # Will re-read maps when heap segment not found yet.
41
41
  #
42
- # @return [HeapInfo::Segment] The <tt>Segment</tt> 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
- private
47
+ private
48
+
48
49
  attr_reader :maps
49
50
 
50
51
  # force reload maps
@@ -57,7 +58,7 @@ module HeapInfo
57
58
  end
58
59
 
59
60
  def match_maps(maps, pattern)
60
- maps.map{|s| s[3]}.find{|seg| pattern.is_a?(Regexp) ? seg =~ pattern : seg.include?(pattern)}
61
+ maps.map { |s| s[3] }.find { |seg| pattern.is_a?(Regexp) ? seg =~ pattern : seg.include?(pattern) }
61
62
  end
62
63
  end
63
64
  end
@@ -1,7 +1,6 @@
1
1
  module HeapInfo
2
2
  # Record the base address and name in maps
3
3
  class Segment
4
-
5
4
  # Base address of segment
6
5
  attr_reader :base
7
6
  # Name of segment
@@ -17,20 +16,22 @@ module HeapInfo
17
16
  # Hook <tt>#to_s</tt> for pretty printing
18
17
  # @return [String] Information of name and base address wrapper with color codes.
19
18
  def to_s
20
- "%-28s\tbase @ #{Helper.color("%#x" % base)}\n" % Helper.color(name.split('/')[-1])
19
+ format("%-28s\tbase @ #{Helper.color(format('%#x', base))}\n", Helper.color(name.split('/')[-1]))
21
20
  end
22
21
 
23
- # Helper for create an <tt>Segment</tt>
22
+ # Helper for create a {HeapInfo::Segment}.
24
23
  #
25
- # Search the specific <tt>pattern</tt> in <tt>maps</tt> and return a <tt>HeapInfo::Segment</tt> object.
24
+ # Search the specific <tt>pattern</tt> in <tt>maps</tt> and return a {HeapInfo::Segment} object.
26
25
  #
27
- # @param [Array] maps <tt>maps</tt> is in the form of the return value of <tt>HeapInfo::Helper.parse_maps</tt>
28
- # @param [Regexp, String] pattern The segment name want to match in maps. If <tt>String</tt> is given, the pattern is matched as a substring.
29
- # @return [HeapInfo::Segment, NilClass] The request <tt>Segment</tt> object. If the pattern is not matched, <tt>nil</tt> will be returned.
26
+ # @param [Array] maps <tt>maps</tt> is in the form of the return value of <tt>HeapInfo::Helper.parse_maps</tt>.
27
+ # @param [Regexp, String] pattern
28
+ # The segment name want to match in maps. If +String+ is given, the pattern is matched as a substring.
29
+ # @return [HeapInfo::Segment, NilClass]
30
+ # The request {HeapInfo::Segment} object. If the pattern is not matched, <tt>nil</tt> will be returned.
30
31
  def self.find(maps, pattern)
31
32
  return Nil.new if pattern.nil?
32
- needs = maps.select{|m| pattern.is_a?(Regexp) ? m[3] =~ pattern : m[3].include?(pattern)}
33
- self.new needs.map{|m| m[0]}.min, needs[0][3] unless needs.empty?
33
+ needs = maps.select { |m| pattern.is_a?(Regexp) ? m[3] =~ pattern : m[3].include?(pattern) }
34
+ new(needs.map(&:first).min, needs[0][3]) unless needs.empty?
34
35
  end
35
36
  end
36
37
  end
@@ -21,7 +21,6 @@ int main() {
21
21
  void *z = malloc(SZ); // prevent p merge with top chunk
22
22
  *p = z; // prevent compiler optimize
23
23
  free(p); // now *p must be the pointer of the (chunk_ptr) unsorted bin
24
- //TODO: check if this offset change in different version glibc
25
24
  z = (void*)((*p) - (4 + 4 + SZ * 10 )); // mutex+flags+fastbin[]
26
25
  void* a = search_head((size_t)__builtin_return_address(0));
27
26
  printf("%p\n", z-a);
@@ -1,3 +1,3 @@
1
1
  module HeapInfo
2
- VERSION = '0.0.5'.freeze
2
+ VERSION = '0.1.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,15 +1,85 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: heapinfo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.1.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-01-03 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2017-02-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.5'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rubocop
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.46'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.46'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '12.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '12.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: simplecov
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.13.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.13.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: codeclimate-test-reporter
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.6'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.6'
13
83
  description: create an interactive memory info interface while pwn / exploiting
14
84
  email:
15
85
  - david942j@gmail.com
@@ -37,19 +107,6 @@ files:
37
107
  - lib/heapinfo/segment.rb
38
108
  - lib/heapinfo/tools/get_arena.c
39
109
  - lib/heapinfo/version.rb
40
- - spec/cache_spec.rb
41
- - spec/chunk_spec.rb
42
- - spec/chunks_spec.rb
43
- - spec/dumper_spec.rb
44
- - spec/files/32bit_maps
45
- - spec/files/64bit_maps
46
- - spec/files/victim.cpp
47
- - spec/helper_spec.rb
48
- - spec/libc_spec.rb
49
- - spec/nil_spec.rb
50
- - spec/process_spec.rb
51
- - spec/spec_helper.rb
52
- - spec/string_spec.rb
53
110
  homepage: https://github.com/david942j/heapinfo
54
111
  licenses:
55
112
  - MIT
@@ -70,21 +127,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
70
127
  version: '0'
71
128
  requirements: []
72
129
  rubyforge_project:
73
- rubygems_version: 2.5.2
130
+ rubygems_version: 2.6.10
74
131
  signing_key:
75
132
  specification_version: 4
76
133
  summary: HeapInfo - interactive heap exploitation helper
77
- test_files:
78
- - spec/files/32bit_maps
79
- - spec/files/victim.cpp
80
- - spec/files/64bit_maps
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
86
- - spec/string_spec.rb
87
- - spec/spec_helper.rb
88
- - spec/helper_spec.rb
89
- - spec/nil_spec.rb
90
- - spec/libc_spec.rb
134
+ test_files: []