metasm 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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) }
|