metasm 1.0.3 → 1.0.4

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 (114) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +3 -0
  3. data.tar.gz.sig +0 -0
  4. data/Gemfile +3 -2
  5. data/metasm.gemspec +3 -2
  6. data/metasm.rb +4 -1
  7. data/metasm/compile_c.rb +2 -2
  8. data/metasm/cpu/arc/decode.rb +0 -21
  9. data/metasm/cpu/arc/main.rb +4 -4
  10. data/metasm/cpu/arm/decode.rb +1 -5
  11. data/metasm/cpu/arm/main.rb +3 -3
  12. data/metasm/cpu/arm64/decode.rb +2 -6
  13. data/metasm/cpu/arm64/main.rb +5 -5
  14. data/metasm/cpu/bpf/decode.rb +3 -35
  15. data/metasm/cpu/bpf/main.rb +5 -5
  16. data/metasm/cpu/bpf/render.rb +1 -12
  17. data/metasm/cpu/cy16/decode.rb +0 -6
  18. data/metasm/cpu/cy16/main.rb +3 -3
  19. data/metasm/cpu/cy16/render.rb +0 -11
  20. data/metasm/cpu/dalvik/decode.rb +4 -26
  21. data/metasm/cpu/dalvik/main.rb +20 -2
  22. data/metasm/cpu/dalvik/opcodes.rb +3 -2
  23. data/metasm/cpu/{mips/compile_c.rb → ebpf.rb} +5 -2
  24. data/metasm/cpu/ebpf/debug.rb +61 -0
  25. data/metasm/cpu/ebpf/decode.rb +142 -0
  26. data/metasm/cpu/ebpf/main.rb +58 -0
  27. data/metasm/cpu/ebpf/opcodes.rb +97 -0
  28. data/metasm/cpu/ebpf/render.rb +36 -0
  29. data/metasm/cpu/ia32/debug.rb +39 -1
  30. data/metasm/cpu/ia32/decode.rb +111 -90
  31. data/metasm/cpu/ia32/decompile.rb +45 -37
  32. data/metasm/cpu/ia32/main.rb +10 -0
  33. data/metasm/cpu/ia32/parse.rb +6 -0
  34. data/metasm/cpu/mcs51/decode.rb +1 -1
  35. data/metasm/cpu/mcs51/main.rb +11 -0
  36. data/metasm/cpu/mips/decode.rb +8 -18
  37. data/metasm/cpu/mips/main.rb +3 -3
  38. data/metasm/cpu/mips/opcodes.rb +1 -1
  39. data/metasm/cpu/msp430/decode.rb +2 -6
  40. data/metasm/cpu/msp430/main.rb +3 -3
  41. data/metasm/cpu/openrisc.rb +11 -0
  42. data/metasm/cpu/openrisc/debug.rb +106 -0
  43. data/metasm/cpu/openrisc/decode.rb +182 -0
  44. data/metasm/cpu/openrisc/decompile.rb +350 -0
  45. data/metasm/cpu/openrisc/main.rb +70 -0
  46. data/metasm/cpu/openrisc/opcodes.rb +109 -0
  47. data/metasm/cpu/openrisc/render.rb +37 -0
  48. data/metasm/cpu/ppc/decode.rb +0 -25
  49. data/metasm/cpu/ppc/main.rb +6 -6
  50. data/metasm/cpu/ppc/opcodes.rb +3 -4
  51. data/metasm/cpu/python/decode.rb +0 -20
  52. data/metasm/cpu/python/main.rb +1 -1
  53. data/metasm/cpu/sh4/decode.rb +2 -6
  54. data/metasm/cpu/sh4/main.rb +25 -23
  55. data/metasm/cpu/st20/decode.rb +0 -7
  56. data/metasm/cpu/webasm.rb +11 -0
  57. data/metasm/cpu/webasm/debug.rb +31 -0
  58. data/metasm/cpu/webasm/decode.rb +321 -0
  59. data/metasm/cpu/webasm/decompile.rb +386 -0
  60. data/metasm/cpu/webasm/encode.rb +104 -0
  61. data/metasm/cpu/webasm/main.rb +81 -0
  62. data/metasm/cpu/webasm/opcodes.rb +214 -0
  63. data/metasm/cpu/x86_64/compile_c.rb +13 -9
  64. data/metasm/cpu/x86_64/parse.rb +1 -1
  65. data/metasm/cpu/z80/decode.rb +0 -27
  66. data/metasm/cpu/z80/main.rb +3 -3
  67. data/metasm/cpu/z80/render.rb +0 -11
  68. data/metasm/debug.rb +43 -8
  69. data/metasm/decode.rb +62 -14
  70. data/metasm/decompile.rb +793 -466
  71. data/metasm/disassemble.rb +188 -131
  72. data/metasm/disassemble_api.rb +30 -17
  73. data/metasm/dynldr.rb +2 -2
  74. data/metasm/encode.rb +8 -2
  75. data/metasm/exe_format/autoexe.rb +2 -0
  76. data/metasm/exe_format/coff.rb +21 -3
  77. data/metasm/exe_format/coff_decode.rb +12 -0
  78. data/metasm/exe_format/coff_encode.rb +6 -3
  79. data/metasm/exe_format/dex.rb +13 -3
  80. data/metasm/exe_format/elf.rb +12 -2
  81. data/metasm/exe_format/elf_decode.rb +59 -1
  82. data/metasm/exe_format/main.rb +2 -0
  83. data/metasm/exe_format/mz.rb +1 -0
  84. data/metasm/exe_format/pe.rb +25 -3
  85. data/metasm/exe_format/wasm.rb +402 -0
  86. data/metasm/gui/dasm_decomp.rb +171 -95
  87. data/metasm/gui/dasm_graph.rb +61 -2
  88. data/metasm/gui/dasm_hex.rb +2 -2
  89. data/metasm/gui/dasm_main.rb +45 -19
  90. data/metasm/gui/debug.rb +13 -4
  91. data/metasm/gui/gtk.rb +12 -4
  92. data/metasm/main.rb +108 -103
  93. data/metasm/os/emulator.rb +175 -0
  94. data/metasm/os/main.rb +11 -6
  95. data/metasm/parse.rb +23 -12
  96. data/metasm/parse_c.rb +189 -135
  97. data/metasm/preprocessor.rb +16 -1
  98. data/misc/openrisc-parser.rb +79 -0
  99. data/samples/dasm-plugins/scanxrefs.rb +6 -4
  100. data/samples/dasm-plugins/selfmodify.rb +8 -8
  101. data/samples/dbg-plugins/trace_func.rb +1 -1
  102. data/samples/disassemble-gui.rb +14 -3
  103. data/samples/emubios.rb +251 -0
  104. data/samples/emudbg.rb +127 -0
  105. data/samples/lindebug.rb +79 -78
  106. data/samples/metasm-shell.rb +8 -8
  107. data/tests/all.rb +1 -1
  108. data/tests/expression.rb +2 -0
  109. data/tests/graph_layout.rb +1 -1
  110. data/tests/ia32.rb +1 -0
  111. data/tests/mips.rb +1 -1
  112. data/tests/preprocessor.rb +18 -0
  113. metadata +124 -6
  114. metadata.gz.sig +0 -0
