metasm 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (235) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.hgtags +3 -0
  4. data/Gemfile +1 -0
  5. data/INSTALL +61 -0
  6. data/LICENCE +458 -0
  7. data/README +29 -21
  8. data/Rakefile +10 -0
  9. data/TODO +10 -12
  10. data/doc/code_organisation.txt +2 -0
  11. data/doc/core/DynLdr.txt +247 -0
  12. data/doc/core/ExeFormat.txt +43 -0
  13. data/doc/core/Expression.txt +220 -0
  14. data/doc/core/GNUExports.txt +27 -0
  15. data/doc/core/Ia32.txt +236 -0
  16. data/doc/core/SerialStruct.txt +108 -0
  17. data/doc/core/VirtualString.txt +145 -0
  18. data/doc/core/WindowsExports.txt +61 -0
  19. data/doc/core/index.txt +1 -0
  20. data/doc/style.css +6 -3
  21. data/doc/usage/debugger.txt +327 -0
  22. data/doc/usage/index.txt +1 -0
  23. data/doc/use_cases.txt +2 -2
  24. data/metasm.gemspec +22 -0
  25. data/{lib/metasm.rb → metasm.rb} +11 -3
  26. data/{lib/metasm → metasm}/compile_c.rb +13 -7
  27. data/metasm/cpu/arc.rb +8 -0
  28. data/metasm/cpu/arc/decode.rb +425 -0
  29. data/metasm/cpu/arc/main.rb +191 -0
  30. data/metasm/cpu/arc/opcodes.rb +588 -0
  31. data/{lib/metasm → metasm/cpu}/arm.rb +7 -5
  32. data/{lib/metasm → metasm/cpu}/arm/debug.rb +2 -2
  33. data/{lib/metasm → metasm/cpu}/arm/decode.rb +13 -12
  34. data/{lib/metasm → metasm/cpu}/arm/encode.rb +23 -8
  35. data/{lib/metasm → metasm/cpu}/arm/main.rb +0 -3
  36. data/metasm/cpu/arm/opcodes.rb +324 -0
  37. data/{lib/metasm → metasm/cpu}/arm/parse.rb +25 -13
  38. data/{lib/metasm → metasm/cpu}/arm/render.rb +2 -2
  39. data/metasm/cpu/arm64.rb +15 -0
  40. data/metasm/cpu/arm64/debug.rb +38 -0
  41. data/metasm/cpu/arm64/decode.rb +289 -0
  42. data/metasm/cpu/arm64/encode.rb +41 -0
  43. data/metasm/cpu/arm64/main.rb +105 -0
  44. data/metasm/cpu/arm64/opcodes.rb +232 -0
  45. data/metasm/cpu/arm64/parse.rb +20 -0
  46. data/metasm/cpu/arm64/render.rb +95 -0
  47. data/{lib/metasm/ppc.rb → metasm/cpu/bpf.rb} +2 -4
  48. data/metasm/cpu/bpf/decode.rb +142 -0
  49. data/metasm/cpu/bpf/main.rb +60 -0
  50. data/metasm/cpu/bpf/opcodes.rb +81 -0
  51. data/metasm/cpu/bpf/render.rb +41 -0
  52. data/metasm/cpu/cy16.rb +9 -0
  53. data/metasm/cpu/cy16/decode.rb +253 -0
  54. data/metasm/cpu/cy16/main.rb +63 -0
  55. data/metasm/cpu/cy16/opcodes.rb +78 -0
  56. data/metasm/cpu/cy16/render.rb +41 -0
  57. data/metasm/cpu/dalvik.rb +11 -0
  58. data/{lib/metasm → metasm/cpu}/dalvik/decode.rb +35 -13
  59. data/{lib/metasm → metasm/cpu}/dalvik/main.rb +51 -2
  60. data/{lib/metasm → metasm/cpu}/dalvik/opcodes.rb +19 -11
  61. data/metasm/cpu/ia32.rb +17 -0
  62. data/{lib/metasm → metasm/cpu}/ia32/compile_c.rb +5 -7
  63. data/{lib/metasm → metasm/cpu}/ia32/debug.rb +5 -5
  64. data/{lib/metasm → metasm/cpu}/ia32/decode.rb +246 -59
  65. data/{lib/metasm → metasm/cpu}/ia32/decompile.rb +7 -7
  66. data/{lib/metasm → metasm/cpu}/ia32/encode.rb +19 -13
  67. data/{lib/metasm → metasm/cpu}/ia32/main.rb +51 -8
  68. data/metasm/cpu/ia32/opcodes.rb +1424 -0
  69. data/{lib/metasm → metasm/cpu}/ia32/parse.rb +47 -16
  70. data/{lib/metasm → metasm/cpu}/ia32/render.rb +31 -4
  71. data/metasm/cpu/mips.rb +14 -0
  72. data/{lib/metasm → metasm/cpu}/mips/compile_c.rb +1 -1
  73. data/metasm/cpu/mips/debug.rb +42 -0
  74. data/{lib/metasm → metasm/cpu}/mips/decode.rb +46 -16
  75. data/{lib/metasm → metasm/cpu}/mips/encode.rb +4 -3
  76. data/{lib/metasm → metasm/cpu}/mips/main.rb +11 -4
  77. data/{lib/metasm → metasm/cpu}/mips/opcodes.rb +86 -17
  78. data/{lib/metasm → metasm/cpu}/mips/parse.rb +1 -1
  79. data/{lib/metasm → metasm/cpu}/mips/render.rb +1 -1
  80. data/{lib/metasm/dalvik.rb → metasm/cpu/msp430.rb} +1 -1
  81. data/metasm/cpu/msp430/decode.rb +247 -0
  82. data/metasm/cpu/msp430/main.rb +62 -0
  83. data/metasm/cpu/msp430/opcodes.rb +101 -0
  84. data/{lib/metasm → metasm/cpu}/pic16c/decode.rb +6 -7
  85. data/{lib/metasm → metasm/cpu}/pic16c/main.rb +0 -0
  86. data/{lib/metasm → metasm/cpu}/pic16c/opcodes.rb +1 -1
  87. data/{lib/metasm/mips.rb → metasm/cpu/ppc.rb} +4 -4
  88. data/{lib/metasm → metasm/cpu}/ppc/decode.rb +18 -12
  89. data/{lib/metasm → metasm/cpu}/ppc/decompile.rb +3 -3
  90. data/{lib/metasm → metasm/cpu}/ppc/encode.rb +2 -2
  91. data/{lib/metasm → metasm/cpu}/ppc/main.rb +17 -12
  92. data/{lib/metasm → metasm/cpu}/ppc/opcodes.rb +11 -5
  93. data/metasm/cpu/ppc/parse.rb +55 -0
  94. data/metasm/cpu/python.rb +8 -0
  95. data/metasm/cpu/python/decode.rb +136 -0
  96. data/metasm/cpu/python/main.rb +36 -0
  97. data/metasm/cpu/python/opcodes.rb +180 -0
  98. data/{lib/metasm → metasm/cpu}/sh4.rb +1 -1
  99. data/{lib/metasm → metasm/cpu}/sh4/decode.rb +48 -17
  100. data/{lib/metasm → metasm/cpu}/sh4/main.rb +13 -4
  101. data/{lib/metasm → metasm/cpu}/sh4/opcodes.rb +7 -8
  102. data/metasm/cpu/x86_64.rb +15 -0
  103. data/{lib/metasm → metasm/cpu}/x86_64/compile_c.rb +28 -17
  104. data/{lib/metasm → metasm/cpu}/x86_64/debug.rb +4 -4
  105. data/{lib/metasm → metasm/cpu}/x86_64/decode.rb +57 -15
  106. data/{lib/metasm → metasm/cpu}/x86_64/encode.rb +55 -26
  107. data/{lib/metasm → metasm/cpu}/x86_64/main.rb +14 -6
  108. data/metasm/cpu/x86_64/opcodes.rb +136 -0
  109. data/{lib/metasm → metasm/cpu}/x86_64/parse.rb +10 -2
  110. data/metasm/cpu/x86_64/render.rb +35 -0
  111. data/metasm/cpu/z80.rb +9 -0
  112. data/metasm/cpu/z80/decode.rb +313 -0
  113. data/metasm/cpu/z80/main.rb +67 -0
  114. data/metasm/cpu/z80/opcodes.rb +224 -0
  115. data/metasm/cpu/z80/render.rb +59 -0
  116. data/{lib/metasm/os/main.rb → metasm/debug.rb} +160 -401
  117. data/{lib/metasm → metasm}/decode.rb +35 -4
  118. data/{lib/metasm → metasm}/decompile.rb +15 -16
  119. data/{lib/metasm → metasm}/disassemble.rb +201 -45
  120. data/{lib/metasm → metasm}/disassemble_api.rb +651 -87
  121. data/{lib/metasm → metasm}/dynldr.rb +220 -133
  122. data/{lib/metasm → metasm}/encode.rb +10 -1
  123. data/{lib/metasm → metasm}/exe_format/a_out.rb +9 -6
  124. data/{lib/metasm → metasm}/exe_format/autoexe.rb +1 -0
  125. data/{lib/metasm → metasm}/exe_format/bflt.rb +57 -27
  126. data/{lib/metasm → metasm}/exe_format/coff.rb +11 -3
  127. data/{lib/metasm → metasm}/exe_format/coff_decode.rb +53 -20
  128. data/{lib/metasm → metasm}/exe_format/coff_encode.rb +11 -13
  129. data/{lib/metasm → metasm}/exe_format/dex.rb +13 -5
  130. data/{lib/metasm → metasm}/exe_format/dol.rb +1 -0
  131. data/{lib/metasm → metasm}/exe_format/elf.rb +93 -57
  132. data/{lib/metasm → metasm}/exe_format/elf_decode.rb +143 -34
  133. data/{lib/metasm → metasm}/exe_format/elf_encode.rb +122 -31
  134. data/metasm/exe_format/gb.rb +65 -0
  135. data/metasm/exe_format/javaclass.rb +424 -0
  136. data/{lib/metasm → metasm}/exe_format/macho.rb +204 -16
  137. data/{lib/metasm → metasm}/exe_format/main.rb +26 -3
  138. data/{lib/metasm → metasm}/exe_format/mz.rb +1 -0
  139. data/{lib/metasm → metasm}/exe_format/nds.rb +7 -4
  140. data/{lib/metasm → metasm}/exe_format/pe.rb +71 -8
  141. data/metasm/exe_format/pyc.rb +167 -0
  142. data/{lib/metasm → metasm}/exe_format/serialstruct.rb +67 -14
  143. data/{lib/metasm → metasm}/exe_format/shellcode.rb +7 -3
  144. data/metasm/exe_format/shellcode_rwx.rb +114 -0
  145. data/metasm/exe_format/swf.rb +205 -0
  146. data/{lib/metasm → metasm}/exe_format/xcoff.rb +7 -7
  147. data/metasm/exe_format/zip.rb +335 -0
  148. data/metasm/gui.rb +13 -0
  149. data/{lib/metasm → metasm}/gui/cstruct.rb +35 -41
  150. data/{lib/metasm → metasm}/gui/dasm_coverage.rb +11 -11
  151. data/{lib/metasm → metasm}/gui/dasm_decomp.rb +7 -20
  152. data/{lib/metasm → metasm}/gui/dasm_funcgraph.rb +0 -0
  153. data/metasm/gui/dasm_graph.rb +1695 -0
  154. data/{lib/metasm → metasm}/gui/dasm_hex.rb +12 -8
  155. data/{lib/metasm → metasm}/gui/dasm_listing.rb +43 -28
  156. data/{lib/metasm → metasm}/gui/dasm_main.rb +310 -53
  157. data/{lib/metasm → metasm}/gui/dasm_opcodes.rb +5 -19
  158. data/{lib/metasm → metasm}/gui/debug.rb +93 -27
  159. data/{lib/metasm → metasm}/gui/gtk.rb +162 -40
  160. data/{lib/metasm → metasm}/gui/qt.rb +12 -2
  161. data/{lib/metasm → metasm}/gui/win32.rb +179 -42
  162. data/{lib/metasm → metasm}/gui/x11.rb +59 -59
  163. data/{lib/metasm → metasm}/main.rb +389 -264
  164. data/{lib/metasm/os/remote.rb → metasm/os/gdbremote.rb} +146 -54
  165. data/{lib/metasm → metasm}/os/gnu_exports.rb +1 -1
  166. data/{lib/metasm → metasm}/os/linux.rb +628 -151
  167. data/metasm/os/main.rb +330 -0
  168. data/{lib/metasm → metasm}/os/windows.rb +132 -42
  169. data/{lib/metasm → metasm}/os/windows_exports.rb +141 -0
  170. data/{lib/metasm → metasm}/parse.rb +26 -24
  171. data/{lib/metasm → metasm}/parse_c.rb +221 -116
  172. data/{lib/metasm → metasm}/preprocessor.rb +55 -40
  173. data/{lib/metasm → metasm}/render.rb +14 -38
  174. data/misc/hexdump.rb +2 -1
  175. data/misc/lint.rb +58 -0
  176. data/misc/txt2html.rb +9 -7
  177. data/samples/bindiff.rb +3 -4
  178. data/samples/dasm-plugins/bindiff.rb +15 -0
  179. data/samples/dasm-plugins/bookmark.rb +133 -0
  180. data/samples/dasm-plugins/c_constants.rb +57 -0
  181. data/samples/dasm-plugins/colortheme_solarized.rb +125 -0
  182. data/samples/dasm-plugins/cppobj_funcall.rb +60 -0
  183. data/samples/dasm-plugins/dasm_all.rb +70 -0
  184. data/samples/dasm-plugins/demangle_cpp.rb +31 -0
  185. data/samples/dasm-plugins/deobfuscate.rb +251 -0
  186. data/samples/dasm-plugins/dump_text.rb +35 -0
  187. data/samples/dasm-plugins/export_graph_svg.rb +86 -0
  188. data/samples/dasm-plugins/findgadget.rb +75 -0
  189. data/samples/dasm-plugins/hl_opcode.rb +32 -0
  190. data/samples/dasm-plugins/hotfix_gtk_dbg.rb +19 -0
  191. data/samples/dasm-plugins/imm2off.rb +34 -0
  192. data/samples/dasm-plugins/match_libsigs.rb +93 -0
  193. data/samples/dasm-plugins/patch_file.rb +95 -0
  194. data/samples/dasm-plugins/scanfuncstart.rb +36 -0
  195. data/samples/dasm-plugins/scanxrefs.rb +26 -0
  196. data/samples/dasm-plugins/selfmodify.rb +197 -0
  197. data/samples/dasm-plugins/stringsxrefs.rb +28 -0
  198. data/samples/dasmnavig.rb +1 -1
  199. data/samples/dbg-apihook.rb +24 -9
  200. data/samples/dbg-plugins/heapscan.rb +283 -0
  201. data/samples/dbg-plugins/heapscan/compiled_heapscan_lin.c +155 -0
  202. data/samples/dbg-plugins/heapscan/compiled_heapscan_win.c +128 -0
  203. data/samples/dbg-plugins/heapscan/graphheap.rb +616 -0
  204. data/samples/dbg-plugins/heapscan/heapscan.rb +709 -0
  205. data/samples/dbg-plugins/heapscan/winheap.h +174 -0
  206. data/samples/dbg-plugins/heapscan/winheap7.h +307 -0
  207. data/samples/dbg-plugins/trace_func.rb +214 -0
  208. data/samples/disassemble-gui.rb +35 -5
  209. data/samples/disassemble.rb +31 -6
  210. data/samples/dump_upx.rb +24 -12
  211. data/samples/dynamic_ruby.rb +12 -3
  212. data/samples/exeencode.rb +6 -5
  213. data/samples/factorize-headers-peimports.rb +1 -1
  214. data/samples/lindebug.rb +175 -381
  215. data/samples/metasm-shell.rb +1 -2
  216. data/samples/peldr.rb +2 -2
  217. data/tests/all.rb +1 -1
  218. data/tests/arc.rb +26 -0
  219. data/tests/dynldr.rb +22 -4
  220. data/tests/expression.rb +55 -0
  221. data/tests/graph_layout.rb +285 -0
  222. data/tests/ia32.rb +79 -26
  223. data/tests/mips.rb +9 -2
  224. data/tests/x86_64.rb +66 -18
  225. metadata +330 -218
  226. data/lib/metasm/arm/opcodes.rb +0 -177
  227. data/lib/metasm/gui.rb +0 -23
  228. data/lib/metasm/gui/dasm_graph.rb +0 -1354
  229. data/lib/metasm/ia32.rb +0 -14
  230. data/lib/metasm/ia32/opcodes.rb +0 -873
  231. data/lib/metasm/ppc/parse.rb +0 -52
  232. data/lib/metasm/x86_64.rb +0 -12
  233. data/lib/metasm/x86_64/opcodes.rb +0 -118
  234. data/samples/gdbclient.rb +0 -583
  235. 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
@@ -115,7 +115,7 @@ class Viewer
115
115
  $stdout.write Ansi::ClearScreen
116
116
  begin
117
117
  loop do
118
- refresh if not s = IO.select([$stdin], nil, nil, 0)
118
+ refresh if not IO.select([$stdin], nil, nil, 0)
119
119
  handle_key(Ansi.getkey)
120
120
  end
121
121
  ensure
@@ -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
- setup.each { |h| setup_hook(h) }
39
- init_prerun if respond_to?(:init_prerun) # allow subclass to do stuff before main loop
40
- @dbg.run_forever
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
- @nargs = h[:nargs] || method(pre).arity if respond_to?(pre)
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
- # name says it all
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
+ }