heapinfo 1.0.2 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 961ba248f261232617b7ed7ee649fd3fc21b72b9
4
- data.tar.gz: 7bffd6cf6c33d2a6868ec59ba9d8b3509ab1512f
3
+ metadata.gz: b734871451b91231d5c1c159633f1de9ad3255bb
4
+ data.tar.gz: 2742c1043b6466cad49415191c9753819e499e2d
5
5
  SHA512:
6
- metadata.gz: 13d13edbcaab506b3b9044894f51bf10f5b9a32eb2e10d81a397f913a2488b947877d1d3747e1cb8d02f3f8304aa7c59e44f20ee3009bce20c7cd8c7f47ca658
7
- data.tar.gz: 6680a5c6ab2a2483acbd0317d2dc3bf3871800d66136cd2d4263a23cff4b5367b5c10901e0788e21b3b4dc8af675fab1b270f1ac57d7d8c9bc1bea0162fb5b4a
6
+ metadata.gz: 93861f6cd8f05a68666e2e67caff87d810f784f42aac009a4db4ca7b90031dc792a6643f780f316e7e5b43211e34c18eb5bd4a9dab3634dfad44a17d43c9de24
7
+ data.tar.gz: e6349ece2cba17b12af3daafb2a62dad995e4a03b0ca83e33a35661ed6985db16cc26e4d50810dedc411ceef497537c964a2804c84a081887cddd3cb1159f4f1
data/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
  ## HeapInfo
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
- #### Why
12
+ ### Why
13
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
 
@@ -17,9 +17,7 @@ Implement with ruby because I love ruby :P. But might also implement with Python
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
 