@@ -6,7 +6,7 @@
6
6
  module Metasm
7
7
  module Gui
8
8
  class CdecompListingWidget < DrawableWidget
9
- attr_accessor :dasm, :curaddr, :tabwidth
9
+ attr_accessor :dasm, :curfuncaddr, :tabwidth
10
10
 
11
11
  def initialize_widget(dasm, parent_widget)
12
12
  @dasm = dasm
@@ -16,7 +16,8 @@ class CdecompListingWidget < DrawableWidget
16
16
  @cwidth = @cheight = 1 # widget size in chars
17
17
  @line_text = []
18
18
  @line_text_col = [] # each line is [[:col, 'text'], [:col, 'text']]
19
- @curaddr = nil
19
+ @line_addr = []
20
+ @curfuncaddr = nil
20
21
  @tabwidth = 8
21
22
 
22
23
  @default_color_association = ColorTheme.merge :keyword => :blue, :localvar => :darkred,
@@ -24,7 +25,7 @@ class CdecompListingWidget < DrawableWidget
24
25
  end
25
26
 
26
27
  def curfunc
27
- @dasm.c_parser and (@dasm.c_parser.toplevel.symbol[@curaddr] or @dasm.c_parser.toplevel.struct[@curaddr])
28
+ @dasm.c_parser and (@dasm.c_parser.toplevel.symbol[@curfuncaddr] or @dasm.c_parser.toplevel.struct[@curfuncaddr])
28
29
  end
29
30
 
30
31
  def click(x, y)
@@ -59,7 +60,6 @@ class CdecompListingWidget < DrawableWidget
59
60
  if @caret_y < @line_text.length - 1
60
61
  @view_y += 4
61
62
  @caret_y += 4
62
- redraw
63
63
  end
64
64
  end
65
65
  redraw
@@ -148,92 +148,127 @@ class CdecompListingWidget < DrawableWidget
148
148
  @caret_x = @line_text[@caret_y].to_s.length
149
149
  update_caret
150
150
  when :pgup
151
- @caret_y -= @cheight/2
152
- @caret_y = 0 if @caret_y < 0
153
- update_caret
151
+ if @caret_y > 0
152
+ @view_y -= @cheight/2
153
+ @caret_y -= @cheight/2
154
+ @caret_y = 0 if @caret_y < 0
155
+ redraw
156
+ end
154
157
  when :pgdown
155
- @caret_y += @cheight/2
156
- @caret_y = @line_text.length if @caret_y > @line_text.length
157
- update_caret
158
- when ?n # rename local/global variable
159
- f = curfunc.initializer if curfunc and curfunc.initializer.kind_of? C::Block
160
- n = @hl_word
161
- if (f and f.symbol[n]) or @dasm.c_parser.toplevel.symbol[n]
162
- @parent_widget.inputbox("new name for #{n}", :text => n) { |v|
163
- if v !~ /^[a-z_$][a-z_0-9$]*$/i
164
- @parent_widget.messagebox("invalid name #{v.inspect} !")
165
- next
166
- end
167
- if f and f.symbol[n]
168
- # TODO add/update comment to the asm instrs
169
- s = f.symbol[v] = f.symbol.delete(n)
170
- s.name = v
171
- f.decompdata[:stackoff_name][s.stackoff] = v if s.stackoff
172
- elsif @dasm.c_parser.toplevel.symbol[n]
173
- @dasm.rename_label(n, v)
174
- @curaddr = v if @curaddr == n
175
- end
176
- gui_update
177
- }
158
+ if @caret_y < @line_text.length
159
+ @view_y += @cheight/2
160
+ @caret_y += @cheight/2
161
+ @caret_y = @line_text.length if @caret_y > @line_text.length
162
+ redraw
178
163
  end
