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.
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
+ }