metasm 1.0.1 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/.hgtags +3 -0
- data/Gemfile +1 -0
- data/INSTALL +61 -0
- data/LICENCE +458 -0
- data/README +29 -21
- data/Rakefile +10 -0
- data/TODO +10 -12
- data/doc/code_organisation.txt +2 -0
- data/doc/core/DynLdr.txt +247 -0
- data/doc/core/ExeFormat.txt +43 -0
- data/doc/core/Expression.txt +220 -0
- data/doc/core/GNUExports.txt +27 -0
- data/doc/core/Ia32.txt +236 -0
- data/doc/core/SerialStruct.txt +108 -0
- data/doc/core/VirtualString.txt +145 -0
- data/doc/core/WindowsExports.txt +61 -0
- data/doc/core/index.txt +1 -0
- data/doc/style.css +6 -3
- data/doc/usage/debugger.txt +327 -0
- data/doc/usage/index.txt +1 -0
- data/doc/use_cases.txt +2 -2
- data/metasm.gemspec +22 -0
- data/{lib/metasm.rb → metasm.rb} +11 -3
- data/{lib/metasm → metasm}/compile_c.rb +13 -7
- data/metasm/cpu/arc.rb +8 -0
- data/metasm/cpu/arc/decode.rb +425 -0
- data/metasm/cpu/arc/main.rb +191 -0
- data/metasm/cpu/arc/opcodes.rb +588 -0
- data/{lib/metasm → metasm/cpu}/arm.rb +7 -5
- data/{lib/metasm → metasm/cpu}/arm/debug.rb +2 -2
- data/{lib/metasm → metasm/cpu}/arm/decode.rb +13 -12
- data/{lib/metasm → metasm/cpu}/arm/encode.rb +23 -8
- data/{lib/metasm → metasm/cpu}/arm/main.rb +0 -3
- data/metasm/cpu/arm/opcodes.rb +324 -0
- data/{lib/metasm → metasm/cpu}/arm/parse.rb +25 -13
- data/{lib/metasm → metasm/cpu}/arm/render.rb +2 -2
- data/metasm/cpu/arm64.rb +15 -0
- data/metasm/cpu/arm64/debug.rb +38 -0
- data/metasm/cpu/arm64/decode.rb +289 -0
- data/metasm/cpu/arm64/encode.rb +41 -0
- data/metasm/cpu/arm64/main.rb +105 -0
- data/metasm/cpu/arm64/opcodes.rb +232 -0
- data/metasm/cpu/arm64/parse.rb +20 -0
- data/metasm/cpu/arm64/render.rb +95 -0
- data/{lib/metasm/ppc.rb → metasm/cpu/bpf.rb} +2 -4
- data/metasm/cpu/bpf/decode.rb +142 -0
- data/metasm/cpu/bpf/main.rb +60 -0
- data/metasm/cpu/bpf/opcodes.rb +81 -0
- data/metasm/cpu/bpf/render.rb +41 -0
- data/metasm/cpu/cy16.rb +9 -0
- data/metasm/cpu/cy16/decode.rb +253 -0
- data/metasm/cpu/cy16/main.rb +63 -0
- data/metasm/cpu/cy16/opcodes.rb +78 -0
- data/metasm/cpu/cy16/render.rb +41 -0
- data/metasm/cpu/dalvik.rb +11 -0
- data/{lib/metasm → metasm/cpu}/dalvik/decode.rb +35 -13
- data/{lib/metasm → metasm/cpu}/dalvik/main.rb +51 -2
- data/{lib/metasm → metasm/cpu}/dalvik/opcodes.rb +19 -11
- data/metasm/cpu/ia32.rb +17 -0
- data/{lib/metasm → metasm/cpu}/ia32/compile_c.rb +5 -7
- data/{lib/metasm → metasm/cpu}/ia32/debug.rb +5 -5
- data/{lib/metasm → metasm/cpu}/ia32/decode.rb +246 -59
- data/{lib/metasm → metasm/cpu}/ia32/decompile.rb +7 -7
- data/{lib/metasm → metasm/cpu}/ia32/encode.rb +19 -13
- data/{lib/metasm → metasm/cpu}/ia32/main.rb +51 -8
- data/metasm/cpu/ia32/opcodes.rb +1424 -0
- data/{lib/metasm → metasm/cpu}/ia32/parse.rb +47 -16
- data/{lib/metasm → metasm/cpu}/ia32/render.rb +31 -4
- data/metasm/cpu/mips.rb +14 -0
- data/{lib/metasm → metasm/cpu}/mips/compile_c.rb +1 -1
- data/metasm/cpu/mips/debug.rb +42 -0
- data/{lib/metasm → metasm/cpu}/mips/decode.rb +46 -16
- data/{lib/metasm → metasm/cpu}/mips/encode.rb +4 -3
- data/{lib/metasm → metasm/cpu}/mips/main.rb +11 -4
- data/{lib/metasm → metasm/cpu}/mips/opcodes.rb +86 -17
- data/{lib/metasm → metasm/cpu}/mips/parse.rb +1 -1
- data/{lib/metasm → metasm/cpu}/mips/render.rb +1 -1
- data/{lib/metasm/dalvik.rb → metasm/cpu/msp430.rb} +1 -1
- data/metasm/cpu/msp430/decode.rb +247 -0
- data/metasm/cpu/msp430/main.rb +62 -0
- data/metasm/cpu/msp430/opcodes.rb +101 -0
- data/{lib/metasm → metasm/cpu}/pic16c/decode.rb +6 -7
- data/{lib/metasm → metasm/cpu}/pic16c/main.rb +0 -0
- data/{lib/metasm → metasm/cpu}/pic16c/opcodes.rb +1 -1
- data/{lib/metasm/mips.rb → metasm/cpu/ppc.rb} +4 -4
- data/{lib/metasm → metasm/cpu}/ppc/decode.rb +18 -12
- data/{lib/metasm → metasm/cpu}/ppc/decompile.rb +3 -3
- data/{lib/metasm → metasm/cpu}/ppc/encode.rb +2 -2
- data/{lib/metasm → metasm/cpu}/ppc/main.rb +17 -12
- data/{lib/metasm → metasm/cpu}/ppc/opcodes.rb +11 -5
- data/metasm/cpu/ppc/parse.rb +55 -0
- data/metasm/cpu/python.rb +8 -0
- data/metasm/cpu/python/decode.rb +136 -0
- data/metasm/cpu/python/main.rb +36 -0
- data/metasm/cpu/python/opcodes.rb +180 -0
- data/{lib/metasm → metasm/cpu}/sh4.rb +1 -1
- data/{lib/metasm → metasm/cpu}/sh4/decode.rb +48 -17
- data/{lib/metasm → metasm/cpu}/sh4/main.rb +13 -4
- data/{lib/metasm → metasm/cpu}/sh4/opcodes.rb +7 -8
- data/metasm/cpu/x86_64.rb +15 -0
- data/{lib/metasm → metasm/cpu}/x86_64/compile_c.rb +28 -17
- data/{lib/metasm → metasm/cpu}/x86_64/debug.rb +4 -4
- data/{lib/metasm → metasm/cpu}/x86_64/decode.rb +57 -15
- data/{lib/metasm → metasm/cpu}/x86_64/encode.rb +55 -26
- data/{lib/metasm → metasm/cpu}/x86_64/main.rb +14 -6
- data/metasm/cpu/x86_64/opcodes.rb +136 -0
- data/{lib/metasm → metasm/cpu}/x86_64/parse.rb +10 -2
- data/metasm/cpu/x86_64/render.rb +35 -0
- data/metasm/cpu/z80.rb +9 -0
- data/metasm/cpu/z80/decode.rb +313 -0
- data/metasm/cpu/z80/main.rb +67 -0
- data/metasm/cpu/z80/opcodes.rb +224 -0
- data/metasm/cpu/z80/render.rb +59 -0
- data/{lib/metasm/os/main.rb → metasm/debug.rb} +160 -401
- data/{lib/metasm → metasm}/decode.rb +35 -4
- data/{lib/metasm → metasm}/decompile.rb +15 -16
- data/{lib/metasm → metasm}/disassemble.rb +201 -45
- data/{lib/metasm → metasm}/disassemble_api.rb +651 -87
- data/{lib/metasm → metasm}/dynldr.rb +220 -133
- data/{lib/metasm → metasm}/encode.rb +10 -1
- data/{lib/metasm → metasm}/exe_format/a_out.rb +9 -6
- data/{lib/metasm → metasm}/exe_format/autoexe.rb +1 -0
- data/{lib/metasm → metasm}/exe_format/bflt.rb +57 -27
- data/{lib/metasm → metasm}/exe_format/coff.rb +11 -3
- data/{lib/metasm → metasm}/exe_format/coff_decode.rb +53 -20
- data/{lib/metasm → metasm}/exe_format/coff_encode.rb +11 -13
- data/{lib/metasm → metasm}/exe_format/dex.rb +13 -5
- data/{lib/metasm → metasm}/exe_format/dol.rb +1 -0
- data/{lib/metasm → metasm}/exe_format/elf.rb +93 -57
- data/{lib/metasm → metasm}/exe_format/elf_decode.rb +143 -34
- data/{lib/metasm → metasm}/exe_format/elf_encode.rb +122 -31
- data/metasm/exe_format/gb.rb +65 -0
- data/metasm/exe_format/javaclass.rb +424 -0
- data/{lib/metasm → metasm}/exe_format/macho.rb +204 -16
- data/{lib/metasm → metasm}/exe_format/main.rb +26 -3
- data/{lib/metasm → metasm}/exe_format/mz.rb +1 -0
- data/{lib/metasm → metasm}/exe_format/nds.rb +7 -4
- data/{lib/metasm → metasm}/exe_format/pe.rb +71 -8
- data/metasm/exe_format/pyc.rb +167 -0
- data/{lib/metasm → metasm}/exe_format/serialstruct.rb +67 -14
- data/{lib/metasm → metasm}/exe_format/shellcode.rb +7 -3
- data/metasm/exe_format/shellcode_rwx.rb +114 -0
- data/metasm/exe_format/swf.rb +205 -0
- data/{lib/metasm → metasm}/exe_format/xcoff.rb +7 -7
- data/metasm/exe_format/zip.rb +335 -0
- data/metasm/gui.rb +13 -0
- data/{lib/metasm → metasm}/gui/cstruct.rb +35 -41
- data/{lib/metasm → metasm}/gui/dasm_coverage.rb +11 -11
- data/{lib/metasm → metasm}/gui/dasm_decomp.rb +7 -20
- data/{lib/metasm → metasm}/gui/dasm_funcgraph.rb +0 -0
- data/metasm/gui/dasm_graph.rb +1695 -0
- data/{lib/metasm → metasm}/gui/dasm_hex.rb +12 -8
- data/{lib/metasm → metasm}/gui/dasm_listing.rb +43 -28
- data/{lib/metasm → metasm}/gui/dasm_main.rb +310 -53
- data/{lib/metasm → metasm}/gui/dasm_opcodes.rb +5 -19
- data/{lib/metasm → metasm}/gui/debug.rb +93 -27
- data/{lib/metasm → metasm}/gui/gtk.rb +162 -40
- data/{lib/metasm → metasm}/gui/qt.rb +12 -2
- data/{lib/metasm → metasm}/gui/win32.rb +179 -42
- data/{lib/metasm → metasm}/gui/x11.rb +59 -59
- data/{lib/metasm → metasm}/main.rb +389 -264
- data/{lib/metasm/os/remote.rb → metasm/os/gdbremote.rb} +146 -54
- data/{lib/metasm → metasm}/os/gnu_exports.rb +1 -1
- data/{lib/metasm → metasm}/os/linux.rb +628 -151
- data/metasm/os/main.rb +330 -0
- data/{lib/metasm → metasm}/os/windows.rb +132 -42
- data/{lib/metasm → metasm}/os/windows_exports.rb +141 -0
- data/{lib/metasm → metasm}/parse.rb +26 -24
- data/{lib/metasm → metasm}/parse_c.rb +221 -116
- data/{lib/metasm → metasm}/preprocessor.rb +55 -40
- data/{lib/metasm → metasm}/render.rb +14 -38
- data/misc/hexdump.rb +2 -1
- data/misc/lint.rb +58 -0
- data/misc/txt2html.rb +9 -7
- data/samples/bindiff.rb +3 -4
- data/samples/dasm-plugins/bindiff.rb +15 -0
- data/samples/dasm-plugins/bookmark.rb +133 -0
- data/samples/dasm-plugins/c_constants.rb +57 -0
- data/samples/dasm-plugins/colortheme_solarized.rb +125 -0
- data/samples/dasm-plugins/cppobj_funcall.rb +60 -0
- data/samples/dasm-plugins/dasm_all.rb +70 -0
- data/samples/dasm-plugins/demangle_cpp.rb +31 -0
- data/samples/dasm-plugins/deobfuscate.rb +251 -0
- data/samples/dasm-plugins/dump_text.rb +35 -0
- data/samples/dasm-plugins/export_graph_svg.rb +86 -0
- data/samples/dasm-plugins/findgadget.rb +75 -0
- data/samples/dasm-plugins/hl_opcode.rb +32 -0
- data/samples/dasm-plugins/hotfix_gtk_dbg.rb +19 -0
- data/samples/dasm-plugins/imm2off.rb +34 -0
- data/samples/dasm-plugins/match_libsigs.rb +93 -0
- data/samples/dasm-plugins/patch_file.rb +95 -0
- data/samples/dasm-plugins/scanfuncstart.rb +36 -0
- data/samples/dasm-plugins/scanxrefs.rb +26 -0
- data/samples/dasm-plugins/selfmodify.rb +197 -0
- data/samples/dasm-plugins/stringsxrefs.rb +28 -0
- data/samples/dasmnavig.rb +1 -1
- data/samples/dbg-apihook.rb +24 -9
- data/samples/dbg-plugins/heapscan.rb +283 -0
- data/samples/dbg-plugins/heapscan/compiled_heapscan_lin.c +155 -0
- data/samples/dbg-plugins/heapscan/compiled_heapscan_win.c +128 -0
- data/samples/dbg-plugins/heapscan/graphheap.rb +616 -0
- data/samples/dbg-plugins/heapscan/heapscan.rb +709 -0
- data/samples/dbg-plugins/heapscan/winheap.h +174 -0
- data/samples/dbg-plugins/heapscan/winheap7.h +307 -0
- data/samples/dbg-plugins/trace_func.rb +214 -0
- data/samples/disassemble-gui.rb +35 -5
- data/samples/disassemble.rb +31 -6
- data/samples/dump_upx.rb +24 -12
- data/samples/dynamic_ruby.rb +12 -3
- data/samples/exeencode.rb +6 -5
- data/samples/factorize-headers-peimports.rb +1 -1
- data/samples/lindebug.rb +175 -381
- data/samples/metasm-shell.rb +1 -2
- data/samples/peldr.rb +2 -2
- data/tests/all.rb +1 -1
- data/tests/arc.rb +26 -0
- data/tests/dynldr.rb +22 -4
- data/tests/expression.rb +55 -0
- data/tests/graph_layout.rb +285 -0
- data/tests/ia32.rb +79 -26
- data/tests/mips.rb +9 -2
- data/tests/x86_64.rb +66 -18
- metadata +330 -218
- data/lib/metasm/arm/opcodes.rb +0 -177
- data/lib/metasm/gui.rb +0 -23
- data/lib/metasm/gui/dasm_graph.rb +0 -1354
- data/lib/metasm/ia32.rb +0 -14
- data/lib/metasm/ia32/opcodes.rb +0 -873
- data/lib/metasm/ppc/parse.rb +0 -52
- data/lib/metasm/x86_64.rb +0 -12
- data/lib/metasm/x86_64/opcodes.rb +0 -118
- data/samples/gdbclient.rb +0 -583
- data/samples/rubstop.rb +0 -399
@@ -0,0 +1,709 @@
|
|
1
|
+
# This file is part of Metasm, the Ruby assembly manipulation suite
|
2
|
+
# Copyright (C) 2006-2009 Yoann GUILLOT
|
3
|
+
#
|
4
|
+
# Licence is LGPL, see LICENCE in the top-level directory
|
5
|
+
|
6
|
+
|
7
|
+
module Metasm
|
8
|
+
class Heap
|
9
|
+
attr_accessor :vm, :range, :ptsz
|
10
|
+
attr_accessor :cp
|
11
|
+
# hash chunk userdata pointer -> chunk userdata size
|
12
|
+
attr_accessor :chunks
|
13
|
+
# hash chunk user pointer -> C::Struct
|
14
|
+
attr_accessor :chunk_struct
|
15
|
+
# the chunk graph: chunk pointer -> [array of chunks addrs pointed]
|
16
|
+
attr_accessor :xrchunksto, :xrchunksfrom
|
17
|
+
|
18
|
+
def initialize(dbg)
|
19
|
+
@dbg = dbg
|
20
|
+
@dbg.pid_stuff_list << :heap
|
21
|
+
@dbg.heap = self
|
22
|
+
@range = {}
|
23
|
+
@dwcache = {}
|
24
|
+
# userdata_ptr => len
|
25
|
+
@chunks = {}
|
26
|
+
@xrchunksto = {}
|
27
|
+
@xrchunksfrom = {}
|
28
|
+
@ptsz = dbg.cpu.size/8
|
29
|
+
# ptr => C::Struct
|
30
|
+
@chunk_struct = {}
|
31
|
+
end
|
32
|
+
|
33
|
+
def pagecache(base, len)
|
34
|
+
@dbg.read_mapped_range(base, len)
|
35
|
+
end
|
36
|
+
|
37
|
+
def dwcache(base, len)
|
38
|
+
@dwcache[[base, len]] ||= pagecache(base, len).unpack(@ptsz == 4 ? 'L*' : 'Q*')
|
39
|
+
end
|
40
|
+
|
41
|
+
# return the array of dwords in the chunk
|
42
|
+
def chunkdw(ptr, len=@chunks[ptr])
|
43
|
+
if base = find_range(ptr)
|
44
|
+
dwcache(base, @range[base])[(ptr-base)/@ptsz, len/@ptsz]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# returns the list of chunks, sorted
|
49
|
+
def chunklist
|
50
|
+
@chunklist ||= @chunks.keys.sort
|
51
|
+
end
|
52
|
+
|
53
|
+
# dichotomic search of the chunk containing ptr
|
54
|
+
# len = hash ptr => length
|
55
|
+
# list = list of hash keys sorted
|
56
|
+
def find_elem(ptr, len, list=nil)
|
57
|
+
return ptr if len[ptr]
|
58
|
+
|
59
|
+
list ||= len.keys.sort
|
60
|
+
|
61
|
+
if list.length < 16
|
62
|
+
return list.find { |p| p <= ptr and p + len[p] > ptr }
|
63
|
+
end
|
64
|
+
|
65
|
+
window = list
|
66
|
+
while window and not window.empty?
|
67
|
+
i = window.length/2
|
68
|
+
wi = window[i]
|
69
|
+
if ptr < wi
|
70
|
+
window = window[0, i]
|
71
|
+
elsif ptr < wi + len[wi]
|
72
|
+
return wi
|
73
|
+
else
|
74
|
+
window = window[i+1, i]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# find the chunk encompassing ptr
|
80
|
+
def find_chunk(ptr)
|
81
|
+
find_elem(ptr, @chunks, chunklist)
|
82
|
+
end
|
83
|
+
|
84
|
+
def find_range(ptr)
|
85
|
+
@range_keys ||= @range.keys.sort
|
86
|
+
find_elem(ptr, @range, @range_keys)
|
87
|
+
end
|
88
|
+
|
89
|
+
# { chunk size => [list of chunk addrs] }
|
90
|
+
attr_accessor :buckets
|
91
|
+
def bucketize
|
92
|
+
@buckets = {}
|
93
|
+
chunklist.each { |a|
|
94
|
+
(@buckets[@chunks[a]] ||= []) << a
|
95
|
+
}
|
96
|
+
end
|
97
|
+
|
98
|
+
# find the kernels of the graph (strongly connected components)
|
99
|
+
# must be called after scan_xr
|
100
|
+
# also find the graph diameter
|
101
|
+
attr_accessor :kernels, :maxpath
|
102
|
+
def find_kernels(adj = @xrchunksto)
|
103
|
+
@maxpath = []
|
104
|
+
kernels = {}
|
105
|
+
adj.keys.sort.each { |ptr|
|
106
|
+
next if kernels[ptr]
|
107
|
+
paths = [[ptr]]
|
108
|
+
while path = paths.pop
|
109
|
+
next if not l = @xrchunksfrom[path.first]
|
110
|
+
l.each { |pl|
|
111
|
+
next if kernels[pl]
|
112
|
+
next if not adj[pl]
|
113
|
+
if path.include?(pl)
|
114
|
+
kernels[pl] = true
|
115
|
+
else
|
116
|
+
paths << [pl, *path]
|
117
|
+
end
|
118
|
+
}
|
119
|
+
@maxpath = paths.last if paths.last and paths.last.length > @maxpath.length
|
120
|
+
end
|
121
|
+
}
|
122
|
+
if @maxpath.first and np = (adj[@maxpath.last] - @maxpath).first
|
123
|
+
@maxpath << np
|
124
|
+
end
|
125
|
+
@kernels = []
|
126
|
+
while k = kernels.index(true)
|
127
|
+
curk = reachfrom(k, adj).find_all { |ok|
|
128
|
+
true == reachfrom(ok, adj) { |tk|
|
129
|
+
break true if tk == k
|
130
|
+
}
|
131
|
+
}
|
132
|
+
@kernels << curk
|
133
|
+
curk.each { |ka| kernels.delete ka }
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
attr_accessor :roots
|
138
|
+
# find the root nodes that allow acces to most other nodes
|
139
|
+
# { root => [reachable nodes] }
|
140
|
+
# does not include single nodes (@chunks.keys - @xrchunksfrom.keys)
|
141
|
+
def find_roots(adj=@xrchunksto)
|
142
|
+
@roots = {}
|
143
|
+
adj.keys.sort.each { |r|
|
144
|
+
if not @roots[r]
|
145
|
+
l = reachfrom(r, adj, @roots)
|
146
|
+
l.each { |t| @roots[t] = true if adj[t] } # may include r !, also dont mark leaves
|
147
|
+
@roots[r] = l
|
148
|
+
end
|
149
|
+
}
|
150
|
+
@roots.delete_if { |k, v| v == true }
|
151
|
+
end
|
152
|
+
|
153
|
+
def reachfrom(p, adj = @xrchunksto, roots={})
|
154
|
+
return roots[p] if roots[p].kind_of? Array
|
155
|
+
hash = {}
|
156
|
+
todo = [p]
|
157
|
+
while p = todo.pop
|
158
|
+
if to = roots[p] || adj[p] and to.kind_of? Array
|
159
|
+
to.each { |tk|
|
160
|
+
if not hash[tk]
|
161
|
+
hash[tk] = true
|
162
|
+
todo << tk
|
163
|
+
yield tk if block_given?
|
164
|
+
end
|
165
|
+
}
|
166
|
+
end
|
167
|
+
end
|
168
|
+
hash.keys
|
169
|
+
end
|
170
|
+
|
171
|
+
# create a subset of xrchunksto from one point
|
172
|
+
def graph_from(p, adj = @xrchunksto)
|
173
|
+
hash = {}
|
174
|
+
todo = [p]
|
175
|
+
while p = todo.pop
|
176
|
+
next if hash[p]
|
177
|
+
if adj[p]
|
178
|
+
hash[p] = adj[p]
|
179
|
+
todo.concat hash[p]
|
180
|
+
end
|
181
|
+
end
|
182
|
+
hash
|
183
|
+
end
|
184
|
+
|
185
|
+
# dump the whole graph in a dot file
|
186
|
+
def dump_graph(fname='graph.gv', graph=@xrchunksto)
|
187
|
+
File.open(fname, 'w') { |fd|
|
188
|
+
fd.puts "digraph foo {"
|
189
|
+
graph.each { |b, l|
|
190
|
+
fd.puts l.map { |e| '"%x" -> "%x";' % [b, e] }
|
191
|
+
}
|
192
|
+
fd.puts "}"
|
193
|
+
}
|
194
|
+
end
|
195
|
+
|
196
|
+
# chunk ptr => { dwindex => [list of ptrs] }
|
197
|
+
attr_accessor :linkedlists, :alllists
|
198
|
+
def find_linkedlists
|
199
|
+
@linkedlists = {}
|
200
|
+
@alllists = []
|
201
|
+
@buckets.sort.each { |sz, lst|
|
202
|
+
#puts "sz #{sz} #{lst.length}"
|
203
|
+
lst.each { |ptr|
|
204
|
+
next if not l = @xrchunksto[ptr]
|
205
|
+
next if not l.find { |tg| @chunks[tg] == sz }
|
206
|
+
dw = chunkdw(ptr)
|
207
|
+
dw.length.times { |dwoff|
|
208
|
+
next if @linkedlists[ptr] and @linkedlists[ptr][dwoff]
|
209
|
+
tg = dw[dwoff]
|
210
|
+
next if @chunks[tg] != sz
|
211
|
+
check_linkedlist(ptr, dwoff)
|
212
|
+
}
|
213
|
+
}
|
214
|
+
}
|
215
|
+
end
|
216
|
+
|
217
|
+
def check_linkedlist(ptr, dwoff)
|
218
|
+
psz = @chunks[ptr]
|
219
|
+
fwd = ptr
|
220
|
+
lst = [fwd]
|
221
|
+
base = find_range(fwd)
|
222
|
+
loop do
|
223
|
+
if not base or base > fwd or base + @range[base] <= fwd
|
224
|
+
base = find_range(fwd)
|
225
|
+
end
|
226
|
+
break if not base
|
227
|
+
fwd = dwcache(base, @range[base])[(fwd-base)/@ptsz + dwoff]
|
228
|
+
break if fwd == 0
|
229
|
+
return if not cl = @chunks[fwd] # XXX root/tail may be in .data
|
230
|
+
return if cl != psz
|
231
|
+
break if lst.include? fwd
|
232
|
+
lst << fwd
|
233
|
+
end
|
234
|
+
fwd = ptr
|
235
|
+
while pv = @xrchunksfrom[fwd]
|
236
|
+
fwd = pv.find { |p|
|
237
|
+
next if @chunks[p] != psz
|
238
|
+
if not base or base > p or base + @range[base] <= p
|
239
|
+
base = find_range(fwd)
|
240
|
+
end
|
241
|
+
dwcache(base, @range[base])[(p-base)/@ptsz + dwoff] == fwd
|
242
|
+
}
|
243
|
+
break if not fwd
|
244
|
+
break if lst.include? fwd
|
245
|
+
lst.unshift fwd
|
246
|
+
end
|
247
|
+
if lst.length > 3
|
248
|
+
lst.each { |p| (@linkedlists[p] ||= {})[dwoff] = lst }
|
249
|
+
@alllists << lst
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
# { chunkinarray => { rootptr => [chunks] } }
|
254
|
+
attr_accessor :arrays, :allarrays
|
255
|
+
def find_arrays
|
256
|
+
@arrays = {}
|
257
|
+
@allarrays = []
|
258
|
+
@buckets.sort.each { |sz, lst|
|
259
|
+
next if sz < @ptsz*6
|
260
|
+
lst.each { |ptr|
|
261
|
+
next if not to = @xrchunksto[ptr]
|
262
|
+
# a table must have at least half its storage space filled with ptrs
|
263
|
+
next if to.length <= sz/@ptsz/2
|
264
|
+
# also, ptrs must point to same-size stuff
|
265
|
+
lsz = Hash.new(0)
|
266
|
+
to.each { |t| lsz[@chunks[t]] += 1 }
|
267
|
+
cnt = lsz.values.max
|
268
|
+
next if cnt <= sz/@ptsz/2
|
269
|
+
tgsz = lsz.index(cnt)
|
270
|
+
ar = to.find_all { |t| @chunks[t] == tgsz }.uniq
|
271
|
+
next if ar.length <= sz/@ptsz/2
|
272
|
+
ar.each { |p| (@arrays[p] ||= {})[ptr] = ar }
|
273
|
+
@allarrays << ar
|
274
|
+
}
|
275
|
+
}
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
class LinuxHeap < Heap
|
280
|
+
# find all chunks in the memory address space
|
281
|
+
attr_accessor :mmaps
|
282
|
+
|
283
|
+
def scan_chunks
|
284
|
+
@chunks = {}
|
285
|
+
each_heap { |a, l, ar|
|
286
|
+
scan_heap(a, l, ar)
|
287
|
+
}
|
288
|
+
@mmapchunks = []
|
289
|
+
@mmaps.each { |a, l|
|
290
|
+
ll = scan_mmap(a, l) || 4096
|
291
|
+
a += ll
|
292
|
+
l -= ll
|
293
|
+
}
|
294
|
+
end
|
295
|
+
|
296
|
+
# scan all chunks for cross-references (one chunk contaning a pointer to some other chunk)
|
297
|
+
def scan_chunks_xr
|
298
|
+
@xrchunksto = {}
|
299
|
+
@xrchunksfrom = {}
|
300
|
+
each_heap { |a, l, ar|
|
301
|
+
scan_heap_xr(a, l)
|
302
|
+
}
|
303
|
+
@mmapchunks.each { |a|
|
304
|
+
scan_mmap_xr(a, @chunks[a])
|
305
|
+
}
|
306
|
+
end
|
307
|
+
|
308
|
+
# scan chunks from a heap base addr
|
309
|
+
def scan_heap(base, len, ar)
|
310
|
+
dw = dwcache(base, len)
|
311
|
+
ptr = 0
|
312
|
+
|
313
|
+
psz = dw[ptr]
|
314
|
+
sz = dw[ptr+1]
|
315
|
+
base += 2*@ptsz # user pointer
|
316
|
+
raise "bad heap base %x %x %x %x" % [psz, sz, base, len] if psz != 0 or sz & 1 == 0
|
317
|
+
|
318
|
+
loop do
|
319
|
+
clen = sz & -8 # chunk size
|
320
|
+
ptr += clen/@ptsz # start of next chk
|
321
|
+
break if ptr >= dw.length or clen == 0
|
322
|
+
sz = dw[ptr+1]
|
323
|
+
if sz & 1 > 0 # pv_inuse
|
324
|
+
# user data length up to chucksize-4 (over next psz)
|
325
|
+
#puts "used #{'%x' % base} #{clen-@ptsz}" if $VERBOSE
|
326
|
+
@chunks[base] = clen-@ptsz
|
327
|
+
else
|
328
|
+
#puts "free #{'%x' % base} #{clen-@ptsz}" if $VERBOSE
|
329
|
+
end
|
330
|
+
base += clen
|
331
|
+
end
|
332
|
+
|
333
|
+
del_fastbin(ar)
|
334
|
+
end
|
335
|
+
|
336
|
+
def scan_heap_xr(base, len)
|
337
|
+
dw = dwcache(base, len)
|
338
|
+
@chunks.each_key { |p|
|
339
|
+
i = (p-base) / @ptsz
|
340
|
+
if i >= 0 and i < dw.length
|
341
|
+
lst = dw[i, @chunks[p]/@ptsz].find_all { |pp| @chunks[pp] }
|
342
|
+
@xrchunksto[p] = lst if not lst.empty?
|
343
|
+
lst.each { |pp| (@xrchunksfrom[pp] ||= []) << p }
|
344
|
+
end
|
345
|
+
}
|
346
|
+
end
|
347
|
+
|
348
|
+
# scan chunks from a mmap base addr
|
349
|
+
# big chunks are allocated on anonymous-mmap areas
|
350
|
+
# for mmap chunks, pv_sz=0 pv_inuse=0, mmap=1, data starts at 8, mmapsz = userlen+12 [roundup 4096]
|
351
|
+
# one entry in /proc/pid/maps may point to multiple consecutive mmap chunks
|
352
|
+
# scans for a mmap chunk header, returns the chunk size if pattern match or nil
|
353
|
+
def scan_mmap(base, len)
|
354
|
+
foo = chunkdata(base)
|
355
|
+
clen = foo[1] & ~0xfff
|
356
|
+
if foo[0] == 0 and foo[1] & 0xfff == 2 and clen > 0 and clen <= len
|
357
|
+
@chunks[base + foo.length] = clen-4*@ptsz
|
358
|
+
@mmapchunks << (base + foo.length)
|
359
|
+
clen
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
def scan_mmap_xr(base, len)
|
364
|
+
dw = dwcache(base, len)
|
365
|
+
lst = dw[2..-1].find_all { |pp| @chunks[pp] }
|
366
|
+
@xrchunksto[base] = lst if not lst.empty?
|
367
|
+
lst.each { |pp| (@xrchunksfrom[pp] ||= []) << base }
|
368
|
+
end
|
369
|
+
|
370
|
+
attr_accessor :main_arena_ptr
|
371
|
+
|
372
|
+
# we need to find the main_arena from the libc
|
373
|
+
# we do this by analysing 'malloc_trim'
|
374
|
+
def scan_libc(addr)
|
375
|
+
raise 'no libc' if not addr
|
376
|
+
|
377
|
+
return if @main_arena_ptr = @dbg.symbols.index('main_arena')
|
378
|
+
|
379
|
+
unless trim = @dbg.symbols.index('malloc_trim') || @dbg.symbols.index('weak_malloc_trim')
|
380
|
+
@dbg.loadsyms 'libc[.-]'
|
381
|
+
trim = @dbg.symbols.index('malloc_trim') || @dbg.symbols.index('weak_malloc_trim')
|
382
|
+
end
|
383
|
+
raise 'cant find malloc_trim' if not trim
|
384
|
+
|
385
|
+
d = @dbg.disassembler
|
386
|
+
|
387
|
+
d.disassemble_fast(trim) if not d.di_at(trim)
|
388
|
+
if d.block_at(trim).list.last.opcode.name == 'call'
|
389
|
+
# x86 getip, need to dasm to have func_binding (cross fingers)
|
390
|
+
d.disassemble d.block_at(trim).to_normal.first
|
391
|
+
end
|
392
|
+
d.each_function_block(trim) { |b|
|
393
|
+
# mutex_lock(&main_arena.mutex) gives us the addr
|
394
|
+
next if not cmpxchg = d.block_at(b).list.find { |di| di.kind_of? DecodedInstruction and di.opcode.name == 'cmpxchg' }
|
395
|
+
@main_arena_ptr = d.backtrace(cmpxchg.instruction.args.first.symbolic.pointer, cmpxchg.address)
|
396
|
+
if @main_arena_ptr.length == 1
|
397
|
+
@main_arena_ptr = @main_arena_ptr[0].reduce
|
398
|
+
break
|
399
|
+
end
|
400
|
+
}
|
401
|
+
raise "cant find mainarena" if not @main_arena_ptr.kind_of? Integer
|
402
|
+
@dbg.symbols[@main_arena_ptr] = 'main_arena'
|
403
|
+
end
|
404
|
+
|
405
|
+
def chunkdata(ptr)
|
406
|
+
@cp.decode_c_ary('uintptr_t', 2, @dbg.memory, ptr).to_array
|
407
|
+
end
|
408
|
+
|
409
|
+
def each_heap
|
410
|
+
if not @cp.toplevel.struct['malloc_state']
|
411
|
+
@cp.parse <<EOS
|
412
|
+
// TODO autotune these 2 defines..
|
413
|
+
#define THREAD_STATS 0
|
414
|
+
//#define PER_THREAD
|
415
|
+
|
416
|
+
#define NFASTBINS 10
|
417
|
+
#define NBINS 128
|
418
|
+
#define BINMAPSIZE (NBINS/32)
|
419
|
+
|
420
|
+
struct malloc_state {
|
421
|
+
int mutex;
|
422
|
+
int flags;
|
423
|
+
#if THREAD_STATS
|
424
|
+
long stat_lock_direct, stat_lock_loop, stat_lock_wait;
|
425
|
+
#endif
|
426
|
+
void *fastbinsY[NFASTBINS];
|
427
|
+
void *top;
|
428
|
+
void *last_remainder;
|
429
|
+
void *bins[NBINS * 2 - 2]; // *2: double-linked list
|
430
|
+
unsigned int binmap[BINMAPSIZE];
|
431
|
+
struct malloc_state *next;
|
432
|
+
#ifdef PER_THREAD
|
433
|
+
struct malloc_state *next_free;
|
434
|
+
#endif
|
435
|
+
uintptr_t system_mem; // XXX int32?
|
436
|
+
uintptr_t max_system_mem;
|
437
|
+
};
|
438
|
+
|
439
|
+
struct heap_info {
|
440
|
+
struct malloc_state *ar_ptr; // Arena for this heap.
|
441
|
+
struct _heap_info *prev; // Previous heap.
|
442
|
+
uintptr_t size; // Current size in bytes. XXX int32?
|
443
|
+
uintptr_t mprotect_size; // Size in bytes that has been mprotected
|
444
|
+
};
|
445
|
+
EOS
|
446
|
+
end
|
447
|
+
|
448
|
+
ptr = @main_arena_ptr
|
449
|
+
loop do
|
450
|
+
ar = @cp.decode_c_struct('malloc_state', @dbg.memory, ptr)
|
451
|
+
if ptr == @main_arena_ptr
|
452
|
+
# main arena: find start from top.end - system_mem
|
453
|
+
toplen = chunkdata(ar.top)[1] & -8
|
454
|
+
yield ar.top + toplen - ar.system_mem, ar.system_mem, ar
|
455
|
+
else
|
456
|
+
# non-main arena: find heap_info for top, follow list
|
457
|
+
iptr = ar.top & -0x10_0000 # XXX
|
458
|
+
while iptr
|
459
|
+
hi = @cp.decode_c_struct('heap_info', @dbg.memory, iptr)
|
460
|
+
off = hi.sizeof
|
461
|
+
off += ar.sizeof if iptr+off == hi.ar_ptr
|
462
|
+
yield iptr+off, hi.size-off, ar
|
463
|
+
|
464
|
+
iptr = hi.prev
|
465
|
+
end
|
466
|
+
end
|
467
|
+
|
468
|
+
ptr = ar.next
|
469
|
+
break if ptr == @main_arena_ptr
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
473
|
+
def del_fastbin(ar)
|
474
|
+
nfastbins = 10
|
475
|
+
nfastbins.times { |i|
|
476
|
+
ptr = ar.fastbinsy[i]
|
477
|
+
while ptr
|
478
|
+
@chunks.delete ptr+2*@ptsz
|
479
|
+
ptr = @cp.decode_c_ary('void *', 3, @dbg.memory, ptr)[2]
|
480
|
+
end
|
481
|
+
}
|
482
|
+
end
|
483
|
+
end
|
484
|
+
|
485
|
+
|
486
|
+
class WindowsHeap < Heap
|
487
|
+
attr_accessor :heaps
|
488
|
+
|
489
|
+
def scan_chunks
|
490
|
+
@hsz = @cp.sizeof(@cp.find_c_struct('_HEAP_ENTRY'))
|
491
|
+
@chunks = {}
|
492
|
+
each_heap { |ar| scan_heap(ar) }
|
493
|
+
end
|
494
|
+
|
495
|
+
# scan all chunks for cross-references (one chunk containing a pointer to some other chunk)
|
496
|
+
def scan_chunks_xr
|
497
|
+
@xrchunksto = {}
|
498
|
+
@xrchunksfrom = {}
|
499
|
+
each_heap { |ar| scan_heap_xr(ar) }
|
500
|
+
end
|
501
|
+
|
502
|
+
# scan chunks from a heap
|
503
|
+
def scan_heap(ar)
|
504
|
+
each_heap_segment(ar) { |p, l|
|
505
|
+
scan_heap_segment(p, l)
|
506
|
+
}
|
507
|
+
scan_frontend(ar)
|
508
|
+
scan_valloc(ar)
|
509
|
+
end
|
510
|
+
|
511
|
+
def scan_frontend(ar)
|
512
|
+
if ar.frontendheaptype == 1
|
513
|
+
laptr = ar.frontendheap
|
514
|
+
@chunks.delete laptr # not a real (user) chunk
|
515
|
+
128.times {
|
516
|
+
la = @cp.decode_c_struct('_HEAP_LOOKASIDE', @dbg.memory, laptr)
|
517
|
+
free = la.listhead.flink
|
518
|
+
while free
|
519
|
+
@chunks.delete free
|
520
|
+
free = @cp.decode_c_struct('_LIST_ENTRY', @dbg.memory, free).flink
|
521
|
+
end
|
522
|
+
laptr += la.sizeof
|
523
|
+
}
|
524
|
+
end
|
525
|
+
end
|
526
|
+
|
527
|
+
def scan_valloc(ar)
|
528
|
+
each_listentry(ar.virtualallocdblocks, '_HEAP_VIRTUAL_ALLOC_ENTRY') { |va|
|
529
|
+
# Unusedbyte count stored in the BusyBlock.Size field
|
530
|
+
@chunks[va.stroff + va.sizeof] = va.CommitSize - va.Size
|
531
|
+
}
|
532
|
+
end
|
533
|
+
|
534
|
+
def scan_heap_segment(first, len)
|
535
|
+
off = 0
|
536
|
+
heapcpy = pagecache(first, len)
|
537
|
+
while off < len
|
538
|
+
he = @cp.decode_c_struct('_HEAP_ENTRY', heapcpy, off)
|
539
|
+
sz = he.Size*8
|
540
|
+
if he.Flags & 1 == 1
|
541
|
+
@chunks[first+off+@hsz] = sz - he.UnusedBytes
|
542
|
+
end
|
543
|
+
off += sz
|
544
|
+
end
|
545
|
+
end
|
546
|
+
|
547
|
+
def scan_heap_xr(ar)
|
548
|
+
each_heap_segment(ar) { |p, l|
|
549
|
+
scan_heap_segment_xr(p, l)
|
550
|
+
}
|
551
|
+
end
|
552
|
+
|
553
|
+
def scan_heap_segment_xr(first, len)
|
554
|
+
off = 0
|
555
|
+
heapcpy = pagecache(first, len)
|
556
|
+
while off < len
|
557
|
+
he = @cp.decode_c_struct('_HEAP_ENTRY', heapcpy, off)
|
558
|
+
sz = he.Size*8
|
559
|
+
ptr = first + off + @hsz
|
560
|
+
if he.Flags & 1 == 1 and csz = @chunks[ptr] and csz > 0
|
561
|
+
heapcpy[off + @hsz, csz].unpack('L*').each { |p|
|
562
|
+
if @chunks[p]
|
563
|
+
(@xrchunksto[ptr] ||= []) << p
|
564
|
+
(@xrchunksfrom[p] ||= []) << ptr
|
565
|
+
end
|
566
|
+
}
|
567
|
+
end
|
568
|
+
off += sz
|
569
|
+
end
|
570
|
+
end
|
571
|
+
|
572
|
+
# yields the _HEAP structure for all heaps
|
573
|
+
def each_heap
|
574
|
+
heaps.each { |a, l|
|
575
|
+
ar = @cp.decode_c_struct('_HEAP', @dbg.memory, a)
|
576
|
+
yield ar
|
577
|
+
}
|
578
|
+
end
|
579
|
+
|
580
|
+
# yields all [ptr, len] for allocated segments of a _HEAP
|
581
|
+
# this maps to the _HEAP_SEGMENT further subdivised to skip
|
582
|
+
# the _HEAP_UNCOMMMTTED_RANGE areas
|
583
|
+
# for the last chunk of the _HEAP_SEGMENT, only yield up to chunk_header
|
584
|
+
def each_heap_segment(ar)
|
585
|
+
ar.segments.to_array.compact.each { |a|
|
586
|
+
sg = @cp.decode_c_struct('_HEAP_SEGMENT', @dbg.memory, a)
|
587
|
+
skiplist = []
|
588
|
+
ptr = sg.uncommittedranges
|
589
|
+
while ptr
|
590
|
+
ucr = @cp.decode_c_struct('_HEAP_UNCOMMMTTED_RANGE', @dbg.memory, ptr)
|
591
|
+
skiplist << [ucr.Address, ucr.Size]
|
592
|
+
ptr = ucr.Next
|
593
|
+
end
|
594
|
+
ptr = sg.firstentry
|
595
|
+
# XXX lastentryinsegment == firstentry ???
|
596
|
+
# lastvalidentry = address of the end of the segment (may point to unmapped space)
|
597
|
+
ptrend = sg.lastvalidentry
|
598
|
+
skiplist.delete_if { |sa, sl| sa < ptr or sa + sl > ptrend }
|
599
|
+
skiplist << [ptrend, 1]
|
600
|
+
skiplist.sort.each { |sa, sl|
|
601
|
+
yield(ptr, sa-ptr)
|
602
|
+
ptr = sa + sl
|
603
|
+
}
|
604
|
+
}
|
605
|
+
end
|
606
|
+
|
607
|
+
# call with a LIST_ENTRY allocstruct, the target structure and LE offset in this structure
|
608
|
+
def each_listentry(le, st, off=0)
|
609
|
+
ptr0 = le.stroff
|
610
|
+
ptr = le.flink
|
611
|
+
while ptr != ptr0
|
612
|
+
yield @cp.decode_c_struct(st, @dbg.memory, ptr-off)
|
613
|
+
ptr = @cp.decode_c_struct('_LIST_ENTRY', @dbg.memory, ptr).flink
|
614
|
+
end
|
615
|
+
end
|
616
|
+
end
|
617
|
+
|
618
|
+
class Windows7Heap < WindowsHeap
|
619
|
+
# 4-byte xor key to decrypt the chunk headers
|
620
|
+
attr_accessor :chunkkey_size, :chunkkey_flags, :chunkkey_unusedbytes
|
621
|
+
def each_heap_segment(ar)
|
622
|
+
if ar.encodeflagmask != 0
|
623
|
+
@chunkkey_size = ar.encoding.size
|
624
|
+
@chunkkey_flags = ar.encoding.flags
|
625
|
+
@chunkkey_unusedbytes = ar.encoding.unusedbytes
|
626
|
+
else
|
627
|
+
@chunkkey_size = 0
|
628
|
+
@chunkkey_flags = 0
|
629
|
+
@chunkkey_unusedbytes = 0
|
630
|
+
end
|
631
|
+
|
632
|
+
each_listentry(ar.segmentlist, '_HEAP_SEGMENT', 0x10) { |sg|
|
633
|
+
skiplist = []
|
634
|
+
each_listentry(sg.ucrsegmentlist, '_HEAP_UCR_DESCRIPTOR', 8) { |ucr|
|
635
|
+
skiplist << [ucr.address, ucr.size]
|
636
|
+
}
|
637
|
+
|
638
|
+
ptr = sg.firstentry
|
639
|
+
ptrend = sg.lastvalidentry + @hsz
|
640
|
+
skiplist.delete_if { |sa, sl| sa < ptr or sa + sl > ptrend }
|
641
|
+
skiplist << [ptrend, 1]
|
642
|
+
skiplist.sort.each { |sa, sl|
|
643
|
+
yield(ptr, sa-ptr)
|
644
|
+
ptr = sa + sl
|
645
|
+
}
|
646
|
+
}
|
647
|
+
end
|
648
|
+
|
649
|
+
def scan_heap_segment(first, len)
|
650
|
+
off = 0
|
651
|
+
heapcpy = pagecache(first, len)
|
652
|
+
while off < len
|
653
|
+
he = @cp.decode_c_struct('_HEAP_ENTRY', heapcpy, off)
|
654
|
+
sz = (he.Size ^ @chunkkey_size)*8
|
655
|
+
if (he.Flags ^ @chunkkey_flags) & 1 == 1
|
656
|
+
@chunks[first+off+@hsz] = sz - (he.UnusedBytes ^ @chunkkey_unusedbytes)
|
657
|
+
end
|
658
|
+
off += sz
|
659
|
+
end
|
660
|
+
end
|
661
|
+
|
662
|
+
def scan_frontend(ar)
|
663
|
+
return if ar.frontendheaptype != 2
|
664
|
+
lfh = @cp.decode_c_struct('_LFH_HEAP', @dbg.memory, ar.frontendheap)
|
665
|
+
lfh.localdata[0].segmentinfo.to_array.each { |sinfo|
|
666
|
+
sinfo.cacheditems.to_array.each { |ssp|
|
667
|
+
next if not ssp
|
668
|
+
subseg = @cp.decode_c_struct('_HEAP_SUBSEGMENT', @dbg.memory, ssp)
|
669
|
+
scan_lfh_ss(subseg)
|
670
|
+
}
|
671
|
+
}
|
672
|
+
end
|
673
|
+
|
674
|
+
def scan_lfh_ss(subseg)
|
675
|
+
up = subseg.userblocks
|
676
|
+
return if not up
|
677
|
+
bs = subseg.blocksize
|
678
|
+
bc = subseg.blockcount
|
679
|
+
list = Array.new(bc) { |i| up + 0x10 + bs*8*i }
|
680
|
+
|
681
|
+
free = []
|
682
|
+
ptr = subseg.freeentryoffset
|
683
|
+
subseg.depth.times {
|
684
|
+
free << (up + 8*ptr)
|
685
|
+
ptr = @dbg.memory[up + 8*ptr + 8, 2].unpack('v')[0]
|
686
|
+
}
|
687
|
+
@foo ||= 0
|
688
|
+
@foo += 1
|
689
|
+
p @foo if @foo % 10 == 0
|
690
|
+
|
691
|
+
up += 0x10
|
692
|
+
list -= free
|
693
|
+
list.each { |p| @chunks[p+8] = bs*8 - (@cp.decode_c_struct('_HEAP_ENTRY', @dbg.memory, p).unusedbytes & 0x7f) }
|
694
|
+
end
|
695
|
+
|
696
|
+
def scan_chunks_xr
|
697
|
+
@xrchunksto = {}
|
698
|
+
@xrchunksfrom = {}
|
699
|
+
@chunks.each { |a, l|
|
700
|
+
pagecache(a, l).unpack('L*').each { |p|
|
701
|
+
if @chunks[p]
|
702
|
+
(@xrchunksto[a] ||= []) << p
|
703
|
+
(@xrchunksfrom[p] ||= []) << a
|
704
|
+
end
|
705
|
+
}
|
706
|
+
}
|
707
|
+
end
|
708
|
+
end
|
709
|
+
end
|