164
+ when ?n # rename local/global variable
165
+ prompt_rename
179
166
  when ?r # redecompile
180
- @parent_widget.decompile(@curaddr)
181
- when ?t # change variable type (you'll want to redecompile after that)
182
- f = curfunc.initializer if curfunc.kind_of? C::Variable and curfunc.initializer.kind_of? C::Block
183
- n = @hl_word
184
- cp = @dasm.c_parser
185
- if (f and s = f.symbol[n]) or s = cp.toplevel.symbol[n] or s = cp.toplevel.symbol[@curaddr]
186
- s_ = s.dup
187
- s_.initializer = nil if s.kind_of? C::Variable # for static var, avoid dumping the initializer in the textbox
188
- s_.attributes &= C::Attributes::DECLSPECS if s_.attributes
189
- @parent_widget.inputbox("new type for #{s.name}", :text => s_.dump_def(cp.toplevel)[0].join(' ')) { |t|
190
- if t == ''
191
- if s.type.kind_of? C::Function and s.initializer and s.initializer.decompdata
192
- s.initializer.decompdata[:stackoff_type].clear
193
- s.initializer.decompdata.delete :return_type
194
- elsif s.kind_of? C::Variable and s.stackoff
195
- f.decompdata[:stackoff_type].delete s.stackoff
196
- end
197
- next
167
+ @parent_widget.decompile(@curfuncaddr)
168
+ when ?t, ?y # change variable type (you'll want to redecompile after that)
169
+ prompt_retype
170
+ when ?h
171
+ display_hex
172
+ else return false
173
+ end
174
+ true
175
+ end
176
+
177
+ def prompt_rename
178
+ f = curfunc.initializer if curfunc and curfunc.initializer.kind_of?(C::Block)
179
+ n = @hl_word
180
+ if (f and f.symbol[n]) or @dasm.c_parser.toplevel.symbol[n]
181
+ @parent_widget.inputbox("new name for #{n}", :text => n) { |v|
182
+ if v !~ /^[a-z_$][a-z_0-9$]*$/i
183
+ @parent_widget.messagebox("invalid name #{v.inspect} !")
184
+ next
185
+ end
186
+ if f and f.symbol[n]
187
+ # TODO add/update comment to the asm instrs
188
+ s = f.symbol[v] = f.symbol.delete(n)
189
+ s.misc ||= {}
190
+ uan = s.misc[:unalias_name] ||= n
191
+ s.name = v
192
+ f.decompdata[:unalias_name][uan] = v
193
+ elsif @dasm.c_parser.toplevel.symbol[n]
194
+ @dasm.rename_label(n, v)
195
+ @curfuncaddr = v if @curfuncaddr == n
196
+ end
197
+ gui_update
198
+ }
199
+ end
200
+ end
201
+
202
+ def prompt_retype
203
+ f = curfunc.initializer if curfunc.kind_of?(C::Variable) and curfunc.initializer.kind_of?(C::Block)
204
+ n = @hl_word
205
+ cp = @dasm.c_parser
206
+ if (f and s = f.symbol[n]) or s = cp.toplevel.symbol[n] or s = cp.toplevel.symbol[@curfuncaddr]
207
+ s_ = s.dup
208
+ s_.initializer = nil if s.kind_of?(C::Variable) # for static var, avoid dumping the initializer in the textbox
209
+ s_.attributes &= C::Attributes::DECLSPECS if s_.attributes
210
+ @parent_widget.inputbox("new type for #{s.name}", :text => s_.dump_def(cp.toplevel)[0].join(' ')) { |t|
211
+ if t == ''
212
+ if s.type.kind_of?(C::Function) and s.initializer and s.initializer.decompdata
213
+ s.initializer.decompdata.delete(:return_type)
214
+ elsif f.symbol[n] and s.kind_of?(C::Variable)
215
+ s.misc ||= {}
216
+ uan = s.misc[:unalias_name] ||= s.name
217
+ f.decompdata[:unalias_type].delete uan
198
218
  end
199
- begin
200
- cp.lexer.feed(t)
201
- raise 'bad type' if not v = C::Variable.parse_type(cp, cp.toplevel, true)
202
- v.parse_declarator(cp, cp.toplevel)
203
- if s.type.kind_of? C::Function and s.initializer and s.initializer.decompdata
204
- # updated type of a decompiled func: update stack
205
- vt = v.type.untypedef
206
- vt = vt.type.untypedef if vt.kind_of? C::Pointer
207
- raise 'function forever !' if not vt.kind_of? C::Function
208
- # TODO _declspec
209
- ao = 1
210
- vt.args.to_a.each { |a|
211
- next if a.has_attribute_var('register')
212
- ao = (ao + [cp.sizeof(a), cp.typesize[:ptr]].max - 1) / cp.typesize[:ptr] * cp.typesize[:ptr]
213
- s.initializer.decompdata[:stackoff_name][ao] = a.name if a.name
214
- s.initializer.decompdata[:stackoff_type][ao] = a.type
215
- ao += cp.sizeof(a)
216
- }
217
- s.initializer.decompdata[:return_type] = vt.type
218
- s.type = v.type
219
- else
220
- f.decompdata[:stackoff_type][s.stackoff] = v.type if f and s.kind_of? C::Variable and s.stackoff
221
- s.type = v.type
222
- end
223
- gui_update
224
- rescue Object
225
- @parent_widget.messagebox([$!.message, $!.backtrace].join("\n"), "error")
219
+ next
220
+ end
221
+ begin
222
+ cp.lexer.feed(t)
223
+ raise 'bad type' if not v = C::Variable.parse_type(cp, cp.toplevel, true)
224
+ v.parse_declarator(cp, cp.toplevel)
225
+ if s.type.kind_of?(C::Function) and s.initializer and s.initializer.decompdata
226
+ # updated type of a decompiled func: update stack
227
+ vt = v.type.untypedef
228
+ vt = vt.type.untypedef if vt.kind_of?(C::Pointer)
229
+ raise 'function forever !' if not vt.kind_of?(C::Function)
230
+ # TODO _declspec
231
+ vt.args.to_a.each_with_index { |a, idx|
232
+ oa = curfunc.type.args.to_a[idx]
233
+ oa.misc ||= {}
234
+ a.misc ||= {}
235
+ uan = a.misc[:unalias_name] = oa.misc[:unalias_name] ||= oa.name
236
+ s.initializer.decompdata[:unalias_name][uan] = a.name if a.name
237
+ s.initializer.decompdata[:unalias_type][uan] = a.type
238
+ }
239
+ s.initializer.decompdata[:return_type] = vt.type
240
+ s.type = v.type
241
+ elsif f and s.kind_of?(C::Variable) and f.symbol[s.name]
242
+ s.misc ||= {}
243
+ uan = s.misc[:unalias_name] ||= s.name
244
+ f.decompdata[:unalias_type][uan] = v.type
245
+ s.type = v.type
226
246
  end
227
- cp.readtok until cp.eos?
228
- }
247
+ gui_update
248
+ rescue Object
249
+ @parent_widget.messagebox([$!.message, $!.backtrace].join("\n"), "error")
250
+ end
251
+ cp.readtok until cp.eos?
252
+ }
253
+ end
254
+ end
255
+
256
+ # change the display of an integer from hex to decimal
257
+ def display_hex
258
+ ce = curobj
259
+ if ce.kind_of?(C::CExpression) and not ce.op and ce.rexpr.kind_of?(::Integer)
260
+ ce.misc ||= {}
261
+ if ce.misc[:custom_display] =~ /^0x/
262
+ ce.misc[:custom_display] = ce.rexpr.to_s
263
+ else
264
+ ce.misc[:custom_display] = '0x%X' % ce.rexpr
229
265
  end
