metasm 1.0.3 → 1.0.4
Sign up to get free protection for your applications and to get access to all the features.
- 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/disassemble.rb
CHANGED
@@ -64,7 +64,13 @@ class DecodedInstruction
|
|
64
64
|
ret = []
|
65
65
|
ret << Expression[address] << ' ' if address
|
66
66
|
ret << @instruction
|
67
|
-
|
67
|
+
if comment
|
68
|
+
ret << ' ; '
|
69
|
+
@comment.each { |c|
|
70
|
+
ret << c << ' '
|
71
|
+
}
|
72
|
+
ret.pop
|
73
|
+
end
|
68
74
|
ret
|
69
75
|
end
|
70
76
|
|
@@ -102,11 +108,11 @@ class BacktraceTrace
|
|
102
108
|
attr_accessor :detached
|
103
109
|
# maxdepth at the point of the object creation
|
104
110
|
attr_accessor :maxdepth
|
111
|
+
# disassembler cpu_context
|
112
|
+
attr_accessor :cpu_context
|
105
113
|
|
106
|
-
def initialize(expr, origin, orig_expr, type, len=nil, maxdepth=nil)
|
107
|
-
@expr, @origin, @orig_expr, @type = expr, origin, orig_expr, type
|
108
|
-
@len = len if len
|
109
|
-
@maxdepth = maxdepth if maxdepth
|
114
|
+
def initialize(expr, origin, orig_expr, type, len=nil, maxdepth=nil, cpu_context=nil)
|
115
|
+
@expr, @origin, @orig_expr, @type, @len, @maxdepth, @cpu_context = expr, origin, orig_expr, type, len, maxdepth, cpu_context
|
110
116
|
end
|
111
117
|
|
112
118
|
def hash ; [origin, expr].hash ; end
|
@@ -246,10 +252,10 @@ class DecodedFunction
|
|
246
252
|
# if btbind_callback is defined, calls it with args [dasm, binding, funcaddr, calladdr, expr, origin, maxdepth]
|
247
253
|
# else update lazily the binding from expr.externals, and return backtrace_binding
|
248
254
|
def get_backtrace_binding(dasm, funcaddr, calladdr, expr, origin, maxdepth)
|
249
|
-
if
|
250
|
-
@btbind_callback[dasm, @backtrace_binding, funcaddr, calladdr, expr, origin, maxdepth]
|
251
|
-
elsif backtrace_binding and dest = @backtrace_binding[:thunk] and target = dasm.function[dest]
|
255
|
+
if backtrace_binding and dest = @backtrace_binding[:thunk] and target = dasm.function[dest]
|
252
256
|
target.get_backtrace_binding(dasm, funcaddr, calladdr, expr, origin, maxdepth)
|
257
|
+
elsif btbind_callback
|
258
|
+
@btbind_callback[dasm, @backtrace_binding, funcaddr, calladdr, expr, origin, maxdepth]
|
253
259
|
else
|
254
260
|
unk_regs = expr.externals.grep(Symbol).uniq - @backtrace_binding.keys - [:unknown]
|
255
261
|
dasm.cpu.backtrace_update_function_binding(dasm, funcaddr, self, return_address, *unk_regs) if not unk_regs.empty?
|
@@ -260,10 +266,10 @@ class DecodedFunction
|
|
260
266
|
# if btfor_callback is defined, calls it with args [dasm, bt_for, funcaddr, calladdr]
|
261
267
|
# else return backtracked_for
|
262
268
|
def get_backtracked_for(dasm, funcaddr, calladdr)
|
263
|
-
if
|
264
|
-
@btfor_callback[dasm, @backtracked_for, funcaddr, calladdr]
|
265
|
-
elsif backtrace_binding and dest = @backtrace_binding[:thunk] and target = dasm.function[dest]
|
269
|
+
if backtrace_binding and dest = @backtrace_binding[:thunk] and target = dasm.function[dest]
|
266
270
|
target.get_backtracked_for(dasm, funcaddr, calladdr)
|
271
|
+
elsif btfor_callback
|
272
|
+
@btfor_callback[dasm, @backtracked_for, funcaddr, calladdr]
|
267
273
|
else
|
268
274
|
@backtracked_for
|
269
275
|
end
|
@@ -286,6 +292,17 @@ class DecodedFunction
|
|
286
292
|
end
|
287
293
|
|
288
294
|
class CPU
|
295
|
+
# decode an instruction with a dasm context
|
296
|
+
# context is a hash, should be modified inplace by the CPU
|
297
|
+
# will be passed to the next instruction(s) in the code flow
|
298
|
+
def decode_instruction_context(dasm, edata, di_addr, context)
|
299
|
+
decode_instruction(edata, di_addr)
|
300
|
+
end
|
301
|
+
|
302
|
+
# return the initial context for the disassembler, starts disassembling from addr
|
303
|
+
def disassemble_init_context(dasm, addr)
|
304
|
+
end
|
305
|
+
|
289
306
|
# return the thing to backtrace to find +value+ before the execution of this instruction
|
290
307
|
# eg backtrace_emu('inc eax', Expression[:eax]) => Expression[:eax + 1]
|
291
308
|
# (the value of :eax after 'inc eax' is the value of :eax before plus 1)
|
@@ -294,8 +311,10 @@ class CPU
|
|
294
311
|
Expression[Expression[value].bind(di.backtrace_binding ||= get_backtrace_binding(di)).reduce]
|
295
312
|
end
|
296
313
|
|
297
|
-
#
|
314
|
+
# return the list of jump targets for insturctions modifying the control flow
|
298
315
|
def get_xrefs_x(dasm, di)
|
316
|
+
return [] if not di.opcode.props[:setip]
|
317
|
+
[symbolic(di.instruction.args.last, di)]
|
299
318
|
end
|
300
319
|
|
301
320
|
# returns a list of [type, address, len]
|
@@ -338,7 +357,7 @@ class CPU
|
|
338
357
|
def replace_instr_arg_immediate(i, old, new)
|
339
358
|
i.args.map! { |a|
|
340
359
|
case a
|
341
|
-
when Expression; Expression[a.bind(old => new).reduce]
|
360
|
+
when Expression; a == old ? new : Expression[a.bind(old => new).reduce]
|
342
361
|
else a
|
343
362
|
end
|
344
363
|
}
|
@@ -396,6 +415,8 @@ class Disassembler
|
|
396
415
|
attr_accessor :disassemble_maxblocklength
|
397
416
|
# a cparser that parsed some C header files, prototypes are converted to DecodedFunction when jumped to
|
398
417
|
attr_accessor :c_parser
|
418
|
+
# if false, disassembler skips internal functions with a prototype defined in a C header (eg static libraries)
|
419
|
+
attr_accessor :disassemble_known_functions
|
399
420
|
# hash address => array of strings
|
400
421
|
# default dasm dump will only show comments at beginning of code blocks
|
401
422
|
attr_accessor :comment
|
@@ -418,6 +439,8 @@ class Disassembler
|
|
418
439
|
attr_accessor :callback_finished
|
419
440
|
# pointer to the gui widget we're displayed in
|
420
441
|
attr_accessor :gui
|
442
|
+
# arbitrary data stored by other objects
|
443
|
+
attr_accessor :misc
|
421
444
|
|
422
445
|
@@backtrace_maxblocks = 50
|
423
446
|
|
@@ -452,7 +475,7 @@ class Disassembler
|
|
452
475
|
# adds a section, updates prog_binding
|
453
476
|
# base addr is an Integer or a String (label name for offset 0)
|
454
477
|
def add_section(encoded, base)
|
455
|
-
encoded, base = base, encoded if base.kind_of?
|
478
|
+
encoded, base = base, encoded if base.kind_of?(EncodedData)
|
456
479
|
case base
|
457
480
|
when ::Integer
|
458
481
|
when ::String
|
@@ -491,7 +514,7 @@ class Disassembler
|
|
491
514
|
end
|
492
515
|
end
|
493
516
|
|
494
|
-
# yields each xref to a given address,
|
517
|
+
# yields each xref to a given address, optionaly restricted to a type
|
495
518
|
def each_xref(addr, type=nil)
|
496
519
|
addr = normalize addr
|
497
520
|
|
@@ -545,7 +568,7 @@ class Disassembler
|
|
545
568
|
# returns the canonical form of addr (absolute address integer or label of start of section + section offset)
|
546
569
|
def normalize(addr)
|
547
570
|
return addr if not addr or addr == :default
|
548
|
-
addr = Expression[addr].bind(@old_prog_binding).reduce if not addr.kind_of?
|
571
|
+
addr = Expression[addr].bind(@old_prog_binding).reduce if not addr.kind_of?(Integer)
|
549
572
|
addr
|
550
573
|
end
|
551
574
|
|
@@ -554,18 +577,18 @@ class Disassembler
|
|
554
577
|
def get_section_at(addr, memcheck=true)
|
555
578
|
case addr = normalize(addr)
|
556
579
|
when ::Integer
|
557
|
-
if s = @sections.find { |b, e| b.kind_of?
|
558
|
-
@sections.find { |b, e| b.kind_of?
|
580
|
+
if s = @sections.find { |b, e| b.kind_of?(::Integer) and addr >= b and addr < b + e.length } ||
|
581
|
+
@sections.find { |b, e| b.kind_of?(::Integer) and addr == b + e.length } # end label
|
559
582
|
s[1].ptr = addr - s[0]
|
560
583
|
return if memcheck and s[1].data.respond_to?(:page_invalid?) and s[1].data.page_invalid?(s[1].ptr)
|
561
584
|
[s[1], s[0]]
|
562
585
|
end
|
563
586
|
when Expression
|
564
|
-
if addr.op == :+ and addr.rexpr.kind_of?
|
587
|
+
if addr.op == :+ and addr.rexpr.kind_of?(::Integer) and addr.rexpr >= 0 and addr.lexpr.kind_of?(::String) and e = @sections[addr.lexpr]
|
565
588
|
e.ptr = addr.rexpr
|
566
589
|
return if memcheck and e.data.respond_to?(:page_invalid?) and e.data.page_invalid?(e.ptr)
|
567
590
|
[e, Expression[addr.lexpr]]
|
568
|
-
elsif addr.op == :+ and addr.rexpr.kind_of?
|
591
|
+
elsif addr.op == :+ and addr.rexpr.kind_of?(::String) and not addr.lexpr and e = @sections[addr.rexpr]
|
569
592
|
e.ptr = 0
|
570
593
|
return if memcheck and e.data.respond_to?(:page_invalid?) and e.data.page_invalid?(e.ptr)
|
571
594
|
[e, addr.rexpr]
|
@@ -582,12 +605,14 @@ class Disassembler
|
|
582
605
|
return if addrstr !~ /^\w+$/
|
583
606
|
e, b = get_section_at(addr)
|
584
607
|
if not e
|
585
|
-
l = Expression[addr].reduce_rec if Expression[addr].reduce_rec.kind_of?
|
586
|
-
l ||= addrstr if addr.kind_of?
|
608
|
+
l = Expression[addr].reduce_rec if Expression[addr].reduce_rec.kind_of?(::String)
|
609
|
+
l ||= addrstr if addr.kind_of?(Expression) and addr.externals.grep(::Symbol).empty?
|
587
610
|
elsif not l = e.inv_export[e.ptr]
|
588
611
|
l = @program.new_label(addrstr)
|
589
612
|
e.add_export l, e.ptr
|
590
|
-
@label_alias_cache
|
613
|
+
if @label_alias_cache ||= nil
|
614
|
+
(@label_alias_cache[b + e.ptr] ||= []) << l
|
615
|
+
end
|
591
616
|
@old_prog_binding[l] = @prog_binding[l] = b + e.ptr
|
592
617
|
elsif rewritepfx.find { |p| base != p and addrstr.sub(base, p) == l }
|
593
618
|
newl = addrstr
|
@@ -629,19 +654,24 @@ class Disassembler
|
|
629
654
|
return false
|
630
655
|
elsif @addrs_todo.empty?
|
631
656
|
ep = entrypoints.shift
|
632
|
-
|
657
|
+
cpu_context = get_initial_cpu_context(ep)
|
658
|
+
l = auto_label_at(normalize(ep), 'entrypoint') || normalize(ep)
|
633
659
|
puts "start disassemble from #{l} (#{entrypoints.length})" if $VERBOSE and not entrypoints.empty?
|
634
660
|
@entrypoints << l
|
635
|
-
@addrs_todo <<
|
661
|
+
@addrs_todo << { :addr => ep, :cpu_context => cpu_context }
|
636
662
|
else
|
637
663
|
disassemble_step
|
638
664
|
end
|
639
665
|
true
|
640
666
|
end
|
641
667
|
|
668
|
+
def get_initial_cpu_context(addr)
|
669
|
+
@cpu.disassemble_init_context(self, addr)
|
670
|
+
end
|
671
|
+
|
642
672
|
def post_disassemble
|
643
673
|
@decoded.each_value { |di|
|
644
|
-
next if not di.kind_of?
|
674
|
+
next if not di.kind_of?(DecodedInstruction)
|
645
675
|
next if not di.opcode or not di.opcode.props[:saveip]
|
646
676
|
if not di.block.to_subfuncret
|
647
677
|
di.add_comment 'noreturn'
|
@@ -671,19 +701,20 @@ puts " finalize subfunc #{Expression[addr]}" if debug_backtrace
|
|
671
701
|
# adds next addresses to handle to addrs_todo
|
672
702
|
# if @function[:default] exists, jumps to unknows locations are interpreted as to @function[:default]
|
673
703
|
def disassemble_step
|
674
|
-
return if not
|
675
|
-
@addrs_done <<
|
704
|
+
return if not x = @addrs_todo.pop or @addrs_done.include?(x)
|
705
|
+
@addrs_done << x if x[:from]
|
676
706
|
|
677
|
-
|
678
|
-
|
707
|
+
addr = x[:addr]
|
708
|
+
from = x[:from]
|
709
|
+
# from_subfuncret is true if from is the address of a function call that returns to addr
|
679
710
|
|
680
711
|
return if from == Expression::Unknown
|
681
712
|
|
682
|
-
puts "disassemble_step #{Expression[addr]} #{Expression[from] if from} #{from_subfuncret} (/#{@addrs_todo.length})" if $DEBUG
|
713
|
+
puts "disassemble_step #{Expression[addr]} #{Expression[from] if from} #{x[:from_subfuncret]} (/#{@addrs_todo.length})" if $DEBUG
|
683
714
|
|
684
715
|
addr = normalize(addr)
|
685
716
|
|
686
|
-
if from and from_subfuncret and di_at(from)
|
717
|
+
if from and x[:from_subfuncret] and di_at(from)
|
687
718
|
@decoded[from].block.each_to_normal { |subfunc|
|
688
719
|
subfunc = normalize(subfunc)
|
689
720
|
next if not f = @function[subfunc] or f.finalized
|
@@ -697,27 +728,36 @@ puts " finalize subfunc #{Expression[subfunc]}" if debug_backtrace
|
|
697
728
|
end
|
698
729
|
|
699
730
|
if di = @decoded[addr]
|
700
|
-
if di.kind_of?
|
731
|
+
if di.kind_of?(DecodedInstruction)
|
701
732
|
split_block(di.block, di.address, true) if not di.block_head? # this updates di.block
|
702
|
-
di.block.add_from(from, from_subfuncret ? :subfuncret : :normal) if from and from != :default
|
733
|
+
di.block.add_from(from, x[:from_subfuncret] ? :subfuncret : :normal) if from and from != :default
|
703
734
|
bf = di.block
|
704
735
|
elsif di == true
|
705
736
|
bf = @function[addr]
|
706
737
|
end
|
707
|
-
elsif bf = @function[addr]
|
738
|
+
elsif from and bf = @function[addr]
|
708
739
|
detect_function_thunk_noreturn(from) if bf.noreturn
|
709
740
|
elsif s = get_section_at(addr)
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
741
|
+
if from and c_parser and not disassemble_known_functions and name = get_all_labels_at(addr).find { |n|
|
742
|
+
cs = c_parser.toplevel.symbol[n] and cs.type.untypedef.kind_of?(C::Function) }
|
743
|
+
# do not disassemble internal function for which we have a prototype (eg static library)
|
744
|
+
puts "found known function #{name} at #{Expression[addr]}" if $VERBOSE
|
745
|
+
bf = @function[addr] = @cpu.decode_c_function_prototype(@c_parser, c_parser.toplevel.symbol[name])
|
746
|
+
detect_function_thunk_noreturn(from) if bf.noreturn
|
747
|
+
else
|
748
|
+
block = InstructionBlock.new(normalize(addr), s[0])
|
749
|
+
block.add_from(from, x[:from_subfuncret] ? :subfuncret : :normal) if from and from != :default
|
750
|
+
disassemble_block(block, x[:cpu_context])
|
751
|
+
end
|
752
|
+
elsif from and c_parser and name = Expression[addr].reduce_rec and name.kind_of?(::String) and
|
753
|
+
cs = c_parser.toplevel.symbol[name] and cs.type.untypedef.kind_of?(C::Function)
|
754
|
+
# use C header prototype for external functions if available
|
755
|
+
bf = @function[addr] = @cpu.decode_c_function_prototype(@c_parser, cs)
|
716
756
|
detect_function_thunk_noreturn(from) if bf.noreturn
|
717
|
-
elsif from
|
757
|
+
elsif from and not @function[addr]
|
718
758
|
if bf = @function[:default]
|
719
759
|
puts "using default function for #{Expression[addr]} from #{Expression[from]}" if $DEBUG
|
720
|
-
if name = Expression[addr].reduce_rec and name.kind_of?
|
760
|
+
if name = Expression[addr].reduce_rec and name.kind_of?(::String)
|
721
761
|
@function[addr] = @function[:default].dup
|
722
762
|
else
|
723
763
|
addr = :default
|
@@ -737,7 +777,7 @@ puts " finalize subfunc #{Expression[subfunc]}" if debug_backtrace
|
|
737
777
|
end
|
738
778
|
|
739
779
|
if bf and from and from != :default
|
740
|
-
if bf.kind_of?
|
780
|
+
if bf.kind_of?(DecodedFunction)
|
741
781
|
bff = bf.get_backtracked_for(self, addr, from)
|
742
782
|
else
|
743
783
|
bff = bf.backtracked_for
|
@@ -745,21 +785,21 @@ puts " finalize subfunc #{Expression[subfunc]}" if debug_backtrace
|
|
745
785
|
end
|
746
786
|
bff.each { |btt|
|
747
787
|
next if btt.address
|
748
|
-
if @decoded[from].kind_of?
|
749
|
-
backtrace_check_found(btt.expr, @decoded[addr], btt.origin, btt.type, btt.len, btt.maxdepth, btt.detached)
|
788
|
+
if @decoded[from].kind_of?(DecodedInstruction) and @decoded[from].opcode.props[:saveip] and not x[:from_subfuncret] and not @function[addr]
|
789
|
+
backtrace_check_found(btt.expr, @decoded[addr], btt.origin, btt.type, btt.len, btt.maxdepth, btt.detached, btt.cpu_context)
|
750
790
|
end
|
751
791
|
next if backtrace_check_funcret(btt, addr, from)
|
752
792
|
backtrace(btt.expr, from,
|
753
|
-
:include_start => true, :from_subfuncret => from_subfuncret,
|
793
|
+
:include_start => true, :from_subfuncret => x[:from_subfuncret],
|
754
794
|
:origin => btt.origin, :orig_expr => btt.orig_expr, :type => btt.type,
|
755
|
-
:len => btt.len, :detached => btt.detached, :maxdepth => btt.maxdepth)
|
795
|
+
:len => btt.len, :detached => btt.detached, :maxdepth => btt.maxdepth, :cpu_context => btt.cpu_context)
|
756
796
|
} if bff
|
757
797
|
end
|
758
798
|
|
759
799
|
# splits an InstructionBlock, updates the blocks backtracked_for
|
760
800
|
def split_block(block, address=nil, rebacktrace=false)
|
761
801
|
if not address # invoked as split_block(0x401012)
|
762
|
-
return if not @decoded[block].kind_of?
|
802
|
+
return if not @decoded[block].kind_of?(DecodedInstruction)
|
763
803
|
block, address = @decoded[block].block, block
|
764
804
|
end
|
765
805
|
return block if address == block.address
|
@@ -770,14 +810,14 @@ puts " finalize subfunc #{Expression[subfunc]}" if debug_backtrace
|
|
770
810
|
:only_upto => block.list.last.address,
|
771
811
|
:include_start => !btt.exclude_instr, :from_subfuncret => btt.from_subfuncret,
|
772
812
|
:origin => btt.origin, :orig_expr => btt.orig_expr, :type => btt.type, :len => btt.len,
|
773
|
-
:detached => btt.detached, :maxdepth => btt.maxdepth)
|
813
|
+
:detached => btt.detached, :maxdepth => btt.maxdepth, :cpu_context => btt.cpu_context)
|
774
814
|
}
|
775
815
|
end
|
776
816
|
new_b
|
777
817
|
end
|
778
818
|
|
779
819
|
# disassembles a new instruction block at block.address (must be normalized)
|
780
|
-
def disassemble_block(block)
|
820
|
+
def disassemble_block(block, cpu_context)
|
781
821
|
raise if not block.list.empty?
|
782
822
|
di_addr = block.address
|
783
823
|
delay_slot = nil
|
@@ -805,7 +845,8 @@ puts " finalize subfunc #{Expression[subfunc]}" if debug_backtrace
|
|
805
845
|
|
806
846
|
# decode instruction
|
807
847
|
block.edata.ptr = di_addr - block.address + block.edata_ptr
|
808
|
-
|
848
|
+
cpu_context = cpu_context.dup if cpu_context
|
849
|
+
if not di = @cpu.decode_instruction_context(self, block.edata, di_addr, cpu_context)
|
809
850
|
ed = block.edata
|
810
851
|
break if ed.ptr >= ed.length and get_section_at(di_addr) and di = block.list.last
|
811
852
|
puts "#{ed.ptr >= ed.length ? "end of section reached" : "unknown instruction #{ed.data[di_addr-block.address+block.edata_ptr, 4].to_s.unpack('H*').first}"} at #{Expression[di_addr]}" if $VERBOSE
|
@@ -845,18 +886,23 @@ puts " finalize subfunc #{Expression[subfunc]}" if debug_backtrace
|
|
845
886
|
if delay_slot
|
846
887
|
di, delay = delay_slot
|
847
888
|
if delay == 0 or not di_addr
|
848
|
-
backtrace_xrefs_di_x(di)
|
889
|
+
backtrace_xrefs_di_x(di, cpu_context)
|
849
890
|
if di.opcode.props[:stopexec] or not di_addr; return
|
850
891
|
else break
|
851
892
|
end
|
852
893
|
end
|
853
894
|
delay_slot[1] = delay - 1
|
854
895
|
end
|
896
|
+
|
897
|
+
if block.edata.inv_export[di_addr - block.address + block.edata_ptr]
|
898
|
+
# ensure there is a block split if we have a label defined
|
899
|
+
break
|
900
|
+
end
|
855
901
|
}
|
856
902
|
|
857
903
|
ar = [di_addr]
|
858
904
|
ar = @callback_newaddr[block.list.last.address, ar] || ar if callback_newaddr
|
859
|
-
ar.each { |di_addr_| backtrace(di_addr_, di.address, :origin => di.address, :type => :x) }
|
905
|
+
ar.each { |di_addr_| backtrace(di_addr_, di.address, :origin => di.address, :type => :x, :cpu_context => cpu_context) }
|
860
906
|
|
861
907
|
block
|
862
908
|
end
|
@@ -878,53 +924,60 @@ puts " finalize subfunc #{Expression[subfunc]}" if debug_backtrace
|
|
878
924
|
@entrypoints ||= []
|
879
925
|
@entrypoints |= entrypoints
|
880
926
|
|
881
|
-
entrypoints.each { |ep| do_disassemble_fast_deep(normalize(ep)) }
|
927
|
+
entrypoints.each { |ep| do_disassemble_fast_deep(:addr => normalize(ep)) }
|
882
928
|
|
883
929
|
@callback_finished[] if callback_finished
|
884
930
|
end
|
885
931
|
|
886
932
|
def do_disassemble_fast_deep(ep)
|
887
933
|
disassemble_fast(ep) { |fa, di|
|
888
|
-
|
889
|
-
do_disassemble_fast_deep(fa)
|
890
|
-
if di and ndi = di_at(fa)
|
891
|
-
ndi.block.add_from_normal(di.address)
|
892
|
-
end
|
934
|
+
do_disassemble_fast_deep(:addr => normalize(fa), :from => di.address)
|
893
935
|
}
|
894
936
|
end
|
895
937
|
|
896
938
|
# disassembles fast from a list of entrypoints
|
897
939
|
# see disassemble_fast_step
|
898
940
|
def disassemble_fast(entrypoint, maxdepth=-1, &b)
|
899
|
-
|
900
|
-
|
901
|
-
|
941
|
+
td = entrypoint
|
942
|
+
td = { :addr => entrypoint } unless td.kind_of?(::Hash)
|
943
|
+
td[:cpu_context] ||= get_initial_cpu_context(td[:addr])
|
944
|
+
todo = [td]
|
945
|
+
until todo.empty?
|
946
|
+
disassemble_fast_step(todo, &b)
|
902
947
|
maxdepth -= 1
|
903
|
-
|
948
|
+
todo.delete_if { |a| not @decoded[normalize(a[:addr])] } if maxdepth == 0
|
904
949
|
end
|
905
|
-
check_noreturn_function(
|
950
|
+
check_noreturn_function(td[:addr])
|
906
951
|
end
|
907
952
|
|
908
953
|
# disassembles one block from the ary, see disassemble_fast_block
|
909
954
|
def disassemble_fast_step(todo, &b)
|
910
955
|
return if not x = todo.pop
|
911
|
-
addr, from, from_subfuncret = x
|
912
956
|
|
913
|
-
addr = normalize(addr)
|
957
|
+
addr = normalize(x[:addr])
|
914
958
|
|
915
959
|
if di = @decoded[addr]
|
916
|
-
if di.kind_of?
|
960
|
+
if di.kind_of?(DecodedInstruction)
|
917
961
|
split_block(di.block, di.address) if not di.block_head?
|
918
|
-
di.block.add_from(from, from_subfuncret ? :subfuncret : :normal) if from and from != :default
|
962
|
+
di.block.add_from(x[:from], x[:from_subfuncret] ? :subfuncret : :normal) if x[:from] and x[:from] != :default
|
919
963
|
end
|
964
|
+
elsif @function[addr] and x[:from]
|
920
965
|
elsif s = get_section_at(addr)
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
@function[addr]
|
927
|
-
|
966
|
+
if x[:from] and c_parser and not disassemble_known_functions and name = get_all_labels_at(addr).find { |n|
|
967
|
+
cs = c_parser.toplevel.symbol[n] and cs.type.untypedef.kind_of?(C::Function) }
|
968
|
+
# do not disassemble internal function for which we have a prototype (eg static library)
|
969
|
+
puts "found known function #{name} at #{Expression[addr]}" if $VERBOSE
|
970
|
+
@function[addr] = @cpu.decode_c_function_prototype(@c_parser, c_parser.toplevel.symbol[name])
|
971
|
+
detect_function_thunk_noreturn(x[:from]) if @function[addr].noreturn
|
972
|
+
else
|
973
|
+
block = InstructionBlock.new(addr, s[0])
|
974
|
+
block.add_from(x[:from], x[:from_subfuncret] ? :subfuncret : :normal) if x[:from] and x[:from] != :default
|
975
|
+
todo.concat disassemble_fast_block(block, x[:cpu_context], &b)
|
976
|
+
end
|
977
|
+
elsif name = Expression[addr].reduce_rec and name.kind_of?(::String) and not @function[addr]
|
978
|
+
if c_parser and cs = c_parser.toplevel.symbol[name] and cs.type.untypedef.kind_of?(C::Function)
|
979
|
+
@function[addr] = @cpu.decode_c_function_prototype(@c_parser, cs)
|
980
|
+
detect_function_thunk_noreturn(x[:from]) if @function[addr].noreturn
|
928
981
|
elsif @function[:default]
|
929
982
|
@function[addr] = @function[:default].dup
|
930
983
|
end
|
@@ -935,7 +988,7 @@ puts " finalize subfunc #{Expression[subfunc]}" if debug_backtrace
|
|
935
988
|
|
936
989
|
# check if an addr has an xref :x from a :saveip, if so mark as Function
|
937
990
|
def disassemble_fast_checkfunc(addr)
|
938
|
-
if @decoded[addr].kind_of?
|
991
|
+
if @decoded[addr].kind_of?(DecodedInstruction) and not @function[addr]
|
939
992
|
func = false
|
940
993
|
each_xref(addr, :x) { |x_|
|
941
994
|
func = true if odi = di_at(x_.origin) and odi.opcode.props[:saveip]
|
@@ -957,8 +1010,8 @@ puts " finalize subfunc #{Expression[subfunc]}" if debug_backtrace
|
|
957
1010
|
# no backtrace for :x (change with backtrace_maxblocks_fast)
|
958
1011
|
# returns a todo-style ary
|
959
1012
|
# assumes @addrs_todo is empty
|
960
|
-
def disassemble_fast_block(block, &b)
|
961
|
-
block = InstructionBlock.new(normalize(block), get_section_at(block)[0]) if not block.kind_of?
|
1013
|
+
def disassemble_fast_block(block, cpu_context, &b)
|
1014
|
+
block = InstructionBlock.new(normalize(block), get_section_at(block)[0]) if not block.kind_of?(InstructionBlock)
|
962
1015
|
di_addr = block.address
|
963
1016
|
delay_slot = nil
|
964
1017
|
di = nil
|
@@ -971,7 +1024,8 @@ puts " finalize subfunc #{Expression[subfunc]}" if debug_backtrace
|
|
971
1024
|
|
972
1025
|
# decode instruction
|
973
1026
|
block.edata.ptr = di_addr - block.address + block.edata_ptr
|
974
|
-
|
1027
|
+
cpu_context = cpu_context.dup if cpu_context
|
1028
|
+
if not di = @cpu.decode_instruction_context(self, block.edata, di_addr, cpu_context)
|
975
1029
|
break if block.edata.ptr >= block.edata.length and get_section_at(di_addr) and di = block.list.last
|
976
1030
|
return ret
|
977
1031
|
end
|
@@ -1002,12 +1056,12 @@ puts " finalize subfunc #{Expression[subfunc]}" if debug_backtrace
|
|
1002
1056
|
ar = @program.get_xrefs_x(self, di)
|
1003
1057
|
ar = @callback_newaddr[di.address, ar] || ar if callback_newaddr
|
1004
1058
|
ar.each { |expr|
|
1005
|
-
backtrace(expr, di.address, :origin => di.address, :type => :x, :maxdepth => @backtrace_maxblocks_fast)
|
1059
|
+
backtrace(expr, di.address, :origin => di.address, :type => :x, :maxdepth => @backtrace_maxblocks_fast, :cpu_context => cpu_context)
|
1006
1060
|
}
|
1007
1061
|
end
|
1008
1062
|
if di.opcode.props[:saveip]
|
1009
1063
|
@addrs_todo = []
|
1010
|
-
ret.concat disassemble_fast_block_subfunc(di, &b)
|
1064
|
+
ret.concat disassemble_fast_block_subfunc(di, cpu_context, &b)
|
1011
1065
|
else
|
1012
1066
|
ret.concat @addrs_todo
|
1013
1067
|
@addrs_todo = []
|
@@ -1028,13 +1082,13 @@ puts " finalize subfunc #{Expression[subfunc]}" if debug_backtrace
|
|
1028
1082
|
ar = @callback_newaddr[block.list.last.address, ar] || ar if callback_newaddr
|
1029
1083
|
ar.each { |a|
|
1030
1084
|
di.block.add_to_normal(a)
|
1031
|
-
ret <<
|
1085
|
+
ret << { :addr => a, :from => di.address, :cpu_context => cpu_context }
|
1032
1086
|
}
|
1033
1087
|
ret
|
1034
1088
|
end
|
1035
1089
|
|
1036
1090
|
# handles when disassemble_fast encounters a call to a subfunction
|
1037
|
-
def disassemble_fast_block_subfunc(di)
|
1091
|
+
def disassemble_fast_block_subfunc(di, cpu_context)
|
1038
1092
|
funcs = di.block.to_normal.to_a
|
1039
1093
|
do_ret = funcs.empty?
|
1040
1094
|
ret = []
|
@@ -1047,10 +1101,10 @@ puts " finalize subfunc #{Expression[subfunc]}" if debug_backtrace
|
|
1047
1101
|
# this includes retaddr unless f is noreturn
|
1048
1102
|
bf.each { |btt|
|
1049
1103
|
next if btt.type != :x
|
1050
|
-
bt = backtrace(btt.expr, di.address, :include_start => true, :origin => btt.origin, :maxdepth => [@backtrace_maxblocks_fast, 1].max)
|
1104
|
+
bt = backtrace(btt.expr, di.address, :include_start => true, :origin => btt.origin, :maxdepth => [@backtrace_maxblocks_fast, 1].max, :cpu_context => cpu_context)
|
1051
1105
|
if btt.detached
|
1052
|
-
ret.concat bt # callback argument
|
1053
|
-
elsif bt.find { |a| normalize(a) == na }
|
1106
|
+
ret.concat bt.map { |a| { :addr => a } } # callback argument
|
1107
|
+
elsif not f.noreturn and bt.find { |a| normalize(a) == na }
|
1054
1108
|
do_ret = true
|
1055
1109
|
end
|
1056
1110
|
}
|
@@ -1060,9 +1114,10 @@ puts " finalize subfunc #{Expression[subfunc]}" if debug_backtrace
|
|
1060
1114
|
}
|
1061
1115
|
if do_ret
|
1062
1116
|
di.block.add_to_subfuncret(na)
|
1063
|
-
ret <<
|
1117
|
+
ret << { :addr => na, :from => di.address, :from_subfuncret => true, :cpu_context => cpu_context }
|
1064
1118
|
di.block.add_to_normal :default if not di.block.to_normal and @function[:default]
|
1065
1119
|
end
|
1120
|
+
di.add_comment 'noreturn' if ret.empty?
|
1066
1121
|
ret
|
1067
1122
|
end
|
1068
1123
|
|
@@ -1085,10 +1140,10 @@ puts " finalize subfunc #{Expression[subfunc]}" if debug_backtrace
|
|
1085
1140
|
end
|
1086
1141
|
|
1087
1142
|
# trace xrefs for execution
|
1088
|
-
def backtrace_xrefs_di_x(di)
|
1143
|
+
def backtrace_xrefs_di_x(di, cpu_context)
|
1089
1144
|
ar = @program.get_xrefs_x(self, di)
|
1090
1145
|
ar = @callback_newaddr[di.address, ar] || ar if callback_newaddr
|
1091
|
-
ar.each { |expr| backtrace(expr, di.address, :origin => di.address, :type => :x) }
|
1146
|
+
ar.each { |expr| backtrace(expr, di.address, :origin => di.address, :type => :x, :cpu_context => cpu_context) }
|
1092
1147
|
end
|
1093
1148
|
|
1094
1149
|
# checks if the function starting at funcaddr is an external function thunk (eg jmp [SomeExtFunc])
|
@@ -1096,7 +1151,7 @@ puts " finalize subfunc #{Expression[subfunc]}" if debug_backtrace
|
|
1096
1151
|
# which must not have return_addresses
|
1097
1152
|
# returns the new thunk name if it was changed
|
1098
1153
|
def detect_function_thunk(funcaddr)
|
1099
|
-
# check thunk linearity (no
|
1154
|
+
# check thunk linearity (no conditional branch etc)
|
1100
1155
|
addr = funcaddr
|
1101
1156
|
count = 0
|
1102
1157
|
while b = block_at(addr)
|
@@ -1129,7 +1184,7 @@ puts " finalize subfunc #{Expression[subfunc]}" if debug_backtrace
|
|
1129
1184
|
f.backtrace_binding = { :thunk => addr }
|
1130
1185
|
f.noreturn = true if @function[addr] and @function[addr].noreturn
|
1131
1186
|
end
|
1132
|
-
return if not fname.kind_of?
|
1187
|
+
return if not fname.kind_of?(::String)
|
1133
1188
|
l = auto_label_at(funcaddr, 'sub', 'loc')
|
1134
1189
|
return if l[0, 4] != 'sub_'
|
1135
1190
|
puts "found thunk for #{fname} at #{Expression[funcaddr]}" if $DEBUG
|
@@ -1167,14 +1222,14 @@ puts " finalize subfunc #{Expression[subfunc]}" if debug_backtrace
|
|
1167
1222
|
# should only be called with fa = target of a call
|
1168
1223
|
def check_noreturn_function(fa)
|
1169
1224
|
fb = function_blocks(fa, false, false)
|
1225
|
+
return if fb.empty?
|
1170
1226
|
lasts = fb.keys.find_all { |k| fb[k] == [] }
|
1171
|
-
return if lasts.empty?
|
1172
1227
|
if lasts.all? { |la|
|
1173
1228
|
b = block_at(la)
|
1174
1229
|
next if not di = b.list.last
|
1175
1230
|
(di.opcode.props[:saveip] and b.to_normal.to_a.all? { |tfa|
|
1176
1231
|
tf = function_at(tfa) and tf.noreturn
|
1177
|
-
}) or (di.opcode.props[:stopexec] and not di.opcode.props[:setip])
|
1232
|
+
}) or (di.opcode.props[:stopexec] and not (di.opcode.props[:setip] or not get_xrefs_x(di).empty?))
|
1178
1233
|
}
|
1179
1234
|
# yay
|
1180
1235
|
@function[fa] ||= DecodedFunction.new
|
@@ -1229,7 +1284,7 @@ puts " finalize subfunc #{Expression[subfunc]}" if debug_backtrace
|
|
1229
1284
|
# (defaults to dasm.backtrace_maxblocks, which defaults do Dasm.backtrace_maxblocks)
|
1230
1285
|
def backtrace_walk(obj, addr, include_start, from_subfuncret, stopaddr, maxdepth)
|
1231
1286
|
start_addr = normalize(addr)
|
1232
|
-
stopaddr = [stopaddr] if stopaddr and not stopaddr.kind_of?
|
1287
|
+
stopaddr = [stopaddr] if stopaddr and not stopaddr.kind_of?(::Array)
|
1233
1288
|
|
1234
1289
|
# array of [obj, addr, from_subfuncret, loopdetect]
|
1235
1290
|
# loopdetect is an array of [obj, addr, from_type] of each end of block encountered
|
@@ -1256,7 +1311,7 @@ puts " finalize subfunc #{Expression[subfunc]}" if debug_backtrace
|
|
1256
1311
|
next if f_type == :indirect
|
1257
1312
|
hadsomething = true
|
1258
1313
|
o_f_addr = f_addr
|
1259
|
-
f_addr = @decoded[f_addr].block.list.last.address if @decoded[f_addr].kind_of?
|
1314
|
+
f_addr = @decoded[f_addr].block.list.last.address if @decoded[f_addr].kind_of?(DecodedInstruction) # delay slot
|
1260
1315
|
if l = w_loopdetect.find { |l_obj, l_addr, l_type| l_addr == f_addr and l_type == f_type }
|
1261
1316
|
f_obj = yield(:loop, w_obj, :looptrace => w_loopdetect[w_loopdetect.index(l)..-1], :loopdetect => w_loopdetect)
|
1262
1317
|
if f_obj and f_obj != w_obj # should avoid infinite loops
|
@@ -1270,7 +1325,7 @@ puts " finalize subfunc #{Expression[subfunc]}" if debug_backtrace
|
|
1270
1325
|
f_loopdetect ||= w_loopdetect
|
1271
1326
|
# only count non-trivial paths in loopdetect (ignore linear links)
|
1272
1327
|
add_detect = [[f_obj, f_addr, f_type]]
|
1273
|
-
add_detect = [] if @decoded[f_addr].kind_of?
|
1328
|
+
add_detect = [] if @decoded[f_addr].kind_of?(DecodedInstruction) and tmp = @decoded[f_addr].block and
|
1274
1329
|
((w_di.block.from_subfuncret.to_a == [] and w_di.block.from_normal == [f_addr] and
|
1275
1330
|
tmp.to_normal == [w_di.address] and tmp.to_subfuncret.to_a == []) or
|
1276
1331
|
(w_di.block.from_subfuncret == [f_addr] and tmp.to_subfuncret == [w_di.address]))
|
@@ -1283,7 +1338,7 @@ puts " finalize subfunc #{Expression[subfunc]}" if debug_backtrace
|
|
1283
1338
|
each_xref(w_addr, :x) { |x|
|
1284
1339
|
f_addr = x.origin
|
1285
1340
|
o_f_addr = f_addr
|
1286
|
-
f_addr = @decoded[f_addr].block.list.last.address if @decoded[f_addr].kind_of?
|
1341
|
+
f_addr = @decoded[f_addr].block.list.last.address if @decoded[f_addr].kind_of?(DecodedInstruction) # delay slot
|
1287
1342
|
if l = w_loopdetect.find { |l_obj, l_addr, l_type| l_addr == w_addr }
|
1288
1343
|
f_obj = yield(:loop, w_obj, :looptrace => w_loopdetect[w_loopdetect.index(l)..-1], :loopdetect => w_loopdetect)
|
1289
1344
|
if f_obj and f_obj != w_obj
|
@@ -1466,6 +1521,7 @@ puts " finalize subfunc #{Expression[subfunc]}" if debug_backtrace
|
|
1466
1521
|
# :only_upto => backtrace only to update bt_for for current block & previous ending at only_upto
|
1467
1522
|
# :no_check => don't use backtrace_check_found (will not backtrace indirection static values)
|
1468
1523
|
# :terminals => array of symbols with constant value (stop backtracking if all symbols in the expr are terminals) (only supported with no_check)
|
1524
|
+
# :cpu_context => disassembler cpu_context
|
1469
1525
|
def backtrace(expr, start_addr, nargs={})
|
1470
1526
|
include_start = nargs.delete :include_start
|
1471
1527
|
from_subfuncret = nargs.delete :from_subfuncret
|
@@ -1482,6 +1538,7 @@ puts " finalize subfunc #{Expression[subfunc]}" if debug_backtrace
|
|
1482
1538
|
only_upto = nargs.delete :only_upto
|
1483
1539
|
no_check = nargs.delete :no_check
|
1484
1540
|
terminals = nargs.delete(:terminals) || []
|
1541
|
+
cpu_context = nargs.delete :cpu_context
|
1485
1542
|
raise ArgumentError, "invalid argument to backtrace #{nargs.keys.inspect}" if not nargs.empty?
|
1486
1543
|
|
1487
1544
|
expr = Expression[expr]
|
@@ -1502,7 +1559,7 @@ puts " not backtracking stack address #{expr}" if debug_backtrace
|
|
1502
1559
|
end
|
1503
1560
|
|
1504
1561
|
if vals = (no_check ? (!need_backtrace(expr, terminals) and [expr]) : backtrace_check_found(expr,
|
1505
|
-
di, origin, type, len, maxdepth, detached, snapshot_addr))
|
1562
|
+
di, origin, type, len, maxdepth, detached, cpu_context, snapshot_addr))
|
1506
1563
|
# no need to update backtracked_for
|
1507
1564
|
return vals
|
1508
1565
|
elsif maxdepth <= 0
|
@@ -1511,7 +1568,7 @@ puts " not backtracking stack address #{expr}" if debug_backtrace
|
|
1511
1568
|
|
1512
1569
|
# create initial backtracked_for
|
1513
1570
|
if type and origin == start_addr and di
|
1514
|
-
btt = BacktraceTrace.new(expr, origin, origexpr, type, len, maxdepth-1)
|
1571
|
+
btt = BacktraceTrace.new(expr, origin, origexpr, type, len, maxdepth-1, cpu_context)
|
1515
1572
|
btt.address = di.address
|
1516
1573
|
btt.exclude_instr = true if not include_start
|
1517
1574
|
btt.from_subfuncret = true if from_subfuncret and include_start
|
@@ -1532,9 +1589,9 @@ puts "backtracking #{type} #{expr} from #{di || Expression[start_addr || 0]} for
|
|
1532
1589
|
when :unknown_addr, :maxdepth
|
1533
1590
|
puts " backtrace end #{ev} #{expr}" if debug_backtrace
|
1534
1591
|
result |= [expr] if not snapshot_addr
|
1535
|
-
@addrs_todo <<
|
1592
|
+
@addrs_todo << { :addr => expr, :from => (detached ? nil : origin), :cpu_context => cpu_context } if not snapshot_addr and type == :x and origin
|
1536
1593
|
when :end
|
1537
|
-
if not expr.kind_of?
|
1594
|
+
if not expr.kind_of?(StoppedExpr)
|
1538
1595
|
oldexpr = expr
|
1539
1596
|
expr = backtrace_emu_blockup(h[:addr], expr)
|
1540
1597
|
puts " backtrace up #{Expression[h[:addr]]} #{oldexpr}#{" => #{expr}" if expr != oldexpr}" if debug_backtrace
|
@@ -1542,7 +1599,7 @@ puts " backtrace up #{Expression[h[:addr]]} #{oldexpr}#{" => #{expr}" if expr
|
|
1542
1599
|
if expr != oldexpr and not snapshot_addr and vals = (no_check ?
|
1543
1600
|
(!need_backtrace(expr, terminals) and [expr]) :
|
1544
1601
|
backtrace_check_found(expr, nil, origin, type, len,
|
1545
|
-
maxdepth-h[:loopdetect].length, detached, snapshot_addr))
|
1602
|
+
maxdepth-h[:loopdetect].length, detached, cpu_context, snapshot_addr))
|
1546
1603
|
result |= vals
|
1547
1604
|
next
|
1548
1605
|
end
|
@@ -1551,14 +1608,14 @@ puts " backtrace end #{ev} #{expr}" if debug_backtrace
|
|
1551
1608
|
if not snapshot_addr
|
1552
1609
|
result |= [expr]
|
1553
1610
|
|
1554
|
-
btt = BacktraceTrace.new(expr, origin, origexpr, type, len, maxdepth-h[:loopdetect].length-1)
|
1611
|
+
btt = BacktraceTrace.new(expr, origin, origexpr, type, len, maxdepth-h[:loopdetect].length-1, cpu_context)
|
1555
1612
|
btt.detached = true if detached
|
1556
1613
|
@decoded[h[:addr]].block.backtracked_for |= [btt] if @decoded[h[:addr]]
|
1557
1614
|
@function[h[:addr]].backtracked_for |= [btt] if @function[h[:addr]] and h[:addr] != :default
|
1558
|
-
@addrs_todo <<
|
1615
|
+
@addrs_todo << { :addr => expr, :from => (detached ? nil : origin), :cpu_context => cpu_context } if type == :x and origin
|
1559
1616
|
end
|
1560
1617
|
when :stopaddr
|
1561
|
-
if not expr.kind_of?
|
1618
|
+
if not expr.kind_of?(StoppedExpr)
|
1562
1619
|
oldexpr = expr
|
1563
1620
|
expr = backtrace_emu_blockup(h[:addr], expr)
|
1564
1621
|
puts " backtrace up #{Expression[h[:addr]]} #{oldexpr}#{" => #{expr}" if expr != oldexpr}" if debug_backtrace
|
@@ -1567,7 +1624,7 @@ puts " backtrace up #{Expression[h[:addr]]} #{oldexpr}#{" => #{expr}" if expr
|
|
1567
1624
|
puts " backtrace end #{ev} #{expr}" if debug_backtrace
|
1568
1625
|
result |= ((expr.kind_of?(StoppedExpr)) ? expr.exprs : [expr])
|
1569
1626
|
when :loop
|
1570
|
-
next false if expr.kind_of?
|
1627
|
+
next false if expr.kind_of?(StoppedExpr)
|
1571
1628
|
t = h[:looptrace]
|
1572
1629
|
oldexpr = t[0][0]
|
1573
1630
|
next false if expr == oldexpr # unmodifying loop
|
@@ -1576,7 +1633,7 @@ puts " bt loop at #{Expression[t[0][1]]}: #{oldexpr} => #{expr} (#{t.map { |z|
|
|
1576
1633
|
false
|
1577
1634
|
when :up
|
1578
1635
|
next false if only_upto and h[:to] != only_upto
|
1579
|
-
next expr if expr.kind_of?
|
1636
|
+
next expr if expr.kind_of?(StoppedExpr)
|
1580
1637
|
oldexpr = expr
|
1581
1638
|
expr = backtrace_emu_blockup(h[:from], expr)
|
1582
1639
|
puts " backtrace up #{Expression[h[:from]]}->#{Expression[h[:to]]} #{oldexpr}#{" => #{expr}" if expr != oldexpr}" if debug_backtrace
|
@@ -1584,7 +1641,7 @@ puts " backtrace up #{Expression[h[:from]]}->#{Expression[h[:to]]} #{oldexpr}#
|
|
1584
1641
|
|
1585
1642
|
if expr != oldexpr and vals = (no_check ? (!need_backtrace(expr, terminals) and [expr]) :
|
1586
1643
|
backtrace_check_found(expr, @decoded[h[:from]], origin, type, len,
|
1587
|
-
maxdepth-h[:loopdetect].length, detached, snapshot_addr))
|
1644
|
+
maxdepth-h[:loopdetect].length, detached, cpu_context, snapshot_addr))
|
1588
1645
|
if snapshot_addr
|
1589
1646
|
expr = StoppedExpr.new vals
|
1590
1647
|
next expr
|
@@ -1606,7 +1663,7 @@ puts " backtrace up #{Expression[h[:from]]}->#{Expression[h[:to]]} #{oldexpr}#
|
|
1606
1663
|
end
|
1607
1664
|
}
|
1608
1665
|
|
1609
|
-
btt = BacktraceTrace.new(expr, origin, origexpr, type, len, maxdepth-h[:loopdetect].length-1)
|
1666
|
+
btt = BacktraceTrace.new(expr, origin, origexpr, type, len, maxdepth-h[:loopdetect].length-1, cpu_context)
|
1610
1667
|
btt.detached = true if detached
|
1611
1668
|
if x = di_at(h[:from])
|
1612
1669
|
update_btf[x.block.backtracked_for, btt]
|
@@ -1630,7 +1687,7 @@ puts " already backtraced" if debug_backtrace
|
|
1630
1687
|
end
|
1631
1688
|
expr
|
1632
1689
|
when :di, :func
|
1633
|
-
next if expr.kind_of?
|
1690
|
+
next if expr.kind_of?(StoppedExpr)
|
1634
1691
|
if not snapshot_addr and @cpu.backtrace_is_stack_address(expr)
|
1635
1692
|
puts " not backtracking stack address #{expr}" if debug_backtrace
|
1636
1693
|
next false
|
@@ -1653,7 +1710,7 @@ puts " backtrace: recursive function #{Expression[h[:funcaddr]]}" if debug_back
|
|
1653
1710
|
end
|
1654
1711
|
puts " backtrace #{h[:di] || Expression[h[:funcaddr]]} #{oldexpr} => #{expr}" if debug_backtrace and expr != oldexpr
|
1655
1712
|
if vals = (no_check ? (!need_backtrace(expr, terminals) and [expr]) : backtrace_check_found(expr,
|
1656
|
-
h[:di], origin, type, len, maxdepth-h[:loopdetect].length, detached, snapshot_addr))
|
1713
|
+
h[:di], origin, type, len, maxdepth-h[:loopdetect].length, detached, cpu_context, snapshot_addr))
|
1657
1714
|
if snapshot_addr
|
1658
1715
|
expr = StoppedExpr.new vals
|
1659
1716
|
else
|
@@ -1685,7 +1742,7 @@ puts ' backtrace result: ' + result.map { |r| Expression[r] }.join(', ') if deb
|
|
1685
1742
|
not need_backtrace(retaddr)
|
1686
1743
|
puts " backtrace addrs_todo << #{Expression[retaddr]} from #{di} (funcret)" if debug_backtrace
|
1687
1744
|
di.block.add_to_subfuncret normalize(retaddr)
|
1688
|
-
if @decoded[funcaddr].kind_of?
|
1745
|
+
if @decoded[funcaddr].kind_of?(DecodedInstruction)
|
1689
1746
|
# check that all callers :saveip returns (eg recursive call that was resolved
|
1690
1747
|
# before we found funcaddr was a function)
|
1691
1748
|
@decoded[funcaddr].block.each_from_normal { |fm|
|
@@ -1703,17 +1760,17 @@ puts " backtrace addrs_todo << #{Expression[retaddr]} from #{di} (funcret)" if
|
|
1703
1760
|
todo = []
|
1704
1761
|
di.block.each_to_normal { |t| todo << normalize(t) }
|
1705
1762
|
while a = todo.pop
|
1706
|
-
next if faddrlist.include?
|
1763
|
+
next if faddrlist.include?(a) or not get_section_at(a)
|
1707
1764
|
faddrlist << a
|
1708
|
-
if @decoded[a].kind_of?
|
1765
|
+
if @decoded[a].kind_of?(DecodedInstruction)
|
1709
1766
|
@decoded[a].block.each_to_samefunc(self) { |t| todo << normalize(t) }
|
1710
1767
|
end
|
1711
1768
|
end
|
1712
1769
|
|
1713
|
-
idx = @addrs_todo.index(@addrs_todo.find { |
|
1714
|
-
@addrs_todo.insert(idx,
|
1770
|
+
idx = @addrs_todo.index(@addrs_todo.find { |aa| faddrlist.include? normalize(aa[:addr]) }) || -1
|
1771
|
+
@addrs_todo.insert(idx, { :addr => retaddr, :from => instraddr, :from_subfuncret => true, :cpu_context => btt.cpu_context })
|
1715
1772
|
else
|
1716
|
-
@addrs_todo <<
|
1773
|
+
@addrs_todo << { :addr => retaddr, :from => instraddr, :from_subfuncret => true, :cpu_context => btt.cpu_context }
|
1717
1774
|
end
|
1718
1775
|
true
|
1719
1776
|
end
|
@@ -1752,7 +1809,7 @@ puts " backtrace addrs_todo << #{Expression[retaddr]} from #{di} (funcret)" if
|
|
1752
1809
|
# returns true if the expression needs more backtrace
|
1753
1810
|
# it checks for the presence of a symbol (not :unknown), which means it depends on some register value
|
1754
1811
|
def need_backtrace(expr, terminals=[])
|
1755
|
-
return if expr.kind_of?
|
1812
|
+
return if expr.kind_of?(::Integer)
|
1756
1813
|
!(expr.externals.grep(::Symbol) - [:unknown] - terminals).empty?
|
1757
1814
|
end
|
1758
1815
|
|
@@ -1770,7 +1827,7 @@ puts " backtrace addrs_todo << #{Expression[retaddr]} from #{di} (funcret)" if
|
|
1770
1827
|
# TODO trace expr evolution through backtrace, to modify immediates to an expr involving label names
|
1771
1828
|
# TODO mov [ptr], imm ; <...> ; jmp [ptr] => rename imm as loc_XX
|
1772
1829
|
# eg. mov eax, 42 ; add eax, 4 ; jmp eax => mov eax, some_label-4
|
1773
|
-
def backtrace_check_found(expr, di, origin, type, len, maxdepth, detached, snapshot_addr=nil)
|
1830
|
+
def backtrace_check_found(expr, di, origin, type, len, maxdepth, detached, cpu_context, snapshot_addr=nil)
|
1774
1831
|
# only entrypoints or block starts called by a :saveip are checked for being a function
|
1775
1832
|
# want to execute [esp] from a block start
|
1776
1833
|
if type == :x and di and di == di.block.list.first and @cpu.backtrace_is_function_return(expr, @decoded[origin]) and (
|
@@ -1812,7 +1869,7 @@ puts "backtrace #{type} found #{expr} from #{di} orig #{@decoded[origin] || Expr
|
|
1812
1869
|
|
1813
1870
|
# create xrefs/labels
|
1814
1871
|
result.each { |e|
|
1815
|
-
backtrace_found_result(e, di, type, origin, len, detached)
|
1872
|
+
backtrace_found_result(e, di, type, origin, len, detached, cpu_context)
|
1816
1873
|
} if type and origin
|
1817
1874
|
|
1818
1875
|
result
|
@@ -1941,7 +1998,7 @@ puts " backtrace_indirection for #{ind.target} failed: #{ev}" if debug_backtra
|
|
1941
1998
|
end
|
1942
1999
|
|
1943
2000
|
# creates xrefs, updates addrs_todo, updates instr args
|
1944
|
-
def backtrace_found_result(expr, di, type, origin, len, detached)
|
2001
|
+
def backtrace_found_result(expr, di, type, origin, len, detached, cpu_context)
|
1945
2002
|
n = normalize(expr)
|
1946
2003
|
fallthrough = true if type == :x and o = di_at(origin) and not o.opcode.props[:stopexec] and n == o.block.list.last.next_addr # delay_slot
|
1947
2004
|
add_xref(n, Xref.new(type, origin, len)) if origin != :default and origin != Expression::Unknown and not fallthrough
|
@@ -2000,7 +2057,7 @@ puts " backtrace_indirection for #{ind.target} failed: #{ev}" if debug_backtra
|
|
2000
2057
|
else
|
2001
2058
|
@decoded[origin].block.add_to_normal(normalize(n)) if @decoded[origin] and not unk
|
2002
2059
|
end
|
2003
|
-
@addrs_todo <<
|
2060
|
+
@addrs_todo << { :addr => n, :from => origin, :cpu_context => cpu_context }
|
2004
2061
|
end
|
2005
2062
|
end
|
2006
2063
|
|
@@ -2014,12 +2071,12 @@ puts " backtrace_indirection for #{ind.target} failed: #{ev}" if debug_backtra
|
|
2014
2071
|
a
|
2015
2072
|
end
|
2016
2073
|
|
2017
|
-
# dumps the source,
|
2074
|
+
# dumps the source, optionally including data
|
2018
2075
|
# yields (defaults puts) each line
|
2019
2076
|
def dump(dump_data=true, &b)
|
2020
2077
|
b ||= lambda { |l| puts l }
|
2021
2078
|
@sections.sort_by { |addr, edata| addr.kind_of?(::Integer) ? addr : 0 }.each { |addr, edata|
|
2022
|
-
addr = Expression[addr] if addr.kind_of?
|
2079
|
+
addr = Expression[addr] if addr.kind_of?(::String)
|
2023
2080
|
blockoffs = @decoded.values.grep(DecodedInstruction).map { |di| Expression[di.block.address, :-, addr].reduce if di.block_head? }.grep(::Integer).sort.reject { |o| o < 0 or o >= edata.length }
|
2024
2081
|
b[@program.dump_section_header(addr, edata)]
|
2025
2082
|
if not dump_data and edata.length > 16*1024 and blockoffs.empty?
|
@@ -2034,7 +2091,7 @@ puts " backtrace_indirection for #{ind.target} failed: #{ev}" if debug_backtra
|
|
2034
2091
|
di = @decoded[addr+unk_off]
|
2035
2092
|
if unk_off != di.block.edata_ptr
|
2036
2093
|
b["\n// ------ overlap (#{unk_off-di.block.edata_ptr}) ------"]
|
2037
|
-
elsif di.block.from_normal.kind_of?
|
2094
|
+
elsif di.block.from_normal.kind_of?(::Array)
|
2038
2095
|
b["\n"]
|
2039
2096
|
end
|
2040
2097
|
dump_block(di.block, &b)
|
@@ -2079,7 +2136,7 @@ puts " backtrace_indirection for #{ind.target} failed: #{ev}" if debug_backtra
|
|
2079
2136
|
label_alias[block.address].each { |name| b["#{name}:"] }
|
2080
2137
|
end
|
2081
2138
|
if c = @comment[block.address]
|
2082
|
-
c = c.join("\n") if c.kind_of?
|
2139
|
+
c = c.join("\n") if c.kind_of?(::Array)
|
2083
2140
|
c.each_line { |l| b["// #{l}"] }
|
2084
2141
|
end
|
2085
2142
|
end
|
@@ -2124,11 +2181,11 @@ puts " backtrace_indirection for #{ind.target} failed: #{ev}" if debug_backtra
|
|
2124
2181
|
dups = edata.virtsize - off
|
2125
2182
|
@prog_binding.each_value { |a|
|
2126
2183
|
tmp = Expression[a, :-, addr].reduce
|
2127
|
-
dups = tmp if tmp.kind_of?
|
2184
|
+
dups = tmp if tmp.kind_of?(::Integer) and tmp > 0 and tmp < dups
|
2128
2185
|
}
|
2129
2186
|
@xrefs.each_key { |a|
|
2130
2187
|
tmp = Expression[a, :-, addr].reduce
|
2131
|
-
dups = tmp if tmp.kind_of?
|
2188
|
+
dups = tmp if tmp.kind_of?(::Integer) and tmp > 0 and tmp < dups
|
2132
2189
|
}
|
2133
2190
|
dups /= elemlen
|
2134
2191
|
dups = 1 if dups < 1
|
@@ -2174,7 +2231,7 @@ puts " backtrace_indirection for #{ind.target} failed: #{ev}" if debug_backtra
|
|
2174
2231
|
if (elemlen == 1 or elemlen == 2)
|
2175
2232
|
case value
|
2176
2233
|
when 0x20..0x7e, 0x0a, 0x0d
|
2177
|
-
if vals_.last.kind_of?
|
2234
|
+
if vals_.last.kind_of?(::String); vals_.last << value ; vals_
|
2178
2235
|
else vals_ << value.chr
|
2179
2236
|
end
|
2180
2237
|
else vals_ << value
|
@@ -2184,7 +2241,7 @@ puts " backtrace_indirection for #{ind.target} failed: #{ev}" if debug_backtra
|
|
2184
2241
|
}
|
2185
2242
|
|
2186
2243
|
vals.map! { |value|
|
2187
|
-
if value.kind_of?
|
2244
|
+
if value.kind_of?(::String)
|
2188
2245
|
if value.length > 2 # or value == vals.first or value == vals.last # if there is no xref, don't care
|
2189
2246
|
value.inspect
|
2190
2247
|
else
|