metasm 1.0.1 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/.hgtags +3 -0
- data/Gemfile +1 -0
- data/INSTALL +61 -0
- data/LICENCE +458 -0
- data/README +29 -21
- data/Rakefile +10 -0
- data/TODO +10 -12
- data/doc/code_organisation.txt +2 -0
- data/doc/core/DynLdr.txt +247 -0
- data/doc/core/ExeFormat.txt +43 -0
- data/doc/core/Expression.txt +220 -0
- data/doc/core/GNUExports.txt +27 -0
- data/doc/core/Ia32.txt +236 -0
- data/doc/core/SerialStruct.txt +108 -0
- data/doc/core/VirtualString.txt +145 -0
- data/doc/core/WindowsExports.txt +61 -0
- data/doc/core/index.txt +1 -0
- data/doc/style.css +6 -3
- data/doc/usage/debugger.txt +327 -0
- data/doc/usage/index.txt +1 -0
- data/doc/use_cases.txt +2 -2
- data/metasm.gemspec +22 -0
- data/{lib/metasm.rb → metasm.rb} +11 -3
- data/{lib/metasm → metasm}/compile_c.rb +13 -7
- data/metasm/cpu/arc.rb +8 -0
- data/metasm/cpu/arc/decode.rb +425 -0
- data/metasm/cpu/arc/main.rb +191 -0
- data/metasm/cpu/arc/opcodes.rb +588 -0
- data/{lib/metasm → metasm/cpu}/arm.rb +7 -5
- data/{lib/metasm → metasm/cpu}/arm/debug.rb +2 -2
- data/{lib/metasm → metasm/cpu}/arm/decode.rb +13 -12
- data/{lib/metasm → metasm/cpu}/arm/encode.rb +23 -8
- data/{lib/metasm → metasm/cpu}/arm/main.rb +0 -3
- data/metasm/cpu/arm/opcodes.rb +324 -0
- data/{lib/metasm → metasm/cpu}/arm/parse.rb +25 -13
- data/{lib/metasm → metasm/cpu}/arm/render.rb +2 -2
- data/metasm/cpu/arm64.rb +15 -0
- data/metasm/cpu/arm64/debug.rb +38 -0
- data/metasm/cpu/arm64/decode.rb +289 -0
- data/metasm/cpu/arm64/encode.rb +41 -0
- data/metasm/cpu/arm64/main.rb +105 -0
- data/metasm/cpu/arm64/opcodes.rb +232 -0
- data/metasm/cpu/arm64/parse.rb +20 -0
- data/metasm/cpu/arm64/render.rb +95 -0
- data/{lib/metasm/ppc.rb → metasm/cpu/bpf.rb} +2 -4
- data/metasm/cpu/bpf/decode.rb +142 -0
- data/metasm/cpu/bpf/main.rb +60 -0
- data/metasm/cpu/bpf/opcodes.rb +81 -0
- data/metasm/cpu/bpf/render.rb +41 -0
- data/metasm/cpu/cy16.rb +9 -0
- data/metasm/cpu/cy16/decode.rb +253 -0
- data/metasm/cpu/cy16/main.rb +63 -0
- data/metasm/cpu/cy16/opcodes.rb +78 -0
- data/metasm/cpu/cy16/render.rb +41 -0
- data/metasm/cpu/dalvik.rb +11 -0
- data/{lib/metasm → metasm/cpu}/dalvik/decode.rb +35 -13
- data/{lib/metasm → metasm/cpu}/dalvik/main.rb +51 -2
- data/{lib/metasm → metasm/cpu}/dalvik/opcodes.rb +19 -11
- data/metasm/cpu/ia32.rb +17 -0
- data/{lib/metasm → metasm/cpu}/ia32/compile_c.rb +5 -7
- data/{lib/metasm → metasm/cpu}/ia32/debug.rb +5 -5
- data/{lib/metasm → metasm/cpu}/ia32/decode.rb +246 -59
- data/{lib/metasm → metasm/cpu}/ia32/decompile.rb +7 -7
- data/{lib/metasm → metasm/cpu}/ia32/encode.rb +19 -13
- data/{lib/metasm → metasm/cpu}/ia32/main.rb +51 -8
- data/metasm/cpu/ia32/opcodes.rb +1424 -0
- data/{lib/metasm → metasm/cpu}/ia32/parse.rb +47 -16
- data/{lib/metasm → metasm/cpu}/ia32/render.rb +31 -4
- data/metasm/cpu/mips.rb +14 -0
- data/{lib/metasm → metasm/cpu}/mips/compile_c.rb +1 -1
- data/metasm/cpu/mips/debug.rb +42 -0
- data/{lib/metasm → metasm/cpu}/mips/decode.rb +46 -16
- data/{lib/metasm → metasm/cpu}/mips/encode.rb +4 -3
- data/{lib/metasm → metasm/cpu}/mips/main.rb +11 -4
- data/{lib/metasm → metasm/cpu}/mips/opcodes.rb +86 -17
- data/{lib/metasm → metasm/cpu}/mips/parse.rb +1 -1
- data/{lib/metasm → metasm/cpu}/mips/render.rb +1 -1
- data/{lib/metasm/dalvik.rb → metasm/cpu/msp430.rb} +1 -1
- data/metasm/cpu/msp430/decode.rb +247 -0
- data/metasm/cpu/msp430/main.rb +62 -0
- data/metasm/cpu/msp430/opcodes.rb +101 -0
- data/{lib/metasm → metasm/cpu}/pic16c/decode.rb +6 -7
- data/{lib/metasm → metasm/cpu}/pic16c/main.rb +0 -0
- data/{lib/metasm → metasm/cpu}/pic16c/opcodes.rb +1 -1
- data/{lib/metasm/mips.rb → metasm/cpu/ppc.rb} +4 -4
- data/{lib/metasm → metasm/cpu}/ppc/decode.rb +18 -12
- data/{lib/metasm → metasm/cpu}/ppc/decompile.rb +3 -3
- data/{lib/metasm → metasm/cpu}/ppc/encode.rb +2 -2
- data/{lib/metasm → metasm/cpu}/ppc/main.rb +17 -12
- data/{lib/metasm → metasm/cpu}/ppc/opcodes.rb +11 -5
- data/metasm/cpu/ppc/parse.rb +55 -0
- data/metasm/cpu/python.rb +8 -0
- data/metasm/cpu/python/decode.rb +136 -0
- data/metasm/cpu/python/main.rb +36 -0
- data/metasm/cpu/python/opcodes.rb +180 -0
- data/{lib/metasm → metasm/cpu}/sh4.rb +1 -1
- data/{lib/metasm → metasm/cpu}/sh4/decode.rb +48 -17
- data/{lib/metasm → metasm/cpu}/sh4/main.rb +13 -4
- data/{lib/metasm → metasm/cpu}/sh4/opcodes.rb +7 -8
- data/metasm/cpu/x86_64.rb +15 -0
- data/{lib/metasm → metasm/cpu}/x86_64/compile_c.rb +28 -17
- data/{lib/metasm → metasm/cpu}/x86_64/debug.rb +4 -4
- data/{lib/metasm → metasm/cpu}/x86_64/decode.rb +57 -15
- data/{lib/metasm → metasm/cpu}/x86_64/encode.rb +55 -26
- data/{lib/metasm → metasm/cpu}/x86_64/main.rb +14 -6
- data/metasm/cpu/x86_64/opcodes.rb +136 -0
- data/{lib/metasm → metasm/cpu}/x86_64/parse.rb +10 -2
- data/metasm/cpu/x86_64/render.rb +35 -0
- data/metasm/cpu/z80.rb +9 -0
- data/metasm/cpu/z80/decode.rb +313 -0
- data/metasm/cpu/z80/main.rb +67 -0
- data/metasm/cpu/z80/opcodes.rb +224 -0
- data/metasm/cpu/z80/render.rb +59 -0
- data/{lib/metasm/os/main.rb → metasm/debug.rb} +160 -401
- data/{lib/metasm → metasm}/decode.rb +35 -4
- data/{lib/metasm → metasm}/decompile.rb +15 -16
- data/{lib/metasm → metasm}/disassemble.rb +201 -45
- data/{lib/metasm → metasm}/disassemble_api.rb +651 -87
- data/{lib/metasm → metasm}/dynldr.rb +220 -133
- data/{lib/metasm → metasm}/encode.rb +10 -1
- data/{lib/metasm → metasm}/exe_format/a_out.rb +9 -6
- data/{lib/metasm → metasm}/exe_format/autoexe.rb +1 -0
- data/{lib/metasm → metasm}/exe_format/bflt.rb +57 -27
- data/{lib/metasm → metasm}/exe_format/coff.rb +11 -3
- data/{lib/metasm → metasm}/exe_format/coff_decode.rb +53 -20
- data/{lib/metasm → metasm}/exe_format/coff_encode.rb +11 -13
- data/{lib/metasm → metasm}/exe_format/dex.rb +13 -5
- data/{lib/metasm → metasm}/exe_format/dol.rb +1 -0
- data/{lib/metasm → metasm}/exe_format/elf.rb +93 -57
- data/{lib/metasm → metasm}/exe_format/elf_decode.rb +143 -34
- data/{lib/metasm → metasm}/exe_format/elf_encode.rb +122 -31
- data/metasm/exe_format/gb.rb +65 -0
- data/metasm/exe_format/javaclass.rb +424 -0
- data/{lib/metasm → metasm}/exe_format/macho.rb +204 -16
- data/{lib/metasm → metasm}/exe_format/main.rb +26 -3
- data/{lib/metasm → metasm}/exe_format/mz.rb +1 -0
- data/{lib/metasm → metasm}/exe_format/nds.rb +7 -4
- data/{lib/metasm → metasm}/exe_format/pe.rb +71 -8
- data/metasm/exe_format/pyc.rb +167 -0
- data/{lib/metasm → metasm}/exe_format/serialstruct.rb +67 -14
- data/{lib/metasm → metasm}/exe_format/shellcode.rb +7 -3
- data/metasm/exe_format/shellcode_rwx.rb +114 -0
- data/metasm/exe_format/swf.rb +205 -0
- data/{lib/metasm → metasm}/exe_format/xcoff.rb +7 -7
- data/metasm/exe_format/zip.rb +335 -0
- data/metasm/gui.rb +13 -0
- data/{lib/metasm → metasm}/gui/cstruct.rb +35 -41
- data/{lib/metasm → metasm}/gui/dasm_coverage.rb +11 -11
- data/{lib/metasm → metasm}/gui/dasm_decomp.rb +7 -20
- data/{lib/metasm → metasm}/gui/dasm_funcgraph.rb +0 -0
- data/metasm/gui/dasm_graph.rb +1695 -0
- data/{lib/metasm → metasm}/gui/dasm_hex.rb +12 -8
- data/{lib/metasm → metasm}/gui/dasm_listing.rb +43 -28
- data/{lib/metasm → metasm}/gui/dasm_main.rb +310 -53
- data/{lib/metasm → metasm}/gui/dasm_opcodes.rb +5 -19
- data/{lib/metasm → metasm}/gui/debug.rb +93 -27
- data/{lib/metasm → metasm}/gui/gtk.rb +162 -40
- data/{lib/metasm → metasm}/gui/qt.rb +12 -2
- data/{lib/metasm → metasm}/gui/win32.rb +179 -42
- data/{lib/metasm → metasm}/gui/x11.rb +59 -59
- data/{lib/metasm → metasm}/main.rb +389 -264
- data/{lib/metasm/os/remote.rb → metasm/os/gdbremote.rb} +146 -54
- data/{lib/metasm → metasm}/os/gnu_exports.rb +1 -1
- data/{lib/metasm → metasm}/os/linux.rb +628 -151
- data/metasm/os/main.rb +330 -0
- data/{lib/metasm → metasm}/os/windows.rb +132 -42
- data/{lib/metasm → metasm}/os/windows_exports.rb +141 -0
- data/{lib/metasm → metasm}/parse.rb +26 -24
- data/{lib/metasm → metasm}/parse_c.rb +221 -116
- data/{lib/metasm → metasm}/preprocessor.rb +55 -40
- data/{lib/metasm → metasm}/render.rb +14 -38
- data/misc/hexdump.rb +2 -1
- data/misc/lint.rb +58 -0
- data/misc/txt2html.rb +9 -7
- data/samples/bindiff.rb +3 -4
- data/samples/dasm-plugins/bindiff.rb +15 -0
- data/samples/dasm-plugins/bookmark.rb +133 -0
- data/samples/dasm-plugins/c_constants.rb +57 -0
- data/samples/dasm-plugins/colortheme_solarized.rb +125 -0
- data/samples/dasm-plugins/cppobj_funcall.rb +60 -0
- data/samples/dasm-plugins/dasm_all.rb +70 -0
- data/samples/dasm-plugins/demangle_cpp.rb +31 -0
- data/samples/dasm-plugins/deobfuscate.rb +251 -0
- data/samples/dasm-plugins/dump_text.rb +35 -0
- data/samples/dasm-plugins/export_graph_svg.rb +86 -0
- data/samples/dasm-plugins/findgadget.rb +75 -0
- data/samples/dasm-plugins/hl_opcode.rb +32 -0
- data/samples/dasm-plugins/hotfix_gtk_dbg.rb +19 -0
- data/samples/dasm-plugins/imm2off.rb +34 -0
- data/samples/dasm-plugins/match_libsigs.rb +93 -0
- data/samples/dasm-plugins/patch_file.rb +95 -0
- data/samples/dasm-plugins/scanfuncstart.rb +36 -0
- data/samples/dasm-plugins/scanxrefs.rb +26 -0
- data/samples/dasm-plugins/selfmodify.rb +197 -0
- data/samples/dasm-plugins/stringsxrefs.rb +28 -0
- data/samples/dasmnavig.rb +1 -1
- data/samples/dbg-apihook.rb +24 -9
- data/samples/dbg-plugins/heapscan.rb +283 -0
- data/samples/dbg-plugins/heapscan/compiled_heapscan_lin.c +155 -0
- data/samples/dbg-plugins/heapscan/compiled_heapscan_win.c +128 -0
- data/samples/dbg-plugins/heapscan/graphheap.rb +616 -0
- data/samples/dbg-plugins/heapscan/heapscan.rb +709 -0
- data/samples/dbg-plugins/heapscan/winheap.h +174 -0
- data/samples/dbg-plugins/heapscan/winheap7.h +307 -0
- data/samples/dbg-plugins/trace_func.rb +214 -0
- data/samples/disassemble-gui.rb +35 -5
- data/samples/disassemble.rb +31 -6
- data/samples/dump_upx.rb +24 -12
- data/samples/dynamic_ruby.rb +12 -3
- data/samples/exeencode.rb +6 -5
- data/samples/factorize-headers-peimports.rb +1 -1
- data/samples/lindebug.rb +175 -381
- data/samples/metasm-shell.rb +1 -2
- data/samples/peldr.rb +2 -2
- data/tests/all.rb +1 -1
- data/tests/arc.rb +26 -0
- data/tests/dynldr.rb +22 -4
- data/tests/expression.rb +55 -0
- data/tests/graph_layout.rb +285 -0
- data/tests/ia32.rb +79 -26
- data/tests/mips.rb +9 -2
- data/tests/x86_64.rb +66 -18
- metadata +330 -218
- data/lib/metasm/arm/opcodes.rb +0 -177
- data/lib/metasm/gui.rb +0 -23
- data/lib/metasm/gui/dasm_graph.rb +0 -1354
- data/lib/metasm/ia32.rb +0 -14
- data/lib/metasm/ia32/opcodes.rb +0 -873
- data/lib/metasm/ppc/parse.rb +0 -52
- data/lib/metasm/x86_64.rb +0 -12
- data/lib/metasm/x86_64/opcodes.rb +0 -118
- data/samples/gdbclient.rb +0 -583
- 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 =
|
46
|
-
:
|
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
|
-
|
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(
|
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 =
|
32
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
433
|
-
|
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
|
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
|
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.
|
582
|
-
|
583
|
-
|
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
|
142
|
-
# if it cannot display the address
|
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
|
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
|
-
|
238
|
-
|
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
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
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
|
-
|
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|
|
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
|
458
|
-
def
|
459
|
-
|
460
|
-
|
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(
|
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[
|
642
|
+
inputbox("label name for #{Expression[what]}", :text => Expression[what]) { |v|
|
471
643
|
next if v == ''
|
472
|
-
@dasm.set_label_at(
|
473
|
-
|
474
|
-
|
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(
|
557
|
-
when ?C; disassemble_fast(
|
558
|
-
when ?d; toggle_data(
|
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 ?
|
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
|
568
|
-
when ?;; add_comment(
|
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) ;
|
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
|
-
|
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
|
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.
|
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.
|
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) }
|