230
- else return false
266
+ gui_update
231
267
  end
232
- true
233
268
  end
234
269
 
235
270
  def get_cursor_pos
236
- [@curaddr, @caret_x, @caret_y, @view_y]
271
+ [@curfuncaddr, @caret_x, @caret_y, @view_y]
237
272
  end
238
273
 
239
274
  def set_cursor_pos(p)
@@ -258,7 +293,7 @@ class CdecompListingWidget < DrawableWidget
258
293
  # returns true on success (address exists & decompiled)
259
294
  def focus_addr(addr)
260
295
  if @dasm.c_parser and (@dasm.c_parser.toplevel.symbol[addr] or @dasm.c_parser.toplevel.struct[addr].kind_of?(C::Union))
261
- @curaddr = addr
296
+ @curfuncaddr = addr
262
297
  @caret_x = @caret_y = 0
263
298
  gui_update
264
299
  return true
@@ -270,34 +305,75 @@ class CdecompListingWidget < DrawableWidget
270
305
  todo = [addr]
271
306
  done = []
272
307
  ep = @dasm.entrypoints.to_a.inject({}) { |h, e| h.update @dasm.normalize(e) => true }
273
- while addr = todo.pop
274
- next if not di = @dasm.di_at(addr)
275
- addr = di.block.address
276
- next if done.include?(addr) or not @dasm.di_at(addr)
277
- done << addr
278
- break if @dasm.function[addr] or ep[addr]
308
+ while laddr = todo.pop
309
+ next if not di = @dasm.di_at(laddr)
310
+ laddr = di.block.address
311
+ next if done.include?(laddr) or not @dasm.di_at(laddr)
312
+ done << laddr
313
+ break if @dasm.function[laddr] or ep[laddr]
279
314
  empty = true
280
- @dasm.decoded[addr].block.each_from_samefunc(@dasm) { |na| empty = false ; todo << na }
315
+ @dasm.decoded[laddr].block.each_from_samefunc(@dasm) { |na| empty = false ; todo << na }
281
316
  break if empty
282
317
  end
