class Metasm::CPU
holds context of a processor endianness, current mode, opcode list…
Attributes
Public Class Methods
# File metasm/main.rb, line 32 def initialize @fields_mask = {} @fields_shift= {} @valid_args = {} @valid_props = { :setip => true, :saveip => true, :stopexec => true } @generate_PIC = true end
Public Instance Methods
return the thing to backtrace to find value
before the
execution of this instruction eg #backtrace_emu('inc
eax', Expression) => Expression[:eax + 1]
(the value of :eax after 'inc eax' is the value of :eax before plus 1)
may return Expression::Unknown
# File metasm/disassemble.rb, line 293 def backtrace_emu(di, value) Expression[Expression[value].bind(di.backtrace_binding ||= get_backtrace_binding(di)).reduce] end
a callback called whenever a backtrace is successful di is the decodedinstruction at the backtrace's origin
# File metasm/disassemble.rb, line 349 def backtrace_found_result(dasm, di, expr, type, len) end
checks if the expression corresponds to a function return value with the instruction (eg di == 'call something' and expr == [esp])
# File metasm/disassemble.rb, line 324 def backtrace_is_function_return(expr, di=nil) end
returns if the expression is an address on the stack (to avoid trying to backtrace its absolute address until we found function boundaries)
# File metasm/disassemble.rb, line 334 def backtrace_is_stack_address(expr) end
updates f.backtrace_binding when a new return address has been found TODO update also when anything changes inside the function (new loop found etc) - use backtracked_for ?
# File metasm/disassemble.rb, line 329 def backtrace_update_function_binding(dasm, faddr, f, retaddrlist, *wantregs) end
returns true if the name is invalid as a label name (eg register name)
# File metasm/main.rb, line 93 def check_reserved_name(name) end
matches the binary opcode at edata.ptr returns di or nil
# File metasm/decode.rb, line 195 def decode_findopcode(edata) DecodedInstruction.new self end
may modify di.instruction.args for eg jump offset => absolute address returns di or nil
# File metasm/decode.rb, line 206 def decode_instr_interpret(di, addr) di end
decodes di.instruction returns di or nil
# File metasm/decode.rb, line 201 def decode_instr_op(edata, di) end
decodes the instruction at edata.ptr, mapped at virtual address off returns a DecodedInstruction or nil
# File metasm/decode.rb, line 185 def decode_instruction(edata, addr) @bin_lookaside ||= build_bin_lookaside di = decode_findopcode edata if edata.ptr <= edata.length di.address = addr if di di = decode_instr_op(edata, di) if di decode_instr_interpret(di, addr) if di end
# File metasm/decompile.rb, line 17 def decompile_check_abi(dcmp, entry, func) end
number of instructions following a jump that are still executed
# File metasm/decode.rb, line 211 def delay_slot(di=nil) 0 end
# File metasm/decode.rb, line 215 def disassembler_default_func DecodedFunction.new end
returns an EncodedData or an ary of them
uses #parse_arg_valid?
to find the opcode whose signature
matches with the instruction uses encode_instr_op
(arch-specific)
# File metasm/encode.rb, line 324 def encode_instruction(program, i) errmsg = '' oplist = opcode_list_byname[i.opname].to_a.find_all { |o| o.args.length == i.args.length and o.args.zip(i.args).all? { |f, a| parse_arg_valid?(o, f, a) } }.map { |op| begin encode_instr_op(program, i, op) rescue EncodeError errmsg = " (#{$!.message})" nil end }.compact.flatten raise EncodeError, "no matching opcode found for #{i}#{errmsg}" if oplist.empty? oplist.each { |ed| ed.reloc.each_value { |v| v.backtrace = i.backtrace } } oplist end
patch a forward binding from the backtrace binding useful only on specific instructions that update a register and dereference that register (eg push)
# File metasm/decode.rb, line 241 def fix_fwdemu_binding(di, fbd) fbd end
compat alias, for scripts using older version of metasm
# File metasm/disassemble_api.rb, line 135 def get_backtrace_binding(di) backtrace_binding(di) end
return something like backtrace_binding in the forward direction set pc_reg to some reg name (eg :pc) to include effects on the instruction pointer
# File metasm/decode.rb, line 221 def get_fwdemu_binding(di, pc_reg=nil) fdi = di.backtrace_binding ||= get_backtrace_binding(di) fdi = fix_fwdemu_binding(di, fdi) if pc_reg if di.opcode.props[:setip] xr = get_xrefs_x(nil, di) if xr and xr.length == 1 fdi[pc_reg] = xr[0] else fdi[:incomplete_binding] = Expression[1] end else fdi[pc_reg] = Expression[pc_reg, :+, di.bin_length] end end fdi end
returns a list [addr, len]
# File metasm/disassemble.rb, line 307 def get_xrefs_r(dasm, di) b = di.backtrace_binding ||= get_backtrace_binding(di) r = b.values x = get_xrefs_x(dasm, di) r |= x if x (r.grep(Indirection) + r.grep(Expression).map { |e| e.expr_indirections }.flatten).map { |e| [e.target, e.len] } end
returns a list of [type, address, len]
# File metasm/disassemble.rb, line 302 def get_xrefs_rw(dasm, di) get_xrefs_r(dasm, di).map { |addr, len| [:r, addr, len] } + get_xrefs_w(dasm, di).map { |addr, len| [:w, addr, len] } end
returns a list [addr, len]
# File metasm/disassemble.rb, line 316 def get_xrefs_w(dasm, di) b = di.backtrace_binding ||= get_backtrace_binding(di) w = b.keys (w.grep(Indirection) + w.grep(Expression).map { |e| e.expr_indirections }.flatten).map { |e| [e.target, e.len] } end
returns a list of Expressions/Integer to backtrace to find an execution target
# File metasm/disassemble.rb, line 298 def get_xrefs_x(dasm, di) end
some userinterface wants to hilight a word, return a regexp useful for register aliases the regexp will be enclosed in b and should not contain captures
# File metasm/main.rb, line 88 def gui_hilight_word_regexp(word) Regexp.escape(word) end
ease debugging in irb
# File metasm/render.rb, line 62 def inspect "#<#{self.class}:#{'%x' % object_id} ... >" end
return a new AsmPreprocessor
# File metasm/main.rb, line 63 def new_asmprepro(str='', exe=nil) pp = AsmPreprocessor.new(str, exe) tune_prepro(pp) exe.tune_prepro(pp) if exe pp end
returns a new C::Compiler
# File metasm/main.rb, line 76 def new_ccompiler(parser, exe=ExeFormat.new) exe.cpu = self if not exe.instance_variable_get("@cpu") C::Compiler.new(parser, exe) end
returns a new & tuned C::Parser
# File metasm/main.rb, line 71 def new_cparser C::Parser.new(self) end
# File metasm/main.rb, line 27 def opcode_list @opcode_list ||= init_opcode_list end
# File metasm/main.rb, line 30 def opcode_list=(l) @opcode_list = l end
returns a hash opcode_name => array of opcodes with this name
# File metasm/main.rb, line 41 def opcode_list_byname @opcode_list_byname ||= opcode_list.inject({}) { |h, o| (h[o.name] ||= []) << o ; h } end
returns a parsed argument add your own arguments parser here (registers, memory references..)
# File metasm/parse.rb, line 82 def parse_argument(lexer) Expression.parse(lexer) end
parses prefix/name/arguments returns an Instruction
or raise a
ParseError if the parameter is a String, a custom AsmPP is built - XXX it will not
be able to create labels (eg jmp 1b / jmp $)
# File metasm/parse.rb, line 20 def parse_instruction(lexer) lexer = new_asmprepro(lexer) if lexer.kind_of? String i = Instruction.new self # find prefixes, break on opcode name while tok = lexer.readtok and parse_prefix(i, tok.raw) lexer.skip_space_eol end return if not tok # allow '.' in opcode name tok = tok.dup while ntok = lexer.nexttok and ntok.type == :punct and ntok.raw == '.' tok.raw << lexer.readtok.raw ntok = lexer.readtok raise tok, 'invalid opcode name' if not ntok or ntok.type != :string tok.raw << ntok.raw end raise tok, 'invalid opcode' if not opcode_list_byname[tok.raw] i.opname = tok.raw i.backtrace = tok.backtrace lexer.skip_space # find arguments list loop do break if not ntok = lexer.nexttok break if i.args.empty? and opcode_list_byname[ntok.raw] and opcode_list_byname[i.opname].find { |op| op.args.empty? } break if not arg = parse_argument(lexer) i.args << arg lexer.skip_space break if not ntok = lexer.nexttok or ntok.type != :punct or ntok.raw != ',' lexer.readtok lexer.skip_space_eol end if not parse_instruction_checkproto(i) raise tok, "invalid opcode arguments #{i.to_s.inspect}, allowed : #{opcode_list_byname[i.opname].to_a.map { |o| o.args }.inspect}" end parse_instruction_fixup(i) i end
# File metasm/parse.rb, line 66 def parse_instruction_checkproto(i) opcode_list_byname[i.opname].to_a.find { |o| o.args.length == i.args.length and o.args.zip(i.args).all? { |f, a| parse_arg_valid?(o, f, a) } } end
called after the instruction is fully parsed
# File metasm/parse.rb, line 73 def parse_instruction_fixup(i) end
handles .instructions XXX handle HLA here ?
# File metasm/parse.rb, line 88 def parse_parser_instruction(lexer, instr) raise instr, 'unknown parser instruction' end
return false if not a prefix
# File metasm/parse.rb, line 77 def parse_prefix(i, word) end
renders an instruction may use instruction-global properties to render an argument (eg specify pointer size if not implicit)
# File metasm/render.rb, line 52 def render_instruction(i) r = [] r << i.opname r << ' ' i.args.each { |a| r << a << ', ' } r.pop r end
updates the instruction arguments: replace an expression with another (eg when a label is renamed)
# File metasm/disassemble.rb, line 338 def replace_instr_arg_immediate(i, old, new) i.args.map! { |a| case a when Expression; Expression[a.bind(old => new).reduce] else a end } end
# File metasm/main.rb, line 81 def shortname self.class.name.sub(/.*::/, '').downcase end
sets up the C parser : standard macro definitions, type model (size of int etc)
# File metasm/main.rb, line 46 def tune_cparser(cp) case @size when 64; cp.lp64 when 32; cp.ilp32 when 16; cp.ilp16 end cp.endianness = @endianness cp.lexer.define_weak('_STDC', 1) # TODO gcc -dM -E - </dev/null tune_prepro(cp.lexer) end
# File metasm/main.rb, line 58 def tune_prepro(pp) # TODO pp.define('BIGENDIAN') end