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
@@ -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) }