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
@@ -42,12 +42,11 @@ class HexWidget < DrawableWidget
42
42
  @relative_addr = nil # show '+42h' in the addr column if not nil
43
43
  @hl_curbyte = true # draw grey bg for current byte
44
44
 
45
- @default_color_association = { :ascii => :black, :data => :black,
46
- :address => :blue, :caret => :black, :background => :white,
47
- :write_pending => :darkred, :caret_mirror => :palegrey }
45
+ @default_color_association = ColorTheme.merge :ascii => :black, :data => :black,
46
+ :write_pending => :darkred, :caret_mirror => :palegrey
48
47
  end
49
48
 
50
- def resized(w, h)
49
+ def resized(w=width, h=height)
51
50
  wc = w/@font_width
52
51
  hc = h/@font_height
53
52
  ca = current_address
@@ -103,6 +102,7 @@ class HexWidget < DrawableWidget
103
102
  end
104
103
  else
105
104
  @data_size = {1 => 2, 2 => 4, 4 => 8, 8 => 1}[@data_size]
105
+ resized
106
106
  end
107
107
  redraw
108
108
  end
@@ -392,11 +392,15 @@ class HexWidget < DrawableWidget
392
392
 
393
393
  # pop a dialog, scans the sections for a hex pattern
394
394
  def prompt_search_hex
