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/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
|