metasm 1.0.0
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.
- data/BUGS +11 -0
- data/CREDITS +17 -0
- data/README +270 -0
- data/TODO +114 -0
- data/doc/code_organisation.txt +146 -0
- data/doc/const_missing.txt +16 -0
- data/doc/core_classes.txt +75 -0
- data/doc/feature_list.txt +53 -0
- data/doc/index.txt +59 -0
- data/doc/install_notes.txt +170 -0
- data/doc/style.css +3 -0
- data/doc/use_cases.txt +18 -0
- data/lib/metasm.rb +80 -0
- data/lib/metasm/arm.rb +12 -0
- data/lib/metasm/arm/debug.rb +39 -0
- data/lib/metasm/arm/decode.rb +167 -0
- data/lib/metasm/arm/encode.rb +77 -0
- data/lib/metasm/arm/main.rb +75 -0
- data/lib/metasm/arm/opcodes.rb +177 -0
- data/lib/metasm/arm/parse.rb +130 -0
- data/lib/metasm/arm/render.rb +55 -0
- data/lib/metasm/compile_c.rb +1457 -0
- data/lib/metasm/dalvik.rb +8 -0
- data/lib/metasm/dalvik/decode.rb +196 -0
- data/lib/metasm/dalvik/main.rb +60 -0
- data/lib/metasm/dalvik/opcodes.rb +366 -0
- data/lib/metasm/decode.rb +213 -0
- data/lib/metasm/decompile.rb +2659 -0
- data/lib/metasm/disassemble.rb +2068 -0
- data/lib/metasm/disassemble_api.rb +1280 -0
- data/lib/metasm/dynldr.rb +1329 -0
- data/lib/metasm/encode.rb +333 -0
- data/lib/metasm/exe_format/a_out.rb +194 -0
- data/lib/metasm/exe_format/autoexe.rb +82 -0
- data/lib/metasm/exe_format/bflt.rb +189 -0
- data/lib/metasm/exe_format/coff.rb +455 -0
- data/lib/metasm/exe_format/coff_decode.rb +901 -0
- data/lib/metasm/exe_format/coff_encode.rb +1078 -0
- data/lib/metasm/exe_format/dex.rb +457 -0
- data/lib/metasm/exe_format/dol.rb +145 -0
- data/lib/metasm/exe_format/elf.rb +923 -0
- data/lib/metasm/exe_format/elf_decode.rb +979 -0
- data/lib/metasm/exe_format/elf_encode.rb +1375 -0
- data/lib/metasm/exe_format/macho.rb +827 -0
- data/lib/metasm/exe_format/main.rb +228 -0
- data/lib/metasm/exe_format/mz.rb +164 -0
- data/lib/metasm/exe_format/nds.rb +172 -0
- data/lib/metasm/exe_format/pe.rb +437 -0
- data/lib/metasm/exe_format/serialstruct.rb +246 -0
- data/lib/metasm/exe_format/shellcode.rb +114 -0
- data/lib/metasm/exe_format/xcoff.rb +167 -0
- data/lib/metasm/gui.rb +23 -0
- data/lib/metasm/gui/cstruct.rb +373 -0
- data/lib/metasm/gui/dasm_coverage.rb +199 -0
- data/lib/metasm/gui/dasm_decomp.rb +369 -0
- data/lib/metasm/gui/dasm_funcgraph.rb +103 -0
- data/lib/metasm/gui/dasm_graph.rb +1354 -0
- data/lib/metasm/gui/dasm_hex.rb +543 -0
- data/lib/metasm/gui/dasm_listing.rb +599 -0
- data/lib/metasm/gui/dasm_main.rb +906 -0
- data/lib/metasm/gui/dasm_opcodes.rb +291 -0
- data/lib/metasm/gui/debug.rb +1228 -0
- data/lib/metasm/gui/gtk.rb +884 -0
- data/lib/metasm/gui/qt.rb +495 -0
- data/lib/metasm/gui/win32.rb +3004 -0
- data/lib/metasm/gui/x11.rb +621 -0
- data/lib/metasm/ia32.rb +14 -0
- data/lib/metasm/ia32/compile_c.rb +1523 -0
- data/lib/metasm/ia32/debug.rb +193 -0
- data/lib/metasm/ia32/decode.rb +1167 -0
- data/lib/metasm/ia32/decompile.rb +564 -0
- data/lib/metasm/ia32/encode.rb +314 -0
- data/lib/metasm/ia32/main.rb +233 -0
- data/lib/metasm/ia32/opcodes.rb +872 -0
- data/lib/metasm/ia32/parse.rb +327 -0
- data/lib/metasm/ia32/render.rb +91 -0
- data/lib/metasm/main.rb +1193 -0
- data/lib/metasm/mips.rb +11 -0
- data/lib/metasm/mips/compile_c.rb +7 -0
- data/lib/metasm/mips/decode.rb +253 -0
- data/lib/metasm/mips/encode.rb +51 -0
- data/lib/metasm/mips/main.rb +72 -0
- data/lib/metasm/mips/opcodes.rb +443 -0
- data/lib/metasm/mips/parse.rb +51 -0
- data/lib/metasm/mips/render.rb +43 -0
- data/lib/metasm/os/gnu_exports.rb +270 -0
- data/lib/metasm/os/linux.rb +1112 -0
- data/lib/metasm/os/main.rb +1686 -0
- data/lib/metasm/os/remote.rb +527 -0
- data/lib/metasm/os/windows.rb +2027 -0
- data/lib/metasm/os/windows_exports.rb +745 -0
- data/lib/metasm/parse.rb +876 -0
- data/lib/metasm/parse_c.rb +3938 -0
- data/lib/metasm/pic16c/decode.rb +42 -0
- data/lib/metasm/pic16c/main.rb +17 -0
- data/lib/metasm/pic16c/opcodes.rb +68 -0
- data/lib/metasm/ppc.rb +11 -0
- data/lib/metasm/ppc/decode.rb +264 -0
- data/lib/metasm/ppc/decompile.rb +251 -0
- data/lib/metasm/ppc/encode.rb +51 -0
- data/lib/metasm/ppc/main.rb +129 -0
- data/lib/metasm/ppc/opcodes.rb +410 -0
- data/lib/metasm/ppc/parse.rb +52 -0
- data/lib/metasm/preprocessor.rb +1277 -0
- data/lib/metasm/render.rb +130 -0
- data/lib/metasm/sh4.rb +8 -0
- data/lib/metasm/sh4/decode.rb +336 -0
- data/lib/metasm/sh4/main.rb +292 -0
- data/lib/metasm/sh4/opcodes.rb +381 -0
- data/lib/metasm/x86_64.rb +12 -0
- data/lib/metasm/x86_64/compile_c.rb +1025 -0
- data/lib/metasm/x86_64/debug.rb +59 -0
- data/lib/metasm/x86_64/decode.rb +268 -0
- data/lib/metasm/x86_64/encode.rb +264 -0
- data/lib/metasm/x86_64/main.rb +135 -0
- data/lib/metasm/x86_64/opcodes.rb +118 -0
- data/lib/metasm/x86_64/parse.rb +68 -0
- data/misc/bottleneck.rb +61 -0
- data/misc/cheader-findpppath.rb +58 -0
- data/misc/hexdiff.rb +74 -0
- data/misc/hexdump.rb +55 -0
- data/misc/metasm-all.rb +13 -0
- data/misc/objdiff.rb +47 -0
- data/misc/objscan.rb +40 -0
- data/misc/pdfparse.rb +661 -0
- data/misc/ppc_pdf2oplist.rb +192 -0
- data/misc/tcp_proxy_hex.rb +84 -0
- data/misc/txt2html.rb +440 -0
- data/samples/a.out.rb +31 -0
- data/samples/asmsyntax.rb +77 -0
- data/samples/bindiff.rb +555 -0
- data/samples/compilation-steps.rb +49 -0
- data/samples/cparser_makestackoffset.rb +55 -0
- data/samples/dasm-backtrack.rb +38 -0
- data/samples/dasmnavig.rb +318 -0
- data/samples/dbg-apihook.rb +228 -0
- data/samples/dbghelp.rb +143 -0
- data/samples/disassemble-gui.rb +102 -0
- data/samples/disassemble.rb +133 -0
- data/samples/dump_upx.rb +95 -0
- data/samples/dynamic_ruby.rb +1929 -0
- data/samples/elf_list_needed.rb +46 -0
- data/samples/elf_listexports.rb +33 -0
- data/samples/elfencode.rb +25 -0
- data/samples/exeencode.rb +128 -0
- data/samples/factorize-headers-elfimports.rb +77 -0
- data/samples/factorize-headers-peimports.rb +109 -0
- data/samples/factorize-headers.rb +43 -0
- data/samples/gdbclient.rb +583 -0
- data/samples/generate_libsigs.rb +102 -0
- data/samples/hotfix_gtk_dbg.rb +59 -0
- data/samples/install_win_env.rb +78 -0
- data/samples/lindebug.rb +924 -0
- data/samples/linux_injectsyscall.rb +95 -0
- data/samples/machoencode.rb +31 -0
- data/samples/metasm-shell.rb +91 -0
- data/samples/pe-hook.rb +69 -0
- data/samples/pe-ia32-cpuid.rb +203 -0
- data/samples/pe-mips.rb +35 -0
- data/samples/pe-shutdown.rb +78 -0
- data/samples/pe-testrelocs.rb +51 -0
- data/samples/pe-testrsrc.rb +24 -0
- data/samples/pe_listexports.rb +31 -0
- data/samples/peencode.rb +19 -0
- data/samples/peldr.rb +494 -0
- data/samples/preprocess-flatten.rb +19 -0
- data/samples/r0trace.rb +308 -0
- data/samples/rubstop.rb +399 -0
- data/samples/scan_pt_gnu_stack.rb +54 -0
- data/samples/scanpeexports.rb +62 -0
- data/samples/shellcode-c.rb +40 -0
- data/samples/shellcode-dynlink.rb +146 -0
- data/samples/source.asm +34 -0
- data/samples/struct_offset.rb +47 -0
- data/samples/testpe.rb +32 -0
- data/samples/testraw.rb +45 -0
- data/samples/win32genloader.rb +132 -0
- data/samples/win32hooker-advanced.rb +169 -0
- data/samples/win32hooker.rb +96 -0
- data/samples/win32livedasm.rb +33 -0
- data/samples/win32remotescan.rb +133 -0
- data/samples/wintrace.rb +92 -0
- data/tests/all.rb +8 -0
- data/tests/dasm.rb +39 -0
- data/tests/dynldr.rb +35 -0
- data/tests/encodeddata.rb +132 -0
- data/tests/ia32.rb +82 -0
- data/tests/mips.rb +116 -0
- data/tests/parse_c.rb +239 -0
- data/tests/preprocessor.rb +269 -0
- data/tests/x86_64.rb +62 -0
- metadata +255 -0
|
@@ -0,0 +1,906 @@
|
|
|
1
|
+
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
2
|
+
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
3
|
+
#
|
|
4
|
+
# Licence is LGPL, see LICENCE in the top-level directory
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
require 'metasm/gui/dasm_hex'
|
|
8
|
+
require 'metasm/gui/dasm_listing'
|
|
9
|
+
require 'metasm/gui/dasm_opcodes'
|
|
10
|
+
require 'metasm/gui/dasm_coverage'
|
|
11
|
+
require 'metasm/gui/dasm_graph'
|
|
12
|
+
require 'metasm/gui/dasm_decomp'
|
|
13
|
+
require 'metasm/gui/dasm_funcgraph'
|
|
14
|
+
require 'metasm/gui/cstruct'
|
|
15
|
+
|
|
16
|
+
module Metasm
|
|
17
|
+
module Gui
|
|
18
|
+
# the main disassembler widget: this is a container for all the lower-level widgets that actually render the dasm state
|
|
19
|
+
class DisasmWidget < ContainerChoiceWidget
|
|
20
|
+
attr_accessor :entrypoints, :gui_update_counter_max
|
|
21
|
+
attr_accessor :keyboard_callback, :keyboard_callback_ctrl # hash key => lambda { |key| true if handled }
|
|
22
|
+
attr_accessor :clones
|
|
23
|
+
attr_accessor :pos_history, :pos_history_redo
|
|
24
|
+
attr_accessor :bg_color_callback # proc { |address| "rgb" # "00f" -> blue }
|
|
25
|
+
attr_accessor :focus_changed_callback
|
|
26
|
+
attr_accessor :parent_widget
|
|
27
|
+
|
|
28
|
+
def initialize_widget(dasm, ep=[])
|
|
29
|
+
@dasm = dasm
|
|
30
|
+
@dasm.gui = self
|
|
31
|
+
ep = [ep] if not ep.kind_of? Array
|
|
32
|
+
@entrypoints = ep
|
|
33
|
+
@pos_history = []
|
|
34
|
+
@pos_history_redo = []
|
|
35
|
+
@keyboard_callback = {}
|
|
36
|
+
@keyboard_callback_ctrl = {}
|
|
37
|
+
@clones = [self]
|
|
38
|
+
@parent_widget = nil
|
|
39
|
+
@gui_update_counter_max = 100
|
|
40
|
+
@dasm.callback_prebacktrace ||= lambda { Gui.main_iter }
|
|
41
|
+
start_disassemble_bg
|
|
42
|
+
|
|
43
|
+
addview :listing, AsmListingWidget.new(@dasm, self)
|
|
44
|
+
addview :graph, GraphViewWidget.new(@dasm, self)
|
|
45
|
+
addview :decompile, CdecompListingWidget.new(@dasm, self)
|
|
46
|
+
addview :opcodes, AsmOpcodeWidget.new(@dasm, self)
|
|
47
|
+
addview :hex, HexWidget.new(@dasm, self)
|
|
48
|
+
addview :coverage, CoverageWidget.new(@dasm, self)
|
|
49
|
+
addview :funcgraph, FuncGraphViewWidget.new(@dasm, self)
|
|
50
|
+
addview :cstruct, CStructWidget.new(@dasm, self)
|
|
51
|
+
|
|
52
|
+
view(:listing).grab_focus
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
attr_reader :dasm
|
|
56
|
+
# when updating @dasm, also update dasm for all views
|
|
57
|
+
def dasm=(d)
|
|
58
|
+
@dasm = d
|
|
59
|
+
view_indexes.each { |v|
|
|
60
|
+
w = view(v)
|
|
61
|
+
w.dasm = d if w.respond_to?(:'dasm=')
|
|
62
|
+
}
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# start an idle callback that will run one round of @dasm.disassemble_mainiter
|
|
66
|
+
def start_disassemble_bg
|
|
67
|
+
gui_update_counter = 0
|
|
68
|
+
run = false
|
|
69
|
+
Gui.idle_add {
|
|
70
|
+
# metasm disassembler loop
|
|
71
|
+
# update gui once in a while
|
|
72
|
+
if run or not @entrypoints.empty? or not @dasm.addrs_todo.empty?
|
|
73
|
+
protect { run = @dasm.disassemble_mainiter(@entrypoints) }
|
|
74
|
+
gui_update_counter += 1
|
|
75
|
+
if gui_update_counter > @gui_update_counter_max
|
|
76
|
+
gui_update_counter = 0
|
|
77
|
+
gui_update
|
|
78
|
+
end
|
|
79
|
+
true
|
|
80
|
+
else
|
|
81
|
+
gui_update
|
|
82
|
+
false
|
|
83
|
+
end
|
|
84
|
+
}
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def terminate
|
|
88
|
+
@clones.delete self
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# returns the address of the item under the cursor in current view
|
|
92
|
+
def curaddr
|
|
93
|
+
curview.current_address
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# returns the object under the cursor in current view (@dasm.decoded[curaddr])
|
|
97
|
+
def curobj
|
|
98
|
+
@dasm.decoded[curaddr]
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# returns the address of the label under the cursor or the address of the line of the cursor
|
|
102
|
+
def pointed_addr
|
|
103
|
+
hl = curview.hl_word
|
|
104
|
+
if hl =~ /^[0-9].*h$/ and a = hl.to_i(16) and @dasm.get_section_at(a)
|
|
105
|
+
return a
|
|
106
|
+
end
|
|
107
|
+
@dasm.prog_binding[hl] || curview.current_address
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# parse an address and change it to a canonical address form
|
|
111
|
+
# supported formats: label names, or string with numerical value, incl hex (0x42 and 42h)
|
|
112
|
+
# if the string is full decimal, a check against mapped space is done to find if it is
|
|
113
|
+
# hexadecimal (eg 08048000)
|
|
114
|
+
def normalize(addr)
|
|
115
|
+
case addr
|
|
116
|
+
when ::String
|
|
117
|
+
if @dasm.prog_binding[addr]
|
|
118
|
+
addr = @dasm.prog_binding[addr]
|
|
119
|
+
elsif (?0..?9).include? addr[0] or (?a..?f).include? addr.downcase[0]
|
|
120
|
+
case addr
|
|
121
|
+
when /^0x/i
|
|
122
|
+
when /h$/i; addr = '0x' + addr[0...-1]
|
|
123
|
+
when /[a-f]/i; addr = '0x' + addr
|
|
124
|
+
when /^[0-9]+$/
|
|
125
|
+
addr = '0x' + addr if not @dasm.get_section_at(addr.to_i) and
|
|
126
|
+
@dasm.get_section_at(addr.to_i(16))
|
|
127
|
+
end
|
|
128
|
+
begin
|
|
129
|
+
addr = Integer(addr)
|
|
130
|
+
rescue ::ArgumentError
|
|
131
|
+
return
|
|
132
|
+
end
|
|
133
|
+
else
|
|
134
|
+
return
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
addr
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# display the specified address
|
|
141
|
+
# the display first searches in the current view (graph, listing, etc),
|
|
142
|
+
# if it cannot display the address all other views are tried in order
|
|
143
|
+
# the current focus address is saved in @pos_history (see focus_addr_back/redo)
|
|
144
|
+
# a messagebox is popped if no view can display the address unless quiet is true
|
|
145
|
+
def focus_addr(addr, viewidx=nil, quiet=false, *a)
|
|
146
|
+
viewidx ||= curview_index || :listing
|
|
147
|
+
return if not addr
|
|
148
|
+
return if viewidx == curview_index and addr == curaddr
|
|
149
|
+
oldpos = [curview_index, (curview.get_cursor_pos if curview)]
|
|
150
|
+
views = [viewidx, oldpos[0]]
|
|
151
|
+
views += [:listing, :graph] & view_indexes
|
|
152
|
+
if views.compact.uniq.find { |i|
|
|
153
|
+
o_p = view(i).get_cursor_pos
|
|
154
|
+
if (view(i).focus_addr(addr, *a) rescue nil)
|
|
155
|
+
view(i).gui_update if i != oldpos[0]
|
|
156
|
+
showview(i)
|
|
157
|
+
true
|
|
158
|
+
else
|
|
159
|
+
view(i).set_cursor_pos o_p
|
|
160
|
+
false
|
|
161
|
+
end
|
|
162
|
+
}
|
|
163
|
+
@pos_history << oldpos if oldpos[0] # ignore start focus_addr
|
|
164
|
+
@pos_history_redo.clear
|
|
165
|
+
true
|
|
166
|
+
else
|
|
167
|
+
messagebox "Invalid address #{addr}" if not quiet
|
|
168
|
+
if oldpos[0]
|
|
169
|
+
showview oldpos[0]
|
|
170
|
+
curview.set_cursor_pos oldpos[1]
|
|
171
|
+
end
|
|
172
|
+
false
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# focus on the last address seen before the last focus_addr
|
|
177
|
+
def focus_addr_back(val = @pos_history.pop)
|
|
178
|
+
return if not val
|
|
179
|
+
@pos_history_redo << [curview_index, curview.get_cursor_pos]
|
|
180
|
+
showview val[0]
|
|
181
|
+
curview.set_cursor_pos val[1]
|
|
182
|
+
true
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# undo focus_addr_back
|
|
186
|
+
def focus_addr_redo
|
|
187
|
+
if val = @pos_history_redo.pop
|
|
188
|
+
@pos_history << [curview_index, curview.get_cursor_pos]
|
|
189
|
+
showview val[0]
|
|
190
|
+
curview.set_cursor_pos val[1]
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# ask the current view to update itself and redraw (incl all cloned widgets)
|
|
195
|
+
def gui_update
|
|
196
|
+
@clones.each { |c| c.do_gui_update }
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# ask the current view to update itself
|
|
200
|
+
def do_gui_update
|
|
201
|
+
curview.gui_update # invalidate all views ?
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# redraw the window
|
|
205
|
+
def redraw
|
|
206
|
+
curview.redraw
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# calls focus_addr(pre_yield_curaddr) after yield
|
|
210
|
+
def keep_focus_while
|
|
211
|
+
addr = curaddr
|
|
212
|
+
yield
|
|
213
|
+
focus_addr curaddr if addr
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# calls listwindow with the same argument, but also creates a new bg_color_callback
|
|
217
|
+
# that will color lines whose address is to be found in list[0] in green
|
|
218
|
+
# the callback is put only for the duration of the listwindow, and is not reentrant.
|
|
219
|
+
def list_bghilight(title, list, a={}, &b)
|
|
220
|
+
prev_colorcb = bg_color_callback
|
|
221
|
+
hash = list[1..-1].inject({}) { |h, l| h.update Expression[l[0] || :unknown].reduce => true }
|
|
222
|
+
@bg_color_callback = lambda { |addr| hash[addr] ? '0f0' : prev_colorcb ? prev_colorcb[addr] : nil }
|
|
223
|
+
redraw
|
|
224
|
+
popupend = lambda { @bg_color_callback = prev_colorcb ; redraw }
|
|
225
|
+
listwindow(title, list, a.merge(:ondestroy => popupend), &b)
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
# add/change a comment @addr
|
|
229
|
+
def add_comment(addr)
|
|
230
|
+
cmt = @dasm.comment[addr].to_a.join(' ')
|
|
231
|
+
if di = @dasm.di_at(addr)
|
|
232
|
+
cmt += di.comment.to_a.join(' ')
|
|
233
|
+
end
|
|
234
|
+
inputbox("new comment for #{Expression[addr]}", :text => cmt) { |c|
|
|
235
|
+
c = c.split("\n")
|
|
236
|
+
c = nil if c == []
|
|
237
|
+
if di = @dasm.di_at(addr)
|
|
238
|
+
di.comment = c
|
|
239
|
+
else
|
|
240
|
+
@dasm.comment[addr] = c
|
|
241
|
+
end
|
|
242
|
+
gui_update
|
|
243
|
+
}
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# disassemble from this point
|
|
247
|
+
# if points to a call, make it return
|
|
248
|
+
def disassemble(addr)
|
|
249
|
+
if di = @dasm.di_at(addr) and di.opcode.props[:saveip]
|
|
250
|
+
di.block.each_to_normal { |t|
|
|
251
|
+
t = @dasm.normalize t
|
|
252
|
+
next if not @dasm.decoded[t]
|
|
253
|
+
@dasm.function[t] ||= @dasm.function[:default] ? @dasm.function[:default].dup : DecodedFunction.new
|
|
254
|
+
}
|
|
255
|
+
di.block.add_to_subfuncret(di.next_addr)
|
|
256
|
+
@dasm.addrs_todo << [di.next_addr, addr, true]
|
|
257
|
+
elsif addr
|
|
258
|
+
@dasm.addrs_todo << [addr]
|
|
259
|
+
end
|
|
260
|
+
start_disassemble_bg
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
# disassemble fast from this point (don't dasm subfunctions, don't backtrace)
|
|
264
|
+
def disassemble_fast(addr)
|
|
265
|
+
@dasm.disassemble_fast(addr)
|
|
266
|
+
gui_update
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
# disassemble fast & deep from this point (don't backtrace, but still dasm subfuncs)
|
|
270
|
+
def disassemble_fast_deep(addr)
|
|
271
|
+
@dasm.disassemble_fast_deep(addr)
|
|
272
|
+
gui_update
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
# (re)decompile
|
|
276
|
+
def decompile(addr)
|
|
277
|
+
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
|
+
@dasm.decompiler.redecompile(addr)
|
|
279
|
+
view(:decompile).curaddr = nil
|
|
280
|
+
end
|
|
281
|
+
focus_addr(addr, :decompile)
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
# change the format of displayed data under addr (byte, word, dword, qword)
|
|
285
|
+
# currently this is done using a fake empty xref
|
|
286
|
+
def toggle_data(addr)
|
|
287
|
+
return if @dasm.decoded[addr] or not @dasm.get_section_at(addr)
|
|
288
|
+
@dasm.add_xref(addr, Xref.new(nil, nil, 1)) if not @dasm.xrefs[addr]
|
|
289
|
+
@dasm.each_xref(addr) { |x|
|
|
290
|
+
x.len = {1 => 2, 2 => 4, 4 => 8}[x.len] || 1
|
|
291
|
+
break
|
|
292
|
+
}
|
|
293
|
+
gui_update
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
def list_functions
|
|
297
|
+
list = [['name', 'addr']]
|
|
298
|
+
@dasm.function.keys.each { |f|
|
|
299
|
+
addr = @dasm.normalize(f)
|
|
300
|
+
next if not @dasm.di_at(addr)
|
|
301
|
+
list << [@dasm.get_label_at(addr), Expression[addr]]
|
|
302
|
+
}
|
|
303
|
+
title = "list of functions"
|
|
304
|
+
listwindow(title, list) { |i| focus_addr i[1] }
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
def list_labels
|
|
308
|
+
list = [['name', 'addr']]
|
|
309
|
+
@dasm.prog_binding.each { |k, v|
|
|
310
|
+
list << [k, Expression[@dasm.normalize(v)]]
|
|
311
|
+
}
|
|
312
|
+
listwindow("list of labels", list) { |i| focus_addr i[1] }
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
def list_sections
|
|
316
|
+
list = [['addr', 'length', 'name', 'info']]
|
|
317
|
+
@dasm.section_info.each { |n,a,l,i|
|
|
318
|
+
list << [Expression[a], Expression[l], n, i]
|
|
319
|
+
}
|
|
320
|
+
listwindow("list of sections", list) { |i| focus_addr i[0] if i[0] != '0' or @dasm.get_section_at(0) }
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
def list_strings
|
|
324
|
+
list = [['addr', 'string', 'length']]
|
|
325
|
+
@dasm.strings_scan { |o, str|
|
|
326
|
+
list << [Expression[o], str[0, 24].inspect, str.length]
|
|
327
|
+
}
|
|
328
|
+
listwindow("list of strings", list) { |i| focus_addr i[0] }
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
def list_xrefs(addr)
|
|
332
|
+
list = [['address', 'type', 'instr']]
|
|
333
|
+
@dasm.each_xref(addr) { |xr|
|
|
334
|
+
next if not xr.origin
|
|
335
|
+
list << [Expression[xr.origin], "#{xr.type}#{xr.len}"]
|
|
336
|
+
if di = @dasm.di_at(xr.origin)
|
|
337
|
+
list.last << di.instruction
|
|
338
|
+
end
|
|
339
|
+
}
|
|
340
|
+
if list.length == 1
|
|
341
|
+
messagebox "no xref to #{Expression[addr]}" if addr
|
|
342
|
+
else
|
|
343
|
+
listwindow("list of xrefs to #{Expression[addr]}", list) { |i| focus_addr(i[0], nil, true) }
|
|
344
|
+
end
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
# jump to address
|
|
348
|
+
def prompt_goto
|
|
349
|
+
inputbox('address to go', :text => Expression[curaddr]) { |v|
|
|
350
|
+
focus_addr_autocomplete(v)
|
|
351
|
+
}
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
def prompt_backtrace
|
|
355
|
+
addr = curaddr
|
|
356
|
+
inputbox('expression to backtrace', :text => curview.hl_word) { |e|
|
|
357
|
+
expr = IndExpression.parse_string(e)
|
|
358
|
+
bd = {}
|
|
359
|
+
registers = (@dasm.cpu.dbg_register_list.map { |r| r.to_s } rescue [])
|
|
360
|
+
expr.externals.grep(String).each { |w|
|
|
361
|
+
if registers.include? w.downcase
|
|
362
|
+
bd[w] = w.downcase.to_sym
|
|
363
|
+
end
|
|
364
|
+
}
|
|
365
|
+
expr = expr.bind(bd).reduce { |e_| e_.len ||= @dasm.cpu.size/8 if e_.kind_of? Indirection ; nil }
|
|
366
|
+
|
|
367
|
+
log = []
|
|
368
|
+
dasm.backtrace(expr, addr, :log => log)
|
|
369
|
+
list = [['address', 'type', 'old value', 'value']]
|
|
370
|
+
log.each { |t, *a|
|
|
371
|
+
list << [Expression[a[-1]], t]
|
|
372
|
+
case t
|
|
373
|
+
when :start
|
|
374
|
+
list.last << a[0]
|
|
375
|
+
when :up
|
|
376
|
+
list.pop
|
|
377
|
+
when :di
|
|
378
|
+
list.last << a[1] << a[0]
|
|
379
|
+
when :func
|
|
380
|
+
list.last << a[1] << a[0]
|
|
381
|
+
when :found
|
|
382
|
+
list.pop
|
|
383
|
+
a[0].each { |e_| list << [nil, :found, Expression[e_]] }
|
|
384
|
+
else
|
|
385
|
+
list.last << a[0] << a[1..-1].inspect
|
|
386
|
+
end
|
|
387
|
+
}
|
|
388
|
+
list_bghilight("backtrace #{expr} from #{Expression[addr]}", list) { |i|
|
|
389
|
+
a = i[0].empty? ? i[2] : i[0]
|
|
390
|
+
focus_addr(a, nil, true)
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
# same as focus_addr, also understands partial label names
|
|
396
|
+
# if the partial part is ambiguous, show a listwindow with all matches (if show_alt)
|
|
397
|
+
def focus_addr_autocomplete(v, show_alt=true)
|
|
398
|
+
if not focus_addr(v, nil, true)
|
|
399
|
+
labels = @dasm.prog_binding.map { |k, vv|
|
|
400
|
+
[k, Expression[@dasm.normalize(vv)]] if k.downcase.include? v.downcase
|
|
401
|
+
}.compact
|
|
402
|
+
case labels.length
|
|
403
|
+
when 0; focus_addr(v)
|
|
404
|
+
when 1; focus_addr(labels[0][0])
|
|
405
|
+
else
|
|
406
|
+
if labels.all? { |k, vv| vv == labels[0][1] }
|
|
407
|
+
focus_addr(labels[0][0])
|
|
408
|
+
elsif show_alt
|
|
409
|
+
labels.unshift ['name', 'addr']
|
|
410
|
+
listwindow("list of labels", labels) { |i| focus_addr i[1] }
|
|
411
|
+
end
|
|
412
|
+
end
|
|
413
|
+
end
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
# parses a C header
|
|
417
|
+
def prompt_parse_c_file
|
|
418
|
+
openfile('open C header') { |f|
|
|
419
|
+
@dasm.parse_c_file(f) rescue messagebox("#{$!}\n#{$!.backtrace}")
|
|
420
|
+
}
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
# run arbitrary ruby
|
|
424
|
+
def prompt_run_ruby
|
|
425
|
+
inputbox('ruby code to eval()') { |c| messagebox eval(c).inspect[0, 512], 'eval' }
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
# run ruby plugin
|
|
429
|
+
def prompt_run_ruby_plugin
|
|
430
|
+
openfile('ruby plugin') { |f| @dasm.load_plugin(f) }
|
|
431
|
+
end
|
|
432
|
+
|
|
433
|
+
# search for a regexp in #dasm.decoded.to_s
|
|
434
|
+
def prompt_search_decoded
|
|
435
|
+
inputbox('text to search in instrs (regex)', :text => curview.hl_word) { |pat|
|
|
436
|
+
re = /#{pat}/i
|
|
437
|
+
found = []
|
|
438
|
+
@dasm.decoded.each { |k, v|
|
|
439
|
+
found << k if v.to_s =~ re
|
|
440
|
+
}
|
|
441
|
+
list = [['addr', 'str']] + found.map { |a| [Expression[a], @dasm.decoded[a].to_s] }
|
|
442
|
+
list_bghilight("search result for /#{pat}/i", list) { |i| focus_addr i[0] }
|
|
443
|
+
}
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
# calls the @dasm.rebase method to change the load base address of the current program
|
|
447
|
+
def rebase(addr=nil)
|
|
448
|
+
if not addr
|
|
449
|
+
inputbox('rebase address') { |a| rebase(Integer(a)) }
|
|
450
|
+
else
|
|
451
|
+
na = curaddr + dasm.rebase(addr)
|
|
452
|
+
gui_update
|
|
453
|
+
focus_addr na
|
|
454
|
+
end
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
# prompts for a new name for addr
|
|
458
|
+
def rename_label(addr)
|
|
459
|
+
old = addr
|
|
460
|
+
if @dasm.prog_binding[old] or old = @dasm.get_label_at(addr)
|
|
461
|
+
inputbox("new name for #{old}", :text => old) { |v|
|
|
462
|
+
if v == ''
|
|
463
|
+
@dasm.del_label_at(addr)
|
|
464
|
+
else
|
|
465
|
+
@dasm.rename_label(old, v)
|
|
466
|
+
end
|
|
467
|
+
gui_update
|
|
468
|
+
}
|
|
469
|
+
else
|
|
470
|
+
inputbox("label name for #{Expression[addr]}", :text => Expression[addr]) { |v|
|
|
471
|
+
next if v == ''
|
|
472
|
+
@dasm.set_label_at(addr, v)
|
|
473
|
+
if di = @dasm.di_at(addr)
|
|
474
|
+
@dasm.split_block(di.block, di.address)
|
|
475
|
+
end
|
|
476
|
+
gui_update
|
|
477
|
+
}
|
|
478
|
+
end
|
|
479
|
+
end
|
|
480
|
+
|
|
481
|
+
# pause/play disassembler
|
|
482
|
+
# returns true if playing
|
|
483
|
+
# this empties @dasm.addrs_todo, the dasm may still continue to work if this msg is
|
|
484
|
+
# handled during an instr decoding/backtrace (the backtrace may generate new addrs_todo)
|
|
485
|
+
# addresses in addrs_todo pointing to existing decoded instructions are left to create a prettier graph
|
|
486
|
+
def playpause_dasm
|
|
487
|
+
@dasm_pause ||= []
|
|
488
|
+
if @dasm_pause.empty? and @dasm.addrs_todo.empty?
|
|
489
|
+
true
|
|
490
|
+
elsif @dasm_pause.empty?
|
|
491
|
+
@dasm_pause = @dasm.addrs_todo.dup
|
|
492
|
+
@dasm.addrs_todo.replace @dasm_pause.find_all { |a, *b| @dasm.decoded[@dasm.normalize(a)] }
|
|
493
|
+
@dasm_pause -= @dasm.addrs_todo
|
|
494
|
+
puts "dasm paused (#{@dasm_pause.length})"
|
|
495
|
+
else
|
|
496
|
+
@dasm.addrs_todo.concat @dasm_pause
|
|
497
|
+
@dasm_pause.clear
|
|
498
|
+
puts "dasm restarted (#{@dasm.addrs_todo.length})"
|
|
499
|
+
start_disassemble_bg
|
|
500
|
+
true
|
|
501
|
+
end
|
|
502
|
+
end
|
|
503
|
+
|
|
504
|
+
# toggles <41h> vs <'A'> display
|
|
505
|
+
def toggle_expr_char(o)
|
|
506
|
+
@dasm.toggle_expr_char(o)
|
|
507
|
+
gui_update
|
|
508
|
+
end
|
|
509
|
+
|
|
510
|
+
# toggle <401000h> vs <'sub_fancyname'> in the current instr display
|
|
511
|
+
def toggle_expr_offset(o)
|
|
512
|
+
@dasm.toggle_expr_offset(o)
|
|
513
|
+
gui_update
|
|
514
|
+
end
|
|
515
|
+
|
|
516
|
+
def toggle_view(idx)
|
|
517
|
+
default = (idx == :graph ? :listing : :graph)
|
|
518
|
+
# switch to idx ; if already in idx, use default
|
|
519
|
+
focus_addr(curaddr, ((curview_index == idx) ? default : idx))
|
|
520
|
+
end
|
|
521
|
+
|
|
522
|
+
# undefines the whole function body
|
|
523
|
+
def undefine_function(addr, incl_subfuncs = false)
|
|
524
|
+
list = []
|
|
525
|
+
@dasm.each_function_block(addr, incl_subfuncs) { |b| list << b }
|
|
526
|
+
list.each { |b| @dasm.undefine_from(b) }
|
|
527
|
+
gui_update
|
|
528
|
+
end
|
|
529
|
+
|
|
530
|
+
def keypress_ctrl(key)
|
|
531
|
+
return true if @keyboard_callback_ctrl[key] and @keyboard_callback_ctrl[key][key]
|
|
532
|
+
case key
|
|
533
|
+
when :enter; focus_addr_redo
|
|
534
|
+
when ?o; w = toplevel ; w.promptopen if w.respond_to? :promptopen
|
|
535
|
+
when ?s; w = toplevel ; w.promptsave if w.respond_to? :promptsave
|
|
536
|
+
when ?r; prompt_run_ruby
|
|
537
|
+
when ?C; disassemble_fast_deep(curaddr)
|
|
538
|
+
when ?f; prompt_search_decoded
|
|
539
|
+
else return @parent_widget ? @parent_widget.keypress_ctrl(key) : false
|
|
540
|
+
end
|
|
541
|
+
true
|
|
542
|
+
end
|
|
543
|
+
|
|
544
|
+
def keypress(key)
|
|
545
|
+
return true if @keyboard_callback[key] and @keyboard_callback[key][key]
|
|
546
|
+
case key
|
|
547
|
+
when :enter; focus_addr curview.hl_word
|
|
548
|
+
when :esc; focus_addr_back
|
|
549
|
+
when ?/; inputbox('search word') { |w|
|
|
550
|
+
next unless curview.respond_to? :hl_word
|
|
551
|
+
next if w == ''
|
|
552
|
+
curview.hl_word = w
|
|
553
|
+
curview.redraw
|
|
554
|
+
}
|
|
555
|
+
when ?b; prompt_backtrace
|
|
556
|
+
when ?c; disassemble(curview.current_address)
|
|
557
|
+
when ?C; disassemble_fast(curview.current_address)
|
|
558
|
+
when ?d; toggle_data(curview.current_address)
|
|
559
|
+
when ?f; list_functions
|
|
560
|
+
when ?g; prompt_goto
|
|
561
|
+
when ?l; list_labels
|
|
562
|
+
when ?n; rename_label(pointed_addr)
|
|
563
|
+
when ?o; toggle_expr_offset(curobj)
|
|
564
|
+
when ?p; playpause_dasm
|
|
565
|
+
when ?r; toggle_expr_char(curobj)
|
|
566
|
+
when ?v; $VERBOSE = ! $VERBOSE ; puts "#{'not ' if not $VERBOSE}verbose" # toggle verbose flag
|
|
567
|
+
when ?x; list_xrefs(pointed_addr)
|
|
568
|
+
when ?;; add_comment(curview.current_address)
|
|
569
|
+
|
|
570
|
+
when ?\ ; toggle_view(:listing)
|
|
571
|
+
when :tab; toggle_view(:decompile)
|
|
572
|
+
when ?j; curview.keypress(:down)
|
|
573
|
+
when ?k; curview.keypress(:up)
|
|
574
|
+
else
|
|
575
|
+
p key if $DEBUG
|
|
576
|
+
return @parent_widget ? @parent_widget.keypress(key) : false
|
|
577
|
+
end
|
|
578
|
+
true
|
|
579
|
+
end
|
|
580
|
+
|
|
581
|
+
# creates a new dasm window with the same disassembler object, focus it on addr#win
|
|
582
|
+
def clone_window(*focus)
|
|
583
|
+
return if not popup = DasmWindow.new
|
|
584
|
+
popup.display(@dasm, @entrypoints)
|
|
585
|
+
w = popup.dasm_widget
|
|
586
|
+
w.bg_color_callback = @bg_color_callback if bg_color_callback
|
|
587
|
+
w.keyboard_callback = @keyboard_callback
|
|
588
|
+
w.keyboard_callback_ctrl = @keyboard_callback_ctrl
|
|
589
|
+
w.clones = @clones.concat w.clones
|
|
590
|
+
w.focus_addr(*focus)
|
|
591
|
+
popup
|
|
592
|
+
end
|
|
593
|
+
|
|
594
|
+
def dragdropfile(f)
|
|
595
|
+
case f
|
|
596
|
+
when /\.(c|h|cpp)$/; @dasm.parse_c_file(f)
|
|
597
|
+
when /\.map$/; @dasm.load_map(f)
|
|
598
|
+
when /\.rb$/; @dasm.load_plugin(f)
|
|
599
|
+
else messagebox("unsupported file extension #{f}")
|
|
600
|
+
end
|
|
601
|
+
end
|
|
602
|
+
end
|
|
603
|
+
|
|
604
|
+
# this widget is loaded in an empty DasmWindow to handle shortcuts (open file, etc)
|
|
605
|
+
class NoDasmWidget < DrawableWidget
|
|
606
|
+
def initialize_widget(window)
|
|
607
|
+
@window = window
|
|
608
|
+
end
|
|
609
|
+
|
|
610
|
+
def paint
|
|
611
|
+
end
|
|
612
|
+
|
|
613
|
+
def keypress(key)
|
|
614
|
+
case key
|
|
615
|
+
when ?v; $VERBOSE = !$VERBOSE
|
|
616
|
+
when ?d; $DEBUG = !$DEBUG
|
|
617
|
+
end
|
|
618
|
+
end
|
|
619
|
+
|
|
620
|
+
def keypress_ctrl(key)
|
|
621
|
+
case key
|
|
622
|
+
when ?o; @window.promptopen
|
|
623
|
+
when ?r; @window.promptruby
|
|
624
|
+
end
|
|
625
|
+
end
|
|
626
|
+
|
|
627
|
+
def dragdropfile(f)
|
|
628
|
+
case f
|
|
629
|
+
when /\.(c|h|cpp)$/; messagebox('load a binary first')
|
|
630
|
+
else @window.loadfile(f) # TODO prompt to start debugger instead of dasm
|
|
631
|
+
end
|
|
632
|
+
end
|
|
633
|
+
end
|
|
634
|
+
|
|
635
|
+
class DasmWindow < Window
|
|
636
|
+
attr_accessor :dasm_widget, :menu
|
|
637
|
+
def initialize_window(title = 'metasm disassembler', dasm=nil, *ep)
|
|
638
|
+
self.title = title
|
|
639
|
+
@dasm_widget = nil
|
|
640
|
+
if dasm
|
|
641
|
+
ep = ep.first if ep.length == 1 and ep.first.kind_of? Array
|
|
642
|
+
display(dasm, ep)
|
|
643
|
+
else
|
|
644
|
+
self.widget = NoDasmWidget.new(self)
|
|
645
|
+
end
|
|
646
|
+
end
|
|
647
|
+
|
|
648
|
+
def widget=(w)
|
|
649
|
+
super(w || NoDasmWidget.new(self))
|
|
650
|
+
end
|
|
651
|
+
|
|
652
|
+
def destroy_window
|
|
653
|
+
@dasm_widget.terminate if @dasm_widget
|
|
654
|
+
super()
|
|
655
|
+
end
|
|
656
|
+
|
|
657
|
+
# sets up a DisasmWidget as main widget of the window, replaces the current if it exists
|
|
658
|
+
# returns the widget
|
|
659
|
+
def display(dasm, ep=[])
|
|
660
|
+
@dasm_widget.terminate if @dasm_widget
|
|
661
|
+
ep = [ep] if not ep.kind_of? Array
|
|
662
|
+
@dasm_widget = DisasmWidget.new(dasm, ep)
|
|
663
|
+
self.widget = @dasm_widget
|
|
664
|
+
@dasm_widget.focus_addr(ep.first) if ep.first
|
|
665
|
+
@dasm_widget
|
|
666
|
+
end
|
|
667
|
+
|
|
668
|
+
# returns the specified widget from the @dasm_widget (idx in :hex, :listing, :graph etc)
|
|
669
|
+
def widget(idx=nil)
|
|
670
|
+
idx && @dasm_widget ? @dasm_widget.view(idx) : @dasm_widget
|
|
671
|
+
end
|
|
672
|
+
|
|
673
|
+
def loadfile(path, cpu='Ia32', exefmt=nil)
|
|
674
|
+
if exefmt
|
|
675
|
+
exefmt = Metasm.const_get(exefmt) if exefmt.kind_of? String
|
|
676
|
+
else
|
|
677
|
+
exefmt = AutoExe.orshellcode { cpu = Metasm.const_get(cpu) if cpu.kind_of? String ; cpu.new }
|
|
678
|
+
end
|
|
679
|
+
|
|
680
|
+
exe = exefmt.decode_file(path) { |type, str|
|
|
681
|
+
# Disassembler save file will use this callback with unhandled sections / invalid binary file path
|
|
682
|
+
case type
|
|
683
|
+
when 'binarypath'
|
|
684
|
+
ret = nil
|
|
685
|
+
openfile("please locate #{str}", :blocking => true) { |f| ret = f }
|
|
686
|
+
return if not ret
|
|
687
|
+
ret
|
|
688
|
+
end
|
|
689
|
+
}
|
|
690
|
+
(@dasm_widget ? DasmWindow.new : self).display(exe.disassembler)
|
|
691
|
+
exe
|
|
692
|
+
end
|
|
693
|
+
|
|
694
|
+
def promptopen(caption='chose target binary')
|
|
695
|
+
openfile(caption) { |exename| loadfile(exename) ; yield self if block_given? }
|
|
696
|
+
end
|
|
697
|
+
|
|
698
|
+
def promptdebug(caption='chose target')
|
|
699
|
+
l = nil
|
|
700
|
+
i = inputbox(caption) { |name|
|
|
701
|
+
i = nil ; l.destroy if l and not l.destroyed?
|
|
702
|
+
if pr = OS.current.find_process(name)
|
|
703
|
+
target = pr.debugger
|
|
704
|
+
elsif name =~ /^(udp|tcp|.*\d+.*):/i # don't match c:\kikoo, but allow 127.0.0.1 / [1:2::3]
|
|
705
|
+
target = GdbRemoteDebugger.new(name)
|
|
706
|
+
elsif pr = OS.current.create_process(name)
|
|
707
|
+
target = pr.debugger
|
|
708
|
+
else
|
|
709
|
+
messagebox('no such target')
|
|
710
|
+
next
|
|
711
|
+
end
|
|
712
|
+
DbgWindow.new(target)
|
|
713
|
+
destroy if not @dasm_widget
|
|
714
|
+
yield self if block_given?
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
# build process list in bg (exe name resolution takes a few seconds)
|
|
718
|
+
list = [['pid', 'name']]
|
|
719
|
+
list_pr = OS.current.list_processes
|
|
720
|
+
Gui.idle_add {
|
|
721
|
+
if pr = list_pr.shift
|
|
722
|
+
list << [pr.pid, pr.path] if pr.path
|
|
723
|
+
true
|
|
724
|
+
elsif i
|
|
725
|
+
me = ::Process.pid.to_s
|
|
726
|
+
l = listwindow('running processes', list,
|
|
727
|
+
:noshow => true,
|
|
728
|
+
:color_callback => lambda { |le| [:grey, :palegrey] if le[0] == me }
|
|
729
|
+
) { |e| i.text = e[0] }
|
|
730
|
+
l.x += l.width
|
|
731
|
+
l.show
|
|
732
|
+
false
|
|
733
|
+
end
|
|
734
|
+
} if not list_pr.empty?
|
|
735
|
+
end
|
|
736
|
+
|
|
737
|
+
# reuse last @savefile to save dasm, prompt for file if undefined
|
|
738
|
+
def promptsave
|
|
739
|
+
return if not @dasm_widget
|
|
740
|
+
if @savefile ||= nil
|
|
741
|
+
@dasm_widget.dasm.save_file @savefile
|
|
742
|
+
return
|
|
743
|
+
end
|
|
744
|
+
openfile('chose save file') { |file|
|
|
745
|
+
@savefile = file
|
|
746
|
+
@dasm_widget.dasm.save_file(file)
|
|
747
|
+
}
|
|
748
|
+
end
|
|
749
|
+
|
|
750
|
+
# same as promptsave, but always prompt
|
|
751
|
+
def promptsaveas
|
|
752
|
+
@savefile = nil
|
|
753
|
+
promptsave
|
|
754
|
+
end
|
|
755
|
+
|
|
756
|
+
def promptruby
|
|
757
|
+
if @dasm_widget
|
|
758
|
+
@dasm_widget.prompt_run_ruby
|
|
759
|
+
else
|
|
760
|
+
inputbox('code to eval') { |c| messagebox eval(c).inspect[0, 512], 'eval' }
|
|
761
|
+
end
|
|
762
|
+
end
|
|
763
|
+
|
|
764
|
+
def build_menu
|
|
765
|
+
# TODO dynamic checkboxes (check $VERBOSE when opening the options menu to (un)set the mark)
|
|
766
|
+
filemenu = new_menu
|
|
767
|
+
|
|
768
|
+
# a fake unreferenced accel group, so that the shortcut keys appear in the menu, but the widget keypress is responsible
|
|
769
|
+
# of handling them (otherwise this would take precedence and :hex couldn't get 'c' etc)
|
|
770
|
+
# but ^o still works (must work even without DasmWidget loaded)
|
|
771
|
+
|
|
772
|
+
addsubmenu(filemenu, 'OPEN', '^o') { promptopen }
|
|
773
|
+
addsubmenu(filemenu, '_Debug') { promptdebug }
|
|
774
|
+
addsubmenu(filemenu, 'SAVE', '^s') { promptsave }
|
|
775
|
+
addsubmenu(filemenu, 'Save _as...') { promptsaveas }
|
|
776
|
+
addsubmenu(filemenu, 'CLOSE') {
|
|
777
|
+
if @dasm_widget
|
|
778
|
+
@dasm_widget.terminate
|
|
779
|
+
@dasm_widget = nil
|
|
780
|
+
self.widget = nil
|
|
781
|
+
end
|
|
782
|
+
}
|
|
783
|
+
addsubmenu(filemenu)
|
|
784
|
+
|
|
785
|
+
importmenu = new_menu
|
|
786
|
+
addsubmenu(importmenu, 'Load _map') {
|
|
787
|
+
openfile('chose map file') { |file|
|
|
788
|
+
@dasm_widget.dasm.load_map(File.read(file)) if @dasm_widget
|
|
789
|
+
} if @dasm_widget
|
|
790
|
+
}
|
|
791
|
+
addsubmenu(importmenu, 'Load _C') {
|
|
792
|
+
openfile('chose C file') { |file|
|
|
793
|
+
@dasm_widget.dasm.parse_c(File.read(file)) if @dasm_widget
|
|
794
|
+
} if @dasm_widget
|
|
795
|
+
}
|
|
796
|
+
addsubmenu(filemenu, '_Import', importmenu)
|
|
797
|
+
|
|
798
|
+
exportmenu = new_menu
|
|
799
|
+
addsubmenu(exportmenu, 'Save _map') {
|
|
800
|
+
savefile('chose map file') { |file|
|
|
801
|
+
File.open(file, 'w') { |fd|
|
|
802
|
+
fd.puts @dasm_widget.dasm.save_map
|
|
803
|
+
} if @dasm_widget
|
|
804
|
+
} if @dasm_widget
|
|
805
|
+
}
|
|
806
|
+
addsubmenu(exportmenu, 'Save _asm') {
|
|
807
|
+
savefile('chose asm file') { |file|
|
|
808
|
+
File.open(file, 'w') { |fd|
|
|
809
|
+
fd.puts @dasm_widget.dasm
|
|
810
|
+
} if @dasm_widget
|
|
811
|
+
} if @dasm_widget
|
|
812
|
+
}
|
|
813
|
+
addsubmenu(exportmenu, 'Save _C') {
|
|
814
|
+
savefile('chose C file') { |file|
|
|
815
|
+
File.open(file, 'w') { |fd|
|
|
816
|
+
fd.puts @dasm_widget.dasm.c_parser
|
|
817
|
+
} if @dasm_widget
|
|
818
|
+
} if @dasm_widget
|
|
819
|
+
}
|
|
820
|
+
addsubmenu(filemenu, '_Export', exportmenu)
|
|
821
|
+
addsubmenu(filemenu)
|
|
822
|
+
addsubmenu(filemenu, 'QUIT') { destroy } # post_quit_message ?
|
|
823
|
+
|
|
824
|
+
addsubmenu(@menu, filemenu, '_File')
|
|
825
|
+
|
|
826
|
+
actions = new_menu
|
|
827
|
+
dasm = new_menu
|
|
828
|
+
addsubmenu(dasm, '_Disassemble from here', 'c') { @dasm_widget.disassemble(@dasm_widget.curview.current_address) }
|
|
829
|
+
addsubmenu(dasm, 'Disassemble _fast from here', 'C') { @dasm_widget.disassemble_fast(@dasm_widget.curview.current_address) }
|
|
830
|
+
addsubmenu(dasm, 'Disassemble fast & dee_p from here', '^C') { @dasm_widget.disassemble_fast_deep(@dasm_widget.curview.current_address) }
|
|
831
|
+
addsubmenu(actions, dasm, '_Disassemble')
|
|
832
|
+
navigate = new_menu
|
|
833
|
+
addsubmenu(navigate, 'Follow', '<enter>') { @dasm_widget.focus_addr @dasm_widget.curview.hl_word } # XXX
|
|
834
|
+
addsubmenu(navigate, 'Jmp back', '<esc>') { @dasm_widget.focus_addr_back }
|
|
835
|
+
addsubmenu(navigate, 'Undo jmp back', '^<enter>') { @dasm_widget.focus_addr_redo }
|
|
836
|
+
addsubmenu(navigate, 'Goto', 'g') { @dasm_widget.prompt_goto }
|
|
837
|
+
addsubmenu(actions, navigate, 'Navigate')
|
|
838
|
+
addsubmenu(actions, '_Backtrace', 'b') { @dasm_widget.prompt_backtrace }
|
|
839
|
+
addsubmenu(actions, 'List functions', 'f') { @dasm_widget.list_functions }
|
|
840
|
+
addsubmenu(actions, 'List labels', 'l') { @dasm_widget.list_labels }
|
|
841
|
+
addsubmenu(actions, 'List xrefs', 'x') { @dasm_widget.list_xrefs(@dasm_widget.pointed_addr) }
|
|
842
|
+
addsubmenu(actions, 'Rebase') { @dasm_widget.rebase }
|
|
843
|
+
addsubmenu(actions, 'Rename label', 'n') { @dasm_widget.rename_label(@dasm_widget.pointed_addr) }
|
|
844
|
+
addsubmenu(actions, 'Decompile', '<tab>') { @dasm_widget.decompile(@dasm_widget.curview.current_address) }
|
|
845
|
+
addsubmenu(actions, 'Decompile finali_ze') { @dasm_widget.dasm.decompiler.finalize ; @dasm_widget.gui_update }
|
|
846
|
+
addsubmenu(actions, 'Comment', ';') { @dasm_widget.decompile(@dasm_widget.curview.current_address) }
|
|
847
|
+
addsubmenu(actions, '_Undefine') { @dasm_widget.dasm.undefine_from(@dasm_widget.curview.current_address) ; @dasm_widget.gui_update }
|
|
848
|
+
addsubmenu(actions, 'Unde_fine function') { @dasm_widget.undefine_function(@dasm_widget.curview.current_address) }
|
|
849
|
+
addsubmenu(actions, 'Undefine function & _subfuncs') { @dasm_widget.undefine_function(@dasm_widget.curview.current_address, true) }
|
|
850
|
+
addsubmenu(actions, 'Data', 'd') { @dasm_widget.toggle_data(@dasm_widget.curview.current_address) }
|
|
851
|
+
addsubmenu(actions, 'Pause dasm', 'p', :check) { |ck| !@dasm_widget.playpause_dasm }
|
|
852
|
+
addsubmenu(actions, 'Run ruby snippet', '^r') { promptruby }
|
|
853
|
+
addsubmenu(actions, 'Run _ruby plugin') { @dasm_widget.prompt_run_ruby_plugin }
|
|
854
|
+
|
|
855
|
+
addsubmenu(@menu, actions, '_Actions')
|
|
856
|
+
|
|
857
|
+
options = new_menu
|
|
858
|
+
addsubmenu(options, '_Verbose', :check, $VERBOSE, 'v') { |ck| $VERBOSE = ck }
|
|
859
|
+
addsubmenu(options, 'Debu_g', :check, $DEBUG) { |ck| $DEBUG = ck }
|
|
860
|
+
addsubmenu(options, 'Debug _backtrace', :check) { |ck| @dasm_widget.dasm.debug_backtrace = ck if @dasm_widget }
|
|
861
|
+
addsubmenu(options, 'Backtrace li_mit') {
|
|
862
|
+
inputbox('max blocks to backtrace', :text => @dasm_widget.dasm.backtrace_maxblocks) { |target|
|
|
863
|
+
@dasm_widget.dasm.backtrace_maxblocks = Integer(target) if not target.empty?
|
|
864
|
+
} if @dasm_widget
|
|
865
|
+
}
|
|
866
|
+
addsubmenu(options, 'Backtrace _limit (data)') {
|
|
867
|
+
inputbox('max blocks to backtrace data (-1 to never start)',
|
|
868
|
+
:text => @dasm_widget.dasm.backtrace_maxblocks_data) { |target|
|
|
869
|
+
@dasm_widget.dasm.backtrace_maxblocks_data = Integer(target) if not target.empty?
|
|
870
|
+
} if @dasm_widget
|
|
871
|
+
}
|
|
872
|
+
addsubmenu(options)
|
|
873
|
+
addsubmenu(options, 'Forbid decompile _types', :check) { |ck| @dasm_widget.dasm.decompiler.forbid_decompile_types = ck }
|
|
874
|
+
addsubmenu(options, 'Forbid decompile _if/while', :check) { |ck| @dasm_widget.dasm.decompiler.forbid_decompile_ifwhile = ck }
|
|
875
|
+
addsubmenu(options, 'Forbid decomp _optimize', :check) { |ck| @dasm_widget.dasm.decompiler.forbid_optimize_code = ck }
|
|
876
|
+
addsubmenu(options, 'Forbid decomp optim_data', :check) { |ck| @dasm_widget.dasm.decompiler.forbid_optimize_dataflow = ck }
|
|
877
|
+
addsubmenu(options, 'Forbid decomp optimlab_els', :check) { |ck| @dasm_widget.dasm.decompiler.forbid_optimize_labels = ck }
|
|
878
|
+
addsubmenu(options, 'Decompiler _recurse', :check, true) { |ck| @dasm_widget.dasm.decompiler.recurse = (ck ? 1/0.0 : 1) ; ck } # XXX race if changed while decompiling
|
|
879
|
+
# TODO CPU type, size, endian...
|
|
880
|
+
# factorize headers
|
|
881
|
+
|
|
882
|
+
addsubmenu(@menu, options, '_Options')
|
|
883
|
+
|
|
884
|
+
views = new_menu
|
|
885
|
+
addsubmenu(views, 'Dis_assembly') { @dasm_widget.focus_addr(@dasm_widget.curaddr, :listing) }
|
|
886
|
+
addsubmenu(views, '_Graph') { @dasm_widget.focus_addr(@dasm_widget.curaddr, :graph) }
|
|
887
|
+
addsubmenu(views, 'De_compiled') { @dasm_widget.focus_addr(@dasm_widget.curaddr, :decompile) }
|
|
888
|
+
addsubmenu(views, 'Raw _opcodes') { @dasm_widget.focus_addr(@dasm_widget.curaddr, :opcodes) }
|
|
889
|
+
addsubmenu(views, '_Hex') { @dasm_widget.focus_addr(@dasm_widget.curaddr, :hex) }
|
|
890
|
+
addsubmenu(views, 'C S_truct') { @dasm_widget.focus_addr(@dasm_widget.curaddr, :cstruct) }
|
|
891
|
+
addsubmenu(views, 'Co_verage') { @dasm_widget.focus_addr(@dasm_widget.curaddr, :coverage) }
|
|
892
|
+
addsubmenu(views, '_Sections') { @dasm_widget.list_sections }
|
|
893
|
+
addsubmenu(views, 'St_rings') { @dasm_widget.list_strings }
|
|
894
|
+
|
|
895
|
+
funcgraph = new_menu
|
|
896
|
+
addsubmenu(funcgraph, 'Fu_ll') { @dasm_widget.focus_addr(@dasm_widget.curaddr, :funcgraph, false, :full) }
|
|
897
|
+
addsubmenu(funcgraph, '_From there') { @dasm_widget.focus_addr(@dasm_widget.curaddr, :funcgraph, false, :from) }
|
|
898
|
+
addsubmenu(funcgraph, '_To there') { @dasm_widget.focus_addr(@dasm_widget.curaddr, :funcgraph, false, :to) }
|
|
899
|
+
addsubmenu(funcgraph, '_Butterfly') { @dasm_widget.focus_addr(@dasm_widget.curaddr, :funcgraph, false, :both) }
|
|
900
|
+
addsubmenu(views, '_Func graph', funcgraph)
|
|
901
|
+
|
|
902
|
+
addsubmenu(@menu, views, '_Views')
|
|
903
|
+
end
|
|
904
|
+
end
|
|
905
|
+
end
|
|
906
|
+
end
|