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