node/lib/metasm/metasm/disassemble_api.rb
# dataflow method
# walks a function, starting at addr
# follows the usage of registers, computing the evolution from the value they had at start_addr
# whenever an instruction references the register (or anything derived from it),
#  yield [di, used_register, reg_value, trace_state] where reg_value is the Expression holding the value of
#  the register wrt the initial value at start_addr, and trace_state the value of all registers (reg_value
#  not yet applied)
#  reg_value may be nil if used_register is not modified by the function (eg call [eax])
#  the yield return value is propagated, unless it is nil/false
# init_state is a hash { :reg => initial value }
dataflow method
walks a function, starting at addr
follows the usage of registers, computing the evolution from the value they had at start_addr
whenever an instruction references the register (or anything derived from it),
yield [di, used_register, reg_value, trace_state] where reg_value is the Expression holding the value of
the register wrt the initial value at start_addr, and trace_state the value of all registers (reg_value
not yet applied)
reg_value may be nil if used_register is not modified by the function
the yield return value is propagated, unless it is nil/false
init_state is a hash { :reg => initial value }
dataflow method walks a function starting at addr follows the usage of registers computing the evolution from the value they had at start_addr whenever an instruction references the register ( or anything derived from it ) yield [ di used_register reg_value trace_state ] where reg_value is the Expression holding the value of the register wrt the initial value at start_addr and trace_state the value of all registers ( reg_value not yet applied ) reg_value may be nil if used_register is not modified by the function the yield return value is propagated unless it is nil / false init_state is a hash { : reg = > initial value }
def trace_function_register(start_addr, init_state)
		function_walk(start_addr, init_state) { |args|
			trace_state = args.last
			case args.first
			when :di
				di = args[2]
				update = {}
				get_fwdemu_binding(di).each { |r, v|
					if v.kind_of?(Expression) and v.externals.find { |e| trace_state[e] }
						newv = v.bind(trace_state)
						update[r] = yield(di, r, newv, trace_state)
					elsif r.kind_of?(ExpressionType) and rr = r.externals.find { |e| trace_state[e] }
						next if update.has_key?(rr)	
						if yield(di, rr, trace_state[rr], trace_state) == false
							update[rr] = false
						end
					elsif trace_state[r]
						next if di.address == start_addr
						update[r] = false
					end
				}
				@cpu.instr_args_memoryptr(di).each { |ind|
					b = @cpu.instr_args_memoryptr_getbase(ind)
					if b and b = b.symbolic and not update.has_key?(b)
						yield(di, b, nil, trace_state)
					end
				}
				@cpu.instr_args_regs(di).each { |r|
					r = r.symbolic
					if not update.has_key?(r)
						yield(di, r, nil, trace_state)
					end
				}
				update.each { |r, v|
					trace_state = trace_state.dup
					if v
						trace_state[r] = v if r.kind_of?(::Symbol)
					else
						trace_state.delete r
					end
				}
			when :subfunc
				faddr = args[1]
				f = @function[faddr]
				f = @function[f.backtrace_binding[:thunk]] if f and f.backtrace_binding[:thunk]
				if f
					binding = f.backtrace_binding
					if binding.empty?
						backtrace_update_function_binding(faddr)
						binding = f.backtrace_binding
					end
					binding.each { |r, v|
						if v.externals.find { |e| trace_state[e] }
							if r.kind_of?(::Symbol)
								trace_state = trace_state.dup
								trace_state[r] = Expression[v.bind(trace_state)].reduce
							end
						elsif trace_state[r]
							trace_state = trace_state.dup
							trace_state.delete r
						end
					}
				end
			when :merge
				conflicts = args[2]
				trace_state = trace_state.dup
				conflicts.each { |addr, st|
					trace_state.delete_if { |k, v| st[k] != v }
				}
			end
			trace_state = false if trace_state.empty?
			trace_state
		}
	end
def trace_function_register ( start_addr , init_state ) function_walk ( start_addr , init_state ) { | args | trace_state = args . last case args . first when :di di = args [ 2 ] update = { } get_fwdemu_binding ( di ) . each { | r , v | if v . kind_of? ( Expression ) and v . externals . find { | e | trace_state [ e ] } newv = v . bind ( trace_state ) update [ r ] = yield ( di , r , newv , trace_state ) elsif r . kind_of? ( ExpressionType ) and rr = r . externals . find { | e | trace_state [ e ] } next if update . has_key? ( rr ) if yield ( di , rr , trace_state [ rr ] , trace_state ) == false update [ rr ] = false end elsif trace_state [ r ] next if di . address == start_addr update [ r ] = false end } @cpu . instr_args_memoryptr ( di ) . each { | ind | b = @cpu . instr_args_memoryptr_getbase ( ind ) if b and b = b . symbolic and not update . has_key? ( b ) yield ( di , b , nil , trace_state ) end } @cpu . instr_args_regs ( di ) . each { | r | r = r . symbolic if not update . has_key? ( r ) yield ( di , r , nil , trace_state ) end } update . each { | r , v | trace_state = trace_state . dup if v trace_state [ r ] = v if r . kind_of? ( :: Symbol ) else trace_state . delete r end } when :subfunc faddr = args [ 1 ] f = @function [ faddr ] f = @function [ f . backtrace_binding [ :thunk ] ] if f and f . backtrace_binding [ :thunk ] if f binding = f . backtrace_binding if binding . empty? backtrace_update_function_binding ( faddr ) binding = f . backtrace_binding end binding . each { | r , v | if v . externals . find { | e | trace_state [ e ] } if r . kind_of? ( :: Symbol ) trace_state = trace_state . dup trace_state [ r ] = Expression [ v . bind ( trace_state ) ] . reduce end elsif trace_state [ r ] trace_state = trace_state . dup trace_state . delete r end } end when :merge conflicts = args [ 2 ] trace_state = trace_state . dup conflicts . each { | addr , st | trace_state . delete_if { | k , v | st [ k ] != v } } end trace_state = false if trace_state . empty? trace_state } end
dataflow method
walks a function, starting at addr
follows the usage of registers, computing the evolution from the value they had at start_addr
whenever an instruction references the register (or anything derived from it),
yield [di, used_register, reg_value, trace_state] where reg_value is the Expression holding the value of
the register wrt the initial value at start_addr, and trace_state the value of all registers (reg_value
not yet applied)
reg_value may be nil if used_register is not modified by the function (eg call [eax])
the yield return value is propagated, unless it is nil/false
init_state is a hash { :reg => initial value }
dataflow method walks a function starting at addr follows the usage of registers computing the evolution from the value they had at start_addr whenever an instruction references the register ( or anything derived from it ) yield [ di used_register reg_value trace_state ] where reg_value is the Expression holding the value of the register wrt the initial value at start_addr and trace_state the value of all registers ( reg_value not yet applied ) reg_value may be nil if used_register is not modified by the function ( eg call [ eax ] ) the yield return value is propagated unless it is nil / false init_state is a hash { : reg = > initial value }
# XXX may mix old (from trace) and current (from v) registers # reg dereferenced in a write (eg mov [esp], 42) # already yielded # started on mov reg, foo # directly walk the instruction argument list for registers not appearing in the binding # cannot follow non-registers, or we would have to emulate every single # instruction (try following [esp+4] across a __stdcall..) # XXX fwdemu_binding ? # when merging paths, keep the smallest common state subset # XXX may have unexplored froms
start_addr
init_state