395
- inputbox('hex pattern to search (hex regexp, use .. for wildcard)') { |pat|
395
+ text = ''
396
+ if current_address.kind_of?(::Integer)
397
+ text = Expression.encode_imm(current_address, "u#{@dasm.cpu.size}".to_sym, @dasm.cpu).unpack('H*').first
398
+ end
399
+ inputbox('hex pattern to search (hex regexp, use .. for wildcard)', :text => text) { |pat|
396
400
  pat = pat.gsub(' ', '').gsub('..', '.').gsub(/[0-9a-f][0-9a-f]/i) { |o| "\\x#{o}" }
397
401
  pat = Regexp.new(pat, Regexp::MULTILINE, 'n') # 'n' = force ascii-8bit
398
402
  list = [['addr']] + @dasm.pattern_scan(pat).map { |a| [Expression[a]] }
399
- listwindow("hex search #{pat}", list) { |i| focus_addr i[0] }
403
+ listwindow("hex search #{pat}", list) { |i| @parent_widget.focus_addr i[0] }
400
404
  }
401
405
  end
402
406
 
@@ -404,7 +408,7 @@ class HexWidget < DrawableWidget
404
408
  def prompt_search_ascii
405
409
  inputbox('data pattern to search (regexp)') { |pat|
406
410
  list = [['addr']] + @dasm.pattern_scan(/#{pat}/).map { |a| [Expression[a]] }
407
- listwindow("data search #{pat}", list) { |i| focus_addr i[0] }
411
+ listwindow("data search #{pat}", list) { |i| @parent_widget.focus_addr i[0] }
408
412
  }
409
413
  end
410
414
 
@@ -483,7 +487,7 @@ class HexWidget < DrawableWidget
483
487
  }
484
488
  @write_pending.clear
485
489
  rescue
486
- @parent_widget.messagebox($!, $!.class.to_s)
490
+ @parent_widget.messagebox($!.message.to_s, $!.class.to_s)
487
491
  end
488
492
 
489
493
  def get_cursor_pos
@@ -28,10 +28,8 @@ class AsmListingWidget < DrawableWidget
28
28
  @maxaddr = (addrs.max + @dasm.sections[addrs.max].length rescue (1 << @dasm.cpu.size))
29
29
  @startaddr = @dasm.prog_binding['entrypoint'] || @minaddr
30
30
 
31
- @default_color_association = { :comment => :darkblue, :label => :darkgreen, :text => :black,
32
- :instruction => :black, :address => :blue, :caret => :black, :raw_data => :black,
33
- :background => :white, :cursorline_bg => :paleyellow, :hl_word => :palered,
34
- :arrows_bg => :palegrey, :arrow_up => :darkblue, :arrow_dn => :darkyellow, :arrow_hl => :red }
31
+ @default_color_association = ColorTheme.merge :raw_data => :black, :arrows_bg => :palegrey,
32
+ :arrow_up => :darkblue, :arrow_dn => :darkyellow, :arrow_hl => :red
35
33
  end
36
34
 
37
35
  def resized(w, h)
@@ -55,11 +53,26 @@ class AsmListingWidget < DrawableWidget
55
53
 
56
54
  def click(x, y)
57
55
  set_caret_from_click(x - @arrow_zone_w, y)
56
+ @caret_x = 0 if @caret_x < 0
58
57
  end
59
58
 
60
59
  def rightclick(x, y)
61
60
  click(x, y)
62
- @parent_widget.clone_window(@hl_word, :listing)
61
+ cx = (x - @arrow_zone_w) / @font_width
62
+ cy = y / @font_height
63
+ if cx > 0
64
+ m = new_menu
65
+ cm = new_menu
66
+ addsubmenu(cm, 'copy _word') { clipboard_copy(@hl_word) if @hl_word }
67
+ addsubmenu(cm, 'copy _line') { clipboard_copy(@line_text[cy]) if @line_text[cy] }
68
+ addsubmenu(cm, 'copy _all') { clipboard_copy(@line_text.join("\r\n")) } # XXX auto \r\n vs \n
69
+ addsubmenu(m, '_clipboard', cm)
70
+ addsubmenu(m, 'clone _window') { @parent_widget.clone_window(@hl_word, :listing) }
71
+ if @parent_widget.respond_to?(:extend_contextmenu)
72
+ @parent_widget.extend_contextmenu(self, m, @line_address[@caret_y])
73
+ end
74
+ popupmenu(m, x, y)
75
+ end
63
76
  end
64
77
 
65
78
  def doubleclick(x, y)
@@ -118,19 +131,7 @@ class AsmListingWidget < DrawableWidget
118
131
  render = lambda { |str, color|
119
132
  # function ends when we write under the bottom of the listing
120
133
  next if not str or y >= w_h or x >= w_w
121
- if @hl_word and @hl_word != ''
122
- stmp = str
123
- pre_x = 0
124
- while stmp =~ /^(.*?)(\b#{Regexp.escape @hl_word}\b)/
125
- s1, s2 = $1, $2
126
- pre_x += s1.length * @font_width
127
- hl_x = s2.length * @font_width
128
- draw_rectangle_color(:hl_word, x+pre_x, y, hl_x, @font_height)
129
- pre_x += hl_x
130
- stmp = stmp[s1.length+s2.length..-1]
131
- end
132
- end
133
- draw_string_color(color, x, y, str)
134
+ draw_string_hl(color, x, y, str)
134
135
  x += str.length * @font_width
135
136
  }
136
137
 
@@ -275,6 +276,12 @@ class AsmListingWidget < DrawableWidget
275
276
 
276
277
  def keypress(key)
277
278
  case key
279
+ when ?u # undef data formatting with ?d
280
+ addr = current_address
281
+ if not @dasm.decoded[addr] and @dasm.xrefs[addr].kind_of?(Xref)
282
+ @dasm.xrefs.delete addr
283
+ gui_update
284
+ end
278
285
  when :left
279
286
  if @caret_x >= 1
280
287
  @caret_x -= 1
@@ -315,6 +322,8 @@ class AsmListingWidget < DrawableWidget
315
322
  when :end
316
323
  @caret_x = @line_text[@caret_y].to_s.length
317
324
  update_caret
325
+ when :popupmenu
326
+ rightclick(@caret_x*@font_width + @arrow_zone_w+1, @caret_y*@font_height)
318
327
  else return false
319
328
  end
320
329
  true
@@ -422,16 +431,18 @@ class AsmListingWidget < DrawableWidget
422
431
  # ary
423
432
  di.block.each_from_samefunc(@dasm) { |addr|
424
433
  addr = @dasm.normalize addr
425
- next if not addr.kind_of? ::Integer or (ndi = @dasm.di_at(addr) and ndi.next_addr == curaddr)
434
+ # block.list.last for delayslot
435
+ next if ndi = @dasm.di_at(addr) and ndi.block.list.last.next_addr == curaddr
426
436
  arrows_addr << [addr, curaddr]
427
437
  }
428
438
  end
429
439
  if di.block.list.last == di
440
+ # kikoo delayslot
441
+ rdi = di.block.list[-[4, di.block.list.length].min, 4].reverse.find { |_di| _di.opcode.props[:setip] } || di
430
442
  di.block.each_to_samefunc(@dasm) { |addr|
431
443
  addr = @dasm.normalize addr
432
- next if not addr.kind_of? ::Integer or (di.next_addr == addr and
433
- (not di.opcode.props[:saveip] or di.block.to_subfuncret))
434
- arrows_addr << [curaddr, addr]
444
+ next if di.next_addr == addr and (not rdi.opcode.props[:saveip] or rdi.block.to_subfuncret)
445
+ arrows_addr << [rdi.address, addr]
435
446
  }
436
447
  end
437
448
  str_c << ["#{Expression[di.address]} ", :address]
@@ -485,11 +496,11 @@ class AsmListingWidget < DrawableWidget
485
496
  xlen ||= xref.len || 1 if xref.len
486
497
  comment << " #{xref.type}#{xref.len}:#{Expression[xref.origin]}" if xref.origin
487
498
  } if @dasm.xrefs[curaddr]
488
- len = xlen if xlen and xlen > 2 # db xref may point a string
499
+ len = xlen if xlen and xlen >= 2 # db xref may point a string
489
500
  comment = nil if comment.empty?
490
501
  len = (1..len).find { |l| @dasm.xrefs[curaddr+l] or s.inv_export[s.ptr+l] or s.reloc[s.ptr+l] } || len
491
502
  str = str[0, len] if len < str.length
492
- str = str.pack('C*').unpack(@dasm.cpu.endianness == :big ? 'n*' : 'v*') if len == 2
503
+ str = str.pack('C*').unpack(@dasm.cpu.endianness == :big ? 'n*' : 'v*') if xlen == 2
493
504
  if (xlen == 1 or xlen == 2) and asc = str.inject('') { |asc_, c|
494
505
  case c
495
506
  when 0x20..0x7e, 9, 10, 13; asc_ << c
@@ -534,7 +545,7 @@ class AsmListingWidget < DrawableWidget
534
545
  comment = []
535
546
  @dasm.each_xref(curaddr) { |xref|
536
547
  len = xref.len if xref.len
537
- comment << " #{xref.type}#{xref.len}:#{Expression[xref.origin]} "
548
+ comment << " #{xref.type}#{xref.len}:#{Expression[xref.origin] if xref.origin} "
538
549
  }
539
550
  len = 1 if (len != 2 and len != 4 and len != 8) or len < 1
540
551
  dat = "#{%w[x db dw x dd x x x dq][len]} ? "
@@ -578,9 +589,13 @@ class AsmListingWidget < DrawableWidget
578
589
  prev_arrows = @arrows
579
590
  addr_line = {} # addr => last line (di)
580
591
  @line_address.each_with_index { |a, l| addr_line[a] = l }
581
- @arrows = arrows_addr.uniq.sort.map { |from, to|
582
- [(addr_line[from] || (from < curaddr ? :up : :down) rescue :up),
583
- (addr_line[ to ] || ( to < curaddr ? :up : :down) rescue :up)]
592
+ @arrows = arrows_addr.uniq.find_all { |from, to|
593
+ ((from-curaddr)+(to-curaddr)).kind_of?(::Integer) rescue nil
594
+ }.sort_by { |from, to|
595
+ [from-curaddr, to-curaddr]
596
+ }.map { |from, to|
597
+ [(addr_line[from] || (from-curaddr < 0 ? :up : :down)),
598
+ (addr_line[ to ] || (to - curaddr < 0 ? :up : :down))]
584
599
  }
585
600
  invalidate(0, 0, @arrow_zone_w, 100000) if prev_arrows != @arrows
586
601
  end
@@ -15,6 +15,16 @@ require 'metasm/gui/cstruct'
15
15
 
16
16
  module Metasm
17
17
  module Gui
18
+ class DrawableWidget
19
+ ColorTheme = { :comment => :darkblue, :label => :darkgreen, :text => :black,
20
+ :instruction => :black, :address => :blue, :caret => :black, :background => :white,
21
+ :cursorline_bg => :paleyellow, :hl_word_bg => :palered, :hl_word => :black,
22
+ :red_bg => 'f88', :green_bg => '8f8', :blue_bg => '88f',
23
+ :cyan_bg => '8ff', :magenta_bg => 'f8f', :yellow_bg => 'ff8',
24
+ :orange_bg => 'fc8'
25
+ }
26
+ end
27
+
18
28
  # the main disassembler widget: this is a container for all the lower-level widgets that actually render the dasm state
19
29
  class DisasmWidget < ContainerChoiceWidget
20
30
  attr_accessor :entrypoints, :gui_update_counter_max
@@ -64,6 +74,7 @@ class DisasmWidget < ContainerChoiceWidget
64
74
 
65
75
  # start an idle callback that will run one round of @dasm.disassemble_mainiter
66
76
  def start_disassemble_bg
77
+ return if @dasm.addrs_todo.empty? and @entrypoints.all? { |ep| @dasm.decoded[ep] }
67
78
  gui_update_counter = 0
68
79
  run = false
69
80
  Gui.idle_add {
@@ -84,6 +95,10 @@ class DisasmWidget < ContainerChoiceWidget
84
95
  }
85
96
  end
86
97
 
98
+ def wait_disassemble_bg
99
+ Gui.main_iter until @entrypoints.empty? and @dasm.addrs_todo.empty?
100
+ end
101
+
87
102
  def terminate
88
103
  @clones.delete self
89
104
  end
@@ -107,6 +122,17 @@ class DisasmWidget < ContainerChoiceWidget
107
122
  @dasm.prog_binding[hl] || curview.current_address
108
123
  end
109
124
 
125
+ # returns the ExpressionString if the currently hilighted word is a :stackvar
126
+ def pointed_localvar(obj=curobj, hl=curview.hl_word)
127
+ return if not obj.kind_of?(Renderable)
128
+ localvar = nil
129
+ obj.each_expr { |e|
130
+ next unless e.kind_of?(ExpressionString)
131
+ localvar = e if e.type == :stackvar and e.str == hl
132
+ }
133
+ localvar
134
+ end
135
+
110
136
  # parse an address and change it to a canonical address form
111
137
  # supported formats: label names, or string with numerical value, incl hex (0x42 and 42h)
112
138
  # if the string is full decimal, a check against mapped space is done to find if it is
@@ -138,17 +164,17 @@ class DisasmWidget < ContainerChoiceWidget
138
164
  end
139
165
 
140
166
  # display the specified address
141
- # the display first searches in the current view (graph, listing, etc),
142
- # if it cannot display the address all other views are tried in order
167
+ # the display first searches in the current view
168
+ # if it cannot display the address, the listing, graph and decompile views are tried (in that order)
143
169
  # the current focus address is saved in @pos_history (see focus_addr_back/redo)
144
- # a messagebox is popped if no view can display the address unless quiet is true
170
+ # if quiet is false, a messagebox is popped if no view can display the address
145
171
  def focus_addr(addr, viewidx=nil, quiet=false, *a)
146
172
  viewidx ||= curview_index || :listing
147
173
  return if not addr
148
- return if viewidx == curview_index and addr == curaddr
174
+ return if viewidx == curview_index and addr == curaddr and a.empty?
149
175
  oldpos = [curview_index, (curview.get_cursor_pos if curview)]
150
176
  views = [viewidx, oldpos[0]]
151
- views += [:listing, :graph] & view_indexes
177
+ views += [:listing, :graph, :decompile] & view_indexes
152
178
  if views.compact.uniq.find { |i|
153
179
  o_p = view(i).get_cursor_pos
154
180
  if (view(i).focus_addr(addr, *a) rescue nil)
@@ -157,11 +183,13 @@ class DisasmWidget < ContainerChoiceWidget
157
183
  true
158
184
  else
159
185
  view(i).set_cursor_pos o_p
186
+ a.clear
160
187
  false
161
188
  end
162
189
  }
163
190
  @pos_history << oldpos if oldpos[0] # ignore start focus_addr
164
191
  @pos_history_redo.clear
192
+ session_append "@session_focus_addr = #{addr.inspect} ; @pos_history = #{@pos_history.inspect}"
165
193
  true
166
194
  else
167
195
  messagebox "Invalid address #{addr}" if not quiet
@@ -198,7 +226,7 @@ class DisasmWidget < ContainerChoiceWidget
198
226
 
199
227
  # ask the current view to update itself
200
228
  def do_gui_update
201
- curview.gui_update # invalidate all views ?
229
+ curview.gui_update if curview # invalidate all views ?
202
230
  end
203
231
 
204
232
  # redraw the window
@@ -212,7 +240,7 @@ class DisasmWidget < ContainerChoiceWidget
212
240
  yield
213
241
  focus_addr curaddr if addr
214
242
  end
215
-
243
+
216
244
  # calls listwindow with the same argument, but also creates a new bg_color_callback
217
245
  # that will color lines whose address is to be found in list[0] in green
218
246
  # the callback is put only for the duration of the listwindow, and is not reentrant.
@@ -234,18 +262,24 @@ class DisasmWidget < ContainerChoiceWidget
234
262
  inputbox("new comment for #{Expression[addr]}", :text => cmt) { |c|
235
263
  c = c.split("\n")
236
264
  c = nil if c == []
237
- if di = @dasm.di_at(addr)
238
- di.comment = c
239
- else
240
- @dasm.comment[addr] = c
241
- end
265
+ do_add_comment(addr, c)
266
+ session_append "do_add_comment(#{addr.inspect}, #{c.inspect})"
242
267
  gui_update
243
268
  }
244
269
  end
245
270
 
271
+ def do_add_comment(addr, c)
272
+ if di = @dasm.di_at(addr)
273
+ di.comment = c
274
+ else
275
+ @dasm.comment[addr] = c
276
+ end
277
+ end
278
+
246
279
  # disassemble from this point
247
280
  # if points to a call, make it return
248
281
  def disassemble(addr)
282
+ session_append "disassemble(#{addr.inspect}) ; wait_disassemble_bg"
249
283
  if di = @dasm.di_at(addr) and di.opcode.props[:saveip]
250
284
  di.block.each_to_normal { |t|
251
285
  t = @dasm.normalize t
@@ -263,17 +297,20 @@ class DisasmWidget < ContainerChoiceWidget
263
297
  # disassemble fast from this point (don't dasm subfunctions, don't backtrace)
264
298
  def disassemble_fast(addr)
265
299
  @dasm.disassemble_fast(addr)
300
+ session_append "dasm.disassemble_fast(#{addr.inspect})"
266
301
  gui_update
267
302
  end
268
303
 
269
304
  # disassemble fast & deep from this point (don't backtrace, but still dasm subfuncs)
270
305
  def disassemble_fast_deep(addr)
271
306
  @dasm.disassemble_fast_deep(addr)
307
+ session_append "dasm.disassemble_fast_deep(#{addr.inspect})"
272
308
  gui_update
273
309
  end
274
310
 
275
311
  # (re)decompile
276
312
  def decompile(addr)
313
+ session_append "decompile(#{addr.inspect})"
277
314
  if @dasm.c_parser and var = @dasm.c_parser.toplevel.symbol[addr] and (var.type.kind_of? C::Function or @dasm.di_at(addr))
278
315
  @dasm.decompiler.redecompile(addr)
279
316
  view(:decompile).curaddr = nil
@@ -284,6 +321,7 @@ class DisasmWidget < ContainerChoiceWidget
284
321
  # change the format of displayed data under addr (byte, word, dword, qword)
285
322
  # currently this is done using a fake empty xref
286
323
  def toggle_data(addr)
324
+ session_append "toggle_data(#{addr.inspect})"
287
325
  return if @dasm.decoded[addr] or not @dasm.get_section_at(addr)
288
326
  @dasm.add_xref(addr, Xref.new(nil, nil, 1)) if not @dasm.xrefs[addr]
289
327
  @dasm.each_xref(addr) { |x|
@@ -328,15 +366,31 @@ class DisasmWidget < ContainerChoiceWidget
328
366
  listwindow("list of strings", list) { |i| focus_addr i[0] }
329
367
  end
330
368
 
331
- def list_xrefs(addr)
369
+ def list_xrefs(addr=nil)
332
370
  list = [['address', 'type', 'instr']]
333
- @dasm.each_xref(addr) { |xr|
334
- next if not xr.origin
335
- list << [Expression[xr.origin], "#{xr.type}#{xr.len}"]
336
- if di = @dasm.di_at(xr.origin)
337
- list.last << di.instruction
371
+ if not addr and pointed_localvar
372
+ addr = curview.hl_word
373
+ faddr = @dasm.find_function_start(curaddr)
374
+ func = @dasm.function[faddr]
375
+ if func and func.localvars_xrefs
376
+ stoff = func.localvars.index(addr)
377
+ func.localvars_xrefs[stoff].to_a.each { |a|
378
+ list << [Expression[a], '?']
379
+ if di = @dasm.di_at(a)
380
+ list.last << di.instruction
381
+ end
382
+ }
338
383
  end
339
- }
384
+ else
385
+ addr ||= pointed_addr
386
+ @dasm.each_xref(addr) { |xr|
387
+ next if not xr.origin
388
+ list << [Expression[xr.origin], "#{xr.type}#{xr.len}"]
389
+ if di = @dasm.di_at(xr.origin)
390
+ list.last << di.instruction
391
+ end
392
+ }
393
+ end
340
394
  if list.length == 1
341
395
  messagebox "no xref to #{Expression[addr]}" if addr
342
396
  else
@@ -351,8 +405,7 @@ class DisasmWidget < ContainerChoiceWidget
351
405
  }
352
406
  end
353
407
 
354
- def prompt_backtrace
355
- addr = curaddr
408
+ def prompt_backtrace(addr=curaddr)
356
409
  inputbox('expression to backtrace', :text => curview.hl_word) { |e|
357
410
  expr = IndExpression.parse_string(e)
358
411
  bd = {}
@@ -388,7 +441,106 @@ class DisasmWidget < ContainerChoiceWidget
388
441
  list_bghilight("backtrace #{expr} from #{Expression[addr]}", list) { |i|
389
442
  a = i[0].empty? ? i[2] : i[0]
390
443
  focus_addr(a, nil, true)
391
- }
444
+ }
445
+ }
446
+ end
447
+
448
+ # prompt the contant to use in place of some numeric value
449
+ def prompt_constant(di=curobj)
450
+ return if not di.kind_of?(DecodedInstruction)
451
+ di.each_expr { |e|
452
+ next unless e.kind_of?(Expression)
453
+ if (e.lexpr.kind_of?(Integer) or e.lexpr.kind_of?(ExpressionString)) and
454
+ (!curview.hl_word or curview.hl_word == Expression[e.lexpr].to_s)
455
+ v = Expression[e.lexpr].reduce
456
+ lst = []
457
+ dasm.c_constants.each { |cn, cv, fm| lst << [cn, fm] if v == cv }
458
+ if not lst.empty?
459
+ default = Expression[v].to_s
460
+ lst << [default]
461
+ listwindow("constant for #{Expression[v]}", [['name', 'enum']] + lst) { |a|
462
+ if a[0] == default
463
+ e.lexpr = v
464
+ else
465
+ e.lexpr = ExpressionString.new(v, a[0], :constant)
466
+ end
467
+ session_append "if di = dasm.di_at(#{di.address.inspect}) ; di.each_expr { |e| e.lexpr = #{e.lexpr.inspect} if e.kind_of?(Expression) and e.lexpr and Expression[e.lexpr].reduce == #{v.inspect} } ; end"
468
+ gui_update
469
+ }
470
+ end
471
+ end
472
+ if (e.rexpr.kind_of? Integer or e.rexpr.kind_of?(ExpressionString)) and
473
+ (!curview.hl_word or curview.hl_word == Expression[e.rexpr].to_s)
474
+ v = Expression[e.rexpr].reduce
475
+ lst = []
476
+ dasm.c_constants.each { |cn, cv, fm| lst << [cn, fm] if v == cv }
477
+ if not lst.empty?
478
+ default = Expression[v].to_s
479
+ lst << [default]
480
+ listwindow("constant for #{Expression[v]}", [['name', 'enum']] + lst) { |a|
481
+ if a[0] == default
482
+ e.rexpr = v
483
+ else
484
+ e.rexpr = ExpressionString.new(v, a[0], :constant)
485
+ end
486
+ session_append "if di = dasm.di_at(#{di.address.inspect}) ; di.each_expr { |e| e.rexpr = #{e.rexpr.inspect} if e.kind_of?(Expression) and e.rexpr and Expression[e.rexpr].reduce == #{v.inspect} } ; end"
487
+ gui_update
488
+ }
489
+ end
490
+ end
491
+ }
492
+ end
493
+
494
+ # prompts for a structure name, autocompletes to known structures, and/or display a listwindow with
495
+ # possible completions, yields the target structure name
496
+ def prompt_c_struct(prompt, opts={})
497
+ inputbox(prompt, opts) { |st_name|
498
+ stars = ''
499
+ if opts[:allow_stars]
500
+ stars = st_name[/\**$/]
501
+ st_name[stars] = ''
502
+ end
503
+
504
+ # TODO propose typedef struct {} moo; too
505
+ sh = @dasm.c_parser.toplevel.struct
506
+ if sh[st_name].kind_of?(C::Union)
507
+ stn_list = [st_name]
508
+ else
509
+ stn_list = sh.keys.grep(String).find_all { |k| sh[k].kind_of?(C::Union) }
510
+ end
511
+
512
+ if name = stn_list.find { |n| n == st_name } || stn_list.find { |n| n.downcase == st_name.downcase }
513
+ # single match
514
+ yield(name+stars)
515
+ else
516
+ # try autocomplete
517
+ list = [['name']]
518
+ list += stn_list.sort.grep(/#{st_name}/i).map { |stn| [stn+stars] }
519
+ if list.length == 2
520
+ # single autocompletion
521
+ yield(list[1][0])
522
+ else
523
+ listwindow(prompt, list) { |ans|
524
+ yield(ans[0])
525
+ }
526
+ end
527
+ end
528
+ }
529
+ end
530
+
531
+ # prompt the struct to use for offset in a given instr
532
+ def prompt_struct_ptr(reg=curview.hl_word, addr=curaddr)
533
+ return if not reg or not @dasm.cpu.register_symbols.find { |rs| rs.to_s == reg.to_s }
534
+ reg = reg.to_sym
535
+
536
+ di = @dasm.di_at(addr)
537
+ return if not di.kind_of?(DecodedInstruction)
538
+
539
+ prompt_c_struct("struct pointed by #{reg}", :allow_stars => true) { |st|
540
+ # TODO store that info for the decompiler ?
541
+ @dasm.trace_update_reg_structptr(addr, reg, st)
542
+ session_append "dasm.trace_update_reg_structptr(#{addr.inspect}, #{reg.inspect}, #{st.inspect})"
543
+ gui_update
392
544
  }
393
545
  end
394
546
 
@@ -397,7 +549,7 @@ class DisasmWidget < ContainerChoiceWidget
397
549
  def focus_addr_autocomplete(v, show_alt=true)
398
550
  if not focus_addr(v, nil, true)
399
551
  labels = @dasm.prog_binding.map { |k, vv|
400
- [k, Expression[@dasm.normalize(vv)]] if k.downcase.include? v.downcase
552
+ [k, Expression[@dasm.normalize(vv)]] if k.downcase.include? v.downcase
401
553
  }.compact
402
554
  case labels.length
403
555
  when 0; focus_addr(v)
@@ -422,7 +574,10 @@ class DisasmWidget < ContainerChoiceWidget
422
574
 
423
575
  # run arbitrary ruby
424
576
  def prompt_run_ruby
425
- inputbox('ruby code to eval()') { |c| messagebox eval(c).inspect[0, 512], 'eval' }
577
+ inputbox('ruby code to eval()') { |c|
578
+ messagebox eval(c).inspect[0, 512], 'eval'
579
+ session_append "#eval #{c.inspect}"
580
+ }
426
581
  end
427
582
 
428
583
  # run ruby plugin
@@ -454,25 +609,41 @@ class DisasmWidget < ContainerChoiceWidget
454
609
  end
455
610
  end
456
611
 
457
- # prompts for a new name for addr
458
- def rename_label(addr)
459
- old = addr
460
- if @dasm.prog_binding[old] or old = @dasm.get_label_at(addr)
612
+ # prompts for a new name for what is under the cursor (or the current address)
613
+ def rename(what=nil)
614
+ if not what and localvar = pointed_localvar
615
+ addr = curaddr
616
+ str = localvar.str.dup
617
+ inputbox("new name for #{localvar}", :text => localvar.to_s) { |v|
618
+ if v =~ /^[a-z_][a-z0-9_]*$/i
619
+ localvar.str.replace v
620
+ session_append "pointed_localvar(dasm.decoded[#{addr.inspect}], #{str.inspect}).str.replace(#{v.inspect})"
621
+ gui_update
622
+ else messagebox("invalid local var name #{v.inspect}")
623
+ end
624
+ }
625
+ return
626
+ end
627
+
628
+ what ||= pointed_addr
629
+ if @dasm.prog_binding[what] or old = @dasm.get_label_at(what)
630
+ old ||= what
461
631
  inputbox("new name for #{old}", :text => old) { |v|
462
632
  if v == ''
463
- @dasm.del_label_at(addr)
633
+ @dasm.del_label_at(what)
634
+ session_append "dasm.del_label_at(#{what.inspect})"
464
635
  else
465
636
  @dasm.rename_label(old, v)
637
+ session_append "dasm.rename_label(#{old.inspect}, #{v.inspect})"
466
638
  end
467
639
  gui_update
468
640
  }
469
641
  else
470
- inputbox("label name for #{Expression[addr]}", :text => Expression[addr]) { |v|
642
+ inputbox("label name for #{Expression[what]}", :text => Expression[what]) { |v|
471
643
  next if v == ''
472
- @dasm.set_label_at(addr, v)
473
- if di = @dasm.di_at(addr)
474
- @dasm.split_block(di.block, di.address)
475
- end
644
+ @dasm.set_label_at(what, v)
645
+ @dasm.split_block(what)
646
+ session_append "dasm.set_label_at(#{what.inspect}, #{v.inspect}) ; dasm.split_block(#{what.inspect})"
476
647
  gui_update
477
648
  }
478
649
  end
@@ -504,12 +675,34 @@ class DisasmWidget < ContainerChoiceWidget
504
675
  # toggles <41h> vs <'A'> display
505
676
  def toggle_expr_char(o)
506
677
  @dasm.toggle_expr_char(o)
678
+ session_append "dasm.toggle_expr_char(dasm.decoded[#{curaddr.inspect}])"
679
+ gui_update
680
+ end
681
+
682
+ # toggle <10h> vs <16> display
683
+ def toggle_expr_dec(o)
684
+ @dasm.toggle_expr_dec(o)
685
+ session_append "dasm.toggle_expr_dec(dasm.decoded[#{curaddr.inspect}])"
507
686
  gui_update
508
687
  end
509
688
 
510
689
  # toggle <401000h> vs <'sub_fancyname'> in the current instr display
511
690
  def toggle_expr_offset(o)
512
691
  @dasm.toggle_expr_offset(o)
692
+ session_append "dasm.toggle_expr_offset(dasm.decoded[#{curaddr.inspect}])"
693
+ gui_update
694
+ end
695
+
696
+ # toggle constant/localvar names with raw value
697
+ def toggle_expr_str(o)
698
+ @dasm.toggle_expr_str(o)
699
+ session_append "dasm.toggle_expr_str(dasm.decoded[#{curaddr.inspect}])"
700
+ gui_update
701
+ end
702
+
703
+ def name_local_vars(a)
704
+ @dasm.name_local_vars(a)
705
+ session_append "dasm.name_local_vars(#{a.inspect})"
513
706
  gui_update
514
707
  end
515
708
 
@@ -524,6 +717,7 @@ class DisasmWidget < ContainerChoiceWidget
524
717
  list = []
525
718
  @dasm.each_function_block(addr, incl_subfuncs) { |b| list << b }
526
719
  list.each { |b| @dasm.undefine_from(b) }
720
+ session_append "undefine_function(#{addr.inspect}, #{incl_subfuncs.inspect})"
527
721
  gui_update
528
722
  end
529
723
 
@@ -549,28 +743,33 @@ class DisasmWidget < ContainerChoiceWidget
549
743
  when ?/; inputbox('search word') { |w|
550
744
  next unless curview.respond_to? :hl_word
551
745
  next if w == ''
552
- curview.hl_word = w
746
+ curview.hl_word = w
747
+ curview.hl_word_re = /(.*)(#{w})/
553
748
  curview.redraw
554
749
  }
555
- when ?b; prompt_backtrace
556
- when ?c; disassemble(curview.current_address)
557
- when ?C; disassemble_fast(curview.current_address)
558
- when ?d; toggle_data(curview.current_address)
750
+ when ?b; prompt_backtrace(curaddr)
751
+ when ?c; disassemble(curaddr)
752
+ when ?C; disassemble_fast(curaddr)
753
+ when ?d; curobj.kind_of?(DecodedInstruction) ? toggle_expr_dec(curobj) : toggle_data(curaddr)
559
754
  when ?f; list_functions
560
755
  when ?g; prompt_goto
756
+ when ?k; toggle_expr_str(curobj)
757
+ when ?K; name_local_vars(curaddr)
561
758
  when ?l; list_labels
562
- when ?n; rename_label(pointed_addr)
759
+ when ?m; prompt_constant(curobj)
760
+ when ?n; rename
563
761
  when ?o; toggle_expr_offset(curobj)
564
762
  when ?p; playpause_dasm
565
763
  when ?r; toggle_expr_char(curobj)
764
+ when ?t; prompt_struct_ptr
566
765
  when ?v; $VERBOSE = ! $VERBOSE ; puts "#{'not ' if not $VERBOSE}verbose" # toggle verbose flag
567
- when ?x; list_xrefs(pointed_addr)
568
- when ?;; add_comment(curview.current_address)
766
+ when ?x; list_xrefs
767
+ when ?;; add_comment(curaddr)
569
768
 
570
769
  when ?\ ; toggle_view(:listing)
571
770
  when :tab; toggle_view(:decompile)
572
771
  when ?j; curview.keypress(:down)
573
- when ?k; curview.keypress(:up)
772
+ #when ?k; curview.keypress(:up)
574
773
  else
575
774
  p key if $DEBUG
576
775
  return @parent_widget ? @parent_widget.keypress(key) : false
@@ -578,6 +777,48 @@ class DisasmWidget < ContainerChoiceWidget
578
777
  true
579
778
  end
580
779
 
780
+ attr_accessor :session_file
781
+ def save_session(filename)
782
+ @session_file = filename
783
+ end
784
+
785
+ def replay_session(filename)
786
+ i = 0
787
+ File.readlines(filename).each { |l|
788
+ instance_eval l
789
+ i += 1
790
+ }
791
+ focus_addr(@session_focus_addr) if @session_focus_addr
792
+ puts "Session replay finished"
793
+ rescue ::Exception
794
+ puts "Session replay: error on line #{i}: #{$!.class} #{$!}"
795
+ end
796
+
797
+ # append one line to the session file
798
+ # converts addresses to hex, deletes consecutive set_focus lines
799
+ def session_append(str)
800
+ return if not session_file
801
+
802
+ # convert decimal addrs to hex
803
+ str = str.sub(/(\(|\[|= )(\d\d\d\d\d\d+)/) { $1 + ('0x%x' % $2.to_i) }
804
+
805
+ @session_lastsz_setfocus ||= nil # prevent warning
806
+ if str =~ /^@session_focus_addr = / and @session_lastsz_setfocus
807
+ # overwrite previous set_focus
808
+ File.truncate(session_file, @session_lastsz_setfocus) if File.size(session_file) == @session_lastsz
809
+ is_setfocus = true
810
+ end
811
+
812
+ File.open(session_file, 'a') { |fd| fd.puts str }
813
+
814
+ @session_lastsz = File.size(session_file)
815
+ @session_lastsz_setfocus = @session_lastsz if not is_setfocus
816
+
817
+ rescue
818
+ @session_file = nil
819
+ puts "Failed to save session, disabling (#{$!.class} #{$!})"
820
+ end
821
+
581
822
  # creates a new dasm window with the same disassembler object, focus it on addr#win
582
823
  def clone_window(*focus)
583
824
  return if not popup = DasmWindow.new
@@ -594,11 +835,21 @@ class DisasmWidget < ContainerChoiceWidget
594
835
  def dragdropfile(f)
595
836
  case f
596
837
  when /\.(c|h|cpp)$/; @dasm.parse_c_file(f)
597
- when /\.map$/; @dasm.load_map(f)
838
+ when /\.map$/; @dasm.load_map(f) ; gui_update
598
839
  when /\.rb$/; @dasm.load_plugin(f)
599
840
  else messagebox("unsupported file extension #{f}")
600
841
  end
601
842
  end
843
+
844
+ def extend_contextmenu(tg, menu, addr=nil)
845
+ if @parent_widget.respond_to?(:extend_contextmenu)
846
+ @parent_widget.extend_contextmenu(tg, menu, addr)
847
+ end
848
+ end
849
+
850
+ def inspect
851
+ "<DisasmWidget @%x @dasm=#{dasm.inspect}>" % object_id
852
+ end
602
853
  end
603
854
 
604
855
  # this widget is loaded in an empty DasmWindow to handle shortcuts (open file, etc)
@@ -644,7 +895,7 @@ class DasmWindow < Window
644
895
  self.widget = NoDasmWidget.new(self)
645
896
  end
646
897
  end
647
-
898
+
648
899
  def widget=(w)
649
900
  super(w || NoDasmWidget.new(self))
650
901
  end
@@ -673,8 +924,11 @@ class DasmWindow < Window
673
924
  def loadfile(path, cpu='Ia32', exefmt=nil)
674
925
  if exefmt
675
926
  exefmt = Metasm.const_get(exefmt) if exefmt.kind_of? String
927
+ if exefmt.kind_of?(::Class) and exefmt.name.split('::').last == 'Shellcode'
928
+ exefmt = Shellcode.withcpu(cpu)
929
+ end
676
930
  else
677
- exefmt = AutoExe.orshellcode { cpu = Metasm.const_get(cpu) if cpu.kind_of? String ; cpu.new }
931
+ exefmt = AutoExe.orshellcode { cpu = Metasm.const_get(cpu) if cpu.kind_of? String ; cpu = cpu.new if cpu.kind_of?(::Class) ; cpu }
678
932
  end
679
933
 
680
934
  exe = exefmt.decode_file(path) { |type, str|
@@ -688,14 +942,15 @@ class DasmWindow < Window
688
942
  end
689
943
  }
690
944
  (@dasm_widget ? DasmWindow.new : self).display(exe.disassembler)
945
+ self.title = "#{File.basename(path)} - metasm disassembler"
691
946
  exe
692
947
  end
693
948
 
694
- def promptopen(caption='chose target binary')
695
- openfile(caption) { |exename| loadfile(exename) ; yield self if block_given? }
949
+ def promptopen(caption='chose target binary', &b)
950
+ openfile(caption) { |exename| loadfile(exename) ; b.call(self) if b }
696
951
  end
697
952
 
698
- def promptdebug(caption='chose target')
953
+ def promptdebug(caption='chose target', &b)
699
954
  l = nil
700
955
  i = inputbox(caption) { |name|
701
956
  i = nil ; l.destroy if l and not l.destroyed?
@@ -711,7 +966,7 @@ class DasmWindow < Window
711
966
  end
712
967
  DbgWindow.new(target)
713
968
  destroy if not @dasm_widget
714
- yield self if block_given?
969
+ b.call(self) if b
715
970
  }
716
971
 
717
972
  # build process list in bg (exe name resolution takes a few seconds)
@@ -786,6 +1041,7 @@ class DasmWindow < Window
786
1041
  addsubmenu(importmenu, 'Load _map') {
787
1042
  openfile('chose map file') { |file|
788
1043
  @dasm_widget.dasm.load_map(File.read(file)) if @dasm_widget
1044
+ @dasm_widget.gui_update if @dasm_widget
789
1045
  } if @dasm_widget
790
1046
  }
791
1047
  addsubmenu(importmenu, 'Load _C') {
@@ -838,12 +1094,13 @@ class DasmWindow < Window
838
1094
  addsubmenu(actions, '_Backtrace', 'b') { @dasm_widget.prompt_backtrace }
839
1095
  addsubmenu(actions, 'List functions', 'f') { @dasm_widget.list_functions }
840
1096
  addsubmenu(actions, 'List labels', 'l') { @dasm_widget.list_labels }
841
- addsubmenu(actions, 'List xrefs', 'x') { @dasm_widget.list_xrefs(@dasm_widget.pointed_addr) }
1097
+ addsubmenu(actions, 'List xrefs', 'x') { @dasm_widget.list_xrefs }
1098
+ addsubmenu(actions, 'Find local vars', 'K') { @dasm_widget.name_local_vars(@dasm_widget.curview.current_address) }
842
1099
  addsubmenu(actions, 'Rebase') { @dasm_widget.rebase }
843
- addsubmenu(actions, 'Rename label', 'n') { @dasm_widget.rename_label(@dasm_widget.pointed_addr) }
1100
+ addsubmenu(actions, 'Rename label', 'n') { @dasm_widget.rename }
844
1101
  addsubmenu(actions, 'Decompile', '<tab>') { @dasm_widget.decompile(@dasm_widget.curview.current_address) }
845
1102
  addsubmenu(actions, 'Decompile finali_ze') { @dasm_widget.dasm.decompiler.finalize ; @dasm_widget.gui_update }
846
- addsubmenu(actions, 'Comment', ';') { @dasm_widget.decompile(@dasm_widget.curview.current_address) }
1103
+ addsubmenu(actions, 'Comment', ';') { @dasm_widget.add_comment(@dasm_widget.curview.current_address) }
847
1104
  addsubmenu(actions, '_Undefine') { @dasm_widget.dasm.undefine_from(@dasm_widget.curview.current_address) ; @dasm_widget.gui_update }
848
1105
  addsubmenu(actions, 'Unde_fine function') { @dasm_widget.undefine_function(@dasm_widget.curview.current_address) }
849
1106
  addsubmenu(actions, 'Undefine function & _subfuncs') { @dasm_widget.undefine_function(@dasm_widget.curview.current_address, true) }