metasm 1.0.1 → 1.0.2
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 +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
|