20
- Any suggestion of features or bug issues is welcome.
21
-
22
- Related works are [pwntools-ruby](https://github.com/peter50216/pwntools-ruby) and [gdbpwn](https://github.com/scwuaptx/Pwngdb).
20
+ Any suggestion of features or bug issues are welcome.
23
21
 
24
22
  ## Install
25
23
  **HeapInfo** is still under developing for more features, so the version might change frequently :p
@@ -156,12 +154,17 @@ h.find(/E.F/, 0x400000, 4)
156
154
  #=> 4194305 # 0x400001
157
155
  h.find(/E.F/, 0x400000, 3)
158
156
  #=> nil
157
+
158
+ # Get relative offset of searching result
159
+ h.find('/bin/sh', :libc, rel: true)
160
+ #=> 1622391 # 0x18c177
159
161
  h.offset(h.find('/bin/sh', :libc))
160
162
  # 0x18c177 after libc
161
163
  ```
162
164
 
163
165
  ## Tests
164
166
  **HeapInfo** currently only run tests on ubuntu, followings are tested glibc versions:
167
+
165
168
  * libc-2.19
166
169
  * libc-2.23
167
170
  * libc-2.24
@@ -47,8 +47,8 @@ module HeapInfo
47
47
  f
48
48
  end
49
49
  @unsorted_bin = UnsortedBin.new(size_t, top_ptr_offset, @dumper, head: true)
50
- @smallbin = Array.new(55) do |idx|
51
- s = Smallbin.new(size_t, @base + 8 + size_t * (26 + 2 * idx), @dumper, head: true)
50
+ @smallbin = Array.new(62) do |idx|
51
+ s = Smallbin.new(size_t, @base + 8 + size_t * (12 + 2 * idx), @dumper, head: true)
52
52
  s.index = idx
53
53
  s
54
54
  end
@@ -61,7 +61,9 @@ module HeapInfo
61
61
  # @return [String] Bin layouts that wrapper with color codes.
62
62
  # @example
63
63
  # puts h.libc.main_arena.layouts(:fast, :unsorted, :small)
64
+ # puts h.libc.main_arena.layouts(:all)
64
65
  def layouts(*args)
66
+ args.concat(%i[fast unsort small large]) if args.map(&:to_s).include?('all')
65
67
  args = args.map(&:to_s).join('|')
66
68
  res = ''
67
69
  res += fastbin.map(&:inspect).join if args.include?('fast')
@@ -227,7 +229,7 @@ module HeapInfo
227
229
  # Mapping index of smallbin to chunk size.
228
230
  # @return [Integer] size
229
231
  def idx_to_size
230
- index * size_t * 2 + size_t * 18
232
+ index * size_t * 2 + size_t * 4
231
233
  end
232
234
  end
233
235
 
@@ -38,8 +38,8 @@ module HeapInfo
38
38
  filepath = realpath(key)
39
39
  return unless File.file?(filepath)
40
40
  Marshal.load(IO.binread(filepath))
41
- rescue
42
- nil # handle if file content invalid
41
+ rescue TypeError, ArgumentError
42
+ nil # handle if file content is invalid
43
43
  end
44
44
 
45
45
  # Clear the cache directory.
@@ -53,7 +53,7 @@ module HeapInfo
53
53
  # @return [void]
54
54
  def init
55
55
  FileUtils.mkdir_p(CACHE_DIR)
56
- rescue
56
+ rescue Errno::EACCES
57
57
  # To prevent ~/ is not writable.
58
58
  send(:remove_const, :CACHE_DIR)
59
59
  const_set(:CACHE_DIR, File.join(HeapInfo::TMP_DIR, '.cache/heapinfo'))
@@ -27,7 +27,10 @@ module HeapInfo
27
27
  @size_t = size_t
28
28
  @base = base
29
29
  sz = dump(@base, size_t * 2)
30
- return @data = dump(@base + size_t * 2, size_t * 4) if head # no need to read size if is bin
30
+ if head # no need to read size if is bin
31
+ @data = dump(@base + size_t * 2, size_t * 4)
32
+ return
33
+ end
31
34
  @prev_size = Helper.unpack(size_t, sz[0, size_t])
32
35
  @size = Helper.unpack(size_t, sz[size_t..-1])
33
36
  r_size = [size - size_t * 2, size_t * 4].min # don't read too much data
@@ -30,7 +30,7 @@ module HeapInfo
30
30
  mem = file.readpartial len
31
31
  file.close
32
32
  mem
33
- rescue => e
33
+ rescue => e # rubocop:disable Style/RescueStandardError
34
34
  raise e if e.is_a? ArgumentError
35
35
  nil
36
36
  end
@@ -67,6 +67,8 @@ module HeapInfo
67
67
  puts str
68
68
  end
69
69
 
70
+ # @api private
71
+ #
70
72
  # Search a specific value/string/regexp in memory.
71
73
  # +#find+ only return the first matched address.
72
74
  # @param [Integer, String, Regexp] pattern
@@ -75,22 +77,25 @@ module HeapInfo
75
77
  # Start address for searching, can be segment(+Symbol+)
76
78
  # or segments with offset. See examples for more information.
77
79
  # @param [Integer] length The length limit for searching.
80
+ # @param [Boolean] rel Return relative or absolute.
78
81
  # @return [Integer, nil] The first matched address, +nil+ is returned when no such pattern found.
79
82
  # @example
80
- # find(/E.F/, :elf)
83
+ # find(/E.F/, :elf, false)
81
84
  # #=> 4194305
82
- # find(0x4141414141414141, 'heap+0x10', 0x1000)
85
+ # find(0x4141414141414141, 'heap+0x10', 0x1000, false)
83
86
  # #=> 6291472
84
- # find('/bin/sh', :libc)
85
- # #=> 140662379588827
86
- def find(pattern, from, length)
87
+ # find('/bin/sh', :libc, true)
88
+ # #=> 1622391 # 0x18c177
89
+ def find(pattern, from, length, rel)
87
90
  from = base_of(from)
88
91
  length = 1 << 40 if length.is_a? Symbol
89
- case pattern
90
- when Integer then find_integer(pattern, from, length)
91
- when String then find_string(pattern, from, length)
92
- when Regexp then find_regexp(pattern, from, length)
93
- end
92
+ ret = case pattern
93
+ when Integer then find_integer(pattern, from, length)
94
+ when String then find_string(pattern, from, length)
95
+ when Regexp then find_regexp(pattern, from, length)
96
+ end
97
+ ret -= from if ret && rel
98
+ ret
94
99
  end
95
100
 
96
101
  private
@@ -107,12 +112,8 @@ module HeapInfo
107
112
  def dumpable?
108
113
  mem_f.close
109
114
  true
110
- rescue => e
111
- if e.is_a? Errno::EACCES
112
- false
113
- else
114
- throw e
115
- end
115
+ rescue Errno::EACCES, Errno::ENOENT
116
+ false
116
117
  end
117
118
 
118
119
  def mem_f
@@ -25,7 +25,7 @@ module HeapInfo
25
25
  # The original method in C is too long, split to multiple methods to match ruby convention.
26
26
  # @param [HeapInfo::Arena] av
27
27
  # @param [Integer] ptr Use <tt>ptr</tt> instead of <tt>p</tt> to prevent confusing with ruby native method.
28
- def int_free(av, ptr) # is have_lock important?
28
+ def int_free(av, ptr)
29
29
  chunk = dumper.call(ptr, size_t * 2).to_chunk
30
30
  size = ulong chunk.size
31
31
  invalid_pointer(ptr, size)
@@ -1,15 +1,16 @@
1
1
  require 'dentaku'
2
2
  require 'shellwords'
3
+ require 'time'
3
4
 
4
5
  module HeapInfo
5
6
  # Some helper functions.
6
7
  module Helper
7
8
  # Create read +/proc/[pid]/*+ methods.
8
- %w(exe maps auxv).each do |method|
9
+ %w[exe maps auxv].each do |method|
9
10
  define_singleton_method("#{method}_of".to_sym) do |pid|
10
11
  begin
11
12
  IO.binread("/proc/#{pid}/#{method}")
12
- rescue
13
+ rescue Errno::ENOENT
13
14
  throw "reading /proc/#{pid}/#{method} error"
14
15
  end
15
16
  end
@@ -4,7 +4,7 @@ module HeapInfo
4
4
  # Can be the return value of {HeapInfo::Process#dump} and {HeapInfo::Process#dump_chunks},
5
5
  # to prevent use the return value for calculating accidentally while exploiting remote.
6
6
  class Nil
7
- %i(nil? inspect to_s).each do |method_sym|
7
+ %i[nil? inspect to_s].each do |method_sym|
8
8
  define_method(method_sym) { |*args, &block| nil.send(method_sym, *args, &block) }
9
9
  end
10
10
 
@@ -1,4 +1,5 @@
1
1
  # encoding: ascii-8bit
2
+
2
3
  module HeapInfo
3
4
  # Main class of heapinfo.
4
5
  class Process
@@ -20,9 +21,9 @@ module HeapInfo
20
21
  load!
21
22
  end
22
23
 
23
- # Reload a new process with same program name
24
+ # Reload a new process with same program name.
24
25
  #
25
- # @return [HeapInfo::Process] return +self+ for chainable.
26
+ # @return [HeapInfo::Process] return +self+ so that this method is chainable.
26
27
  # @example
27
28
  # puts h.reload!
28
29
  def reload!
@@ -30,6 +31,7 @@ module HeapInfo
30
31
  load!
31
32
  self
32
33
  end
34
+ alias reload reload!
33
35
 
34
36
  # Use this method to wrapper all HeapInfo methods.
35
37
  #
@@ -111,11 +113,14 @@ module HeapInfo
111
113
  segment = @info.send(sym) if HeapInfo::ProcessInfo::EXPORT.include?(sym)
112
114
  segment = nil unless segment.is_a?(HeapInfo::Segment)
113
115
  if segment.nil?
114
- sym, segment = @info.segments.select { |_, seg| seg.base <= addr }.min_by { |_, seg| addr - seg.base }
116
+ sym, segment = @info.segments
117
+ .select { |_, seg| seg.base <= addr }
118
+ .min_by { |_, seg| addr - seg }
115
119
  end
116
120
  return puts "Invalid address #{Helper.hex(addr)}" if segment.nil?
117
- puts Helper.color(Helper.hex(addr - segment.base)) + ' after ' + Helper.color(sym, sev: :sym)
121
+ puts Helper.color(Helper.hex(addr - segment)) + ' after ' + Helper.color(sym, sev: :sym)
118
122
  end
123
+ alias off offset
119
124
 
120
125
  # Gdb-like command
121
126
  #
@@ -153,6 +158,8 @@ module HeapInfo
153
158
  # @param [Integer] length
154
159
  # The search length limit, default is unlimited,
155
160
  # which will search until pattern found or reach unreadable memory.
161
+ # @param [Boolean] rel
162
+ # To show relative offset of +from+ or absolute address.
156
163
  # @return [Integer, nil] The first matched address, +nil+ is returned when no such pattern found.
157
164
  # @example
158
165
  # h.find(0xdeadbeef, 'heap+0x10', 0x1000)
@@ -161,12 +168,13 @@ module HeapInfo
161
168
  # #=> 4194305 # 0x400001
162
169
  # h.find(/E.F/, 0x400000, 3)
163
170
  # #=> nil
164
- # sh_offset = h.find('/bin/sh', :libc) - h.libc.base
171
+ # sh_offset = h.find('/bin/sh', :libc) - h.libc
165
172
  # #=> 1559771 # 0x17ccdb
166
- def find(pattern, from, length = :unlimited)
173
+ # h.find('/bin/sh', :libc, rel: true) == h.find('/bin/sh', :libc) - h.libc
174
+ # #=> true
175
+ def find(pattern, from, length = :unlimited, rel: false)
167
176
  return Nil.new unless load?
168
- length = 1 << 40 if length.is_a? Symbol
169
- dumper.find(pattern, from, length)
177
+ dumper.find(pattern, from, length, rel)
170
178
  end
171
179
 
172
180
  # +search+ is more intutive to me
@@ -215,19 +223,21 @@ module HeapInfo
215
223
 
216
224
  # Make pry not so verbose.
217
225
  #
218
- # @return [nil]
226
+ # @return [String]
219
227
  def inspect
220
- nil
228
+ format('#<HeapInfo::Process:0x%016x>', __id__)
221
229
  end
222
230
 
223
231
  private
224
232
 
225
233
  attr_accessor :dumper
234
+
226
235
  def load?
227
236
  @pid != nil
228
237
  end
229
238
 
230
- def load! # try to load
239
+ # try to load
240
+ def load!
231
241
  return true if @pid
232
242
  @pid = fetch_pid
233
243
  return clear_process if @pid.nil? # still can't load
@@ -1,4 +1,5 @@
1
1
  # encoding: ascii-8bit
2
+
2
3
  module HeapInfo
3
4
  # For {Process} to record basic process information.
4
5
  #
@@ -7,7 +8,7 @@ module HeapInfo
7
8
  class ProcessInfo
8
9
  # Methods to be transparent to +process+.
9
10
  # e.g. +process.libc+ alias to +process.info.libc+.
10
- EXPORT = %i(libc ld heap program elf stack bits auxv).freeze
11
+ EXPORT = %i[libc ld heap program elf stack bits auxv].freeze
11
12
 
12
13
  # @return [Integer] 32 or 64.
13
14
  attr_reader :bits
@@ -47,8 +48,10 @@ module HeapInfo
47
48
  # Heap will not be mmapped if the process not use heap yet, so create a lazy loading method.
48
49
  # Will re-read maps when heap segment not found yet.
49
50
  #
51
+ # Special handling here because heap might not be initialized in the beginning.
52
+ #
50
53
  # @return [HeapInfo::Segment] The {Segment} of heap.
51
- def heap # special handle because heap might not be initialized in the beginning
54
+ def heap
52
55
  @heap ||= Segment.find(load_maps, '[heap]')
53
56
  end
54
57
 
@@ -70,7 +73,7 @@ module HeapInfo
70
73
  def parse_auxv(str)
71
74
  auxv = {}
72
75
  sio = StringIO.new(str)
73
- fetch = ->() { Helper.unpack(@bits / 8, sio.read(@bits / 8)) }
76
+ fetch = -> { Helper.unpack(@bits / 8, sio.read(@bits / 8)) }
74
77
  loop do
75
78
  type = fetch.call
76
79
  val = fetch.call
@@ -19,6 +19,17 @@ module HeapInfo
19
19
  format("%-28s\tbase @ #{Helper.color(format('%#x', base))}\n", Helper.color(name.split('/')[-1]))
20
20
  end
21
21
 
22
+ # To support +addr - h.libc+.
23
+ # Treat all operations are manipulating on +base+.
24
+ #
25
+ # @param [Object] other
26
+ # Any object.
27
+ #
28
+ # @return [(Object, Integer)]
29
+ def coerce(other)
30
+ [other, base]
31
+ end
32
+
22
33
  # Helper for creating a {HeapInfo::Segment}.
23
34
  #
24
35
  # Search the specific <tt>pattern</tt> in <tt>maps</tt> and return a {HeapInfo::Segment} object.
@@ -1,4 +1,4 @@
1
1
  module HeapInfo
2
2
  # Current gem version.
3
- VERSION = '1.0.2'.freeze
3
+ VERSION = '1.1.0'.freeze
4
4
  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: 1.0.2
4
+ version: 1.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-04-06 00:00:00.000000000 Z
11
+ date: 2018-01-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dentaku
@@ -25,75 +25,75 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '2'
27
27
  - !ruby/object:Gem::Dependency
28
- name: rspec
28
+ name: codeclimate-test-reporter
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '3.5'
33
+ version: '0.6'
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: '3.5'
40
+ version: '0.6'
41
41
  - !ruby/object:Gem::Dependency
42
- name: rubocop
42
+ name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '0.46'
47
+ version: '12.3'
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: '0.46'
54
+ version: '12.3'
55
55
  - !ruby/object:Gem::Dependency
56
- name: rake
56
+ name: rspec
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '12.0'
61
+ version: '3.7'
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: '12.0'
68
+ version: '3.7'
69
69
  - !ruby/object:Gem::Dependency
70
- name: simplecov
70
+ name: rubocop
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: 0.13.0
75
+ version: '0.52'
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: 0.13.0
82
+ version: '0.52'
83
83
  - !ruby/object:Gem::Dependency
84
- name: codeclimate-test-reporter
84
+ name: simplecov
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '0.6'
89
+ version: 0.13.0
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.6'
96
+ version: 0.13.0
97
97
  description: |
98
98
  Create an interactive memory info interface while pwn / exploiting.
99
99
  Useful for rubiers writing exploit scripts.
@@ -145,7 +145,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
145
145
  version: '0'
146
146
  requirements: []
147
147
  rubyforge_project:
148
- rubygems_version: 2.6.10
148
+ rubygems_version: 2.6.14
149
149
  signing_key:
150
150
  specification_version: 4
151
151
  summary: HeapInfo - interactive heap exploitation helper