283
- @dasm.auto_label_at(addr, 'loc') if @dasm.get_section_at(addr) and not @dasm.get_label_at(addr)
284
- return if not l = @dasm.get_label_at(addr)
285
- @curaddr = l
318
+ @dasm.auto_label_at(laddr, 'loc') if @dasm.get_section_at(laddr) and not @dasm.get_label_at(laddr)
319
+ return if not l = @dasm.get_label_at(laddr)
320
+ @curfuncaddr = l
286
321
  @caret_x = @caret_y = 0
322
+ @want_addr = addr
287
323
  gui_update
288
324
  true
289
325
  end
290
326
 
291
327
  # returns the address of the data under the cursor
292
328
  def current_address
293
- @curaddr
329
+ if @line_c[@caret_y]
330
+ lc = {}
331
+ @line_c[@caret_y].each { |k, v|
332
+ lc[k] = v if v.misc and v.misc[:di_addr]
333
+ }
334
+ o = lc.keys.sort.reverse.find { |oo| oo < @caret_x } || lc.keys.min
335
+ end
336
+ o ? lc[o].misc[:di_addr] : @curfuncaddr
337
+ end
338
+
339
+ # return the C object under the cursor
340
+ def curobj
341
+ if lc = @line_c[@caret_y]
342
+ o = lc.keys.sort.reverse.find { |oo| oo < @caret_x } || lc.keys.min
343
+ end
344
+ o ? lc[o] : curfunc
294
345
  end
295
346
 
347
+
296
348
  def update_line_text
297
- @line_text = curfunc.dump_def(@dasm.c_parser.toplevel)[0].map { |l| l.gsub("\t", ' '*@tabwidth) }
349
+ text = curfunc.dump_def(@dasm.c_parser.toplevel)[0]
350
+ @line_text = text.map { |l| l.gsub("\t", ' '*@tabwidth) }
351
+ # y => { x => C }
352
+ @line_c = text.map { |l|
353
+ h = {}
354
+ l.c_at_offset.each { |o, c|
355
+ oo = l[0, o].gsub("\t", ' '*@tabwidth).length
356
+ h[oo] = c
357
+ }
358
+ h
359
+ }
360
+
361
+ @want_addr ||= nil
362
+ # define @caret_y to match @want_addr from focus_addr()
363
+ @line_c.each_with_index { |lc, y|
364
+ next if not @want_addr
365
+ lc.each { |o, c|
366
+ if @want_addr and c.misc and c.misc[:di_addr]
367
+ @caret_x, @caret_y = o, y+1 if @want_addr > c.misc[:di_addr]
368
+ @want_addr = nil if @want_addr <= c.misc[:di_addr]
369
+ end
370
+ }
371
+ }
372
+ @want_addr = nil
373
+
298
374
  @line_text_col = []
299
375
 
300
- if f = curfunc and f.kind_of? C::Variable and f.initializer.kind_of? C::Block
376
+ if f = curfunc and f.kind_of?(C::Variable) and f.initializer.kind_of?(C::Block)
301
377
  keyword_re = /\b(#{C::Keyword.keys.join('|')})\b/
302
378
  intrinsic_re = /\b(intrinsic_\w+)\b/
303
379
  lv = f.initializer.symbol.keys
@@ -342,7 +418,7 @@ class CdecompListingWidget < DrawableWidget
342
418
  @line_text_col = [[[:text, 'please wait']]]
343
419
  redraw
344
420
  @decompiling = true
345
- @dasm.decompile_func(@curaddr)
421
+ protect { @dasm.decompile_func(@curfuncaddr) }
346
422
  @decompiling = false
347
423
  end
348
424
  if curfunc
@@ -78,6 +78,27 @@ class Graph
78
78
  ar << head
79
79
  end
80
80
 
81
+ # recenter boxes (a -> [b, c, d] => center a around b+c+d)
82
+ (ar.length - 1).times { |i|
83
+ g = ar[i]
84
+ gn = ar[i+1]
85
+ withchld = g.content.find_all { |b| not b.to.empty? }
86
+ wc = withchld[0]
87
+ if withchld.length == 1 and wc.to.length >= 1 and wc.to & gn.content == wc.to
88
+ tox = wc.to.map { |b| b.x }.min
89
+ toxmax = wc.to.map { |b| b.x + b.w }.max
90
+ # dx1: center wc center around (wc.to.xmin + wc.to.xmax)/2
91
+ dx1 = tox + (toxmax-tox)/2 - (wc.x + wc.w/2)
92
+ # dx2: center wc center around the mean of the centers of wc.to
93
+ dx2 = wc.to.map { |b| b.x + b.w/2 }.inject(0) { |a, b| a+b } / wc.to.length - (wc.x + wc.w/2)
94
+ dx = (dx1 + dx2) / 2
95
+ ar.length.times { |j|
96
+ ar[j].w += dx
97
+ ar[j].content.each { |b| b.x += (j<=i ? dx/2 : -dx/2) }
98
+ }
99
+ end
100
+ }
101
+
81
102
  # move boxes inside this group
82
103
  maxw = ar.map { |g| g.w }.max
83
104
  fullh = ar.inject(0) { |h, g| h + g.h }
@@ -158,10 +179,48 @@ class Graph
158
179
  g.content.each { |b| b.x += dx ; b.y += dy }
159
180
  curx += g.w
160
181
  }
