class Metasm::CPU

holds context of a processor endianness, current mode, opcode list…

Attributes

endianness[RW]
fields_mask[RW]
generate_PIC[RW]
size[RW]
valid_args[RW]
valid_props[RW]

Public Class Methods

new() click to toggle source
# 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

backtrace_binding() click to toggle source

hash opcode_name => lambda { |dasm, di, *symbolic_args| instr_binding }

# File metasm/decode.rb, line 242
def backtrace_binding
        @backtrace_binding ||= init_backtrace_binding
end
backtrace_binding=(b) click to toggle source
# File metasm/decode.rb, line 245
def backtrace_binding=(b) @backtrace_binding = b end
backtrace_emu(di, value) click to toggle source

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 310
def backtrace_emu(di, value)
        Expression[Expression[value].bind(di.backtrace_binding ||= get_backtrace_binding(di)).reduce]
end
backtrace_found_result(dasm, di, expr, type, len) click to toggle source

a callback called whenever a backtrace is successful di is the decodedinstruction at the backtrace's origin

# File metasm/disassemble.rb, line 368
def backtrace_found_result(dasm, di, expr, type, len)
end
backtrace_is_function_return(expr, di=nil) click to toggle source

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 343
def backtrace_is_function_return(expr, di=nil)
end
backtrace_is_stack_address(expr) click to toggle source

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 353
def backtrace_is_stack_address(expr)
end
backtrace_update_function_binding(dasm, faddr, f, retaddrlist, *wantregs) click to toggle source

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 348
def backtrace_update_function_binding(dasm, faddr, f, retaddrlist, *wantregs)
end
bin_lookaside() click to toggle source
# File metasm/decode.rb, line 189
def bin_lookaside
        @bin_lookaside ||= build_bin_lookaside
end
check_reserved_name(name) click to toggle source

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
dbg_disable_bp(dbg, bp) click to toggle source

deactivate a software breakpoint

# File metasm/debug.rb, line 1477
def dbg_disable_bp(dbg, bp)
end
dbg_enable_bp(dbg, bp) click to toggle source

activate a software breakpoint

# File metasm/debug.rb, line 1473
def dbg_enable_bp(dbg, bp)
end
dbg_flag_list() click to toggle source

return the list of flags for the CPU

# File metasm/debug.rb, line 1458
def dbg_flag_list
        @dbg_flag_list ||= []
end
dbg_need_stepover(dbg, addr, di) click to toggle source

returns true if stepover is different from stepinto for this instruction

# File metasm/debug.rb, line 1468
def dbg_need_stepover(dbg, addr, di)
        di and di.opcode.props[:saveip]
end
dbg_register_list() click to toggle source

return the list of CPU registers

# File metasm/debug.rb, line 1453
def dbg_register_list
        @dbg_register_list ||= [dbg_register_pc]
end
dbg_register_pc() click to toggle source

return the CPU register used to store the current instruction pointer

# File metasm/debug.rb, line 1448
def dbg_register_pc
        @dbg_register_pc ||= :pc
end
dbg_register_size() click to toggle source

return a hash with register name => register size in bits

# File metasm/debug.rb, line 1463
def dbg_register_size
        @dbg_register_size ||= Hash.new(@size)
end
dbg_resolve_pc(di, fbd, pc_reg, dbg_ctx) click to toggle source

resolve the program counter following a conditional jump using a debugging context

# File metasm/decode.rb, line 283
def dbg_resolve_pc(di, fbd, pc_reg, dbg_ctx)
        fbd[:incomplete_binding] = Expression[1]
end
decode_findopcode(edata) click to toggle source

matches the binary opcode at edata.ptr returns di or nil

# File metasm/decode.rb, line 205
def decode_findopcode(edata)
        DecodedInstruction.new self
end
decode_instr_interpret(di, addr) click to toggle source

may modify di.instruction.args for eg jump offset => absolute address returns di or nil

# File metasm/decode.rb, line 216
def decode_instr_interpret(di, addr)
        di
end
decode_instr_op(edata, di) click to toggle source

decodes di.instruction returns di or nil

# File metasm/decode.rb, line 211
def decode_instr_op(edata, di)
end
decode_instruction(edata, addr) click to toggle source

decodes the instruction at edata.ptr, mapped at virtual address off returns a DecodedInstruction or nil

# File metasm/decode.rb, line 195
def decode_instruction(edata, addr)
        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
decode_instruction_context(dasm, edata, di_addr, context) click to toggle source

decode an instruction with a dasm context context is a hash, should be modified inplace by the CPU will be passed to the next instruction(s) in the code flow

# File metasm/disassemble.rb, line 298
def decode_instruction_context(dasm, edata, di_addr, context)
        decode_instruction(edata, di_addr)
end
delay_slot(di=nil) click to toggle source

number of instructions following a jump that are still executed

# File metasm/decode.rb, line 233
def delay_slot(di=nil)
        0
end
disassemble_init_context(dasm, addr) click to toggle source

return the initial context for the disassembler, starts disassembling from addr

# File metasm/disassemble.rb, line 303
def disassemble_init_context(dasm, addr)
end
disassembler_default_func() click to toggle source
# File metasm/decode.rb, line 237
def disassembler_default_func
        DecodedFunction.new
end
encode_instruction(program, i) click to toggle source

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 330
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
fix_fwdemu_binding(di, fbd) click to toggle source

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 289
def fix_fwdemu_binding(di, fbd)
        fbd
