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,28 @@
|
|
|
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
|
+
# metasm dasm plugin
|
|
8
|
+
# walks all disassembled instructions referencing an address
|
|
9
|
+
# if this address points a C string, show that in the instruction comments
|
|
10
|
+
# esp. useful after a disassemble_fast
|
|
11
|
+
|
|
12
|
+
def stringsxrefs(maxsz = 32)
|
|
13
|
+
@decoded.each_value { |di|
|
|
14
|
+
next if not di.kind_of?(DecodedInstruction)
|
|
15
|
+
di.instruction.args.grep(Expression).each { |e|
|
|
16
|
+
if str = decode_strz(e) and str.length >= 4 and str =~ /^[\x20-\x7e]*$/
|
|
17
|
+
di.add_comment str[0, maxsz].inspect
|
|
18
|
+
add_xref(normalize(e), Xref.new(:r, di.address, 1))
|
|
19
|
+
end
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
nil
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
if gui
|
|
26
|
+
stringsxrefs
|
|
27
|
+
gui.gui_update
|
|
28
|
+
end
|
data/samples/dasmnavig.rb
CHANGED
data/samples/dbg-apihook.rb
CHANGED
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
require 'metasm'
|
|
18
18
|
|
|
19
19
|
class ApiHook
|
|
20
|
+
attr_accessor :dbg
|
|
21
|
+
|
|
20
22
|
# rewrite this function to list the hooks you want
|
|
21
23
|
# return an array of hashes
|
|
22
24
|
def setup
|
|
@@ -33,19 +35,31 @@ class ApiHook
|
|
|
33
35
|
raise 'no such process' if not process
|
|
34
36
|
dbg = process.debugger
|
|
35
37
|
end
|
|
36
|
-
dbg.loadallsyms
|
|
37
38
|
@dbg = dbg
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
begin
|
|
40
|
+
setup.each { |h| setup_hook(h) }
|
|
41
|
+
init_prerun if respond_to?(:init_prerun) # allow subclass to do stuff before main loop
|
|
42
|
+
@dbg.run_forever
|
|
43
|
+
rescue Interrupt
|
|
44
|
+
@dbg.detach #rescue nil
|
|
45
|
+
end
|
|
41
46
|
end
|
|
42
47
|
|
|
43
48
|
# setup one function hook
|
|
44
49
|
def setup_hook(h)
|
|
50
|
+
@las ||= false
|
|
51
|
+
if not h[:lib] and not @las
|
|
52
|
+
@dbg.loadallsyms
|
|
53
|
+
@las = false
|
|
54
|
+
elsif h[:lib]
|
|
55
|
+
# avoid loadallsyms if specified (regexp against pathname, not exported lib name)
|
|
56
|
+
@dbg.loadsyms(h[:lib])
|
|
57
|
+
end
|
|
58
|
+
|
|
45
59
|
pre = "pre_#{h[:hookname] || h[:function]}"
|
|
46
60
|
post = "post_#{h[:hookname] || h[:function]}"
|
|
47
61
|
|
|
48
|
-
|
|
62
|
+
nargs = h[:nargs] || method(pre).arity if respond_to?(pre)
|
|
49
63
|
|
|
50
64
|
if target = h[:address]
|
|
51
65
|
elsif target = h[:rva]
|
|
@@ -56,7 +70,8 @@ class ApiHook
|
|
|
56
70
|
target = h[:function]
|
|
57
71
|
end
|
|
58
72
|
|
|
59
|
-
@dbg.bpx(target) {
|
|
73
|
+
@dbg.bpx(target, false, h[:condition]) {
|
|
74
|
+
@nargs = nargs
|
|
60
75
|
catch(:finish) {
|
|
61
76
|
@cur_abi = h[:abi]
|
|
62
77
|
@ret_longlong = h[:ret_longlong]
|
|
@@ -206,7 +221,8 @@ class MyHook < ApiHook
|
|
|
206
221
|
#patch_retval(42)
|
|
207
222
|
|
|
208
223
|
# finish messing with the args: fake the nrofbyteswritten
|
|
209
|
-
handle, pbuf, size, pwritten, overlap = arglistcopy
|
|
224
|
+
#handle, pbuf, size, pwritten, overlap = arglistcopy
|
|
225
|
+
size, pwritten = arglistcopy.values_at(2, 3)
|
|
210
226
|
written = @dbg.memory_read_int(pwritten)
|
|
211
227
|
if written == size
|
|
212
228
|
# if written everything, patch the value so that the program dont detect our intervention
|
|
@@ -217,8 +233,7 @@ class MyHook < ApiHook
|
|
|
217
233
|
end
|
|
218
234
|
end
|
|
219
235
|
|
|
220
|
-
|
|
221
|
-
Metasm::WinOS.get_debug_privilege
|
|
236
|
+
Metasm::OS.current.get_debug_privilege if Metasm::OS.current.respond_to? :get_debug_privilege
|
|
222
237
|
|
|
223
238
|
# run our Hook engine on a running 'notepad' instance
|
|
224
239
|
MyHook.new('notepad')
|
|
@@ -0,0 +1,283 @@
|
|
|
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
|
+
# metasm debugger plugin
|
|
8
|
+
# adds some heap_* functions to interract with the target heap chunks
|
|
9
|
+
# functions:
|
|
10
|
+
# heap_scan, scan for malloc chunks in the heaps and xrefs between them
|
|
11
|
+
# heap_scanstruct, scan for arrays/linkedlists in the chunk graph
|
|
12
|
+
# heap_chunk [addr], display a chunk
|
|
13
|
+
# heap_array [addr], display an array of chunks from their root
|
|
14
|
+
# heap_list [addr], display a linkedlist
|
|
15
|
+
# heap_strscan [str], scan the memory for a raw string, display chunks xrefs
|
|
16
|
+
# heap_snap, make a snapshot of the currently displayed structure, hilight fields change
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# use precompiled native version when available
|
|
20
|
+
$heapscan_dir = File.join(File.dirname(plugin_filename).gsub('\\', '/'), 'heapscan')
|
|
21
|
+
require File.join($heapscan_dir, 'heapscan')
|
|
22
|
+
|
|
23
|
+
fname = case OS.current.shortname
|
|
24
|
+
when 'linos'
|
|
25
|
+
'compiled_heapscan_lin'
|
|
26
|
+
when 'winos'
|
|
27
|
+
case OS.current.version[0]
|
|
28
|
+
when 5; 'compiled_heapscan_win'
|
|
29
|
+
when 6; 'compiled_heapscan_win7'
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
fname = File.join($heapscan_dir, fname)
|
|
33
|
+
if not File.exist?(fname + '.so') and File.exist?(fname + '.c')
|
|
34
|
+
puts "compiling native scanner..."
|
|
35
|
+
exe = DynLdr.host_exe.compile_c_file(DynLdr.host_cpu, fname + '.c')
|
|
36
|
+
DynLdr.compile_binary_module_hack(exe)
|
|
37
|
+
exe.encode_file(fname + '.so', :lib)
|
|
38
|
+
end
|
|
39
|
+
require fname if File.exist?(fname + '.so')
|
|
40
|
+
|
|
41
|
+
def heapscan_time(s='')
|
|
42
|
+
@heapscan_time ||= nil
|
|
43
|
+
t = Time.now
|
|
44
|
+
log s + ' %.2fs' % (t-@heapscan_time) if @heapscan_time and s != ''
|
|
45
|
+
@heapscan_time = t
|
|
46
|
+
Gui.main_iter if gui
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def heap; @heap ; end
|
|
50
|
+
def heap=(h) ; @heap = h ; end
|
|
51
|
+
|
|
52
|
+
def heapscan_scan(xr=true)
|
|
53
|
+
heaps = []
|
|
54
|
+
mmaps = []
|
|
55
|
+
libc = nil
|
|
56
|
+
pr = os_process
|
|
57
|
+
pr.mappings.each { |a, l, p, f|
|
|
58
|
+
case f.to_s
|
|
59
|
+
when /heap/
|
|
60
|
+
heaps << [a, l]
|
|
61
|
+
when /libc[^a-zA-Z]/
|
|
62
|
+
libc ||= a if p == 'r-xp'
|
|
63
|
+
when ''
|
|
64
|
+
mmaps << [a, l]
|
|
65
|
+
end
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
heapscan_time ''
|
|
69
|
+
@disassembler.parse_c ''
|
|
70
|
+
if pr and OS.current.name =~ /winos/i
|
|
71
|
+
if OS.current.version[0] == 5
|
|
72
|
+
@heap = WindowsHeap.new(self)
|
|
73
|
+
@heap.cp = @disassembler.c_parser
|
|
74
|
+
@heap.cp.parse_file File.join($heapscan_dir, 'winheap.h') unless @heap.cp.toplevel.struct['_HEAP']
|
|
75
|
+
else
|
|
76
|
+
@heap = Windows7Heap.new(self)
|
|
77
|
+
@heap.cp = @disassembler.c_parser
|
|
78
|
+
@heap.cp.parse_file File.join($heapscan_dir, 'winheap7.h') unless @heap.cp.toplevel.struct['_HEAP']
|
|
79
|
+
end
|
|
80
|
+
@heap.heaps = heaps
|
|
81
|
+
else
|
|
82
|
+
@heap = LinuxHeap.new(self)
|
|
83
|
+
@heap.cp = @disassembler.c_parser
|
|
84
|
+
@heap.mmaps = mmaps
|
|
85
|
+
@heap.scan_libc(libc)
|
|
86
|
+
heapscan_time "libc!main_arena #{'%x' % @heap.main_arena_ptr}"
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
hsz = 0
|
|
90
|
+
(heaps + mmaps).each { |a, l|
|
|
91
|
+
hsz += l
|
|
92
|
+
@heap.range.update a => l
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
log "#{hsz/1024/1024}M heap"
|
|
96
|
+
|
|
97
|
+
@heap.scan_chunks
|
|
98
|
+
heapscan_time "#{@heap.chunks.length} chunks"
|
|
99
|
+
return if not xr
|
|
100
|
+
|
|
101
|
+
@heap.scan_chunks_xr
|
|
102
|
+
heapscan_time "#{@heap.xrchunksto.length} src, #{@heap.xrchunksfrom.length} dst"
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def heapscan_structs
|
|
106
|
+
heapscan_time
|
|
107
|
+
@heap.bucketize
|
|
108
|
+
heapscan_time "#{@heap.buckets.length} buckets"
|
|
109
|
+
|
|
110
|
+
@heap.find_arrays
|
|
111
|
+
heapscan_time "#{@heap.allarrays.length} arrays (#{@heap.allarrays.flatten.length} elems)"
|
|
112
|
+
|
|
113
|
+
@heap.find_linkedlists
|
|
114
|
+
heapscan_time "#{@heap.alllists.length} lists (#{@heap.alllists.flatten.length} elems)"
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def heapscan_kernels
|
|
118
|
+
heapscan_time
|
|
119
|
+
@heap.find_kernels
|
|
120
|
+
heapscan_time "#{@heap.kernels.length} kernels"
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def heapscan_roots
|
|
124
|
+
heapscan_time
|
|
125
|
+
@heap.find_roots
|
|
126
|
+
heapscan_time "#{@heap.roots.length} roots"
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def heapscan_graph
|
|
130
|
+
heapscan_time
|
|
131
|
+
@heap.dump_graph
|
|
132
|
+
heapscan_time 'graph.gv'
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def gui_show_list(addr)
|
|
136
|
+
a = resolve(addr)
|
|
137
|
+
#@heap.cp.parse("struct ptr { void *ptr; };") if not @heap.cp.toplevel.struct['ptr']
|
|
138
|
+
h = @heap.linkedlists[a]
|
|
139
|
+
off = h.keys.first
|
|
140
|
+
lst = h[off]
|
|
141
|
+
|
|
142
|
+
if not st = lst.map { |l| @heap.chunk_struct[l] }.compact.first
|
|
143
|
+
st = Metasm::C::Struct.new
|
|
144
|
+
st.name = "list_#{'%x' % lst.first}"
|
|
145
|
+
st.members = []
|
|
146
|
+
(@heap.chunks[lst.first] / 4).times { |i|
|
|
147
|
+
n = "u#{i}"
|
|
148
|
+
t = Metasm::C::BaseType.new(:int)
|
|
149
|
+
if i == off/4
|
|
150
|
+
n = "next"
|
|
151
|
+
t = Metasm::C::Pointer.new(st)
|
|
152
|
+
end
|
|
153
|
+
st.members << Metasm::C::Variable.new(n, t)
|
|
154
|
+
}
|
|
155
|
+
@heap.cp.toplevel.struct[st.name] = st
|
|
156
|
+
end
|
|
157
|
+
lst.each { |l| @heap.chunk_struct[l] = st }
|
|
158
|
+
|
|
159
|
+
$ghw.addr_struct = {}
|
|
160
|
+
lst.each { |aa|
|
|
161
|
+
$ghw.addr_struct[aa] = @heap.cp.decode_c_struct(st.name, @memory, aa)
|
|
162
|
+
}
|
|
163
|
+
gui.parent_widget.mem.focus_addr(lst.first, :graphheap)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def gui_show_array(addr)
|
|
167
|
+
head = resolve(addr)
|
|
168
|
+
e = @heap.xrchunksto[head].to_a.find { |ee| @heap.arrays[ee] and @heap.arrays[ee][head] }
|
|
169
|
+
return if not e
|
|
170
|
+
lst = @heap.arrays[e][head]
|
|
171
|
+
|
|
172
|
+
if not st = @heap.chunk_struct[head]
|
|
173
|
+
st = Metasm::C::Struct.new
|
|
174
|
+
st.name = "array_#{'%x' % head}"
|
|
175
|
+
st.members = []
|
|
176
|
+
(@heap.chunks[head] / 4).times { |i|
|
|
177
|
+
n = "u#{i}"
|
|
178
|
+
v = @memory[head+4*i, 4].unpack('L').first
|
|
179
|
+
if @heap.chunks[v]
|
|
180
|
+
t = Metasm::C::Pointer.new(Metasm::C::BaseType.new(:void))
|
|
181
|
+
else
|
|
182
|
+
t = Metasm::C::BaseType.new(:int)
|
|
183
|
+
end
|
|
184
|
+
st.members << Metasm::C::Variable.new(n, t)
|
|
185
|
+
}
|
|
186
|
+
@heap.cp.toplevel.struct[st.name] ||= st
|
|
187
|
+
end
|
|
188
|
+
@heap.chunk_struct[head] = st
|
|
189
|
+
|
|
190
|
+
$ghw.addr_struct = { head => @heap.cp.decode_c_struct(st.name, @memory, head) }
|
|
191
|
+
|
|
192
|
+
if not st = lst.map { |l| @heap.chunk_struct[l] }.compact.first
|
|
193
|
+
e = lst.first
|
|
194
|
+
st = Metasm::C::Struct.new
|
|
195
|
+
st.name = "elem_#{'%x' % head}"
|
|
196
|
+
st.members = []
|
|
197
|
+
(@heap.chunks[e] / 4).times { |i|
|
|
198
|
+
n = "u#{i}"
|
|
199
|
+
v = @memory[e+4*i, 4].unpack('L').first
|
|
200
|
+
if @heap.chunks[v]
|
|
201
|
+
t = Metasm::C::Pointer.new(Metasm::C::BaseType.new(:void))
|
|
202
|
+
else
|
|
203
|
+
t = Metasm::C::BaseType.new(:int)
|
|
204
|
+
end
|
|
205
|
+
st.members << Metasm::C::Variable.new(n, t)
|
|
206
|
+
}
|
|
207
|
+
@heap.cp.toplevel.struct[st.name] ||= st
|
|
208
|
+
end
|
|
209
|
+
lst.each { |l| @heap.chunk_struct[l] = st }
|
|
210
|
+
|
|
211
|
+
lst.each { |aa|
|
|
212
|
+
$ghw.addr_struct[aa] = @heap.cp.decode_c_struct(st.name, @memory, aa)
|
|
213
|
+
}
|
|
214
|
+
gui.parent_widget.mem.focus_addr(head, :graphheap)
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
if gui
|
|
219
|
+
require File.join($heapscan_dir, 'graphheap')
|
|
220
|
+
$ghw = Metasm::Gui::GraphHeapWidget.new(@disassembler, gui.parent_widget.mem)
|
|
221
|
+
gui.parent_widget.mem.addview :graphheap, $ghw
|
|
222
|
+
$ghw.show if $ghw.respond_to?(:show)
|
|
223
|
+
|
|
224
|
+
gui.new_command('heap_scan', 'scan the heap(s)') { |*a| heapscan_scan ; $ghw.heap = @heap }
|
|
225
|
+
gui.new_command('heap_scan_noxr', 'scan the heap(s), no xrefs') { |*a| heapscan_scan(false) ; $ghw.heap = @heap }
|
|
226
|
+
gui.new_command('heap_scan_xronly', 'scan the heap(s) for xrefs') { |*a| $ghw.heap.scan_chunks_xr }
|
|
227
|
+
gui.new_command('heap_scanstructs', 'scan the heap for arrays/lists') { |*a| heapscan_structs }
|
|
228
|
+
gui.new_command('heap_list', 'show a linked list') { |a|
|
|
229
|
+
if a.to_s != ''
|
|
230
|
+
gui_show_list(a)
|
|
231
|
+
else
|
|
232
|
+
l = [['addr', 'len']]
|
|
233
|
+
@heap.alllists.each { |al|
|
|
234
|
+
l << [Expression[al.first], al.length]
|
|
235
|
+
}
|
|
236
|
+
gui.listwindow('lists', l) { |*aa| gui_show_list(aa[0][0]) }
|
|
237
|
+
end
|
|
238
|
+
}
|
|
239
|
+
gui.new_command('heap_array', 'show an array') { |a|
|
|
240
|
+
if a.to_s != ''
|
|
241
|
+
gui_show_array(a)
|
|
242
|
+
else
|
|
243
|
+
l = [['addr', 'len']]
|
|
244
|
+
@heap.allarrays.each { |al|
|
|
245
|
+
l << [Expression[al.first], al.length]
|
|
246
|
+
}
|
|
247
|
+
gui.listwindow('arrays', l) { |*aa| gui_show_array(aa[0][0]) }
|
|
248
|
+
end
|
|
249
|
+
}
|
|
250
|
+
gui.new_command('heap_chunk', 'show a chunk') { |a|
|
|
251
|
+
a = resolve(a)
|
|
252
|
+
gui.parent_widget.mem.focus_addr(a, :graphheap)
|
|
253
|
+
$ghw.do_focus_addr(a)
|
|
254
|
+
}
|
|
255
|
+
gui.new_command('heap_strscan', 'scan a string') { |a|
|
|
256
|
+
sa = pattern_scan(a)
|
|
257
|
+
log "found #{sa.length} strings : #{sa.map { |aa| Expression[aa] }.join(' ')}"
|
|
258
|
+
sa.each { |aa|
|
|
259
|
+
next if not ck = @heap.find_chunk(aa)
|
|
260
|
+
log "ptr #{Expression[aa]} in chunk #{Expression[ck]} (#{Expression[@heap.chunks[ck]]}) in list #{@heap.linkedlists && @heap.linkedlists[ck] && true} in array #{@heap.arrays[ck].map { |k, v| "#{Expression[k]} (#{v.length})" }.join(', ') if @heap.arrays and @heap.arrays[ck]}"
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
gui.new_command('heap_ptrscan', 'scan a pointer') { |a|
|
|
264
|
+
a = resolve(a)
|
|
265
|
+
if @heap.chunks[a]
|
|
266
|
+
pa = @heap.xrchunksfrom[a].to_a
|
|
267
|
+
else
|
|
268
|
+
pa = pattern_scan(Expression.encode_imm(a, @cpu.size/8, @cpu.endianness))
|
|
269
|
+
end
|
|
270
|
+
log "found #{pa.length} pointers : #{pa.map { |aa| Expression[aa] }.join(' ')}"
|
|
271
|
+
pa.each { |aa|
|
|
272
|
+
next if not ck = @heap.find_chunk(aa)
|
|
273
|
+
log "ptr @#{Expression[aa]} in chunk #{Expression[ck]} (#{Expression[@heap.chunks[ck]]}) in list #{@heap.linkedlists && @heap.linkedlists[ck] && true} in array #{@heap.arrays[ck].map { |k, v| "#{Expression[k]} (#{v.length})" }.join(', ') if @heap.arrays and @heap.arrays[ck]}"
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
gui.new_command('heap_snap', 'snapshot the current heap struct') { |a|
|
|
278
|
+
$ghw.snap
|
|
279
|
+
}
|
|
280
|
+
gui.new_command('heap_snap_add', 'snapshot, ignore fields changed between now and last snap') { |a|
|
|
281
|
+
$ghw.snap_add
|
|
282
|
+
}
|
|
283
|
+
end
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
#ifdef __ELF__
|
|
2
|
+
asm .pt_gnu_stack rw;
|
|
3
|
+
#endif
|
|
4
|
+
typedef uintptr_t VALUE;
|
|
5
|
+
static VALUE const_File;
|
|
6
|
+
static VALUE const_LinuxHeap;
|
|
7
|
+
VALUE rb_ary_new(void);
|
|
8
|
+
VALUE rb_ary_push(VALUE, VALUE);
|
|
9
|
+
extern VALUE *rb_cObject __attribute__((import));
|
|
10
|
+
VALUE rb_const_get(VALUE, VALUE);
|
|
11
|
+
void rb_define_method(VALUE, char*, VALUE(*)(), int);
|
|
12
|
+
VALUE rb_funcall(VALUE recv, unsigned int id, int nargs, ...);
|
|
13
|
+
VALUE rb_gv_get(const char*);
|
|
14
|
+
VALUE rb_intern(char*);
|
|
15
|
+
VALUE rb_ivar_get(VALUE, unsigned int);
|
|
16
|
+
VALUE rb_iv_get(VALUE, char*);
|
|
17
|
+
void *rb_method_node(VALUE, unsigned int);
|
|
18
|
+
VALUE rb_obj_as_string(VALUE);
|
|
19
|
+
VALUE rb_str_append(VALUE, VALUE);
|
|
20
|
+
VALUE rb_str_cat2(VALUE, const char*);
|
|
21
|
+
VALUE rb_str_new2(const char*);
|
|
22
|
+
VALUE rb_hash_aset(VALUE, VALUE, VALUE);
|
|
23
|
+
VALUE rb_hash_aref(VALUE, VALUE);
|
|
24
|
+
VALUE rb_uint2inum(VALUE);
|
|
25
|
+
VALUE rb_num2ulong(VALUE);
|
|
26
|
+
char *rb_string_value_ptr(VALUE*);
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
int printf(char*, ...);
|
|
30
|
+
|
|
31
|
+
static VALUE heap_entry(void *heap, VALUE idx, VALUE psz)
|
|
32
|
+
{
|
|
33
|
+
if (psz == 4)
|
|
34
|
+
return (VALUE)(((__int32*)heap)[idx]);
|
|
35
|
+
return (VALUE)(((__int64*)heap)[idx]);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
static VALUE m_LinuxHeap23scan_heap(VALUE self, VALUE vbase, VALUE vlen, VALUE ar)
|
|
39
|
+
{
|
|
40
|
+
VALUE *heap;
|
|
41
|
+
VALUE chunks;
|
|
42
|
+
VALUE base = rb_num2ulong(vbase);
|
|
43
|
+
VALUE len = vlen >> 1;
|
|
44
|
+
VALUE sz, clen;
|
|
45
|
+
VALUE page;
|
|
46
|
+
VALUE psz = rb_iv_get(self, "@ptsz") >> 1;
|
|
47
|
+
VALUE ptr = 0;
|
|
48
|
+
|
|
49
|
+
chunks = rb_iv_get(self, "@chunks");
|
|
50
|
+
page = rb_funcall(self, rb_intern("pagecache"), 2, vbase, vlen);
|
|
51
|
+
heap = rb_string_value_ptr(&page);
|
|
52
|
+
|
|
53
|
+
sz = heap_entry(heap, 1, psz);
|
|
54
|
+
if (heap_entry(heap, 0, psz) != 0 || (sz & 1) != 1)
|
|
55
|
+
return 4;
|
|
56
|
+
|
|
57
|
+
base += 8;
|
|
58
|
+
|
|
59
|
+
for (;;) {
|
|
60
|
+
clen = sz & -8;
|
|
61
|
+
ptr += clen/psz;
|
|
62
|
+
if (ptr >= len/psz || clen == 0)
|
|
63
|
+
break;
|
|
64
|
+
|
|
65
|
+
sz = heap_entry(heap, ptr+1, psz);
|
|
66
|
+
if (sz & 1)
|
|
67
|
+
rb_hash_aset(chunks, rb_uint2inum(base), ((clen-psz)<<1)|1);
|
|
68
|
+
base += clen;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
rb_funcall(self, rb_intern("del_fastbin"), 1, ar);
|
|
72
|
+
|
|
73
|
+
return 4;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
static VALUE m_LinuxHeap23scan_heap_xr(VALUE self, VALUE vbase, VALUE vlen)
|
|
79
|
+
{
|
|
80
|
+
VALUE *heap;
|
|
81
|
+
VALUE chunks, xrchunksto, xrchunksfrom;
|
|
82
|
+
VALUE psz = rb_iv_get(self, "@ptsz") >> 1;
|
|
83
|
+
VALUE base = rb_num2ulong(vbase) + 2*psz;
|
|
84
|
+
VALUE len = vlen >> 1;
|
|
85
|
+
VALUE sz, clen;
|
|
86
|
+
VALUE page;
|
|
87
|
+
|
|
88
|
+
chunks = rb_iv_get(self, "@chunks");
|
|
89
|
+
xrchunksto = rb_iv_get(self, "@xrchunksto");
|
|
90
|
+
xrchunksfrom = rb_iv_get(self, "@xrchunksfrom");
|
|
91
|
+
page = rb_funcall(self, rb_intern("pagecache"), 2, vbase, vlen);
|
|
92
|
+
heap = rb_string_value_ptr(&page);
|
|
93
|
+
|
|
94
|
+
sz = heap_entry(heap, 1, psz);
|
|
95
|
+
if (heap_entry(heap, 0, psz) != 0 || (sz & 1) != 1)
|
|
96
|
+
return 4;
|
|
97
|
+
|
|
98
|
+
/* re-walk the heap, simpler than iterating over @chunks */
|
|
99
|
+
VALUE ptr = 0;
|
|
100
|
+
VALUE ptr0, ptrl;
|
|
101
|
+
for (;;) {
|
|
102
|
+
clen = sz & -8;
|
|
103
|
+
ptr0 = ptr+2;
|
|
104
|
+
ptrl = clen/psz-1;
|
|
105
|
+
ptr += clen/psz;
|
|
106
|
+
if (ptr >= len/psz || clen == 0)
|
|
107
|
+
break;
|
|
108
|
+
|
|
109
|
+
sz = heap_entry(heap, ptr+1, psz);
|
|
110
|
+
if ((sz & 1) &&
|
|
111
|
+
((rb_hash_aref(chunks, rb_uint2inum(base))|4) != 4)) {
|
|
112
|
+
VALUE tabto = 0;
|
|
113
|
+
VALUE tabfrom;
|
|
114
|
+
while (ptrl--) {
|
|
115
|
+
VALUE p = heap_entry(heap, ptr0++, psz);
|
|
116
|
+
//if (p == base) // ignore self-references
|
|
117
|
+
// continue;
|
|
118
|
+
if ((rb_hash_aref(chunks, rb_uint2inum(p))|4) != 4) {
|
|
119
|
+
if (!tabto) {
|
|
120
|
+
tabto = rb_ary_new();
|
|
121
|
+
rb_hash_aset(xrchunksto, rb_uint2inum(base), tabto);
|
|
122
|
+
}
|
|
123
|
+
rb_ary_push(tabto, rb_uint2inum(p));
|
|
124
|
+
|
|
125
|
+
tabfrom = rb_hash_aref(xrchunksfrom, rb_uint2inum(p));
|
|
126
|
+
if ((tabfrom|4) == 4) {
|
|
127
|
+
tabfrom = rb_ary_new();
|
|
128
|
+
rb_hash_aset(xrchunksfrom, rb_uint2inum(p), tabfrom);
|
|
129
|
+
}
|
|
130
|
+
rb_ary_push(tabfrom, rb_uint2inum(base));
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
base += clen;
|
|
135
|
+
}
|
|
136
|
+
return 4;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
static void do_init_once(void)
|
|
142
|
+
{
|
|
143
|
+
const_LinuxHeap = rb_const_get(*rb_cObject, rb_intern("Metasm"));
|
|
144
|
+
const_LinuxHeap = rb_const_get(const_LinuxHeap, rb_intern("LinuxHeap"));
|
|
145
|
+
rb_define_method(const_LinuxHeap, "scan_heap", m_LinuxHeap23scan_heap, 3);
|
|
146
|
+
rb_define_method(const_LinuxHeap, "scan_heap_xr", m_LinuxHeap23scan_heap_xr, 2);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
int Init_compiled_heapscan_lin __attribute__((export))(void)
|
|
152
|
+
{
|
|
153
|
+
do_init_once();
|
|
154
|
+
return 0;
|
|
155
|
+
}
|