182
+
183
+ # shrink horizontally if possible
184
+ (ar.length - 1).times { |i1|
185
+ g1 = ar[i1]
186
+ g2 = ar[i1+1]
187
+ next if g1.content.empty? or g2.content.empty?
188
+ # only work with full groups, dont try to interleave gaps
189
+ # see if all of one's boxes can be slightly moved inside the other
190
+ g1ymin = g1.content.map { |b| b.y - 9 }.min
191
+ g1ymax = g1.content.map { |b| b.y + b.h + 9 }.max
192
+ g2ymin = g2.content.map { |b| b.y - 9 }.min
193
+ g2ymax = g2.content.map { |b| b.y + b.h + 9 }.max
194
+ g1_matchg2 = g1.content.find_all { |b| b.y + b.h > g2ymin and b.y < g2ymax }
195
+ g2_matchg1 = g2.content.find_all { |b| b.y + b.h > g1ymin and b.y < g1ymax }
196
+ if g1_matchg2.length > 0 and g2_matchg1.length > 0
197
+ g1_up = g1.content.find_all { |b| b.y + b.h < g2ymin }
198
+ g1_down = g1.content.find_all { |b| b.y > g2ymax }
199
+ g2_up = g2.content.find_all { |b| b.y + b.h < g1ymin }
200
+ g2_down = g2.content.find_all { |b| b.y > g1ymax }
201
+ # avoid moving into an arrow
202
+ xmin = g1_matchg2.map { |b| b.x + b.w + 8 }.max
203
+ xmax = g2_matchg1.map { |b| b.x - 8 }.min
204
+ if g1_up.length > 0 and g1_down.length > 0
205
+ xmin = [xmin, g1_up.map { |b| b.x + b.w/2 + 8 }.max, g1_down.map { |b| b.x + b.w/2 + 8 }.max].max
206
+ end
207
+ if g2_up.length > 0 and g2_down.length > 0
208
+ xmax = [xmax, g2_up.map { |b| b.x + b.w/2 + 8 }.min, g2_down.map { |b| b.x + b.w/2 + 8 }.min].min
209
+ end
210
+ dx = xmax - xmin
211
+ if dx > 0
212
+ ar.length.times { |i2|
213
+ ar[i2].content.each { |b| b.x += (i1 >= i2 ? dx/2 : -dx/2) }
214
+ }
215
+ end
216
+ end
217
+ }
218
+
161
219
  # add a 'margin-top' proportionnal to the ar width
162
220
  # this gap should be relative to the real boxes and not possible previous gaps when
163
221
  # merging lines (eg long line + many if patterns -> dont duplicate gaps)
164
222
  boxen = ar.map { |g| g.content }.flatten
223
+ fullw = boxen.map { |g| g.x + g.w + 8 }.max - boxen.map { |g| g.x - 8 }.min
165
224
  realh = boxen.map { |g| g.y + g.h }.max - boxen.map { |g| g.y }.min
166
225
  if maxh < realh + fullw/4
167
226
  maxh = realh + fullw/4
@@ -640,8 +699,8 @@ class Graph
640
699
  # no self references
641
700
  # a box is in one and only one group in 'groups'
642
701
  @groups.each { |g|
643
- g.to = g.content.first.to.map { |t| h[t] if t != g }.compact
644
- g.from = g.content.first.from.map { |f| h[f] if f != g }.compact
702
+ g.to = g.content.first.to.map { |t| h[t] if h[t] != g }.compact
703
+ g.from = g.content.first.from.map { |f| h[f] if h[f] != g }.compact
645
704
  }
646
705
 
647
706
  # order boxes
@@ -27,7 +27,7 @@ class HexWidget < DrawableWidget
27
27
  @oldcaret_x_data = 42
28
28
  @focus_zone = @oldfocus_zone = :hex
29
29
  @addr_min = @dasm.sections.keys.grep(Integer).min rescue nil
30
- @addr_max = @dasm.sections.map { |s, e| s + e.length }.max rescue nil
30
+ @addr_max = @dasm.sections.select { |s, _| s.kind_of?(Integer) }.map { |s, e| s + e.length }.max rescue nil
31
31
  @view_addr = @dasm.prog_binding['entrypoint'] || @addr_min || 0
32
32
  @show_address = @show_data = @show_ascii = true
33
33
  @data_size = 1
@@ -538,7 +538,7 @@ class HexWidget < DrawableWidget
538
538
 
539
539
  def gui_update
540
540
  @addr_min = @dasm.sections.keys.grep(Integer).min rescue nil
541
- @addr_max = @dasm.sections.map { |s, e| s + e.length }.max rescue nil
541
+ @addr_max = @dasm.sections.select { |s, _| s.kind_of?(Integer) }.map { |s, e| s + e.length }.max rescue nil
542
542
  @raw_data_cache.clear
543
543
  redraw
544
544
  end
@@ -60,6 +60,10 @@ class DisasmWidget < ContainerChoiceWidget
60
60
  addview :cstruct, CStructWidget.new(@dasm, self)
61
61
 
62
62
  view(:listing).grab_focus