end
get_backtrace_binding(di) click to toggle source

return the backtrace binding for a specific di

# File metasm/decode.rb, line 248
def get_backtrace_binding(di)
        a = di.instruction.args.map { |arg| symbolic(arg, di) }

        if binding = backtrace_binding[di.opcode.name]
                binding[di, *a]
        else
                puts "unhandled instruction to backtrace: #{di}" if $VERBOSE
                {:incomplete_binding => Expression[1]}
        end
end
get_fwdemu_binding(di, pc_reg=nil, dbg_ctx=nil) click to toggle source

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 pass a debugger to allow reading the context and actually resolve the next pc in case of conditional jumps

# File metasm/decode.rb, line 262
def get_fwdemu_binding(di, pc_reg=nil, dbg_ctx=nil)
        fbd = di.backtrace_binding ||= get_backtrace_binding(di)
        fbd = fix_fwdemu_binding(di, fbd)
        if pc_reg
                n_a = Expression[pc_reg, :+, di.bin_length]
                if di.opcode.props[:setip]
                        xr = get_xrefs_x(nil, di).to_a
                        xr |= [n_a] if not di.opcode.props[:stopexec]
                        if xr.length == 1
                                fbd[pc_reg] = xr[0]
                        else
                                dbg_resolve_pc(di, fbd, pc_reg, dbg_ctx)
                        end
                else
                        fbd[pc_reg] = Expression[pc_reg, :+, di.bin_length]
                end
        end
        fbd
end
get_xrefs_r(dasm, di) click to toggle source

returns a list [addr, len]

# File metasm/disassemble.rb, line 326
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
get_xrefs_rw(dasm, di) click to toggle source

returns a list of [type, address, len]

# File metasm/disassemble.rb, line 321
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
get_xrefs_w(dasm, di) click to toggle source

returns a list [addr, len]

# File metasm/disassemble.rb, line 335
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
get_xrefs_x(dasm, di) click to toggle source

return the list of jump targets for insturctions modifying the control flow

# File metasm/disassemble.rb, line 315
def get_xrefs_x(dasm, di)
        return [] if not di.opcode.props[:setip]
        [symbolic(di.instruction.args.last, di)]
end
gui_hilight_word_regexp(word) click to toggle source

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
inspect() click to toggle source

ease debugging in irb

# File metasm/render.rb, line 62
def inspect
        "#<#{self.class}:#{'%x' % object_id} ... >"
end
new_asmprepro(str='', exe=nil) click to toggle source

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
new_ccompiler(parser, exe=ExeFormat.new) click to toggle source

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
new_cparser() click to toggle source

returns a new & tuned C::Parser

# File metasm/main.rb, line 71
def new_cparser
        C::Parser.new(self)
end
opcode_list() click to toggle source
# File metasm/main.rb, line 27
def opcode_list
        @opcode_list ||= init_opcode_list
end
opcode_list=(l) click to toggle source
# File metasm/main.rb, line 30
def opcode_list=(l) @opcode_list = l end
opcode_list_byname() click to toggle source

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
parse_argument(lexer) click to toggle source

returns a parsed argument add your own arguments parser here (registers, memory references..)

# File metasm/parse.rb, line 91
def parse_argument(lexer)
        Expression.parse(lexer)
end
parse_instruction(lexer) click to toggle source

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

        lexer.unreadtok(tok)
        tok = parse_instruction_mnemonic(lexer)
        return if not tok

        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
parse_instruction_checkproto(i) click to toggle source
# File metasm/parse.rb, line 75
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
parse_instruction_fixup(i) click to toggle source

called after the instruction is fully parsed

# File metasm/parse.rb, line 82
def parse_instruction_fixup(i)
end
parse_instruction_mnemonic(lexer) click to toggle source

return a lexer token with an instruction mnemonic in raw allows '.' in opcode name return nil at eof

# File metasm/parse.rb, line 61
def parse_instruction_mnemonic(lexer)
        return if not tok = lexer.readtok
        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]
        tok
end
parse_parser_instruction(lexer, instr) click to toggle source

handles .instructions XXX handle HLA here ?

# File metasm/parse.rb, line 97
def parse_parser_instruction(lexer, instr)
        raise instr, 'unknown parser instruction'
end
parse_prefix(i, word) click to toggle source

return false if not a prefix

# File metasm/parse.rb, line 86
def parse_prefix(i, word)
end
render_instruction(i) click to toggle source

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
replace_instr_arg_immediate(i, old, new) click to toggle source

updates the instruction arguments: replace an expression with another (eg when a label is renamed)

# File metasm/disassemble.rb, line 357
def replace_instr_arg_immediate(i, old, new)
        i.args.map! { |a|
                case a
                when Expression; a == old ? new : Expression[a.bind(old => new).reduce]
                else a
                end
        }
end
shortname() click to toggle source
# File metasm/main.rb, line 81
def shortname
        self.class.name.sub(/.*::/, '').downcase
end
symbolic(arg, di=nil) click to toggle source

return a symbolic representation of an instruction argument (eg Reg => :eax)

# File metasm/decode.rb, line 221
def symbolic(arg, di=nil)
        case arg
        when ExpressionType
                arg
        when Integer
                Expression[arg]
        else
                arg.symbolic(di)
        end
end
tune_cparser(cp) click to toggle source

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
tune_prepro(pp) click to toggle source
# File metasm/main.rb, line 58
def tune_prepro(pp)
        # TODO pp.define('BIGENDIAN')
end