63
+
64
+ if ENV['METASM_DASM_PLUGINS']
65
+ ENV['METASM_DASM_PLUGINS'].split(',').each { |p| @dasm.load_plugin p }
66
+ end
63
67
  end
64
68
 
65
69
  attr_reader :dasm
@@ -110,7 +114,7 @@ class DisasmWidget < ContainerChoiceWidget
110
114
 
111
115
  # returns the object under the cursor in current view (@dasm.decoded[curaddr])
112
116
  def curobj
113
- @dasm.decoded[curaddr]
117
+ curview.respond_to?(:curobj) ? curview.curobj : @dasm.decoded[curaddr]
114
118
  end
115
119
 
116
120
  # returns the address of the label under the cursor or the address of the line of the cursor
@@ -246,7 +250,8 @@ class DisasmWidget < ContainerChoiceWidget
246
250
  # the callback is put only for the duration of the listwindow, and is not reentrant.
247
251
  def list_bghilight(title, list, a={}, &b)
248
252
  prev_colorcb = bg_color_callback
249
- hash = list[1..-1].inject({}) { |h, l| h.update Expression[l[0] || :unknown].reduce => true }
253
+ addr_idx = a.delete(:bghilight_index) || 0
254
+ hash = list[1..-1].inject({}) { |h, l| h.update Expression[l[addr_idx] || :unknown].reduce => true }
250
255
  @bg_color_callback = lambda { |addr| hash[addr] ? '0f0' : prev_colorcb ? prev_colorcb[addr] : nil }
251
256
  redraw
252
257
  popupend = lambda { @bg_color_callback = prev_colorcb ; redraw }
@@ -287,9 +292,9 @@ class DisasmWidget < ContainerChoiceWidget
287
292
  @dasm.function[t] ||= @dasm.function[:default] ? @dasm.function[:default].dup : DecodedFunction.new
288
293
  }
289
294
  di.block.add_to_subfuncret(di.next_addr)
290
- @dasm.addrs_todo << [di.next_addr, addr, true]
295
+ @dasm.addrs_todo << { :addr => di.next_addr, :from => addr, :from_subfuncret => true }
291
296
  elsif addr
292
- @dasm.addrs_todo << [addr]
297
+ @entrypoints << addr
293
298
  end
294
299
  start_disassemble_bg
295
300
  end
@@ -313,7 +318,7 @@ class DisasmWidget < ContainerChoiceWidget
313
318
  session_append "decompile(#{addr.inspect})"
314
319
  if @dasm.c_parser and var = @dasm.c_parser.toplevel.symbol[addr] and (var.type.kind_of? C::Function or @dasm.di_at(addr))
315
320
  @dasm.decompiler.redecompile(addr)
316
- view(:decompile).curaddr = nil
321
+ view(:decompile).curfuncaddr = nil
317
322
  end
318
323
  focus_addr(addr, :decompile)
319
324
  end
@@ -419,27 +424,31 @@ class DisasmWidget < ContainerChoiceWidget
419
424
 
420
425
  log = []
421
426
  dasm.backtrace(expr, addr, :log => log)
422
- list = [['address', 'type', 'old value', 'value']]
427
+ list = [['order', 'address', 'type', 'old value', 'value']]
428
+ order = 0
423
429
  log.each { |t, *a|
424
- list << [Expression[a[-1]], t]
430
+ order += 1
431
+ list << [('%03d' % order), Expression[a[-1]], t] rescue next
425
432
  case t
426
433
  when :start
427
434
  list.last << a[0]
428
435
  when :up
436
+ order -= 1
429
437
  list.pop
430
438
  when :di
439
+ list.last[-1] = "di #{@dasm.di_at(a[-1]).instruction rescue nil}"
431
440
  list.last << a[1] << a[0]
432
441
  when :func
433
442
  list.last << a[1] << a[0]
434
443
  when :found
435
444
  list.pop
436
- a[0].each { |e_| list << [nil, :found, Expression[e_]] }
445
+ a[0].each { |e_| list << [('%03d' % (order += 1)), nil, :found, Expression[e_]] }
437
446
  else
438
447
  list.last << a[0] << a[1..-1].inspect
439
448
  end
440
449
  }
441
- list_bghilight("backtrace #{expr} from #{Expression[addr]}", list) { |i|
442
- a = i[0].empty? ? i[2] : i[0]
450
+ list_bghilight("backtrace #{expr} from #{Expression[addr]}", list, :bghilight_index => 1) { |i|
451
+ a = i[1].empty? ? i[3] : i[1]
443
452
  focus_addr(a, nil, true)
444
453
  }
445
454
  }
@@ -552,8 +561,11 @@ class DisasmWidget < ContainerChoiceWidget
552
561
  [k, Expression[@dasm.normalize(vv)]] if k.downcase.include? v.downcase
553
562
  }.compact
554
563
  case labels.length
555
- when 0; focus_addr(v)
556
- when 1; focus_addr(labels[0][0])
564
+ when 0
565
+ ve = @dasm.normalize(Expression.parse(v))
566
+ focus_addr(v) if not focus_addr(ve, nil, true)
567
+ when 1
568
+ focus_addr(labels[0][0])
557
569
  else
558
570
  if labels.all? { |k, vv| vv == labels[0][1] }
559
571
  focus_addr(labels[0][0])
@@ -660,7 +672,7 @@ class DisasmWidget < ContainerChoiceWidget
660
672
  true
661
673
  elsif @dasm_pause.empty?
662
674
  @dasm_pause = @dasm.addrs_todo.dup
663
- @dasm.addrs_todo.replace @dasm_pause.find_all { |a, *b| @dasm.decoded[@dasm.normalize(a)] }
675
+ @dasm.addrs_todo.replace @dasm_pause.find_all { |a| @dasm.decoded[@dasm.normalize(a[:addr])] }
664
676
  @dasm_pause -= @dasm.addrs_todo
665
677
  puts "dasm paused (#{@dasm_pause.length})"
666
678
  else
@@ -841,6 +853,12 @@ class DisasmWidget < ContainerChoiceWidget
841
853
  end
842
854
  end
843
855
 
856
+ def spawn_emudbg
857
+ edbg = EmuDebugger.new(@dasm)
858
+ edbg.pc = curaddr
859
+ DbgWindow.new(edbg)
860
+ end
861
+
844
862
  def extend_contextmenu(tg, menu, addr=nil)
845
863
  if @parent_widget.respond_to?(:extend_contextmenu)
846
864
  @parent_widget.extend_contextmenu(tg, menu, addr)
@@ -885,11 +903,14 @@ end
885
903
 
886
904
  class DasmWindow < Window
887
905
  attr_accessor :dasm_widget, :menu
888
- def initialize_window(title = 'metasm disassembler', dasm=nil, *ep)
906
+ def initialize_window(*args)
907
+ dasm = args.grep(Disassembler).first
908
+ args -= [dasm]
909
+ title = args.find { |a| dasm ? !dasm.get_section_at(a) : a.kind_of?(::String) } || 'metasm disassembler'
910
+ ep = args - [title]
889
911
  self.title = title
890
912
  @dasm_widget = nil
891
913
  if dasm
892
- ep = ep.first if ep.length == 1 and ep.first.kind_of? Array
893
914
  display(dasm, ep)
894
915
  else
895
916
  self.widget = NoDasmWidget.new(self)
@@ -909,10 +930,11 @@ class DasmWindow < Window
909
930
  # returns the widget
910
931
  def display(dasm, ep=[])
911
932
  @dasm_widget.terminate if @dasm_widget
912
- ep = [ep] if not ep.kind_of? Array
933
+ ep = [ep] if not ep.kind_of?(Array)
934
+ ep0 = ep.first
913
935
  @dasm_widget = DisasmWidget.new(dasm, ep)
914
936
  self.widget = @dasm_widget
915
- @dasm_widget.focus_addr(ep.first) if ep.first
937
+ @dasm_widget.focus_addr(ep0) if ep0
916
938
  @dasm_widget
917
939
  end
918
940
 
@@ -941,8 +963,10 @@ class DasmWindow < Window
941
963
  ret
942
964
  end
943
965
  }
944
- (@dasm_widget ? DasmWindow.new : self).display(exe.disassembler)
945
- self.title = "#{File.basename(path)} - metasm disassembler"
966
+ tg_win = self
967
+ tg_win = DasmWindow.new if @dasm_widget
968
+ tg_win.display(exe.disassembler)
969
+ tg_win.title = "#{File.basename(path)} - metasm disassembler"
946
970
  exe
947
971
  end
948
972
 
@@ -1106,6 +1130,7 @@ class DasmWindow < Window
1106
1130
  addsubmenu(actions, 'Undefine function & _subfuncs') { @dasm_widget.undefine_function(@dasm_widget.curview.current_address, true) }
1107
1131
  addsubmenu(actions, 'Data', 'd') { @dasm_widget.toggle_data(@dasm_widget.curview.current_address) }
1108
1132
  addsubmenu(actions, 'Pause dasm', 'p', :check) { |ck| !@dasm_widget.playpause_dasm }
1133
+ addsubmenu(actions, 'Spawn EmuDb_g', 'G') { @dasm_widget.spawn_emudbg }
1109
1134
  addsubmenu(actions, 'Run ruby snippet', '^r') { promptruby }
1110
1135
  addsubmenu(actions, 'Run _ruby plugin') { @dasm_widget.prompt_run_ruby_plugin }
1111
1136
 
@@ -1127,6 +1152,7 @@ class DasmWindow < Window
1127
1152
  } if @dasm_widget
1128
1153
  }
1129
1154
  addsubmenu(options)
1155
+ addsubmenu(options, 'Forbid a_ll optimizations', :check) { |ck| @dasm_widget.dasm.decompiler.forbid_all_optimizations = ck }
1130
1156
  addsubmenu(options, 'Forbid decompile _types', :check) { |ck| @dasm_widget.dasm.decompiler.forbid_decompile_types = ck }
1131
1157
  addsubmenu(options, 'Forbid decompile _if/while', :check) { |ck| @dasm_widget.dasm.decompiler.forbid_decompile_ifwhile = ck }
1132
1158
  addsubmenu(options, 'Forbid decomp _optimize', :check) { |ck| @dasm_widget.dasm.decompiler.forbid_optimize_code = ck }