class Metasm::X86

fix autorequire warning

The ia32 aka x86 CPU currently limited to 16 and 32bit modes

Constants

DBG_BPX
DBG_FLAGS
REG_SYMS

return the list of registers as symbols in the order used by pushad for use in backtrace and stuff, for compatibility with x64 esp is [4]

Public Class Methods

new(*a) click to toggle source

Create a new instance of an Ia32 cpu arguments (any order)

  • size in bits (16, 32) [32]

  • instruction set (386, 486, pentium…) [latest]

  • endianness [:little]

Calls superclass method Metasm::CPU::new
# File metasm/cpu/ia32/main.rb, line 217
def initialize(*a)
        super()
        @size = (a & [16, 32]).first || 32
        a.delete @size
        @endianness = (a & [:big, :little]).first || :little
        a.delete @endianness
        @family = a.pop || :latest
        raise "Invalid arguments #{a.inspect}" if not a.empty?
        raise "Invalid Ia32 family #{@family.inspect}" if not respond_to?("init_#@family")
end
new(*a) click to toggle source

wrapper to transparently forward Ia32.new(64) to X86_64.new

Calls superclass method Metasm::CPU::new
# File metasm/cpu/ia32/main.rb, line 229
def self.new(*a)
        return X86_64.new(*a) if a.include? 64 and self == Ia32
        super(*a)
end

Public Instance Methods

abi_funcall() click to toggle source

returns a hash { :retval => r, :changed => [] }

# File metasm/cpu/ia32/decode.rb, line 1233
def abi_funcall
        { :retval => register_symbols[0], :changed => register_symbols[0, 3] }
end
addop(name, bin, hint=nil, *argprops) { |op| ... } click to toggle source

helper function: creates a new Opcode based on the arguments, eventually yields it for further customisation, and append it to the instruction set is responsible of the creation of disambiguating opcodes if necessary (:s flag hardcoding)

# File metasm/cpu/ia32/opcodes.rb, line 1275
def addop(name, bin, hint=nil, *argprops)
        fields = (argprops.first.kind_of?(Hash) ? argprops.shift : {})
        op = Opcode.new name, bin
        op.fields.replace fields

        case hint
        when nil

        when :mrm, :mrmw, :mrmA
                op.fields[:reg]   = [bin.length, 3]
                op.fields[:modrm] = [bin.length, 0]
                op.fields[:w]     = [bin.length - 1, 0] if hint == :mrmw
                argprops.unshift :reg, :modrm
                argprops << :modrmA if hint == :mrmA
                op.bin << 0
        when :reg
                op.fields[:reg] = [bin.length-1, 0]
                argprops.unshift :reg
        when :regfp
                op.fields[:regfp] = [bin.length-1, 0]
                argprops.unshift :regfp, :regfp0
        when :modrmA
                op.fields[:modrm] = [bin.length-1, 0]
                argprops << :modrm << :modrmA

        when Integer          # mod/m, reg == opcode extension = hint
                op.fields[:modrm] = [bin.length, 0]
                op.bin << (hint << 3)
                argprops.unshift :modrm

        when :mrmmmx
                op.fields[:regmmx] = [bin.length, 3]
                op.fields[:modrm] = [bin.length, 0]
                bin << 0
                argprops.unshift :regmmx, :modrmmmx
        when :mrmxmm
                op.fields[:regxmm] = [bin.length, 3]
                op.fields[:modrm] = [bin.length, 0]
                bin << 0
                argprops.unshift :regxmm, :modrmxmm
        when :mrmymm
                op.fields[:regymm] = [bin.length, 3]
                op.fields[:modrm] = [bin.length, 0]
                bin << 0
                argprops.unshift :regymm, :modrmymm
        else
                raise SyntaxError, "invalid hint #{hint.inspect} for #{name}"
        end

        argprops.each { |a|
                op.props[a] = true if @valid_props[a]
                op.args << a if @valid_args[a]
        }

        yield op if block_given?

        if $DEBUG
                argprops -= @valid_props.keys + @valid_args.keys
                raise "Invalid opcode definition: #{name}: unknown #{argprops.inspect}" unless argprops.empty?

                argprops = (op.props.keys - @valid_props.keys) + (op.args - @valid_args.keys) + (op.fields.keys - @fields_mask.keys)
                raise "Invalid opcode customisation: #{name}: #{argprops.inspect}" unless argprops.empty?
        end

        addop_post(op)
end
addop_macro1(name, num, *props) click to toggle source

addop_* macros

# File metasm/cpu/ia32/opcodes.rb, line 1141
def addop_macro1(name, num, *props)
        addop name, [(num << 3) | 4], nil, {:w => [0, 0]}, :reg_eax, :i, *props
        addop(name, [num << 3], :mrmw, {:d => [0, 1]}) { |o| o.args.reverse! }
        addop name, [0x80], num, {:w => [0, 0], :s => [0, 1]}, :i, *props
end
addop_macro2(name, num) click to toggle source
# File metasm/cpu/ia32/opcodes.rb, line 1146
def addop_macro2(name, num)
        addop name, [0x0F, 0xBA], (4 | num), :u8
        addop(name, [0x0F, 0xA3 | (num << 3)], :mrm) { |op| op.args.reverse! }
end
addop_macro3(name, num) click to toggle source
# File metasm/cpu/ia32/opcodes.rb, line 1150
def addop_macro3(name, num)
        addop name, [0xD0], num, {:w => [0, 0]}, :imm_val1
        addop name, [0xD2], num, {:w => [0, 0]}, :reg_cl
        addop name, [0xC0], num, {:w => [0, 0]}, :u8
end
addop_macrofpu1(name, n) click to toggle source
# File metasm/cpu/ia32/opcodes.rb, line 1182
def addop_macrofpu1(name, n)
        addop(name, [0xD8, n<<3], :modrmA, :regfp0) { |o| o.props[:argsz] = 32 }
        addop(name, [0xDC, n<<3], :modrmA, :regfp0) { |o| o.props[:argsz] = 64 }
        addop(name, [0xD8, 0xC0|(n<<3)], :regfp, {:d => [0, 2]}) { |o| o.args.reverse! }
end
addop_macrofpu2(name, n, n2=0) click to toggle source
# File metasm/cpu/ia32/opcodes.rb, line 1187
def addop_macrofpu2(name, n, n2=0)
        addop(name, [0xDE|n2, n<<3], :modrmA, :regfp0) { |o| o.props[:argsz] = 16 }
        addop(name, [0xDA|n2, n<<3], :modrmA, :regfp0) { |o| o.props[:argsz] = 32 }
end
addop_macrofpu3(name, n) click to toggle source
# File metasm/cpu/ia32/opcodes.rb, line 1191
def addop_macrofpu3(name, n)
        addop_macrofpu2 name, n, 1
        addop(name, [0xDF, 0x28|(n<<3)], :modrmA, :regfp0) { |o| o.props[:argsz] = 64 }
end
addop_macrogg(ggrng, name, bin, *args, &blk) click to toggle source
# File metasm/cpu/ia32/opcodes.rb, line 1196
def addop_macrogg(ggrng, name, bin, *args, &blk)
        ggoff = 1
        ggoff = 2 if bin[1] == 0x38 or bin[1] == 0x3A
        ggrng.each { |gg|
                bindup = bin.dup
                bindup[ggoff] |= gg
                sfx = %w(b w d q)[gg]
                addop name+sfx, bindup, *args, &blk
        }
end
addop_macroret(name, bin, *args) click to toggle source

special ret (iret/retf), that still default to 32b mode in x64

# File metasm/cpu/ia32/opcodes.rb, line 1218
def addop_macroret(name, bin, *args)
        addop(name + '.i32', bin.dup, nil, :stopexec, :setip, *args) { |o| o.props[:opsz] = 32 }
        addop(name + '.i16', bin.dup, nil, :stopexec, :setip, *args) { |o| o.props[:opsz] = 16 } if name != 'sysret'
        addop(name, bin.dup, nil, :stopexec, :setip, *args) { |o| o.props[:opsz] = @size }
end
addop_macrosdpd(name, bin, hint, *a) click to toggle source
# File metasm/cpu/ia32/opcodes.rb, line 1212
def addop_macrosdpd(name, bin, hint, *a)
        addop(name, bin.dup, hint, *a) { |o| o.props[:needpfx] = 0x66 }
        addop(name.sub(/pd$/, 'sd'), bin.dup, hint, *a) { |o| o.props[:needpfx] = 0xF2 }
end
addop_macrossps(name, bin, hint, *a) click to toggle source
# File metasm/cpu/ia32/opcodes.rb, line 1207
def addop_macrossps(name, bin, hint, *a)
        addop name, bin.dup, hint, *a
        addop(name.sub(/ps$/, 'ss'), bin.dup, hint, *a) { |o| o.props[:needpfx] = 0xF3 }
end
addop_macrostr(name, bin, type) click to toggle source
# File metasm/cpu/ia32/opcodes.rb, line 1172
def addop_macrostr(name, bin, type)
        # addop(name, bin.dup, {:w => [0, 0]}) { |o| o.props[type] = true }     # TODO allow segment override
        addop(name+'b', bin) { |o| o.props[:opsz] = 16 ; o.props[type] = true }
        addop(name+'b', bin) { |o| o.props[:opsz] = 32 ; o.props[type] = true }
        bin = bin.dup
        bin[0] |= 1
        addop(name+'w', bin) { |o| o.props[:opsz] = 16 ; o.props[type] = true }
        addop(name+'d', bin) { |o| o.props[:opsz] = 32 ; o.props[type] = true }
end
addop_macrotttn(name, bin, hint, *props, &blk) click to toggle source
# File metasm/cpu/ia32/opcodes.rb, line 1156
def addop_macrotttn(name, bin, hint, *props, &blk)
        [%w{o},     %w{no},    %w{b nae c}, %w{nb ae nc},
         %w{z e},   %w{nz ne}, %w{be na}, %w{nbe a},
         %w{s},     %w{ns},    %w{p pe},  %w{np po},
         %w{l nge}, %w{nl ge}, %w{le ng}, %w{nle g}].each_with_index { |e, i|
                b = bin.dup
                if b[0] == 0x0F
                        b[1] |= i
                else
                        b[0] |= i
                end

                e.each { |k| addop(name + k, b.dup, hint, *props, &blk) }
        }
end
addop_post(op) click to toggle source

this recursive method is in charge of Opcode duplication (eg to hardcode some flag)

# File metasm/cpu/ia32/opcodes.rb, line 1343
def addop_post(op)
        if df = op.fields.delete(:d)
                # hardcode the bit
                dop = op.dup
                addop_post dop

                op.bin[df[0]] |= 1 << df[1]
                op.args.reverse!
                addop_post op

                return
        elsif wf = op.fields.delete(:w)
                # hardcode the bit
                dop = op.dup
                dop.props[:argsz] = 8
                # 64-bit w=0 s=1 => UD
                dop.fields.delete(:s) if @size == 64
                addop_post dop

                op.bin[wf[0]] |= 1 << wf[1]
                addop_post op

                return
        elsif sf = op.fields.delete(:s)
                # add explicit choice versions, with lower precedence (so that disassembling will return the general version)
                # eg "jmp", "jmp.i8", "jmp.i"
                # also hardcode the bit
                op32 = op
                addop_post op32

                op8 = op.dup
                op8.bin[sf[0]] |= 1 << sf[1]
                op8.args.map! { |arg| arg == :i ? :i8 : arg }
                addop_post op8

                op32 = op32.dup
                op32.name << '.i'
                addop_post op32

                op8 = op8.dup
                op8.name << '.i8'
                addop_post op8

                return
        elsif op.args.first == :regfp0
                dop = op.dup
                dop.args.delete :regfp0
                addop_post dop
        end

        if op.props[:needpfx]
                @opcode_list.unshift op
        else
                @opcode_list << op
        end

        if (op.args == [:i] or op.args == [:farptr] or op.name == 'ret') and op.name !~ /\.i/
                # define opsz-override version for ambiguous opcodes
                op16 = op.dup
                op16.name << '.i16'
                op16.props[:opsz] = 16
                @opcode_list << op16
                op32 = op.dup
                op32.name << '.i32'
                op32.props[:opsz] = 32
                @opcode_list << op32
        elsif op.props[:strop] or op.props[:stropz] or op.args.include? :mrm_imm or
                        op.args.include? :modrm or op.name =~ /loop|xlat/
                # define adsz-override version for ambiguous opcodes (TODO allow movsd edi / movsd di syntax)
                # XXX loop pfx 67 = eip+cx, 66 = ip+ecx
                op16 = op.dup
                op16.name << '.a16'
                op16.props[:adsz] = 16
                @opcode_list << op16
                op32 = op.dup
                op32.name << '.a32'
                op32.props[:adsz] = 32
                @opcode_list << op32
        end
end
addop_vex(name, vexspec, bin, *args) { |o| ... } click to toggle source

add an AVX instruction needing a VEX prefix (c4h/c5h) the prefix is hardcoded

# File metasm/cpu/ia32/opcodes.rb, line 1226
def addop_vex(name, vexspec, bin, *args)
        argnr = vexspec.shift
        argt = vexspec.shift if argnr and vexspec.first.kind_of?(::Symbol)
        l = vexspec.shift
        pfx = vexspec.shift
        of = vexspec.shift
        w = vexspec.shift
        argt ||= (l == 128 ? :vexvxmm : :vexvymm)

        lpp = ((l >> 8) << 2) | [nil, 0x66, 0xF3, 0xF2].index(pfx)
        mmmmm = [nil, 0x0F, 0x0F38, 0x0F3A].index(of)

        c4bin = [0xC4, mmmmm, lpp, bin]
        c4bin[1] |= 1 << 7 if @size != 64
        c4bin[1] |= 1 << 6 if @size != 64
        c4bin[2] |= 1 << 7 if w == 1
        c4bin[2] |= 0xF << 3 if not argnr

        addop(name, c4bin, *args) { |o|
                o.args.insert(argnr, argt) if argnr

                o.fields[:vex_r] = [1, 7] if @size == 64
                o.fields[:vex_x] = [1, 6] if @size == 64
                o.fields[:vex_b] = [1, 5]
                o.fields[:vex_w] = [2, 7] if not w
                o.fields[:vex_vvvv] = [2, 3] if argnr

                yield o if block_given?
        }

        return if w == 1 or mmmmm != 1

        c5bin = [0xC5, lpp, bin]
        c5bin[1] |= 1 << 7 if @size != 64
        c5bin[1] |= 0xF << 3 if not argnr

        addop(name, c5bin, *args) { |o|
                o.args.insert(argnr, argt) if argnr

                o.fields[:vex_r] = [1, 7] if @size == 64
                o.fields[:vex_vvvv] = [1, 3] if argnr

                yield o if block_given?
        }
end
adsz(di, op=nil) click to toggle source
# File metasm/cpu/ia32/decode.rb, line 332
def adsz(di, op=nil)
        if di and di.instruction.prefix and di.instruction.prefix[:adsz] and (op || di.opcode).props[:needpfx] != 0x67; 48-@size
        else @size
        end
end
backtrace_is_function_return(expr, di=nil) click to toggle source

checks if expr is a valid return expression matching the :saveip instruction

# File metasm/cpu/ia32/decode.rb, line 909
def backtrace_is_function_return(expr, di=nil)
        expr = Expression[expr].reduce_rec
        expr.kind_of? Indirection and expr.len == @size/8 and expr.target == Expression[register_symbols[4]]
end
backtrace_is_stack_address(expr) click to toggle source

returns true if the expression is an address on the stack

# File metasm/cpu/ia32/decode.rb, line 1011
def backtrace_is_stack_address(expr)
        Expression[expr].expr_externals.include? register_symbols[4]
end
backtrace_update_function_binding(dasm, faddr, f, retaddrlist, *wantregs) click to toggle source

updates the function backtrace_binding if the function is big and no specific register is given, do nothing (the binding will be lazily updated later, on demand) XXX assume retaddrlist is either a list of addr of ret or a list with a single entry which is an external function name (thunk)

# File metasm/cpu/ia32/decode.rb, line 917
def backtrace_update_function_binding(dasm, faddr, f, retaddrlist, *wantregs)
        b = f.backtrace_binding

        esp, ebp = register_symbols[4, 2]

        # XXX handle retaddrlist for multiple/mixed thunks
        if retaddrlist and not dasm.decoded[retaddrlist.first] and di = dasm.decoded[faddr]
                # no return instruction, must be a thunk : find the last instruction (to backtrace from it)
                done = []
                while ndi = dasm.decoded[di.block.to_subfuncret.to_a.first] || dasm.decoded[di.block.to_normal.to_a.first] and ndi.kind_of? DecodedInstruction and not done.include? ndi.address
                        done << ndi.address
                        di = ndi
                end
                if not di.block.to_subfuncret.to_a.first and di.block.to_normal and di.block.to_normal.length > 1
                        thunklast = di.block.list.last.address
                end
        end

        bt_val = lambda { |r|
                next if not retaddrlist
                b[r] = Expression::Unknown   # TODO :pending or something ? (for recursive lazy functions)
                bt = []
                retaddrlist.each { |retaddr|
                        bt |= dasm.backtrace(Expression[r], (thunklast ? thunklast : retaddr),
                                :include_start => true, :snapshot_addr => faddr, :origin => retaddr, :from_subfuncret => thunklast)
                }
                if bt.length != 1
                        b[r] = Expression::Unknown
                else
                        b[r] = bt.first
                end
        }

        if not wantregs.empty?
                wantregs.each(&bt_val)
        else
                if dasm.function_blocks(faddr, true).length < 20
                        register_symbols.each(&bt_val)
                else
                        [ebp, esp].each(&bt_val)
                end
        end

        backtrace_update_function_binding_check(dasm, faddr, f, b, &bt_val)

        b
end
backtrace_update_function_binding_check(dasm, faddr, f, b) { |ind| ... } click to toggle source
# File metasm/cpu/ia32/decode.rb, line 965
def backtrace_update_function_binding_check(dasm, faddr, f, b)
        sz = @size/8
        if b[:ebp] and b[:ebp] != Expression[:ebp]
                # may be a custom 'enter' function (eg recent Visual Studio)
                # TODO put all memory writes in the binding ?
                [[:ebp], [:esp, :+, 1*sz], [:esp, :+, 2*sz], [:esp, :+, 3*sz]].each { |ptr|
                        ind = Indirection[ptr, sz, faddr]
                        yield(ind)
                        b.delete(ind) if b[ind] and not [:ebx, :edx, :esi, :edi, :ebp].include? b[ind].reduce_rec
                }
        end
        if dasm.funcs_stdabi
                if b[:esp] and b[:esp] == Expression::Unknown and not f.btbind_callback
                        puts "update_func_bind: #{Expression[faddr]} has esp -> unknown, use dynamic callback" if $DEBUG
                        f.btbind_callback = disassembler_default_btbind_callback
                end
                [:ebp, :ebx, :esi, :edi].each { |reg|
                        if b[reg] and b[reg] == Expression::Unknown
                                puts "update_func_bind: #{Expression[faddr]} has #{reg} -> unknown, presume it is preserved" if $DEBUG
                                b[reg] = Expression[reg]
                        end
                }
        else
                if b[:esp] and not Expression[b[:esp], :-, :esp].reduce.kind_of?(::Integer)
                        puts "update_func_bind: #{Expression[faddr]} has esp -> #{b[:esp]}" if $DEBUG
                end
        end

        # rename some functions
        # TODO database and real signatures
        rename =
        if b[:eax] and Expression[b[:eax], :-, faddr].reduce == 0
                'geteip' # metasm pic linker
        elsif b[:eax] and b[:ebx] and  Expression[b[:eax], :-, :eax].reduce == 0 and Expression[b[:ebx], :-, Indirection[:esp, sz, nil]].reduce == 0
                'get_pc_thunk_ebx' # elf pic convention
        elsif b[:esp] and Expression[b[:esp], :-, [:esp, :-, Indirection[[:esp, :+, 2*sz], sz]]].reduce.kind_of? ::Integer and
                        dasm.decoded[faddr].block.list.find { |di| di.backtrace_binding[Indirection['segment_base_fs', sz]] }
                '__SEH_prolog'
        elsif b[:esp] == Expression[:ebp, :+, sz] and
                        dasm.decoded[faddr].block.list.find { |di| di.backtrace_binding[Indirection['segment_base_fs', sz]] }
                '__SEH_epilog'
        end
        dasm.auto_label_at(faddr, rename, 'loc', 'sub') if rename
end
build_bin_lookaside() click to toggle source
# File metasm/cpu/ia32/decode.rb, line 90
def build_bin_lookaside
        # sets up a hash byte value => list of opcodes that may match
        # opcode.bin_mask is built here
        lookaside = Array.new(256) { [] }
        opcode_list.each { |op|

                build_opcode_bin_mask op

                b   = op.bin[0]
                msk = op.bin_mask[0]

                for i in b..(b | (255^msk))
                        lookaside[i] << op if i & msk == b & msk
                end
        }
        lookaside
end
build_opcode_bin_mask(op) click to toggle source
# File metasm/cpu/ia32/decode.rb, line 81
def build_opcode_bin_mask(op)
        # bit = 0 if can be mutated by an field value, 1 if fixed by opcode
        op.bin_mask = Array.new(op.bin.length, 0)
        op.fields.each { |f, (oct, off)|
                op.bin_mask[oct] |= (@fields_mask[f] << off)
        }
        op.bin_mask.map! { |v| 255 ^ v }
end
check_reserved_name(name) click to toggle source
# File metasm/cpu/ia32/parse.rb, line 353
def check_reserved_name(name)
        Reg.s_to_i[name]
end
code_binding(dasm, entry, finish=nil, nargs={}) click to toggle source

computes the binding of the sequence of code starting at entry included the binding is a hash showing the value of modified elements at the end of the code sequence, relative to their value at entry the elements are all the registers and the memory written to if finish is nil, the binding will include :ip, which is the address to be executed next (if it exists) the binding will not include memory access from subfunctions entry should be an entrypoint of the disassembler if finish is nil the code sequence must have only one end, with no to_normal options:

:include_flags => include EFLAGS in the returned binding
# File metasm/cpu/ia32/decode.rb, line 1249
def code_binding(dasm, entry, finish=nil, nargs={})
        include_flags = nargs.delete :include_flags

        entry = dasm.normalize(entry)
        finish = dasm.normalize(finish) if finish
        lastdi = nil
        bd = {}
        bt = lambda { |from, expr, inc_start|
                ret = dasm.backtrace(Expression[expr], from, :snapshot_addr => entry, :include_start => inc_start)
                ret.length == 1 ? ret.first : Expression::Unknown
        }

        # walk blocks, search for finish, scan memory writes
        todo = [entry]
        done = [Expression::Unknown]
        while addr = todo.pop
                addr = dasm.normalize(addr)
                next if done.include? addr or addr == finish or not dasm.decoded[addr].kind_of? DecodedInstruction
                done << addr
                b = dasm.decoded[addr].block

                next if b.list.find { |di|
                        a = di.address
                        if a == finish
                                lastdi = b.list[b.list.index(di) - 1]
                                true
                        else
                                # check writes from the instruction
                                get_xrefs_w(dasm, di).each { |waddr, len|
                                        # we want the ptr expressed with reg values at entry
                                        ptr = bt[a, waddr, false]
                                        bd[Indirection[ptr, len, a]] = bt[a, Indirection[waddr, len, a], true]
                                }
                                false
                        end
                }

                hasnext = false
                b.each_to_samefunc(dasm) { |t|
                        hasnext = true
                        if t == finish
                                lastdi = b.list.last
                        else
                                todo << t
                        end
                }

                # check end of sequence
                if not hasnext
                        raise "two-ended code_binding #{lastdi} & #{b.list.last}" if lastdi
                        lastdi = b.list.last
                        if lastdi.opcode.props[:setip]
                                e = get_xrefs_x(dasm, lastdi)
                                raise 'bad code_binding ending' if e.to_a.length != 1 or not lastdi.opcode.props[:stopexec]
                                bd[:ip] = bt[lastdi.address, e.first, false]
                        elsif not lastdi.opcode.props[:stopexec]
                                bd[:ip] = lastdi.next_addr
                        end
                end
        end
        bd.delete_if { |k, v| Expression[k] == Expression[v] }

        # add register binding
        raise "no code_binding end" if not lastdi and not finish
        register_symbols.each { |reg|
                val =
                        if lastdi; bt[lastdi.address, reg, true]
                        else bt[finish, reg, false]
                        end
                next if val == Expression[reg]
                mask = 0xffff_ffff   # dont use 1<<@size, because 16bit code may use e.g. edi (through opszoverride)
                mask = 0xffff_ffff_ffff_ffff if @size == 64
                val = Expression[val, :&, mask].reduce
                bd[reg] = Expression[val]
        }

        # add EFLAGS binding
        if include_flags
                [:eflag_z, :eflag_s, :eflag_c, :eflag_o].each { |eflag|
                        val =
                                if lastdi; bt[lastdi.address, eflag, true]
                                else bt[finish, eflag, false]
                                end
                        next if val == Expression[eflag]
                        bd[eflag] = Expression[val.reduce]
                }
        end

        bd
end
context() click to toggle source
# File metasm/cpu/ia32/render.rb, line 22
def context ; {'set sz' => lambda { |s| @sz = s }} end
dbg_alloc_bphw(dbg, bp) click to toggle source

allocate a debug register for a hwbp by checking the list of hwbp existing in dbg

# File metasm/cpu/ia32/debug.rb, line 81
def dbg_alloc_bphw(dbg, bp)
        if not bp.internal[:dr]
                may = [0, 1, 2, 3]
                dbg.breakpoint_thread.values.each { |bb| may.delete bb.internal[:dr] }
                raise 'alloc_bphw: no free debugregister' if may.empty?
                bp.internal[:dr] = may.first
        end
        bp.internal[:type] ||= :x
        bp.internal[:len]  ||= 1
        bp.internal[:dr]
end
dbg_check_pre_run(dbg) click to toggle source
# File metasm/cpu/ia32/debug.rb, line 114
def dbg_check_pre_run(dbg)
        if dbg[:dr6] == 0 and dbg[:dr7] == 0
                dbg[:dr7] = 0x10000  # some OS (eg Windows) only return dr6 if dr7 != 0
        end
        dbg[:dr6] = 0 if dbg[:dr6] & 0x400f != 0
end
dbg_disable_bp(dbg, bp) click to toggle source
# File metasm/cpu/ia32/debug.rb, line 62
def dbg_disable_bp(dbg, bp)
        case bp.type
        when :bpx; dbg_disable_bpx( dbg, bp)
        else       dbg_disable_bphw(dbg, bp)
        end
end
dbg_disable_bphw(dbg, bp) click to toggle source
# File metasm/cpu/ia32/debug.rb, line 107
def dbg_disable_bphw(dbg, bp)
        nr = bp.internal[:dr]
        dr7 = dbg[:dr7]
        dr7 &= ~(3 << (2*nr))
        dbg[:dr7] = dr7
end
dbg_disable_bpx(dbg, bp) click to toggle source
# File metasm/cpu/ia32/debug.rb, line 76
def dbg_disable_bpx(dbg, bp)
        dbg.memory[bp.address, 1] = bp.internal[:previous]
end
dbg_disable_singlestep(dbg) click to toggle source
# File metasm/cpu/ia32/debug.rb, line 51
def dbg_disable_singlestep(dbg)
        dbg_unset_flag(dbg, :t) if dbg_get_flag(dbg, :t) != 0
end
dbg_enable_bp(dbg, bp) click to toggle source
# File metasm/cpu/ia32/debug.rb, line 55
def dbg_enable_bp(dbg, bp)
        case bp.type
        when :bpx; dbg_enable_bpx( dbg, bp)
        else       dbg_enable_bphw(dbg, bp)
        end
end
dbg_enable_bphw(dbg, bp) click to toggle source
# File metasm/cpu/ia32/debug.rb, line 93
def dbg_enable_bphw(dbg, bp)
        nr = dbg_alloc_bphw(dbg, bp)
        dr7 = dbg[:dr7]
        l = { 1 => 0, 2 => 1, 4 => 3, 8 => 2 }[bp.internal[:len]]
        rw = { :x => 0, :w => 1, :r => 3 }[bp.internal[:type]]
        raise "enable_bphw: invalid breakpoint #{bp.inspect}" if not l or not rw
        dr7 &= ~((15 << (16+4*nr)) | (3 << (2*nr)))   # clear
        dr7 |= ((l << 2) | rw) << (16+4*nr)   # set drN len/rw
        dr7 |= 3 << (2*nr)    # enable global/local drN

        dbg["dr#{nr}"] = bp.address
        dbg[:dr7] = dr7
end
dbg_enable_bpx(dbg, bp) click to toggle source
# File metasm/cpu/ia32/debug.rb, line 71
def dbg_enable_bpx(dbg, bp)
        bp.internal[:previous] ||= dbg.memory[bp.address, 1]
        dbg.memory[bp.address, 1] = DBG_BPX
end
dbg_enable_singlestep(dbg) click to toggle source
# File metasm/cpu/ia32/debug.rb, line 48
def dbg_enable_singlestep(dbg)
        dbg_set_flag(dbg, :t) if dbg_get_flag(dbg, :t) == 0
end
dbg_end_stepout(dbg, addr, di) click to toggle source
# File metasm/cpu/ia32/debug.rb, line 144
def dbg_end_stepout(dbg, addr, di)
        di and di.opcode.name == 'ret'
end
dbg_evt_bpx(dbg, b) click to toggle source
# File metasm/cpu/ia32/debug.rb, line 121
def dbg_evt_bpx(dbg, b)
        if b.address == dbg.pc-1
                dbg.pc -= 1
        end
end
dbg_find_bpx(dbg) click to toggle source
# File metasm/cpu/ia32/debug.rb, line 127
def dbg_find_bpx(dbg)
        return if dbg[:dr6] & 0x4000 != 0
        pc = dbg.pc
        dbg.breakpoint[pc-1] || dbg.breakpoint[pc]
end
dbg_find_hwbp(dbg) click to toggle source
# File metasm/cpu/ia32/debug.rb, line 133
def dbg_find_hwbp(dbg)
        dr6 = dbg[:dr6]
        return if dr6 & 0xf == 0
        dn = (0..3).find { |n| dr6 & (1 << n) }
        dbg.breakpoint_thread.values.find { |b| b.internal[:dr] == dn }
end
dbg_flag_list() click to toggle source
# File metasm/cpu/ia32/debug.rb, line 29
def dbg_flag_list
        @dbg_flag_list ||= [:c, :p, :a, :z, :s, :i, :d, :o]
end
dbg_func_arg(dbg, argnr) click to toggle source

retrieve the current function arguments only valid at function entry (eg right after the call)

# File metasm/cpu/ia32/debug.rb, line 188
def dbg_func_arg(dbg, argnr)
        dbg.memory_read_int(Expression[:esp, :+, 4*(argnr+1)])
end
dbg_func_arg_set(dbg, argnr, arg) click to toggle source
# File metasm/cpu/ia32/debug.rb, line 191
def dbg_func_arg_set(dbg, argnr, arg)
        dbg.memory_write_int(Expression[:esp, :+, 4*(argnr+1)], arg)
end
dbg_func_retaddr(dbg) click to toggle source

retrieve the current function return address to be called only on entry of the subfunction

# File metasm/cpu/ia32/debug.rb, line 179
def dbg_func_retaddr(dbg)
        dbg.memory_read_int(dbg_register_list[7])
end
dbg_func_retaddr_set(dbg, ret) click to toggle source
# File metasm/cpu/ia32/debug.rb, line 182
def dbg_func_retaddr_set(dbg, ret)
        dbg.memory_write_int(dbg_register_list[7], ret)
end
dbg_func_retval(dbg) click to toggle source

retrieve the current function return value only valid at function exit

# File metasm/cpu/ia32/debug.rb, line 170
def dbg_func_retval(dbg)
        dbg.get_reg_value(dbg_register_list[0])
end
dbg_func_retval_set(dbg, val) click to toggle source
# File metasm/cpu/ia32/debug.rb, line 173
def dbg_func_retval_set(dbg, val)
        dbg.set_reg_value(dbg_register_list[0], val)
end
dbg_get_flag(dbg, f) click to toggle source
# File metasm/cpu/ia32/debug.rb, line 34
def dbg_get_flag(dbg, f)
        (dbg.get_reg_value(dbg_register_flags) >> DBG_FLAGS[f]) & 1
end
dbg_need_stepover(dbg, addr, di) click to toggle source
# File metasm/cpu/ia32/debug.rb, line 140
def dbg_need_stepover(dbg, addr, di)
        di and ((di.instruction.prefix and di.instruction.prefix[:rep]) or di.opcode.props[:saveip])
end
dbg_register_flags() click to toggle source
# File metasm/cpu/ia32/debug.rb, line 17
def dbg_register_flags
        @dbg_register_flags ||= :eflags
end
dbg_register_list() click to toggle source
# File metasm/cpu/ia32/debug.rb, line 21
def dbg_register_list
        @dbg_register_list ||= [:eax, :ebx, :ecx, :edx, :esi, :edi, :ebp, :esp, :eip]
end
dbg_register_pc() click to toggle source
# File metasm/cpu/ia32/debug.rb, line 11
def dbg_register_pc
        @dbg_register_pc ||= :eip
end
dbg_register_size() click to toggle source
# File metasm/cpu/ia32/debug.rb, line 25
def dbg_register_size
        @dbg_register_size ||= Hash.new(32).update(:cs => 16, :ds => 16, :es => 16, :fs => 16, :gs => 16)
end
dbg_register_sp() click to toggle source
# File metasm/cpu/ia32/debug.rb, line 14
def dbg_register_sp
        @dbg_register_sp ||= dbg_register_list[7]
end
dbg_resolve_pc(di, fbd, pc_reg, dbg_ctx) click to toggle source
Calls superclass method Metasm::CPU#dbg_resolve_pc
# File metasm/cpu/ia32/debug.rb, line 195
def dbg_resolve_pc(di, fbd, pc_reg, dbg_ctx)
        a = di.instruction.args.map { |aa| symbolic(aa) }

        cond = case di.opcode.name
        when 'jz', 'je';    dbg_ctx.get_flag(:z)
        when 'jnz', 'jne'; !dbg_ctx.get_flag(:z)
        when 'jo';   dbg_ctx.get_flag(:o)
        when 'jno'; !dbg_ctx.get_flag(:o)
        when 'js';   dbg_ctx.get_flag(:s)
        when 'jns'; !dbg_ctx.get_flag(:s)
        when 'jc', 'jb', 'jnae';   dbg_ctx.get_flag(:c)
        when 'jnc', 'jnb', 'jae'; !dbg_ctx.get_flag(:c)
        when 'jbe', 'jna';  dbg_ctx.get_flag(:c) or   dbg_ctx.get_flag(:z)
        when 'jnbe', 'ja'; !dbg_ctx.get_flag(:c) and !dbg_ctx.get_flag(:z)
        when 'jl', 'jnge'; dbg_ctx.get_flag(:s) != dbg_ctx.get_flag(:o)
        when 'jnl', 'jge'; dbg_ctx.get_flag(:s) == dbg_ctx.get_flag(:o)
        when 'jle', 'jng';  dbg_ctx.get_flag(:z) or  dbg_ctx.get_flag(:s) != dbg_ctx.get_flag(:o)
        when 'jnle', 'jg'; !dbg_ctx.get_flag(:z) and dbg_ctx.get_flag(:s) == dbg_ctx.get_flag(:o)
        when 'jp', 'jpe';   dbg_ctx.get_flag(:p)
        when 'jnp', 'jpo'; !dbg_ctx.get_flag(:p)
        when 'loop';             dbg_ctx[dbg_register_list[2]] != 0
        when 'loopz', 'loope';   dbg_ctx[dbg_register_list[2]] != 0 and  dbg_ctx.get_flag(:z)
        when 'loopnz', 'loopne'; dbg_ctx[dbg_register_list[2]] != 0 and !dbg_ctx.get_flag(:z)
        when 'jcxz', 'jecxz', 'jrcxz'
                mask = {?c => 0xffff, ?e => 0xffff_ffff, ?r => -1}[di.opcode.name[1]]
                dbg_ctx[dbg_register_list[2]] & mask == 0
        else return super(di, fbd, pc_reg, dbg_ctx)
        end

        if cond
                fbd[pc_reg] = a.last
        else
                fbd[pc_reg] = di.next_addr
        end
end
dbg_set_flag(dbg, f) click to toggle source
# File metasm/cpu/ia32/debug.rb, line 37
def dbg_set_flag(dbg, f)
        fl = dbg.get_reg_value(dbg_register_flags)
        fl |= 1 << DBG_FLAGS[f]
        dbg.set_reg_value(dbg_register_flags, fl)
end
dbg_stacktrace(dbg, rec=500) { |pc, s| ... } click to toggle source

return (yield) a list of [addr, symbolic name]

# File metasm/cpu/ia32/debug.rb, line 149
def dbg_stacktrace(dbg, rec=500)
        ret = []
        s = dbg.addrname!(dbg.pc)
        yield(dbg.pc, s) if block_given?
        ret << [dbg.pc, s]
        fp = dbg.get_reg_value(dbg_register_list[6])
        stack = dbg.get_reg_value(dbg_register_list[7]) - 8
        while fp > stack and fp <= stack+0x10000 and rec != 0
                rec -= 1
                ra = dbg.resolve_expr Indirection[fp+4, 4]
                s = dbg.addrname!(ra)
                yield(ra, s) if block_given?
                ret << [ra, s]
                stack = fp   # ensure we walk the stack upwards
                fp = dbg.resolve_expr Indirection[fp, 4]
        end
        ret
end
dbg_unset_flag(dbg, f) click to toggle source
# File metasm/cpu/ia32/debug.rb, line 42
def dbg_unset_flag(dbg, f)
        fl = dbg.get_reg_value(dbg_register_flags)
        fl &= ~(1 << DBG_FLAGS[f])
        dbg.set_reg_value(dbg_register_flags, fl)
end
decode_c_function_prototype(cp, sym, orig=nil) click to toggle source

returns a DecodedFunction from a parsed C function prototype TODO rebacktrace already decoded functions (load a header file after dasm finished) TODO walk structs args

# File metasm/cpu/ia32/decode.rb, line 1031
def decode_c_function_prototype(cp, sym, orig=nil)
        sym = cp.toplevel.symbol[sym] if sym.kind_of?(::String)
        df = DecodedFunction.new
        orig ||= Expression[sym.name]

        new_bt = lambda { |expr, rlen|
                df.backtracked_for << BacktraceTrace.new(expr, orig, expr, rlen ? :r : :x, rlen)
        }

        # return instr emulation
        if sym.has_attribute 'noreturn' or sym.has_attribute '__noreturn__'
                df.noreturn = true
        else
                new_bt[Indirection[:esp, @size/8, orig], nil]
        end

        # register dirty (XXX assume standard ABI)
        [:eax, :ecx, :edx].each { |r|
                df.backtrace_binding.update r => Expression::Unknown
        }

        # emulate ret <n>
        al = cp.typesize[:ptr]
        stackoff = al
        if sym.has_attribute 'fastcall'
                stackoff = sym.type.args.to_a[2..-1].to_a.inject(al) { |sum, a| sum += (cp.sizeof(a) + al - 1) / al * al }
        elsif sym.has_attribute 'stdcall'
                stackoff = sym.type.args.to_a.inject(al) { |sum, a| sum += (cp.sizeof(a) + al - 1) / al * al }
        end
        df.backtrace_binding[:esp] = Expression[:esp, :+, stackoff]

        # scan args for function pointers
        # TODO walk structs/unions..
        stackoff = al
        sym.type.args.to_a.each { |a|
                p = Indirection[[:esp, :+, stackoff], al, orig]
                stackoff += (cp.sizeof(a) + al - 1) / al * al
                if a.type.untypedef.kind_of? C::Pointer
                        pt = a.type.untypedef.type.untypedef
                        if pt.kind_of? C::Function
                                new_bt[p, nil]
                                df.backtracked_for.last.detached = true
                        elsif pt.kind_of? C::Struct
                                new_bt[p, al]
                        else
                                new_bt[p, cp.sizeof(nil, pt)]
                        end
                end
        }

        df
end
decode_cc_to_expr(cc) click to toggle source

interprets a condition code (in an opcode name) as an expression involving backtracked eflags eflag_p is never computed, and this returns Expression::Unknown for this flag ex: 'z' => Expression

# File metasm/cpu/ia32/decode.rb, line 303
def decode_cc_to_expr(cc)
        case cc
        when 'o'; Expression[:eflag_o]
        when 'no'; Expression[:'!', :eflag_o]
        when 'b', 'nae', 'c'; Expression[:eflag_c]
        when 'nb', 'ae', 'nc'; Expression[:'!', :eflag_c]
        when 'z', 'e'; Expression[:eflag_z]
        when 'nz', 'ne'; Expression[:'!', :eflag_z]
        when 'be', 'na'; Expression[:eflag_c, :|, :eflag_z]
        when 'nbe', 'a'; Expression[:'!', [:eflag_c, :|, :eflag_z]]
        when 's'; Expression[:eflag_s]
        when 'ns'; Expression[:'!', :eflag_s]
        when 'p', 'pe'; Expression::Unknown
        when 'np', 'po'; Expression::Unknown
        when 'l', 'nge'; Expression[:eflag_s, :'!=', :eflag_o]
        when 'nl', 'ge'; Expression[:eflag_s, :==, :eflag_o]
        when 'le', 'ng'; Expression[[:eflag_s, :'!=', :eflag_o], :|, :eflag_z]
        when 'nle', 'g'; Expression[[:eflag_s, :==, :eflag_o], :&, :eflag_z]
        when 'ecxz'; Expression[:'!', register_symbols[1]]
        when 'cxz'; Expression[:'!', [register_symbols[1], :&, 0xffff]]
        end
end
decode_findopcode(edata) click to toggle source

tries to find the opcode encoded at edata.ptr if no match, tries to match a prefix (update di.instruction.prefix) on match, edata.ptr points to the first byte of the opcode (after prefixes)

# File metasm/cpu/ia32/decode.rb, line 137
def decode_findopcode(edata)
        di = DecodedInstruction.new self
        while edata.ptr < edata.data.length
                pfx = di.instruction.prefix || {}
                byte = edata.data[edata.ptr]
                byte = byte.unpack('C').first if byte.kind_of?(::String)
                return di if di.opcode = @bin_lookaside[byte].find { |op|
                        # fetch the relevant bytes from edata
                        bseq = edata.data[edata.ptr, op.bin.length].unpack('C*')

                        # check against full opcode mask
                        op.bin.zip(bseq, op.bin_mask).all? { |b1, b2, m| b2 and ((b1 & m) == (b2 & m)) } and
                        # check special cases
                        !(
                          # fail if any of those is true
                          (fld = op.fields[:seg2A]  and (bseq[fld[0]] >> fld[1]) & @fields_mask[:seg2A] == 1) or
                          (fld = op.fields[:seg3A]  and (bseq[fld[0]] >> fld[1]) & @fields_mask[:seg3A] < 4) or
                          (fld = op.fields[:seg3A] || op.fields[:seg3] and (bseq[fld[0]] >> fld[1]) & @fields_mask[:seg3] > 5) or
                          (op.props[:modrmA] and fld = op.fields[:modrm] and (bseq[fld[0]] >> fld[1]) & 0xC0 == 0xC0) or
                          (op.props[:modrmR] and fld = op.fields[:modrm] and (bseq[fld[0]] >> fld[1]) & 0xC0 != 0xC0) or
                          (fld = op.fields[:vex_vvvv] and @size != 64 and (bseq[fld[0]] >> fld[1]) & @fields_mask[:vex_vvvv] < 8) or
                          (sz = op.props[:opsz] and opsz(di, op) != sz) or
                          (sz = op.props[:adsz] and adsz(di, op) != sz) or
                          (ndpfx = op.props[:needpfx] and not pfx[:list].to_a.include? ndpfx) or
                          (pfx[:adsz] and op.props[:adsz] and op.props[:adsz] == @size) or
                          # return non-ambiguous opcode (eg push.i16 in 32bit mode) / sync with addop_post in opcode.rb
                          (pfx[:opsz] and not op.props[:opsz] and (op.args == [:i] or op.args == [:farptr] or op.name == 'ret')) or
                          (pfx[:adsz] and not op.props[:adsz] and (op.props[:strop] or op.props[:stropz] or op.args.include?(:mrm_imm) or op.args.include?(:modrm) or op.name =~ /loop|xlat/)) or
                          (op.name == 'nop' and op.bin[0] == 0x90 and di.instruction.prefix and di.instruction.prefix[:rex_b])
                         )
                }

                break if not decode_prefix(di.instruction, edata.get_byte)
                di.bin_length += 1
        end
end
decode_instr_interpret(di, addr) click to toggle source

converts relative jump/call offsets to absolute addresses adds the eip delta to the offset off of the instruction (may be an Expression) + its bin_length do not call twice on the same di !

# File metasm/cpu/ia32/decode.rb, line 282
def decode_instr_interpret(di, addr)
        if di.opcode.props[:setip] and di.instruction.args.last.kind_of? Expression and di.instruction.opname !~ /^i?ret/
                delta = di.instruction.args.last.reduce
                arg = Expression[[addr, :+, di.bin_length], :+, delta].reduce
                di.instruction.args[-1] = Expression[arg]
        end

        di
end
decode_instr_op(edata, di) click to toggle source
# File metasm/cpu/ia32/decode.rb, line 174
def decode_instr_op(edata, di)
        before_ptr = edata.ptr
        op = di.opcode
        di.instruction.opname = op.name
        bseq = edata.read(op.bin.length).unpack('C*')         # decode_findopcode ensures that data >= op.length
        pfx = di.instruction.prefix || {}

        case op.props[:needpfx]
        when 0x66; pfx.delete :opsz
        when 0x67; pfx.delete :adsz
        when 0xF2, 0xF3; pfx.delete :rep
        end

        if op.props[:setip] and not op.props[:stopexec] and pfx[:seg]
                case pfx.delete(:seg).val
                when 1; pfx[:jmphint] = 'hintnojmp'
                when 3; pfx[:jmphint] = 'hintjmp'
                end
        end

        field_val = lambda { |f|
                if fld = op.fields[f]
                        (bseq[fld[0]] >> fld[1]) & @fields_mask[f]
                end
        }

        opsz = op.props[:argsz] || opsz(di)
        adsz = (pfx[:adsz] ? 48 - @size : @size)

        mmxsz = ((op.props[:xmmx] && pfx[:opsz]) ? 128 : 64)
        op.args.each { |a|
                di.instruction.args << case a
                when :reg;    Reg.new     field_val[a], opsz
                when :eeec;   CtrlReg.new field_val[a]
                when :eeed;   DbgReg.new  field_val[a]
                when :eeet;   TstReg.new  field_val[a]
                when :seg2, :seg2A, :seg3, :seg3A; SegReg.new field_val[a]
                when :regfp;  FpReg.new   field_val[a]
                when :regmmx; SimdReg.new field_val[a], mmxsz
                when :regxmm; SimdReg.new field_val[a], 128
                when :regymm; SimdReg.new field_val[a], 256

                when :farptr; Farptr.decode edata, @endianness, opsz
                when :i8, :u8, :u16; Expression[edata.decode_imm(a, @endianness)]
                when :i; Expression[edata.decode_imm("#{op.props[:unsigned_imm] ? 'a' : 'i'}#{opsz}".to_sym, @endianness)]

                when :mrm_imm;  ModRM.decode edata, (adsz == 16 ? 6 : 5), @endianness, adsz, opsz, pfx.delete(:seg)
                when :modrm; ModRM.decode edata, field_val[:modrm], @endianness, adsz, opsz, pfx.delete(:seg)
                when :modrmmmx; ModRM.decode edata, field_val[:modrm], @endianness, adsz, mmxsz, pfx.delete(:seg), SimdReg, :argsz => op.props[:argsz]
                when :modrmxmm; ModRM.decode edata, field_val[:modrm], @endianness, adsz, 128, pfx.delete(:seg), SimdReg, :argsz => op.props[:argsz], :mrmvex => op.props[:mrmvex]
                when :modrmymm; ModRM.decode edata, field_val[:modrm], @endianness, adsz, 256, pfx.delete(:seg), SimdReg, :argsz => op.props[:argsz], :mrmvex => op.props[:mrmvex]

                when :vexvreg; Reg.new((field_val[:vex_vvvv] ^ 0xf), opsz)
                when :vexvxmm; SimdReg.new((field_val[:vex_vvvv] ^ 0xf), 128)
                when :vexvymm; SimdReg.new((field_val[:vex_vvvv] ^ 0xf), 256)
                when :i4xmm; SimdReg.new((edata.decode_imm(:u8, @endianness) >> 4) & 7, 128)
                when :i4ymm; SimdReg.new((edata.decode_imm(:u8, @endianness) >> 4) & 7, 256)

                when :imm_val1; Expression[1]
                when :imm_val3; Expression[3]
                when :reg_cl;   Reg.new 1, 8
                when :reg_eax;  Reg.new 0, opsz
                when :reg_dx;   Reg.new 2, 16
                when :regfp0;   FpReg.new nil
                else raise SyntaxError, "Internal error: invalid argument #{a} in #{op.name}"
                end
        }

        di.bin_length += edata.ptr - before_ptr

        return false if edata.ptr > edata.length

        if op.name == 'movsx' or op.name == 'movzx'
                if di.opcode.props[:argsz] == 8
                        di.instruction.args[1].sz = 8
                else
                        di.instruction.args[1].sz = 16
                end
                if pfx[:opsz]
                        di.instruction.args[0].sz = 48-@size
                else
                        di.instruction.args[0].sz = @size
                end
        elsif op.name == 'crc32'
                di.instruction.args[0].sz = 32
        end

        case pfx.delete(:rep)
        when :nz
                if di.opcode.props[:strop]
                        pfx[:rep] = 'rep'
                elsif di.opcode.props[:stropz]
                        pfx[:rep] = 'repnz'
                end
        when :z
                if di.opcode.props[:strop]
                        pfx[:rep] = 'rep'
                elsif di.opcode.props[:stropz]
                        pfx[:rep] = 'repz'
                end
        end

        di
end
decode_prefix(instr, byte) click to toggle source
# File metasm/cpu/ia32/decode.rb, line 108
def decode_prefix(instr, byte)
        instr.prefix ||= {}
        (instr.prefix[:list] ||= []) << byte

        # XXX actual limit = 15-instr.length
        return false if instr.prefix[:list].length >= 15

        case byte
        when 0x66; instr.prefix[:opsz] = true
        when 0x67; instr.prefix[:adsz] = true
        when 0xF0; instr.prefix[:lock] = true
        when 0xF2; instr.prefix[:rep]  = :nz
        when 0xF3; instr.prefix[:rep]  = :z   # postprocessed by decode_instr
        when 0x26, 0x2E, 0x36, 0x3E, 0x64, 0x65
                if byte & 0x40 == 0
                        v = (byte >> 3) & 3
                else
                        v = byte & 7
                end
                instr.prefix[:seg] = SegReg.new(v)
        else
                return false
        end
        true
end
decompile_blocks(dcmp, myblocks, deps, func, nextaddr = nil) click to toggle source
# File metasm/cpu/ia32/decompile.rb, line 196
def decompile_blocks(dcmp, myblocks, deps, func, nextaddr = nil)
        eax, ecx, edx, ebx, esp, ebp, esi, edi = register_symbols
        ebx, esp, ebp = ebx, esp, ebp # fix ruby unused var warning
        scope = func.initializer
        func.type.args.each { |a| scope.symbol[a.name] = a }
        stmts = scope.statements
        blocks_toclean = myblocks.dup
        func_entry = myblocks.first[0]
        di_addr = nil
        until myblocks.empty?
                b, to = myblocks.shift
                if l = dcmp.dasm.get_label_at(b)
                        stmts << C::Label.new(l).with_misc(:di_addr => b)
                end

                # list of assignments [[dest reg, expr assigned]]
                ops = []
                # reg binding (reg => value, values.externals = regs at block start)
                binding = {}
                # Expr => CExpr
                ce  = lambda { |*e|
                        ret = dcmp.decompile_cexpr(Expression[Expression[*e].reduce], scope)
                        dcmp.walk_ce(ret) { |ee| ee.with_misc(:di_addr => di_addr) } if di_addr
                        ret
                }
                # Expr => Expr.bind(binding) => CExpr
                ceb = lambda { |*e| ce[Expression[*e].bind(binding)] }

                # dumps a CExprs that implements an assignment to a reg (uses ops[], patches op => [reg, nil])
                commit = lambda {
                        deps[b].map { |k|
                                [k, ops.rindex(ops.reverse.find { |r, v| r == k })]
                        }.sort_by { |k, i| i.to_i }.each { |k, i|
                                next if not i or not binding[k]
                                e = k
                                final = []
                                ops[0..i].reverse_each { |r, v|
                                        final << r if not v
                                        e = Expression[e].bind(r => v).reduce if not final.include? r
                                }
                                ops[i][1] = nil
                                binding.delete k
                                stmts << ce[k, :'=', e] if k != e
                        }
                }

                # returns an array to use as funcall arguments
                get_func_args = lambda { |di, f|
                        # XXX see remarks in #finddeps
                        # TODO x64
                        bt = dcmp.dasm.backtrace(:esp, di.address, :snapshot_addr => func_entry, :include_start => true)
                        stackoff = Expression[[bt, :+, @size/8], :-, :esp].bind(:esp => :frameptr).reduce rescue nil
                        args_todo = f.type.args.to_a.dup
                        args = []
                        if f.has_attribute('fastcall')      # XXX DRY
                                if a = args_todo.shift
                                        mask = (1 << (8*dcmp.c_parser.sizeof(a))) - 1
                                        mask = 0 if a.has_attribute('unused')
                                        args << Expression[:ecx, :&, mask]
                                end
                                if a = args_todo.shift
                                        mask = (1 << (8*dcmp.c_parser.sizeof(a))) - 1     # char => dl
                                        mask = 0 if a.has_attribute('unused')
                                        args << Expression[:edx, :&, mask]
                                end
                        end
                        args_todo.each { |a_|
                                if r = a_.has_attribute_var('register')
                                        args << Expression[r.to_sym]
                                elsif stackoff.kind_of? Integer
                                        args << Indirection[[:frameptr, :+, stackoff], @size/8]
                                        stackoff += [dcmp.sizeof(a_), @size/8].max
                                else
                                        args << Expression[0]
                                end
                        }

                        if f.type.varargs and f.type.args.last.type.pointer? and stackoff.kind_of? Integer
                                # check if last arg is a fmtstring
                                bt = dcmp.dasm.backtrace(args.last, di.address, :snapshot_addr => func_entry, :include_start => true)
                                if bt.length == 1 and s = dcmp.dasm.get_section_at(bt.first)
                                        fmt = s[0].read(512)
                                        fmt = fmt.unpack('v*').pack('C*') if dcmp.sizeof(f.type.args.last.type.untypedef.type) == 2
                                        if fmt.index(?\0)
                                                fmt = fmt[0...fmt.index(?\0)]
                                                fmt.gsub('%%', '').count('%').times {    # XXX %.*s etc..
                                                        args << Indirection[[:frameptr, :+, stackoff], @size/8]
                                                        stackoff += @size/8
                                                }
                                        end
                                end
                        end

                        args.map { |e| ceb[e] }
                }

                # go !
                dcmp.dasm.decoded[b].block.list.each_with_index { |di, didx|
                        di_addr = di.address
                        a = di.instruction.args
                        if di.opcode.props[:setip] and not di.opcode.props[:stopexec]
                                # conditional jump
                                commit[]
                                n = dcmp.backtrace_target(get_xrefs_x(dcmp.dasm, di).first, di.address)
                                if di.opcode.name =~ /^loop(.+)?/
                                        cx = C::CExpression[:'--', ceb[ecx]]
                                        cc = $1 ? C::CExpression[cx, :'&&', ceb[decode_cc_to_expr($1)]] : cx
                                else
                                        cc = ceb[decode_cc_to_expr(di.opcode.name[1..-1])]
                                end
                                # XXX switch/indirect/multiple jmp
                                stmts << C::If.new(C::CExpression[cc], C::Goto.new(n).with_misc(:di_addr => di_addr)).with_misc(:di_addr => di_addr)
                                to.delete dcmp.dasm.normalize(n)
                                next
                        end

                        if di.opcode.name == 'mov'
                                # mov cr0 etc
                                a1, a2 = di.instruction.args
                                case a1
                                when Ia32::CtrlReg, Ia32::DbgReg, Ia32::TstReg, Ia32::SegReg
                                        sz = a1.kind_of?(Ia32::SegReg) ? 16 : 32
                                        if not dcmp.c_parser.toplevel.symbol["intrinsic_set_#{a1}"]
                                                dcmp.c_parser.parse("void intrinsic_set_#{a1}(__int#{sz});")
                                        end
                                        f = dcmp.c_parser.toplevel.symbol["intrinsic_set_#{a1}"]
                                        a2 = a2.symbolic(di)
                                        a2 = [a2, :&, 0xffff] if sz == 16
                                        stmts << C::CExpression.new(f, :funcall, [ceb[a2]], f.type.type).with_misc(:di_addr => di_addr)
                                        next
                                end
                                case a2
                                when Ia32::CtrlReg, Ia32::DbgReg, Ia32::TstReg, Ia32::SegReg
                                        if not dcmp.c_parser.toplevel.symbol["intrinsic_get_#{a2}"]
                                                sz = a2.kind_of?(Ia32::SegReg) ? 16 : 32
                                                dcmp.c_parser.parse("__int#{sz} intrinsic_get_#{a2}(void);")
                                        end
                                        f = dcmp.c_parser.toplevel.symbol["intrinsic_get_#{a2}"]
                                        t = f.type.type
                                        binding.delete a1.symbolic(di)
                                        stmts << C::CExpression.new(ce[a1.symbolic(di)], :'=', C::CExpression.new(f, :funcall, [], t).with_misc(:di_addr => di_addr), t).with_misc(:di_addr => di_addr)
                                        next
                                end
                        end

                        case di.opcode.name
                        when 'ret'
                                commit[]
                                ret = nil
                                ret = C::CExpression[ceb[eax]] unless func.type.type.kind_of? C::BaseType and func.type.type.name == :void
                                stmts << C::Return.new(ret).with_misc(:di_addr => di_addr)
                        when 'call' # :saveip
                                n = dcmp.backtrace_target(get_xrefs_x(dcmp.dasm, di).first, di.address)
                                args = []
                                if f = dcmp.c_parser.toplevel.symbol[n] and f.type.kind_of? C::Function and f.type.args
                                        args = get_func_args[di, f]
                                elsif defined? @dasm_func_default_off and o = @dasm_func_default_off[[dcmp.dasm, di.address]] and o.kind_of? Integer and o > @size/8
                                        f = C::Variable.new
                                        f.type = C::Function.new(C::BaseType.new(:int), [])
                                        ((o/(@size/8))-1).times { f.type.args << C::Variable.new(nil,C::BaseType.new(:int)) }
                                        args = get_func_args[di, f]
                                end
                                commit[]
                                #next if not di.block.to_subfuncret

                                if not n.kind_of? ::String or (f and not f.type.kind_of? C::Function)
                                        # indirect funcall
                                        fptr = ceb[n]
                                        binding.delete n
                                        proto = C::Function.new(C::BaseType.new(:int))
                                        proto = f.type if f and f.type.kind_of? C::Function
                                        f = C::CExpression[[fptr], C::Pointer.new(proto)]
                                elsif not f
                                        # internal functions are predeclared, so this one is extern
                                        f = C::Variable.new
                                        f.name = n
                                        f.type = C::Function.new(C::BaseType.new(:int))
                                        if dcmp.recurse > 0
                                                dcmp.c_parser.toplevel.symbol[n] = f
                                                dcmp.c_parser.toplevel.statements << C::Declaration.new(f)
                                        end
                                end
                                commit[]
                                binding.delete eax
                                e = C::CExpression[f, :funcall, args].with_misc(:di_addr => di_addr)
                                e = C::CExpression[ce[eax], :'=', e, f.type.type].with_misc(:di_addr => di_addr) if deps[b].include? eax and f.type.type != C::BaseType.new(:void)
                                stmts << e
                        when 'jmp'
                                #if di.comment.to_a.include? 'switch'
                                #  n = di.instruction.args.first.symbolic(di)
                                #  fptr = ceb[n]
                                #  binding.delete n
                                #  commit[]
                                #  sw = C::Switch.new(fptr, C::Block.new(scope))
                                #  di.block.to_normal.to_a.each { |addr|
                                #          addr = dcmp.dasm.normalize addr
                                #          to.delete addr
                                #          next if not l = dcmp.dasm.get_label_at(addr)
                                #          sw.body.statements << C::Goto.new(l)
                                #  }
                                #  stmts << sw
                                a = di.instruction.args.first
                                if a.kind_of? Expression
                                elsif not a.respond_to? :symbolic
                                        stmts << C::Asm.new(di.instruction.to_s, nil, [], [], nil, nil).with_misc(:di_addr => di_addr)
                                else
                                        n = di.instruction.args.first.symbolic(di)
                                        fptr = ceb[n]
                                        binding.delete n
                                        commit[]
                                        if fptr.kind_of? C::CExpression and fptr.type.pointer? and fptr.type.untypedef.type.kind_of? C::Function
                                                proto = fptr.type.untypedef.type
                                                args = get_func_args[di, fptr.type]
                                        else
                                                proto = C::Function.new(C::BaseType.new(:void))
                                                fptr = C::CExpression[[fptr], C::Pointer.new(proto)].with_misc(:di_addr => di_addr)
                                                args = []
                                        end
                                        ret = C::Return.new(C::CExpression[fptr, :funcall, args].with_misc(:di_addr => di_addr)).with_misc(:di_addr => di_addr)
                                        stmts << ret
                                        to = []
                                end
                        when 'lgdt'
                                if not dcmp.c_parser.toplevel.struct['segment_descriptor']
                                        dcmp.c_parser.parse('struct segment_descriptor { __int16 limit; __int16 base0_16; __int8 base16_24; __int8 flags1; __int8 flags2_limit_16_20; __int8 base24_32; };')
                                        dcmp.c_parser.parse('struct segment_table { __int16 size; struct segment_descriptor *table; } __attribute__((pack(2)));')
                                end
                                if not dcmp.c_parser.toplevel.symbol['intrinsic_lgdt']
                                        dcmp.c_parser.parse('void intrinsic_lgdt(struct segment_table *);')
                                end
                                # need a way to transform arg => :frameptr+12
                                arg = di.backtrace_binding.keys.grep(Indirection).first.pointer
                                stmts << C::CExpression.new(dcmp.c_parser.toplevel.symbol['intrinsic_lgdt'], :funcall, [ceb[arg]], C::BaseType.new(:void)).with_misc(:di_addr => di_addr)
                        when 'lidt'
                                if not dcmp.c_parser.toplevel.struct['interrupt_descriptor']
                                        dcmp.c_parser.parse('struct interrupt_descriptor { __int16 offset0_16; __int16 segment; __int16 flags; __int16 offset16_32; };')
                                        dcmp.c_parser.parse('struct interrupt_table { __int16 size; struct interrupt_descriptor *table; } __attribute__((pack(2)));')
                                end
                                if not dcmp.c_parser.toplevel.symbol['intrinsic_lidt']
                                        dcmp.c_parser.parse('void intrinsic_lidt(struct interrupt_table *);')
                                end
                                arg = di.backtrace_binding.keys.grep(Indirection).first.pointer
                                stmts << C::CExpression.new(dcmp.c_parser.toplevel.symbol['intrinsic_lidt'], :funcall, [ceb[arg]], C::BaseType.new(:void)).with_misc(:di_addr => di_addr)
                        when 'ltr', 'lldt'
                                if not dcmp.c_parser.toplevel.symbol["intrinsic_#{di.opcode.name}"]
                                        dcmp.c_parser.parse("void intrinsic_#{di.opcode.name}(int);")
                                end
                                arg = di.backtrace_binding.keys.first
                                stmts << C::CExpression.new(dcmp.c_parser.toplevel.symbol["intrinsic_#{di.opcode.name}"], :funcall, [ceb[arg]], C::BaseType.new(:void)).with_misc(:di_addr => di_addr)
                        when 'out'
                                sz = di.instruction.args.find { |a_| a_.kind_of? Ia32::Reg and a_.val == 0 }.sz
                                if not dcmp.c_parser.toplevel.symbol["intrinsic_out#{sz}"]
                                        dcmp.c_parser.parse("void intrinsic_out#{sz}(unsigned short port, __int#{sz} value);")
                                end
                                port = di.instruction.args.grep(Expression).first || edx
                                stmts << C::CExpression.new(dcmp.c_parser.toplevel.symbol["intrinsic_out#{sz}"], :funcall, [ceb[port], ceb[eax]], C::BaseType.new(:void)).with_misc(:di_addr => di_addr)
                        when 'in'
                                sz = di.instruction.args.find { |a_| a_.kind_of? Ia32::Reg and a_.val == 0 }.sz
                                if not dcmp.c_parser.toplevel.symbol["intrinsic_in#{sz}"]
                                        dcmp.c_parser.parse("__int#{sz} intrinsic_in#{sz}(unsigned short port);")
                                end
                                port = di.instruction.args.grep(Expression).first || edx
                                f = dcmp.c_parser.toplevel.symbol["intrinsic_in#{sz}"]
                                binding.delete eax
                                stmts << C::CExpression.new(ce[eax], :'=', C::CExpression.new(f, :funcall, [ceb[port]], f.type.type), f.type.type).with_misc(:di_addr => di_addr)
                        when 'sti', 'cli'
                                stmts << C::Asm.new(di.instruction.to_s, nil, [], [], nil, nil)
                        when /^(mov|sto|lod)s([bwdq])/
                                op, sz = $1, $2
                                commit[]
                                sz = { 'b' => 1, 'w' => 2, 'd' => 4, 'q' => 8 }[sz]
                                pt = C::Pointer.new(C::BaseType.new("__int#{sz*8}".to_sym))

                                blk = C::Block.new(scope)
                                case op
                                when 'mov'
                                        blk.statements << C::CExpression[[:*, [[ceb[edi]], pt]], :'=', [:*, [[ceb[esi]], pt]]].with_misc(:di_addr => di_addr)
                                        blk.statements << C::CExpression[ceb[edi], :'=', [ceb[edi], :+, [sz]]].with_misc(:di_addr => di_addr)
                                        blk.statements << C::CExpression[ceb[esi], :'=', [ceb[esi], :+, [sz]]].with_misc(:di_addr => di_addr)
                                when 'sto'
                                        blk.statements << C::CExpression[[:*, [[ceb[edi]], pt]], :'=', ceb[eax]].with_misc(:di_addr => di_addr)
                                        blk.statements << C::CExpression[ceb[edi], :'=', [ceb[edi], :+, [sz]]].with_misc(:di_addr => di_addr)
                                when 'lod'
                                        blk.statements << C::CExpression[ceb[eax], :'=', [:*, [[ceb[esi]], pt]]].with_misc(:di_addr => di_addr)
                                        blk.statements << C::CExpression[ceb[esi], :'=', [ceb[esi], :+, [sz]]].with_misc(:di_addr => di_addr)
                                #when 'sca'
                                #when 'cmp'
                                end

                                case (di.instruction.prefix || {})[:rep]
                                when nil
                                        stmts.concat blk.statements
                                when 'rep'
                                        blk.statements << C::CExpression[ceb[ecx], :'=', [ceb[ecx], :-, [1]]].with_misc(:di_addr => di_addr)
                                        stmts << C::While.new(C::CExpression[ceb[ecx]], blk).with_misc(:di_addr => di_addr)
                                #when 'repz'       # sca/cmp only
                                #when 'repnz'
                                end
                                next
                        else
                                bd = get_fwdemu_binding(di)
                                if di.backtrace_binding[:incomplete_binding]
                                        commit[]
                                        stmts << C::Asm.new(di.instruction.to_s, nil, nil, nil, nil, nil).with_misc(:di_addr => di_addr)
                                else
                                        update = {}
                                        bd.each { |k, v|
                                                if k.kind_of? ::Symbol and not deps[b].include? k
                                                        ops << [k, v]
                                                        update[k] = Expression[Expression[v].bind(binding).reduce]
                                                else
                                                        stmts << ceb[k, :'=', v]
                                                        stmts.pop if stmts.last.kind_of? C::Variable    # [:eflag_s, :=, :unknown].reduce
                                                end
                                        }
                                        binding.update update
                                end
                        end
                        di_addr = nil
                }
                commit[]

                case to.length
                when 0
                        if not myblocks.empty? and not %w[ret jmp].include? dcmp.dasm.decoded[b].block.list.last.instruction.opname
                                puts "  block #{Expression[b]} has no to and don't end in ret"
                        end
                when 1
                        if (myblocks.empty? ? nextaddr != to[0] : myblocks.first.first != to[0])
                                stmts << C::Goto.new(dcmp.dasm.auto_label_at(to[0], 'unknown_goto'))
                        end
                else
                        puts "  block #{Expression[b]} with multiple to"
                end
        end

        # cleanup di.bt_binding (we set :frameptr etc in those, this may confuse the dasm)
        blocks_toclean.each { |b_, to_|
                dcmp.dasm.decoded[b_].block.list.each { |di|
                        di.backtrace_binding = nil
                }
        }
end
decompile_check_abi(dcmp, entry, func) click to toggle source
# File metasm/cpu/ia32/decompile.rb, line 540
def decompile_check_abi(dcmp, entry, func)
        a = func.type.args || []
        a.delete_if { |arg| arg.has_attribute_var('register') and arg.has_attribute('unused') }
        ra =  a.map { |arg| arg.has_attribute_var('register') }.compact
        if (a.length == 1 and ra == ['ecx']) or (a.length >= 2 and ra.sort == ['ecx', 'edx'])
                func.add_attribute 'fastcall'
                # reorder args
                ecx = a.find { |arg| arg.has_attribute_var('register') == 'ecx' }
                edx = a.find { |arg| arg.has_attribute_var('register') == 'edx' }
                a.insert(0, a.delete(ecx))
                a.insert(1, a.delete(edx)) if edx
        end

        if not f = dcmp.dasm.function[entry] or not f.return_address
                #func.add_attribute 'noreturn'
        else
                adj = f.return_address.map { |ra_| dcmp.dasm.backtrace(:esp, ra_, :include_start => true, :stopaddr => entry) }.flatten.uniq
                if adj.length == 1 and so = Expression[adj.first, :-, :esp].reduce and so.kind_of? ::Integer
                        argsz = a.map { |fa|
                                next if not fa.stackoff
                                (fa.stackoff + [dcmp.sizeof(fa), dcmp.c_parser.typesize[:ptr]].max-1) / dcmp.c_parser.typesize[:ptr]
                        }.compact.max.to_i
                        so /= dcmp.dasm.cpu.size/8
                        so -= 1
                        if so > argsz
                                aso = a.empty? ? 0 : a.last.stackoff.to_i + dcmp.c_parser.typesize[:ptr]
                                (so-argsz).times {
                                        a << C::Variable.new(dcmp.stackoff_to_varname(aso), C::BaseType.new(:int))
                                        a.last.add_attribute('unused')
                                        aso += dcmp.sizeof(a.last)
                                }
                                argsz = so
                        end
                        case so
                        when 0
                        when argsz
                                func.add_attribute 'stdcall' if not func.has_attribute('fastcall')
                        else
                                func.add_attribute "stackoff:#{so*dcmp.dasm.cpu.size/8}"
                        end
                else
                        func.add_attribute "breakstack:#{adj.inspect}"
                end
        end
end
decompile_func_finddeps(dcmp, blocks, func) click to toggle source

list variable dependency for each block, remove useless writes returns { blockaddr => [list of vars that are needed by a following block] }

# File metasm/cpu/ia32/decompile.rb, line 67
def decompile_func_finddeps(dcmp, blocks, func)
        deps_r = {} ; deps_w = {} ; deps_to = {}
        deps_subfunc = {}     # things read/written by subfuncs

        # find read/writes by each block
        blocks.each { |b, to|
                deps_r[b] = [] ; deps_w[b] = [] ; deps_to[b] = to
                deps_subfunc[b] = []

                blk = dcmp.dasm.decoded[b].block
                blk.list.each { |di|
                        a = di.backtrace_binding.values
                        w = []
                        di.backtrace_binding.keys.each { |k|
                                case k
                                when ::Symbol; w |= [k]
                                else a |= Expression[k].externals  # if dword [eax] <- 42, eax is read
                                end
                        }
                        decompile_func_finddeps_di(dcmp, func, di, a, w)

                        deps_r[b] |= a.map { |ee| Expression[ee].externals.grep(::Symbol) }.flatten - [:unknown] - deps_w[b]
                        deps_w[b] |= w.map { |ee| Expression[ee].externals.grep(::Symbol) }.flatten - [:unknown]
                }
                stackoff = nil
                blk.each_to_normal { |t|
                        t = dcmp.backtrace_target(t, blk.list.last.address)
                        next if not t = dcmp.c_parser.toplevel.symbol[t]
                        t.type = C::Function.new(C::BaseType.new(:int)) if not t.type.kind_of? C::Function  # XXX this may seem a bit extreme, and yes, it is.
                        stackoff ||= Expression[dcmp.dasm.backtrace(:esp, blk.list.last.address, :snapshot_addr => blocks.first[0]).first, :-, :esp].reduce

                        # things that are needed by the subfunction
                        if t.has_attribute('fastcall')
                                a = t.type.args.to_a
                                dep = [:ecx, :edx]
                                dep.shift if not a[0] or a[0].has_attribute('unused')
                                dep.pop   if not a[1] or a[1].has_attribute('unused')
                                deps_subfunc[b] |= dep
                        end
                        t.type.args.to_a.each { |arg|
                                if reg = arg.has_attribute('register')
                                        deps_subfunc[b] |= [reg.to_sym]
                                end
                        }
                }
                if stackoff  # last block instr == subfunction call
                        deps_r[b] |= deps_subfunc[b] - deps_w[b]
                        deps_w[b] |= register_symbols[0, 3]                 # standard ABI
                end
        }


        bt = blocks.transpose
        roots = bt[0] - bt[1].flatten # XXX jmp 1stblock ?

        # find regs read and never written (must have been set by caller and are part of the func ABI)
        uninitialized = lambda { |b, r, done|
                if not deps_r[b]
                elsif deps_r[b].include?(r)
                        blk = dcmp.dasm.decoded[b].block
                        bw = []
                        rdi = blk.list.find { |di|
                                a = di.backtrace_binding.values
                                w = []
                                di.backtrace_binding.keys.each { |k|
                                        case k
                                        when ::Symbol; w |= [k]
                                        else a |= Expression[k].externals # if dword [eax] <- 42, eax is read
                                        end
                                }
                                decompile_func_finddeps_di(dcmp, func, di, a, w)

                                next true if (a.map { |ee| Expression[ee].externals.grep(::Symbol) }.flatten - [:unknown] - bw).include? r
                                bw |= w.map { |ee| Expression[ee].externals.grep(::Symbol) }.flatten - [:unknown]
                                false
                        }
                        if r == register_symbols[0] and (rdi || blk.list.last).opcode.name == 'ret'
                                func.type.type = C::BaseType.new(:void)
                                false
                        elsif rdi and rdi.backtrace_binding[r]
                                false      # mov al, 42 ; ret  -> don't regarg eax
                        else
                                true
                        end
                elsif deps_w[b].include?(r)
                else
                        done << b
                        (deps_to[b] - done).find { |tb| uninitialized[tb, r, done] }
                end
        }

        regargs = []
        register_symbols.each { |r|
                if roots.find { |root| uninitialized[root, r, []] }
                        regargs << r
                end
        }

        # TODO honor user-defined prototype if available (eg no, really, eax is not read in this function returning al)
        regargs.sort_by { |r| r.to_s }.each { |r|
                a = C::Variable.new(r.to_s, C::BaseType.new(:int, :unsigned))
                a.add_attribute("register(#{r})")
                func.type.args << a
        }

        # remove writes from a block if no following block read the value
        dw = {}
        deps_w.each { |b, deps|
                dw[b] = deps.reject { |dep|
                        ret = true
                        done = []
                        todo = deps_to[b].dup
                        while a = todo.pop
                                next if done.include? a
                                done << a
                                if not deps_r[a] or deps_r[a].include? dep
                                        ret = false
                                        break
                                elsif not deps_w[a].include? dep
                                        todo.concat deps_to[a]
                                end
                        end
                        ret
                }
        }

        dw
end
decompile_func_finddeps_di(dcmp, func, di, a, w) click to toggle source

add di-specific registry written/accessed

# File metasm/cpu/ia32/decompile.rb, line 61
def decompile_func_finddeps_di(dcmp, func, di, a, w)
        a << register_symbols[0] if di.opcode.name == 'ret' and (not func.type.kind_of? C::BaseType or func.type.type.name != :void)  # standard ABI
end
decompile_makestackvars(dasm, funcstart, blocks) { |block| ... } click to toggle source

temporarily setup dasm.address_binding so that backtracking stack-related offsets resolve in :frameptr (relative to func start)

# File metasm/cpu/ia32/decompile.rb, line 13
def decompile_makestackvars(dasm, funcstart, blocks)
        esp = register_symbols[4]
        oldfuncbd = dasm.address_binding[funcstart]
        dasm.address_binding[funcstart] = { esp => :frameptr }
        patched_binding = [funcstart] # list of addresses to cleanup later

        if blocks.length <= 12
                blocks.each { |block| yield block }
                return
        end

        # for large function, pre-trace and cache esp/ebp for every block start to improve decompilation time

        ebp = register_symbols[5]
        ebp_frame = true

        # pretrace esp and ebp for each function block (cleared later)
        # TODO with more than 1 unknown __stdcall ext func per path, esp -> unknown, which makes very ugly C (*esp-- = 12...); add heuristics ?
        blocks.each { |block|
                blockstart = block.address
                if not dasm.address_binding[blockstart]
                        patched_binding << blockstart
                        dasm.address_binding[blockstart] = {}
                        foo = dasm.backtrace(esp, blockstart, :snapshot_addr => funcstart)
                        if foo.length == 1 and ee = foo.first and ee.kind_of? Expression and (ee == Expression[:frameptr] or
                                        (ee.lexpr == :frameptr and ee.op == :+ and ee.rexpr.kind_of? ::Integer))
                                dasm.address_binding[blockstart][esp] = ee
                        end
                        if ebp_frame
                                foo = dasm.backtrace(ebp, blockstart, :snapshot_addr => funcstart)
                                if foo.length == 1 and ee = foo.first and ee.kind_of? Expression and (ee == Expression[:frameptr] or
                                                (ee.lexpr == :frameptr and ee.op == :+ and ee.rexpr.kind_of? ::Integer))
                                        dasm.address_binding[blockstart][ebp] = ee
                                else
                                        ebp_frame = false # func does not use ebp as frame ptr, no need to bt for later blocks
                                end
                        end
                end

                yield block
        }

ensure
        patched_binding.each { |a| dasm.address_binding.delete a }
        dasm.address_binding[funcstart] = oldfuncbd if oldfuncbd
end
disassembler_default_btbind_callback() click to toggle source

the lambda for the :default backtrace_binding callback of the disassembler tries to determine the stack offset of unprototyped functions working:

checks that origin is a ret, that expr is an indirection from esp and that expr.origin is the ret
bt_walk from calladdr until we finds a call into us, and assumes it is the current function start
TODO handle foo: call bar ; bar: pop eax ; call <withourcallback> ; ret -> bar is not the function start (foo is)
then backtrace expr from calladdr to funcstart (snapshot), using esp -> esp+<stackoffvariable>
from the result, compute stackoffvariable (only if trivial)

will not work if the current function calls any other unknown function (unless all are __cdecl) will not work if the current function is framed (ebp leave ret): in this case the function will return, but its esp will be unknown if the stack offset is found and funcaddr is a string, fixup the static binding and remove the dynamic binding TODO dynamise thunks bt_for & bt_cb

# File metasm/cpu/ia32/decode.rb, line 1096
def disassembler_default_btbind_callback
        esp = register_symbols[4]

        lambda { |dasm, bind, funcaddr, calladdr, expr, origin, maxdepth|
                @dasm_func_default_off ||= {}
                if off = @dasm_func_default_off[[dasm, calladdr]]
                        bind = bind.merge(esp => Expression[esp, :+, off])
                        break bind
                end
                break bind if not odi = dasm.decoded[origin] or odi.opcode.basename != 'ret'
                expr = expr.reduce_rec if expr.kind_of? Expression
                break bind unless expr.kind_of? Indirection and expr.origin == origin
                break bind unless expr.externals.reject { |e| e =~ /^autostackoffset_/ } == [esp]

                curfunc = dasm.function[funcaddr]
                if curfunc.backtrace_binding and tk = curfunc.backtrace_binding[:thunk] and dasm.function[tk]
                        curfunc = dasm.function[tk]
                end

                # scan from calladdr for the probable parent function start
                func_start = nil
                dasm.backtrace_walk(true, calladdr, false, false, nil, maxdepth) { |ev, foo, h|
                        if ev == :up and h[:sfret] != :subfuncret and di = dasm.decoded[h[:to]] and di.opcode.basename == 'call'
                                func_start = h[:from]
                                break
                        elsif ev == :end
                                # entrypoints are functions too
                                func_start = h[:addr]
                                break
                        end
                }
                break bind if not func_start
                puts "automagic #{Expression[funcaddr]}: found func start for #{dasm.decoded[origin]} at #{Expression[func_start]}" if dasm.debug_backtrace
                s_off = "autostackoffset_#{Expression[funcaddr]}_#{Expression[calladdr]}"
                list = dasm.backtrace(expr.bind(esp => Expression[esp, :+, s_off]), calladdr, :include_start => true, :snapshot_addr => func_start, :maxdepth => maxdepth, :origin => origin)
                # check if this backtrace made us find our binding
                if off = @dasm_func_default_off[[dasm, calladdr]]
                        bind = bind.merge(esp => Expression[esp, :+, off])
                        break bind
                elsif not curfunc.btbind_callback
                        break curfunc.backtrace_binding
                end
                e_expr = list.find { |e_expr_|
                        # TODO cleanup this
                        e_expr_ = Expression[e_expr_].reduce_rec
                        next if not e_expr_.kind_of? Indirection
                        off = Expression[[esp, :+, s_off], :-, e_expr_.target].reduce
                        off.kind_of? Integer and off >= @size/8 and off < 10*@size/8 and (off % (@size/8)) == 0
                } || list.first

                e_expr = e_expr.rexpr if e_expr.kind_of? Expression and e_expr.op == :+ and not e_expr.lexpr
                break bind unless e_expr.kind_of? Indirection

                off = Expression[[esp, :+, s_off], :-, e_expr.target].reduce
                if off.kind_of? Expression
                        bd = off.externals.grep(/^autostackoffset_/).inject({}) { |bd_, xt| bd_.update xt => @size/8 }
                        bd.delete s_off
                        if off.bind(bd).reduce == @size/8
                                # all __cdecl
                                off = @size/8
                        else
                                # check if all calls are to the same extern func
                                bd.delete_if { |k, v| k !~ /^autostackoffset_#{Expression[funcaddr]}_/ }
                                bd.each_key { |k| bd[k] = 0 }
                                if off.bind(bd).reduce.kind_of? Integer
                                        off = off.bind(bd).reduce / (bd.length + 1)
                                end
                        end
                end
                if off.kind_of? Integer
                        if off < @size/8 or off > 20*@size/8 or (off % (@size/8)) != 0
                                puts "autostackoffset: ignoring off #{off} for #{Expression[funcaddr]} from #{dasm.decoded[calladdr]}" if $VERBOSE
                                off = :unknown
                        end
                end

                bind = bind.merge esp => Expression[esp, :+, off] if off != :unknown
                if funcaddr != :default
                        if not off.kind_of? ::Integer
                                #XXX we allow the current function to return, so we should handle the func backtracking its esp
                                #(and other register that are saved and restored in epilog)
                                puts "stackoff #{dasm.decoded[calladdr]} | #{Expression[func_start]} | #{expr} | #{e_expr} | #{off}" if dasm.debug_backtrace
                        else
                                puts "autostackoffset: found #{off} for #{Expression[funcaddr]} from #{dasm.decoded[calladdr]}" if $VERBOSE
                                curfunc.btbind_callback = nil
                                curfunc.backtrace_binding = bind

                                # rebacktrace the return address, so that other unknown funcs that depend on us are solved
                                dasm.backtrace(Indirection[esp, @size/8, origin], origin, :origin => origin)
                        end
                else
                        if off.kind_of? ::Integer and dasm.decoded[calladdr]
                                puts "autostackoffset: found #{off-@size/8} for #{dasm.decoded[calladdr]}" if $VERBOSE
                                di = dasm.decoded[calladdr]
                                di.comment.delete_if { |c| c =~ /^stackoff=/ } if di.comment
                                di.add_comment "stackoff=#{off-@size/8}"
                                @dasm_func_default_off[[dasm, calladdr]] = off

                                dasm.backtrace(Indirection[esp, @size/8, origin], origin, :origin => origin)
                        elsif cachedoff = @dasm_func_default_off[[dasm, calladdr]]
                                bind[esp] = Expression[esp, :+, cachedoff]
                        elsif off.kind_of? ::Integer
                                dasm.decoded[calladdr].add_comment "stackoff=#{off-@size/8}"
                        end

                        puts "stackoff #{dasm.decoded[calladdr]} | #{Expression[func_start]} | #{expr} | #{e_expr} | #{off}" if dasm.debug_backtrace
                end

                bind
        }
end
disassembler_default_btfor_callback() click to toggle source

the :default backtracked_for callback returns empty unless funcaddr is not default or calladdr is a call or a jmp

# File metasm/cpu/ia32/decode.rb, line 1210
def disassembler_default_btfor_callback
        lambda { |dasm, btfor, funcaddr, calladdr|
                if funcaddr != :default; btfor
                elsif di = dasm.decoded[calladdr] and (di.opcode.name == 'call' or di.opcode.name == 'jmp'); btfor
                else []
                end
        }
end
disassembler_default_func() click to toggle source

returns a DecodedFunction suitable for :default uses disassembler_default_bt{for/bind}_callback

# File metasm/cpu/ia32/decode.rb, line 1221
def disassembler_default_func
        esp = register_symbols[4]
        cp = new_cparser
        cp.parse 'void stdfunc(void);'
        f = decode_c_function_prototype(cp, 'stdfunc', :default)
        f.backtrace_binding[esp] = Expression[esp, :+, :unknown]
        f.btbind_callback = disassembler_default_btbind_callback
        f.btfor_callback  = disassembler_default_btfor_callback
        f
end
encode_instr_op(program, i, op) click to toggle source

returns all forms of the encoding of instruction i using opcode op program may be used to create a new label for relative jump/call

# File metasm/cpu/ia32/encode.rb, line 180
def encode_instr_op(program, i, op)
        base      = op.bin.dup
        oi        = op.args.zip(i.args)
        set_field = lambda { |f, v|
                v ||= 0              # ST => ST(0)
                fld = op.fields[f]
                base[fld[0]] |= v << fld[1]
        }

        size = i.prefix[:sz] || @size

        #
        # handle prefixes and bit fields
        #
        pfx = i.prefix.map { |k, v|
                case k
                when :jmp;  {:jmp => 0x3e, :nojmp => 0x2e}[v]
                when :lock; 0xf0
                when :rep;  {'repnz' => 0xf2, 'repz' => 0xf3, 'rep' => 0xf2}[v]
                when :jmphint; {'hintjmp' => 0x3e, 'hintnojmp' => 0x2e}[v]
                when :seg; [0x26, 0x2E, 0x36, 0x3E, 0x64, 0x65][v.val]
                end
        }.compact.pack 'C*'

        if op.name == 'movsx' or op.name == 'movzx'
                pfx << 0x66 if size == 48-i.args[0].sz
        elsif op.name == 'crc32'
                pfx << 0x66 if size == 48-i.args[1].sz
        else
                opsz = op.props[:argsz]
                oi.each { |oa, ia|
                        case oa
                        when :reg, :reg_eax, :modrm, :mrm_imm
                                raise EncodeError, "Incompatible arg size in #{i}" if ia.sz and opsz and opsz != ia.sz
                                opsz = ia.sz
                        end
                }
                pfx << 0x66 if (op.props[:opsz] and size == 48 - op.props[:opsz]) or
                                (not op.props[:argsz] and opsz and size == 48 - opsz)
                opsz ||= op.props[:opsz]
        end
        opsz ||= size

        if op.props[:adsz] and size == 48 - op.props[:adsz]
                pfx << 0x67
                adsz = 48 - size
        end
        adsz ||= size
        # addrsize override / segment override
        if mrm = i.args.grep(ModRM).first
                if not op.props[:adsz] and ((mrm.b and mrm.b.sz == 48 - adsz) or (mrm.i and mrm.i.sz == 48 - adsz))
                        pfx << 0x67
                        adsz = 48 - adsz
                end
                pfx << [0x26, 0x2E, 0x36, 0x3E, 0x64, 0x65][mrm.seg.val] if mrm.seg
        end


        #
        # encode embedded arguments
        #
        postponed = []
        oi.each { |oa, ia|
                case oa
                when :reg, :seg3, :seg3A, :seg2, :seg2A, :eeec, :eeed, :eeet, :regfp, :regmmx, :regxmm, :regymm
                        # field arg
                        set_field[oa, ia.val]
                        pfx << 0x66 if oa == :regmmx and op.props[:xmmx] and ia.sz == 128
                when :vexvreg, :vexvxmm, :vexvymm
                        set_field[:vex_vvvv, ia.val ^ 0xf]
                when :imm_val1, :imm_val3, :reg_cl, :reg_eax, :reg_dx, :regfp0
                        # implicit
                else
                        postponed << [oa, ia]
                end
        }

        if !(op.args & [:modrm, :modrmmmx, :modrmxmm, :modrmymm]).empty?
                # reg field of modrm
                regval = (base[-1] >> 3) & 7
                base.pop
        end

        # convert label name for jmp/call/loop to relative offset
        if op.props[:setip] and op.name[0, 3] != 'ret' and i.args.first.kind_of? Expression
                postlabel = program.new_label('post'+op.name)
                target = postponed.first[1]
                target = target.rexpr if target.kind_of? Expression and target.op == :+ and not target.lexpr
                postponed.first[1] = Expression[target, :-, postlabel]
        end

        pfx << op.props[:needpfx] if op.props[:needpfx]

        #
        # append other arguments
        #
        ret = EncodedData.new(pfx + base.pack('C*'))

        postponed.each { |oa, ia|
                case oa
                when :farptr; ed = ia.encode(@endianness, "a#{opsz}".to_sym)
                when :modrm, :modrmmmx, :modrmxmm, :modrmymm
                        if ia.kind_of? ModRM
                                ed = ia.encode(regval, @endianness)
                                if ed.kind_of?(::Array)
                                        if ed.length > 1
                                                # we know that no opcode can have more than 1 modrm
                                                ary = []
                                                ed.each { |m|
                                                        ary << (ret.dup << m)
                                                }
                                                ret = ary
                                                next
                                        else
                                                ed = ed.first
                                        end
                                end
                        else
                                ed = ModRM.encode_reg(ia, regval)
                        end
                when :mrm_imm; ed = ia.imm.encode("a#{adsz}".to_sym, @endianness)
                when :i8, :u8, :u16; ed = ia.encode(oa, @endianness)
                when :i; ed = ia.encode("a#{opsz}".to_sym, @endianness)
                when :i4xmm, :i4ymm; ed = ia.val << 4        # u8
                else raise SyntaxError, "Internal error: want to encode field #{oa.inspect} as arg in #{i}"
                end

                if ret.kind_of?(::Array)
                        ret.each { |e| e << ed }
                else
                        ret << ed
                end
        }

        # we know that no opcode with setip accept both modrm and immediate arg, so ret is not an ::Array
        ret.add_export(postlabel, ret.virtsize) if postlabel

        ret
end
fix_fwdemu_binding(di, fbd) click to toggle source

patch a forward binding from the backtrace binding fixes fwdemu for push/pop/call/ret

# File metasm/cpu/ia32/decode.rb, line 803
def fix_fwdemu_binding(di, fbd)
        if di.instruction.args.grep(ModRM).find { |m| m.seg and m.symbolic(di).target.lexpr =~ /^segment_base_/ }
                fbd = fbd.dup
                fbd[:incomplete_binding] = Expression[1]
        end

        case di.opcode.name
        when /^push/, 'call'
                ori = fbd
                fbd = {}
                sz = opsz(di)/8
                esp = register_symbols[4]
                if ori[esp] and ori[Indirection[esp, sz]]
                        ori.each { |k, v|
                                if k.kind_of?(Indirection)
                                        fbd[k.bind(esp => ori[esp]).reduce_rec] = v
                                else
                                        fbd[k] = v
                                end
                        }
                else
                        fbd = ori.dup
                        fbd[:incomplete_binding] = Expression[1]    # TODO
                end
        when /^pop/, 'ret' # nothing to do
        when /^(push|pop|call|ret|enter|leave|stos|movs|lods|scas|cmps)/
                fbd = fbd.dup
                fbd[:incomplete_binding] = Expression[1]     # TODO
        end
        fbd
end
get_backtrace_binding(di) click to toggle source
# File metasm/cpu/ia32/decode.rb, line 758
def get_backtrace_binding(di)
        a = di.instruction.args.map { |arg| symbolic(arg, di) }

        if binding = backtrace_binding[di.opcode.basename]
                bd = binding[di, *a]
                # handle modifications to al/ah etc
                bd.keys.grep(Expression).each { |e|
                        # must be in the form (x & mask), with x either :reg or (:reg >> shift) eg ah == ((eax >> 8) & 0xff)
                        if e.op == :& and mask = e.rexpr and mask.kind_of? Integer
                                reg = e.lexpr
                                reg = reg.lexpr if reg.kind_of? Expression and reg.op == :>> and shift = reg.rexpr and shift.kind_of? Integer
                                next if not reg.kind_of? Symbol
                                if bd.has_key? reg
                                        # xchg ah, al ; pop sp..
                                        puts "backtrace: conflict for #{di}: #{e} vs #{reg}" if $VERBOSE
                                        bd[reg] = Expression::Unknown
                                        next
                                end
                                val = bd.delete e
                                mask <<= shift if shift
                                invmask = mask ^ (@size == 64 ? 0xffff_ffff_ffff_ffff : 0xffff_ffff)
                                if invmask == 0xffff_ffff_0000_0000 and not di.opcode.props[:op32no64]
                                        bd[reg] = Expression[val, :&, 0xffff_ffff]
                                elsif invmask == 0
                                        bd[reg] = val
                                else
                                        val = Expression[val, :<<, shift] if shift
                                        bd[reg] = Expression[[reg, :&, invmask], :|, [val, :&, mask]]
                                end
                        end
                }
                bd
        else
                puts "unhandled instruction to backtrace: #{di}" if $VERBOSE
                # assume nothing except the 1st arg is modified
                case a[0]
                when Indirection, Symbol; { a[0] => Expression::Unknown }
                when Expression; (x = a[0].externals.first) ? { x => Expression::Unknown } : {}
                else {}
                end.update(:incomplete_binding => Expression[1])
        end
end
get_jump_condition(di) click to toggle source

returns the condition (bool Expression) under which a conditional jump is taken returns nil if not a conditional jump backtrace for the condition must include the jump itself (eg loop -> ecx–)

# File metasm/cpu/ia32/decode.rb, line 746
def get_jump_condition(di)
        ecx = register_symbols[1]
        case di.opcode.name
        when /^j(.*)/
                decode_cc_to_expr($1)
        when /^loop(.+)?/
                e = Expression[ecx, :'!=', 0]
                e = Expression[e, :'||', decode_cc_to_expr($1)] if $1
                e
        end
end
get_xrefs_x(dasm, di) click to toggle source
# File metasm/cpu/ia32/decode.rb, line 835
def get_xrefs_x(dasm, di)
        return [] if not di.opcode.props[:setip]

        sz = opsz(di)
        case di.opcode.basename
        when 'ret'; return [Indirection[register_symbols[4], sz/8, di.address]]
        when 'jmp', 'call'
                if dasm and not di.instruction.args.first.kind_of?(Expression) and switch_table = get_xrefs_x_jmptable(dasm, di)
                        return switch_table
                end
        end

        case tg = di.instruction.args.first
        when ModRM
                tg.sz ||= sz if tg.kind_of? ModRM
                [Expression[tg.symbolic(di)]]
        when Reg; [Expression[tg.symbolic(di)]]
        when Expression, ::Integer; [Expression[tg]]
        when Farptr; tg.seg.reduce < 0x30 ? [tg.addr] : [Expression[[tg.seg, :*, 0x10], :+, tg.addr]]
        else
                puts "unhandled setip at #{Expression[di.address]} #{di.instruction}" if $DEBUG
                []
        end
end
get_xrefs_x_jmptable(dasm, di) click to toggle source

indirect call, try to match a switch table pattern (eg jmp [base+4*idx]) return a list of target addresses if found, nil otherwise

# File metasm/cpu/ia32/decode.rb, line 862
def get_xrefs_x_jmptable(dasm, di)
        puts "search jmptable for #{Expression[di.address]} #{di.instruction}" if $DEBUG
        arg0 = di.instruction.args.first.symbolic(di)

        bt_log = []
        dasm.backtrace(arg0, di.address, :maxdepth => 3, :log => bt_log)

        expr = nil
        index = nil
        index_max = nil

        bt_log.each { |btl|
                next if btl[0] != :up
                last = dasm.di_at(btl[4])
                break if not last or last.block.to_normal.length > 2
                next if last.block.to_normal.length != 2
                # search cmp eax, 42 ; ja too_big ; jmp [base+4*eax]
                # XXX 256 cases switch => no cmp...
                prelast = last.block.list.reverse.find { |pl| pl.opcode.name == 'cmp' }
                break unless prelast and cmp_value = prelast.instruction.args.last and cmp_value.kind_of?(Expression) and cmp_value.reduce.kind_of?(::Integer)
                cmp_value = cmp_value.reduce % (1 << prelast.instruction.args.first.sz)      # cmp al, -12h ; jnbe => -12h is unsigned 0eeh
                index = prelast.instruction.args.first.symbolic(prelast)
                index = index.externals.first if index.kind_of?(Expression)  # cmp bl, 13 => ebx
                expr = Expression[btl[1], :&, ((1 << @size) - 1)]            # XXX without the mask, additions may overflow (this breaks elsewhere too, need Expr32)
                (expr.externals.grep(Symbol) - [index]).uniq.each { |r|
                        rv = dasm.backtrace(r, prelast.address, :maxdepth => 3)
                        expr = expr.bind(r => rv[0]) if rv.length == 1
                }
                cmp_value = prelast.instruction.args.last.reduce % (1 << prelast.instruction.args.first.sz)
                case last.opcode.name
                when 'jae', 'jb', 'jnae', 'jnb'; index_max = cmp_value-1
                when 'ja', 'jbe', 'jna', 'jnbe'; index_max = cmp_value
                else; expr = nil
                end
                break
        }

        if expr and expr.externals.grep(Symbol).uniq == [index]
                # yay !
                # include the symbolic dest for backtrace stuff
                puts "found jmptable for #{Expression[di.address]} #{di.instruction} (#{index_max+1} entries)" if $VERBOSE
                # TODO add labels / tables / xrefs etc
                [Expression[arg0]] + (0..index_max).map { |i| expr.bind(index => i) }
        end
end
gui_hilight_word_regexp(word) click to toggle source
Calls superclass method Metasm::CPU#gui_hilight_word_regexp
# File metasm/cpu/ia32/render.rb, line 113
def gui_hilight_word_regexp(word)
        @gui_hilight_word_hash ||= gui_hilight_word_regexp_init
        @gui_hilight_word_hash[word] or super(word)
end
gui_hilight_word_regexp_init() click to toggle source
# File metasm/cpu/ia32/render.rb, line 97
def gui_hilight_word_regexp_init
        ret = {}

        %w[a b c d].each { |r|
                ret["#{r}l"] = "e?#{r}x|#{r}l"
                ret["#{r}h"] = "e?#{r}x|#{r}h"
                ret["#{r}x"] = ret["e#{r}x"] = "e?#{r}x|#{r}[hl]"
        }

        %w[sp bp si di].each { |r|
                ret[r] = ret["e#{r}"] = "e?#{r}"
        }

        ret
end
init_386() click to toggle source
# File metasm/cpu/ia32/opcodes.rb, line 1056
def init_386
        init_386_common
        init_386_only
end
init_386_common() click to toggle source

CPU family dependencies

# File metasm/cpu/ia32/opcodes.rb, line 1052
def init_386_common
        init_386_common_only
end
init_386_common_only() click to toggle source

only most common instructions from the 386 instruction set inexhaustive list : no aaa, arpl, mov crX, call/jmp/ret far, in/out, bts, xchg…

# File metasm/cpu/ia32/opcodes.rb, line 38
def init_386_common_only
        init_cpu_constants

        addop_macro1 'adc', 2
        addop_macro1 'add', 0
        addop_macro1 'and', 4, :unsigned_imm
        addop 'bswap', [0x0F, 0xC8], :reg
        addop 'call',  [0xE8], nil, :stopexec, :setip, :i, :saveip
        addop 'call',  [0xFF], 2, :stopexec, :setip, :saveip
        addop('cbw',   [0x98]) { |o| o.props[:opsz] = 16 }
        addop('cwde',  [0x98]) { |o| o.props[:opsz] = 32 }
        addop('cwd',   [0x99]) { |o| o.props[:opsz] = 16 }
        addop('cdq',   [0x99]) { |o| o.props[:opsz] = 32 }
        addop_macro1 'cmp', 7
        addop_macrostr 'cmps',  [0xA6], :stropz
        addop 'dec',   [0x48], :reg
        addop 'dec',   [0xFE], 1,    {:w => [0, 0]}
        addop 'div',   [0xF6], 6,    {:w => [0, 0]}
        addop 'enter', [0xC8], nil, :u16, :u8
        addop 'idiv',  [0xF6], 7,    {:w => [0, 0]}
        addop 'imul',  [0xF6], 5,    {:w => [0, 0]}   # implicit eax, but different semantic from imul eax, ebx (the implicit version updates edx:eax)
        addop 'imul',  [0x0F, 0xAF], :mrm
        addop 'imul',  [0x69], :mrm, {:s => [0, 1]}, :i
        addop 'inc',   [0x40], :reg
        addop 'inc',   [0xFE], 0,    {:w => [0, 0]}
        addop 'int',   [0xCC], nil, :imm_val3, :stopexec
        addop 'int',   [0xCD], nil, :u8
        addop_macrotttn 'j', [0x70], nil, :setip, :i8
        addop_macrotttn('j', [0x70], nil, :setip, :i8) { |o| o.name << '.i8' }
        addop_macrotttn 'j', [0x0F, 0x80], nil, :setip, :i
        addop_macrotttn('j', [0x0F, 0x80], nil, :setip, :i) { |o| o.name << '.i' }
        addop 'jmp',   [0xE9], nil,  {:s => [0, 1]}, :setip, :i,  :stopexec
        addop 'jmp',   [0xFF], 4, :setip, :stopexec
        addop 'lea',   [0x8D], :mrmA
        addop 'leave', [0xC9]
        addop_macrostr 'lods',  [0xAC], :strop
        addop 'loop',  [0xE2], nil, :setip, :i8
        addop 'loopz', [0xE1], nil, :setip, :i8
        addop 'loope', [0xE1], nil, :setip, :i8
        addop 'loopnz',[0xE0], nil, :setip, :i8
        addop 'loopne',[0xE0], nil, :setip, :i8
        addop 'mov',   [0xA0], nil,  {:w => [0, 0], :d => [0, 1]}, :reg_eax, :mrm_imm
        addop('mov',   [0x88], :mrmw,{:d => [0, 1]}) { |o| o.args.reverse! }
        addop 'mov',   [0xB0], :reg, {:w => [0, 3]}, :i, :unsigned_imm
        addop 'mov',   [0xC6], 0,    {:w => [0, 0]}, :i, :unsigned_imm
        addop_macrostr 'movs',  [0xA4], :strop
        addop 'movsx', [0x0F, 0xBE], :mrmw
        addop 'movzx', [0x0F, 0xB6], :mrmw
        addop 'mul',   [0xF6], 4,    {:w => [0, 0]}
        addop 'neg',   [0xF6], 3,    {:w => [0, 0]}
        addop 'nop',   [0x90]
        addop 'not',   [0xF6], 2,    {:w => [0, 0]}
        addop_macro1 'or', 1, :unsigned_imm
        addop 'pop',   [0x58], :reg
        addop 'pop',   [0x8F], 0
        addop 'push',  [0x50], :reg
        addop 'push',  [0xFF], 6
        addop 'push',  [0x68], nil,  {:s => [0, 1]}, :i, :unsigned_imm
        addop 'ret',   [0xC3], nil, :stopexec, :setip
        addop 'ret',   [0xC2], nil, :stopexec, :u16, :setip
        addop_macro3 'rol', 0
        addop_macro3 'ror', 1
        addop_macro3 'sar', 7
        addop_macro1 'sbb', 3
        addop_macrostr 'scas',  [0xAE], :stropz
        addop_macrotttn('set', [0x0F, 0x90], 0) { |o| o.props[:argsz] = 8 }
        addop_macrotttn('set', [0x0F, 0x90], :mrm) { |o| o.props[:argsz] = 8 ; o.args.reverse! }      # :reg field is unused
        addop_macro3 'shl', 4
        addop_macro3 'sal', 6
        addop('shld',  [0x0F, 0xA4], :mrm, :u8) { |o| o.args[0], o.args[1] = o.args[1], o.args[0] }
        addop('shld',  [0x0F, 0xA5], :mrm, :reg_cl) { |o| o.args[0], o.args[1] = o.args[1], o.args[0] }
        addop_macro3 'shr', 5
        addop('shrd',  [0x0F, 0xAC], :mrm, :u8) { |o| o.args[0], o.args[1] = o.args[1], o.args[0] }
        addop('shrd',  [0x0F, 0xAD], :mrm, :reg_cl) { |o| o.args[0], o.args[1] = o.args[1], o.args[0] }
        addop_macrostr 'stos',  [0xAA], :strop
        addop_macro1 'sub', 5
        addop 'test',  [0x84], :mrmw
        addop 'test',  [0xA8], nil,  {:w => [0, 0]}, :reg_eax, :i, :unsigned_imm
        addop 'test',  [0xF6], 0,    {:w => [0, 0]}, :i, :unsigned_imm
        addop 'xchg',  [0x90], :reg, :reg_eax
        addop('xchg',  [0x90], :reg, :reg_eax) { |o| o.args.reverse! }        # xchg eax, ebx == xchg ebx, eax)
        addop 'xchg',  [0x86], :mrmw
        addop('xchg',  [0x86], :mrmw) { |o| o.args.reverse! }
        addop_macro1 'xor', 6, :unsigned_imm
end
init_386_only() click to toggle source
# File metasm/cpu/ia32/opcodes.rb, line 124
        def init_386_only
                init_cpu_constants

                addop 'aaa',   [0x37]
                addop 'aad',   [0xD5, 0x0A]
                addop 'aam',   [0xD4, 0x0A]
                addop 'aas',   [0x3F]
                addop('arpl',  [0x63], :mrm) { |o| o.props[:argsz] = 16 ; o.args.reverse! }
                addop 'bound', [0x62], :mrmA
                addop 'bsf',   [0x0F, 0xBC], :mrm
                addop 'bsr',   [0x0F, 0xBD], :mrm
                addop_macro2 'bt' , 0
                addop_macro2 'btc', 3
                addop_macro2 'btr', 2
                addop_macro2 'bts', 1
                addop 'call',  [0x9A], nil, :stopexec, :setip, :farptr, :saveip
                addop 'callf', [0x9A], nil, :stopexec, :setip, :farptr, :saveip
                addop 'callf', [0xFF], 3, :stopexec, :setip, :saveip
                addop 'clc',   [0xF8]
                addop 'cld',   [0xFC]
                addop 'cli',   [0xFA]
                addop 'clts',  [0x0F, 0x06]
                addop 'cmc',   [0xF5]
                addop('cmpxchg',[0x0F, 0xB0], :mrmw) { |o| o.args.reverse! }
                addop 'cpuid', [0x0F, 0xA2]
                addop 'daa',   [0x27]
                addop 'das',   [0x2F]
                addop 'hlt',   [0xF4], nil, :stopexec
                addop 'in',    [0xE4], nil,  {:w => [0, 0]}, :reg_eax, :u8
                addop 'in',    [0xE4], nil,  {:w => [0, 0]}, :u8
                addop 'in',    [0xEC], nil,  {:w => [0, 0]}, :reg_eax, :reg_dx
                addop 'in',    [0xEC], nil,  {:w => [0, 0]}, :reg_eax
                addop 'in',    [0xEC], nil,  {:w => [0, 0]}
                addop_macrostr 'ins',   [0x6C], :strop
                addop 'into',  [0xCE]
                addop 'invd',  [0x0F, 0x08]
                addop 'invlpg', [0x0F, 0x01, 7<<3], :modrmA
                addop('iretd', [0xCF], nil, :stopexec, :setip) { |o| o.props[:opsz] = 32 }
                addop_macroret 'iret', [0xCF]
                addop('jcxz',  [0xE3], nil, :setip, :i8) { |o| o.props[:adsz] = 16 }
                addop('jecxz', [0xE3], nil, :setip, :i8) { |o| o.props[:adsz] = 32 }
                addop 'jmp',   [0xEA], nil, :farptr, :setip, :stopexec
                addop 'jmpf',  [0xEA], nil, :farptr, :setip, :stopexec
                addop 'jmpf',  [0xFF], 5, :stopexec, :setip           # reg ?
                addop 'lahf',  [0x9F]
                addop 'lar',   [0x0F, 0x02], :mrm
                addop 'lds',   [0xC5], :mrmA
                addop 'les',   [0xC4], :mrmA
                addop 'lfs',   [0x0F, 0xB4], :mrmA
                addop 'lgs',   [0x0F, 0xB5], :mrmA
                addop 'lgdt',  [0x0F, 0x01], 2, :modrmA
                addop 'lidt',  [0x0F, 0x01, 3<<3], :modrmA
                addop 'lldt',  [0x0F, 0x00], 2, :modrmA
                addop 'lmsw',  [0x0F, 0x01], 6
# prefix        addop 'lock',  [0xF0]
                addop 'lsl',   [0x0F, 0x03], :mrm
                addop 'lss',   [0x0F, 0xB2], :mrmA
                addop 'ltr',   [0x0F, 0x00], 3
                addop 'mov',   [0x0F, 0x20, 0xC0], :reg, {:d => [1, 1], :eeec => [2, 3]}, :eeec
                addop 'mov',   [0x0F, 0x21, 0xC0], :reg, {:d => [1, 1], :eeed => [2, 3]}, :eeed
                addop 'mov',   [0x0F, 0x24, 0xC0], :reg, {:d => [1, 1], :eeet => [2, 3]}, :eeet
                addop 'mov',   [0x8C], 0,    {:d => [0, 1], :seg3 => [1, 3]}, :seg3
                addop 'movbe', [0x0F, 0x38, 0xF0], :mrm, { :d => [2, 0] }
                addop 'out',   [0xE6], nil,  {:w => [0, 0]}, :u8, :reg_eax
                addop 'out',   [0xE6], nil,  {:w => [0, 0]}, :reg_eax, :u8
                addop 'out',   [0xE6], nil,  {:w => [0, 0]}, :u8
                addop 'out',   [0xEE], nil,  {:w => [0, 0]}, :reg_dx, :reg_eax
                addop 'out',   [0xEE], nil,  {:w => [0, 0]}, :reg_eax, :reg_dx
                addop 'out',   [0xEE], nil,  {:w => [0, 0]}, :reg_eax                 # implicit arguments
                addop 'out',   [0xEE], nil,  {:w => [0, 0]}
                addop_macrostr 'outs',  [0x6E], :strop
                addop 'pop',   [0x07], nil,  {:seg2A => [0, 3]}, :seg2A
                addop 'pop',   [0x0F, 0x81], nil,  {:seg3A => [1, 3]}, :seg3A
                addop('popa',  [0x61]) { |o| o.props[:opsz] = 16 }
                addop('popad', [0x61]) { |o| o.props[:opsz] = 32 }
                addop('popf',  [0x9D]) { |o| o.props[:opsz] = 16 }
                addop('popfd', [0x9D]) { |o| o.props[:opsz] = 32 }
                addop 'push',  [0x06], nil,  {:seg2 => [0, 3]}, :seg2
                addop 'push',  [0x0F, 0x80], nil,  {:seg3A => [1, 3]}, :seg3A
                addop('pusha', [0x60]) { |o| o.props[:opsz] = 16 }
                addop('pushad',[0x60]) { |o| o.props[:opsz] = 32 }
                addop('pushf', [0x9C]) { |o| o.props[:opsz] = 16 }
                addop('pushfd',[0x9C]) { |o| o.props[:opsz] = 32 }
                addop_macro3 'rcl', 2
                addop_macro3 'rcr', 3
                addop 'rdmsr', [0x0F, 0x32]
                addop 'rdpmc', [0x0F, 0x33]
                addop 'rdtsc', [0x0F, 0x31], nil, :random
                addop_macroret 'retf', [0xCB]
                addop_macroret 'retf', [0xCA], :u16
                addop 'rsm',   [0x0F, 0xAA], nil, :stopexec
                addop 'sahf',  [0x9E]
                addop 'sgdt',  [0x0F, 0x01, 0<<3], :modrmA
                addop 'sidt',  [0x0F, 0x01, 1<<3], :modrmA
                addop 'sldt',  [0x0F, 0x00], 0
                addop 'smsw',  [0x0F, 0x01], 4
                addop 'stc',   [0xF9]
                addop 'std',   [0xFD]
                addop 'sti',   [0xFB]
                addop 'str',   [0x0F, 0x00], 1
                addop 'test',  [0xF6], 1, {:w => [0, 0]}, :i, :unsigned_imm                   # undocumented alias to F6/0
                addop 'ud2',   [0x0F, 0x0B]
                addop 'verr',  [0x0F, 0x00], 4
                addop 'verw',  [0x0F, 0x00], 5
                addop 'wait',  [0x9B]
                addop 'wbinvd',[0x0F, 0x09]
                addop 'wrmsr', [0x0F, 0x30]
                addop('xadd',  [0x0F, 0xC0], :mrmw) { |o| o.args.reverse! }
                addop 'xlat',  [0xD7]

# pfx:  addrsz = 0x67, lock = 0xF0, opsz = 0x66, repnz = 0xF2, rep/repz = 0xF3
#       cs/nojmp = 0x2E, ds/jmp = 0x3E, es = 0x26, fs = 0x64, gs = 0x65, ss = 0x36

                # undocumented opcodes
                addop 'aam',   [0xD4], nil, :u8
                addop 'aad',   [0xD5], nil, :u8
                addop 'setalc',[0xD6]
                addop 'salc',  [0xD6]
                addop 'icebp', [0xF1]
                #addop 'loadall',[0x0F, 0x07] # conflict with syscall
                addop 'ud0',   [0x0F, 0xFF]   # amd
                addop 'ud2',   [0x0F, 0xB9], :mrm
                #addop 'umov',  [0x0F, 0x10], :mrmw, {:d => [1, 1]}   # conflicts with movups/movhlps
        end
init_387() click to toggle source
# File metasm/cpu/ia32/opcodes.rb, line 1061
def init_387
        init_387_only
end
init_387_only() click to toggle source
# File metasm/cpu/ia32/opcodes.rb, line 249
def init_387_only
        init_cpu_constants

        addop 'f2xm1', [0xD9, 0xF0]
        addop 'fabs',  [0xD9, 0xE1]
        addop_macrofpu1 'fadd',  0
        addop 'faddp', [0xDE, 0xC0], :regfp
        addop 'faddp', [0xDE, 0xC1]
        addop('fbld',  [0xDF, 4<<3], :modrmA, :regfp0) { |o| o.props[:argsz] = 80 }
        addop('fbstp', [0xDF, 6<<3], :modrmA, :regfp0) { |o| o.props[:argsz] = 80 }
        addop 'fchs',  [0xD9, 0xE0], nil, :regfp0
        addop 'fnclex',      [0xDB, 0xE2]
        addop_macrofpu1 'fcom',  2
        addop_macrofpu1 'fcomp', 3
        addop 'fcompp',[0xDE, 0xD9]
        addop 'fcomip',[0xDF, 0xF0], :regfp
        addop 'fcos',  [0xD9, 0xFF], nil, :regfp0
        addop 'fdecstp', [0xD9, 0xF6]
        addop_macrofpu1 'fdiv', 6
        addop_macrofpu1 'fdivr', 7
        addop 'fdivp', [0xDE, 0xF8], :regfp
        addop 'fdivp', [0xDE, 0xF9]
        addop 'fdivrp',[0xDE, 0xF0], :regfp
        addop 'fdivrp',[0xDE, 0xF1]
        addop 'ffree', [0xDD, 0xC0], nil,  {:regfp  => [1, 0]}, :regfp
        addop_macrofpu2 'fiadd', 0
        addop_macrofpu2 'fimul', 1
        addop_macrofpu2 'ficom', 2
        addop_macrofpu2 'ficomp',3
        addop_macrofpu2 'fisub', 4
        addop_macrofpu2 'fisubr',5
        addop_macrofpu2 'fidiv', 6
        addop_macrofpu2 'fidivr',7
        addop 'fincstp', [0xD9, 0xF7]
        addop 'fninit',      [0xDB, 0xE3]
        addop_macrofpu2 'fist', 2, 1
        addop_macrofpu3 'fild', 0
        addop_macrofpu3 'fistp',3
        addop('fld', [0xD9, 0<<3], :modrmA, :regfp0) { |o| o.props[:argsz] = 32 }
        addop('fld', [0xDD, 0<<3], :modrmA, :regfp0) { |o| o.props[:argsz] = 64 }
        addop('fld', [0xDB, 5<<3], :modrmA, :regfp0) { |o| o.props[:argsz] = 80 }
        addop 'fld', [0xD9, 0xC0], :regfp

        addop('fldcw',  [0xD9, 5<<3], :modrmA) { |o| o.props[:argsz] = 16 }
        addop 'fldenv', [0xD9, 4<<3], :modrmA
        addop 'fld1',   [0xD9, 0xE8]
        addop 'fldl2t', [0xD9, 0xE9]
        addop 'fldl2e', [0xD9, 0xEA]
        addop 'fldpi',  [0xD9, 0xEB]
        addop 'fldlg2', [0xD9, 0xEC]
        addop 'fldln2', [0xD9, 0xED]
        addop 'fldz',   [0xD9, 0xEE]
        addop_macrofpu1 'fmul',  1
        addop 'fmulp',  [0xDE, 0xC8], :regfp
        addop 'fmulp',  [0xDE, 0xC9]
        addop 'fnop',   [0xD9, 0xD0]
        addop 'fpatan', [0xD9, 0xF3]
        addop 'fprem',  [0xD9, 0xF8]
        addop 'fprem1', [0xD9, 0xF5]
        addop 'fptan',  [0xD9, 0xF2]
        addop 'frndint',[0xD9, 0xFC]
        addop 'frstor', [0xDD, 4<<3], :modrmA
        addop 'fnsave', [0xDD, 6<<3], :modrmA
        addop('fnstcw', [0xD9, 7<<3], :modrmA) { |o| o.props[:argsz] = 16 }
        addop 'fnstenv',[0xD9, 6<<3], :modrmA
        addop 'fnstsw', [0xDF, 0xE0]
        addop('fnstsw', [0xDD, 7<<3], :modrmA) { |o| o.props[:argsz] = 16 }
        addop 'fscale', [0xD9, 0xFD]
        addop 'fsin',   [0xD9, 0xFE]
        addop 'fsincos',[0xD9, 0xFB]
        addop 'fsqrt',  [0xD9, 0xFA]
        addop('fst',  [0xD9, 2<<3], :modrmA, :regfp0) { |o| o.props[:argsz] = 32 }
        addop('fst',  [0xDD, 2<<3], :modrmA, :regfp0) { |o| o.props[:argsz] = 64 }
        addop 'fst',  [0xD9, 0xD0], :regfp
        addop('fstp', [0xD9, 3<<3], :modrmA, :regfp0) { |o| o.props[:argsz] = 32 }
        addop('fstp', [0xDD, 3<<3], :modrmA, :regfp0) { |o| o.props[:argsz] = 64 }
        addop('fstp', [0xDB, 7<<3], :modrmA, :regfp0) { |o| o.props[:argsz] = 80 }
        addop 'fstp', [0xDD, 0xD8], :regfp
        addop_macrofpu1 'fsub',  4
        addop 'fsubp',  [0xDE, 0xE8], :regfp
        addop 'fsubp',  [0xDE, 0xE9]
        addop_macrofpu1 'fsubp', 5
        addop 'fsubrp', [0xDE, 0xE0], :regfp
        addop 'fsubrp', [0xDE, 0xE1]
        addop 'ftst',   [0xD9, 0xE4]
        addop 'fucom',  [0xDD, 0xE0], :regfp
        addop 'fucomp', [0xDD, 0xE8], :regfp
        addop 'fucompp',[0xDA, 0xE9]
        addop 'fucomi', [0xDB, 0xE8], :regfp
        addop 'fxam',   [0xD9, 0xE5]
        addop 'fxch',   [0xD9, 0xC8], :regfp
        addop 'fxtract',[0xD9, 0xF4]
        addop 'fyl2x',  [0xD9, 0xF1]
        addop 'fyl2xp1',[0xD9, 0xF9]
        # fwait prefix
        addop 'fclex',  [0x9B, 0xDB, 0xE2]
        addop 'finit',  [0x9B, 0xDB, 0xE3]
        addop 'fsave',  [0x9B, 0xDD, 6<<3], :modrmA
        addop('fstcw',  [0x9B, 0xD9, 7<<3], :modrmA) { |o| o.props[:argsz] = 16 }
        addop 'fstenv', [0x9B, 0xD9, 6<<3], :modrmA
        addop 'fstsw',  [0x9B, 0xDF, 0xE0]
        addop('fstsw',  [0x9B, 0xDD, 7<<3], :modrmA) { |o| o.props[:argsz] = 16 }
        addop 'fwait',  [0x9B]
end
init_3dnow() click to toggle source
# File metasm/cpu/ia32/opcodes.rb, line 1076
def init_3dnow
        init_pentium
        init_3dnow_only
end
init_3dnow_only() click to toggle source
# File metasm/cpu/ia32/opcodes.rb, line 413
def init_3dnow_only
        init_cpu_constants

        [['pavgusb', 0xBF], ['pfadd', 0x9E], ['pfsub', 0x9A],
         ['pfsubr', 0xAA], ['pfacc', 0xAE], ['pfcmpge', 0x90],
         ['pfcmpgt', 0xA0], ['fpcmpeq', 0xB0], ['pfmin', 0x94],
         ['pfmax', 0xA4], ['pi2fd', 0x0D], ['pf2id', 0x1D],
         ['pfrcp', 0x96], ['pfrsqrt', 0x97], ['pfmul', 0xB4],
         ['pfrcpit1', 0xA6], ['pfrsqit1', 0xA7], ['pfrcpit2', 0xB6],
         ['pmulhrw', 0xB7]].each { |str, bin|
                addop str, [0x0F, 0x0F, bin], :mrmmmx
        }
        # 3dnow prefix fallback
        addop '3dnow', [0x0F, 0x0F], :mrmmmx, :u8

        addop 'femms', [0x0F, 0x0E]
        addop 'prefetch',  [0x0F, 0x0D, 0<<3], :modrmA
        addop 'prefetchw', [0x0F, 0x0D, 1<<3], :modrmA
end
init_486() click to toggle source
# File metasm/cpu/ia32/opcodes.rb, line 1065
def init_486
        init_386
        init_387
        init_486_only
end
init_486_only() click to toggle source
# File metasm/cpu/ia32/opcodes.rb, line 354
def init_486_only
        init_cpu_constants
end
init_aesni_only() click to toggle source
# File metasm/cpu/ia32/opcodes.rb, line 622
def init_aesni_only
        init_cpu_constants

        addop('aesdec',    [0x0F, 0x38, 0xDE], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('aesdeclast',[0x0F, 0x38, 0xDF], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('aesenc',    [0x0F, 0x38, 0xDC], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('aesenclast',[0x0F, 0x38, 0xDD], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('aesimc',    [0x0F, 0x38, 0xDB], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('aeskeygenassist', [0x0F, 0x3A, 0xDF], :mrmxmm, :u8) { |o| o.props[:needpfx] = 0x66 }

        addop('pclmulqdq', [0x0F, 0x3A, 0x44], :mrmxmm, :u8) { |o| o.props[:needpfx] = 0x66 }
end
init_all() click to toggle source
# File metasm/cpu/ia32/opcodes.rb, line 1127
def init_all
        init_avx2
        init_3dnow_only
        init_vmx_only
        init_aesni_only
end
Also aliased as: init_latest
init_avx() click to toggle source
# File metasm/cpu/ia32/opcodes.rb, line 1116
def init_avx
        init_sse42
        init_avx_only
end
init_avx2() click to toggle source
# File metasm/cpu/ia32/opcodes.rb, line 1121
def init_avx2
        init_avx
        init_fma_only
        init_avx2_only
end
init_avx2_only() click to toggle source
# File metasm/cpu/ia32/opcodes.rb, line 883
def init_avx2_only
        init_cpu_constants

        add256 = {}
        %w[packsswb pcmpgtb pcmpgtw pcmpgtd packuswb packssdw
           pcmpeqb pcmpeqw pcmpeqd paddq pmullw psubusb psubusw
           pminub pand paddusb paddusw pmaxub pandn pavgb pavgw
           pmulhuw pmulhw psubsb psubsw pminsw por paddsb paddsw
           pmaxsw pxor pmuludq pmaddwd psadbw
           psubb psubw psubd psubq paddb paddw paddd
           phaddw phaddsw phaddd phsubw phsubsw phsubd
           pmaddubsw palignr pshufb pmulhrsw psignb psignw psignd
           mpsadbw packusdw pblendw pcmpeqq
           pmaxsb pmaxsd pmaxud pmaxuw pminsb pminsd pminud pminuw
           pmuldq pmulld pcmpgtq punpcklbw punpcklwd punpckldq
           punpckhbw punpckhwd punpckhdq punpcklqdq punpckhqdq
        ].each { |n| add256[n] = true }

        varg = Hash.new(1)
        %w[pabsb pabsw pabsd pmovmskb pshufd pshufhw pshuflw movntdqa
           pmovsxbw pmovsxbd pmovsxbq pmovsxwd pmovsxwq pmovsxdq
           pmovzxbw pmovzxbd pmovzxbq pmovzxwd pmovzxwq pmovzxdq
           maskmovdqu].each { |n| add256[n] = true ; varg[n] = nil }

        cvtarg256 = { :regmmx => :regymm, :modrmmmx => :modrmymm,
                        :regxmm => :regymm, :modrmxmm => :modrmymm }

        # autopromote old sseX opcodes
        @opcode_list.each { |o|
                next if o.bin[0] != 0x0F or not add256[o.name]

                mm = (o.bin[1] == 0x38 ? 0x0F38 : o.bin[1] == 0x3A ? 0x0F3A : 0x0F)
                pp = o.props[:needpfx]
                pp = 0x66 if o.props[:xmmx]
                fpxlen = (mm == 0x0F ? 1 : 2)

                addop_vex('v' + o.name, [varg[o.name], 256, pp, mm], o.bin[fpxlen], nil, *o.args.map { |oa| cvtarg256[oa] || oa }) { |oo|
                        oo.bin += [o.bin[fpxlen+1]] if o.bin[fpxlen+1]
                        dbinlen = o.bin.length - oo.bin.length
                        o.fields.each { |k, v| oo.fields[cvtarg256[k] || k] = [v[0]-dbinlen, v[1]] }
                        o.props.each  { |k, v| oo.props[k] = v if k != :xmmx and k != :needpfx }
                }
        }

        # promote special cases
        addop_vex 'vpblendvb', [1, 256, 0x66, 0x0F3A, 0], 0x4C, :mrmymm, :i4ymm
        addop_vex 'vpsllw', [1, 256, 0x66, 0x0F], 0xF1, :mrmymm
        addop_vex('vpsllw', [0, 256, 0x66, 0x0F], 0x71, 6, :u8, :modrmR) { |o| o.args[o.args.index(:modrm)] = :modrmymm }
        addop_vex 'vpslld', [1, 256, 0x66, 0x0F], 0xF2, :mrmymm
        addop_vex('vpslld', [0, 256, 0x66, 0x0F], 0x72, 6, :u8, :modrmR) { |o| o.args[o.args.index(:modrm)] = :modrmymm }
        addop_vex 'vpsllq', [1, 256, 0x66, 0x0F], 0xF3, :mrmymm
        addop_vex('vpsllq', [0, 256, 0x66, 0x0F], 0x73, 6, :u8, :modrmR) { |o| o.args[o.args.index(:modrm)] = :modrmymm }
        addop_vex('vpslldq',[0, 256, 0x66, 0x0F], 0x73, 7, :u8, :modrmR) { |o| o.args[o.args.index(:modrm)] = :modrmymm }
        addop_vex 'vpsraw', [1, 256, 0x66, 0x0F], 0xE1, :mrmymm
        addop_vex('vpsraw', [0, 256, 0x66, 0x0F], 0x71, 4, :u8, :modrmR) { |o| o.args[o.args.index(:modrm)] = :modrmymm }
        addop_vex 'vpsrad', [1, 256, 0x66, 0x0F], 0xE2, :mrmymm
        addop_vex('vpsrad', [0, 256, 0x66, 0x0F], 0x72, 4, :u8, :modrmR) { |o| o.args[o.args.index(:modrm)] = :modrmymm }
        addop_vex 'vpsrlw', [1, 256, 0x66, 0x0F], 0xD1, :mrmymm
        addop_vex('vpsrlw', [0, 256, 0x66, 0x0F], 0x71, 2, :u8, :modrmR) { |o| o.args[o.args.index(:modrm)] = :modrmymm }
        addop_vex 'vpsrld', [1, 256, 0x66, 0x0F], 0xD2, :mrmymm
        addop_vex('vpsrld', [0, 256, 0x66, 0x0F], 0x72, 2, :u8, :modrmR) { |o| o.args[o.args.index(:modrm)] = :modrmymm }
        addop_vex 'vpsrlq', [1, 256, 0x66, 0x0F], 0xD3, :mrmymm
        addop_vex('vpsrlq', [0, 256, 0x66, 0x0F], 0x73, 2, :u8, :modrmR) { |o| o.args[o.args.index(:modrm)] = :modrmymm }
        addop_vex('vpsrldq',[0, 256, 0x66, 0x0F], 0x73, 3, :u8, :modrmR) { |o| o.args[o.args.index(:modrm)] = :modrmymm }

        addop_vex 'vbroadcastss', [nil, 128, 0x66, 0x0F38, 0], 0x18, :mrmxmm, :modrmR
        addop_vex 'vbroadcastss', [nil, 256, 0x66, 0x0F38, 0], 0x18, :mrmymm, :modrmR
        addop_vex 'vbroadcastsd', [nil, 256, 0x66, 0x0F38, 0], 0x19, :mrmymm, :modrmR
        addop_vex 'vbroadcasti128', [nil, 256, 0x66, 0x0F38, 0], 0x5A, :mrmymm, :modrmA
        addop_vex 'vpblendd', [1, 128, 0x66, 0x0F3A, 0], 0x02, :mrmxmm, :u8
        addop_vex 'vpblendd', [1, 256, 0x66, 0x0F3A, 0], 0x02, :mrmymm, :u8
        addop_vex 'vpbroadcastb', [nil, 128, 0x66, 0x0F38, 0], 0x78, :mrmxmm
        addop_vex 'vpbroadcastb', [nil, 256, 0x66, 0x0F38, 0], 0x78, :mrmymm
        addop_vex 'vpbroadcastw', [nil, 128, 0x66, 0x0F38, 0], 0x79, :mrmxmm
        addop_vex 'vpbroadcastw', [nil, 256, 0x66, 0x0F38, 0], 0x79, :mrmymm
        addop_vex 'vpbroadcastd', [nil, 128, 0x66, 0x0F38, 0], 0x58, :mrmxmm
        addop_vex 'vpbroadcastd', [nil, 256, 0x66, 0x0F38, 0], 0x58, :mrmymm
        addop_vex 'vpbroadcastq', [nil, 128, 0x66, 0x0F38, 0], 0x59, :mrmxmm
        addop_vex 'vpbroadcastq', [nil, 256, 0x66, 0x0F38, 0], 0x59, :mrmymm
        addop_vex 'vpermd',     [1,   256, 0x66, 0x0F38, 0], 0x36, :mrmymm
        addop_vex 'vpermpd',    [nil, 256, 0x66, 0x0F3A, 1], 0x01, :mrmymm, :u8
        addop_vex 'vpermps',    [1,   256, 0x66, 0x0F38, 0], 0x16, :mrmymm, :u8
        addop_vex 'vpermq',     [nil, 256, 0x66, 0x0F3A, 1], 0x00, :mrmymm, :u8
        addop_vex 'vperm2i128', [1,   256, 0x66, 0x0F3A, 0], 0x46, :mrmymm, :u8
        addop_vex 'vextracti128', [nil, 256, 0x66, 0x0F3A, 0], 0x39, :mrmymm, :u8
        addop_vex 'vinserti128',  [1,   256, 0x66, 0x0F3A, 0], 0x38, :mrmymm, :u8
        addop_vex 'vpmaskmovd', [1, 128, 0x66, 0x0F38, 0], 0x8C, :mrmxmm, :modrmA
        addop_vex 'vpmaskmovd', [1, 256, 0x66, 0x0F38, 0], 0x8C, :mrmymm, :modrmA
        addop_vex 'vpmaskmovq', [1, 128, 0x66, 0x0F38, 1], 0x8C, :mrmxmm, :modrmA
        addop_vex 'vpmaskmovq', [1, 256, 0x66, 0x0F38, 1], 0x8C, :mrmymm, :modrmA
        addop_vex('vpmaskmovd', [1, 128, 0x66, 0x0F38, 0], 0x8E, :mrmxmm, :modrmA) { |o| o.args.reverse! }
        addop_vex('vpmaskmovd', [1, 256, 0x66, 0x0F38, 0], 0x8E, :mrmymm, :modrmA) { |o| o.args.reverse! }
        addop_vex('vpmaskmovq', [1, 128, 0x66, 0x0F38, 1], 0x8E, :mrmxmm, :modrmA) { |o| o.args.reverse! }
        addop_vex('vpmaskmovq', [1, 256, 0x66, 0x0F38, 1], 0x8E, :mrmymm, :modrmA) { |o| o.args.reverse! }
        addop_vex 'vpsllvd', [1, 128, 0x66, 0x0F38, 0], 0x47, :mrmxmm
        addop_vex 'vpsllvq', [1, 128, 0x66, 0x0F38, 1], 0x47, :mrmxmm
        addop_vex 'vpsllvd', [1, 256, 0x66, 0x0F38, 0], 0x47, :mrmymm
        addop_vex 'vpsllvq', [1, 256, 0x66, 0x0F38, 1], 0x47, :mrmymm
        addop_vex 'vpsravd', [1, 128, 0x66, 0x0F38, 0], 0x46, :mrmxmm
        addop_vex 'vpsravd', [1, 256, 0x66, 0x0F38, 0], 0x46, :mrmymm
        addop_vex 'vpsrlvd', [1, 128, 0x66, 0x0F38, 0], 0x45, :mrmxmm
        addop_vex 'vpsrlvq', [1, 128, 0x66, 0x0F38, 1], 0x45, :mrmxmm
        addop_vex 'vpsrlvd', [1, 256, 0x66, 0x0F38, 0], 0x45, :mrmymm
        addop_vex 'vpsrlvq', [1, 256, 0x66, 0x0F38, 1], 0x45, :mrmymm

        addop_vex('vpgatherdd', [2, 128, 0x66, 0x0F38, 0], 0x90, :mrmxmm) { |o| o.props[:argsz] = 32 ; o.props[:mrmvex] = 128 }
        addop_vex('vpgatherdd', [2, 256, 0x66, 0x0F38, 0], 0x90, :mrmymm) { |o| o.props[:argsz] = 32 ; o.props[:mrmvex] = 256 }
        addop_vex('vpgatherdq', [2, 128, 0x66, 0x0F38, 1], 0x90, :mrmxmm) { |o| o.props[:argsz] = 64 ; o.props[:mrmvex] = 128 }
        addop_vex('vpgatherdq', [2, 256, 0x66, 0x0F38, 1], 0x90, :mrmymm) { |o| o.props[:argsz] = 64 ; o.props[:mrmvex] = 256 }
        addop_vex('vpgatherqd', [2, 128, 0x66, 0x0F38, 0], 0x91, :mrmxmm) { |o| o.props[:argsz] = 32 ; o.props[:mrmvex] = 128 }
        addop_vex('vpgatherqd', [2, 256, 0x66, 0x0F38, 0], 0x91, :mrmymm) { |o| o.props[:argsz] = 32 ; o.props[:mrmvex] = 256 }
        addop_vex('vpgatherqq', [2, 128, 0x66, 0x0F38, 1], 0x91, :mrmxmm) { |o| o.props[:argsz] = 64 ; o.props[:mrmvex] = 128 }
        addop_vex('vpgatherqq', [2, 256, 0x66, 0x0F38, 1], 0x91, :mrmymm) { |o| o.props[:argsz] = 64 ; o.props[:mrmvex] = 256 }
        addop_vex('vgatherdps', [2, 128, 0x66, 0x0F38, 0], 0x92, :mrmxmm) { |o| o.props[:argsz] = 32 ; o.props[:mrmvex] = 128 }
        addop_vex('vgatherdps', [2, 256, 0x66, 0x0F38, 0], 0x92, :mrmymm) { |o| o.props[:argsz] = 32 ; o.props[:mrmvex] = 256 }
        addop_vex('vgatherdpd', [2, 128, 0x66, 0x0F38, 1], 0x92, :mrmxmm) { |o| o.props[:argsz] = 64 ; o.props[:mrmvex] = 128 }
        addop_vex('vgatherdpd', [2, 256, 0x66, 0x0F38, 1], 0x92, :mrmymm) { |o| o.props[:argsz] = 64 ; o.props[:mrmvex] = 256 }
        addop_vex('vgatherqps', [2, 128, 0x66, 0x0F38, 0], 0x93, :mrmxmm) { |o| o.props[:argsz] = 32 ; o.props[:mrmvex] = 128 }
        addop_vex('vgatherqps', [2, 256, 0x66, 0x0F38, 0], 0x93, :mrmymm) { |o| o.props[:argsz] = 32 ; o.props[:mrmvex] = 256 }
        addop_vex('vgatherqpd', [2, 128, 0x66, 0x0F38, 1], 0x93, :mrmxmm) { |o| o.props[:argsz] = 64 ; o.props[:mrmvex] = 128 }
        addop_vex('vgatherqpd', [2, 256, 0x66, 0x0F38, 1], 0x93, :mrmymm) { |o| o.props[:argsz] = 64 ; o.props[:mrmvex] = 256 }
end
init_avx_only() click to toggle source
# File metasm/cpu/ia32/opcodes.rb, line 727
def init_avx_only
        init_cpu_constants

        add128 = {}
        add256 = {}
        %w[movss movsd movlhps movhpd movhlps
           cvtsi2ss cvtsi2sd sqrtss sqrtsd rsqrtss rcpss
           addss addsd mulss mulsd cvtss2sd cvtsd2ss subss subsd
           minss minsd divss divsd maxss maxsd
           punpcklb punpcklw punpckld packsswb pcmpgtb pcmpgtw pcmpgtd packuswb
           punpckhb punpckhw punpckhd packssdw punpcklq punpckhq
           pcmpeqb pcmpeqw pcmpeqd ldmxcsr stmxcsr
           cmpss cmpsd paddq pmullw psubusb psubusw pminub
           pand paddusb paddusw pmaxub pandn pavgb pavgw
           pmulhuw pmulhw psubsb psubsw pminsw por paddsb paddsw pmaxsw pxor
           pmuludq pmaddwd psadbw
           psubb psubw psubd psubq paddb paddw paddd
           phaddw phaddsw phaddd phsubw phsubsw phsubd
           pmaddubsw palignr pshufb pmulhrsw psignb psignw psignd
           dppd insertps mpsadbw packusdw pblendw pcmpeqq
           pinsrb pinsrw pinsrd pinsrq
           pmaxsb pmaxsd pmaxud pmaxuw pminsb pminsd pminud pminuw
           pmuldq pmulld roundsd roundss pcmpgtq
           aesdec aesdeclast aesenc aesenclast
           pclmulqdq punpcklbw punpcklwd punpckldq punpckhbw punpckhwd
           punpckhdq punpcklqdq punpckhqdq].each { |n| add128[n] = true }

        %w[movups movupd movddup movsldup
           unpcklps unpcklpd unpckhps unpckhpd
           movaps movshdup movapd movntps movntpd movmskps movmskpd
           sqrtps sqrtpd rsqrtps rcpps andps andpd andnps andnpd
           orps orpd xorps xorpd addps addpd mulps mulpd
           cvtps2pd cvtpd2ps cvtdq2ps cvtps2dq cvttps2dq
           subps subpd minps minpd divps divpd maxps maxpd
           movdqa movdqu haddpd haddps hsubpd hsubps
           cmpps cmppd shufps shufpd addsubpd addsubps
           cvtpd2dq cvttpd2dq cvtdq2pd movntdq lddqu
           blendps blendpd blendvps blendvpd dpps ptest
           roundpd roundps].each { |n| add128[n] = add256[n] = true }

        varg = Hash.new(1)
        %w[pabsb pabsw pabsd pmovmskb pshufd pshufhw pshuflw movntdqa
           pmovsxbw pmovsxbd pmovsxbq pmovsxwd pmovsxwq pmovsxdq
           pmovzxbw pmovzxbd pmovzxbq pmovzxwd pmovzxwq pmovzxdq
           aesimc aeskeygenassist lddqu maskmovdqu movapd movaps
           pcmpestri pcmpestrm pcmpistri pcmpistrm phminposuw
           cvtpd2dq cvttpd2dq cvtdq2pd cvtps2pd cvtpd2ps cvtdq2ps cvtps2dq
           cvttps2dq movd movq movddup movdqa movdqu movmskps movmskpd
           movntdq movntps movntpd movshdup movsldup movups movupd
           pextrb pextrw pextrd pextrq ptest rcpps roundps roundpd
           extractps sqrtps sqrtpd comiss comisd ucomiss ucomisd
           cvttss2si cvttsd2si cvtss2si cvtsd2si
        ].each { |n| add128[n] = true ; varg[n] = nil }

        cvtarg128 = { :regmmx => :regxmm, :modrmmmx => :modrmxmm }
        cvtarg256 = { :regmmx => :regymm, :modrmmmx => :modrmymm,
                        :regxmm => :regymm, :modrmxmm => :modrmymm }

        # autopromote old sseX opcodes
        @opcode_list.each { |o|
                next if o.bin[0] != 0x0F or not add128[o.name]       # rep cmpsd / movsd

                mm = (o.bin[1] == 0x38 ? 0x0F38 : o.bin[1] == 0x3A ? 0x0F3A : 0x0F)
                pp = o.props[:needpfx]
                pp = 0x66 if o.props[:xmmx]
                fpxlen = (mm == 0x0F ? 1 : 2)

                addop_vex('v' + o.name, [varg[o.name], 128, pp, mm], o.bin[fpxlen], nil, *o.args.map { |oa| cvtarg128[oa] || oa }) { |oo|
                        oo.bin += [o.bin[fpxlen+1]] if o.bin[fpxlen+1]
                        dbinlen = o.bin.length - oo.bin.length
                        o.fields.each { |k, v| oo.fields[cvtarg128[k] || k] = [v[0]-dbinlen, v[1]] }
                        o.props.each  { |k, v| oo.props[k] = v if k != :xmmx and k != :needpfx }
                }

                next if not add256[o.name]
                addop_vex('v' + o.name, [varg[o.name], 256, pp, mm], o.bin[fpxlen], nil, *o.args.map { |oa| cvtarg256[oa] || oa }) { |oo|
                        oo.bin += [o.bin[fpxlen+1]] if o.bin[fpxlen+1]
                        dbinlen = o.bin.length - oo.bin.length
                        o.fields.each { |k, v| oo.fields[cvtarg256[k] || k] = [v[0]-dbinlen, v[1]] }
                        o.props.each  { |k, v| oo.props[k] = v if k != :xmmx and k != :needpfx }
                }
        }

        # sse promotion, special cases
        addop_vex 'vpblendvb', [1, 128, 0x66, 0x0F3A, 0], 0x4C, :mrmxmm, :i4xmm
        addop_vex 'vpsllw', [1, 128, 0x66, 0x0F], 0xF1, :mrmxmm
        addop_vex('vpsllw', [0, 128, 0x66, 0x0F], 0x71, 6, :u8, :modrmR) { |o| o.args[o.args.index(:modrm)] = :modrmxmm }
        addop_vex 'vpslld', [1, 128, 0x66, 0x0F], 0xF2, :mrmxmm
        addop_vex('vpslld', [0, 128, 0x66, 0x0F], 0x72, 6, :u8, :modrmR) { |o| o.args[o.args.index(:modrm)] = :modrmxmm }
        addop_vex 'vpsllq', [1, 128, 0x66, 0x0F], 0xF3, :mrmxmm
        addop_vex('vpsllq', [0, 128, 0x66, 0x0F], 0x73, 6, :u8, :modrmR) { |o| o.args[o.args.index(:modrm)] = :modrmxmm }
        addop_vex('vpslldq',[0, 128, 0x66, 0x0F], 0x73, 7, :u8, :modrmR) { |o| o.args[o.args.index(:modrm)] = :modrmxmm }
        addop_vex 'vpsraw', [1, 128, 0x66, 0x0F], 0xE1, :mrmxmm
        addop_vex('vpsraw', [0, 128, 0x66, 0x0F], 0x71, 4, :u8, :modrmR) { |o| o.args[o.args.index(:modrm)] = :modrmxmm }
        addop_vex 'vpsrad', [1, 128, 0x66, 0x0F], 0xE2, :mrmxmm
        addop_vex('vpsrad', [0, 128, 0x66, 0x0F], 0x72, 4, :u8, :modrmR) { |o| o.args[o.args.index(:modrm)] = :modrmxmm }
        addop_vex 'vpsrlw', [1, 128, 0x66, 0x0F], 0xD1, :mrmxmm
        addop_vex('vpsrlw', [0, 128, 0x66, 0x0F], 0x71, 2, :u8, :modrmR) { |o| o.args[o.args.index(:modrm)] = :modrmxmm }
        addop_vex 'vpsrld', [1, 128, 0x66, 0x0F], 0xD2, :mrmxmm
        addop_vex('vpsrld', [0, 128, 0x66, 0x0F], 0x72, 2, :u8, :modrmR) { |o| o.args[o.args.index(:modrm)] = :modrmxmm }
        addop_vex 'vpsrlq', [1, 128, 0x66, 0x0F], 0xD3, :mrmxmm
        addop_vex('vpsrlq', [0, 128, 0x66, 0x0F], 0x73, 2, :u8, :modrmR) { |o| o.args[o.args.index(:modrm)] = :modrmxmm }
        addop_vex('vpsrldq',[0, 128, 0x66, 0x0F], 0x73, 3, :u8, :modrmR) { |o| o.args[o.args.index(:modrm)] = :modrmxmm }

        # dst==mem => no vreg
        addop_vex 'vmovhps', [1,   128, nil,  0x0F], 0x16, :mrmxmm, :modrmA
        addop_vex('vmovhps', [nil, 128, nil,  0x0F], 0x17, :mrmxmm, :modrmA) { |o| o.args.reverse! }
        addop_vex 'vmovlpd', [1,   128, 0x66, 0x0F], 0x12, :mrmxmm, :modrmA
        addop_vex('vmovlpd', [nil, 128, 0x66, 0x0F], 0x13, :mrmxmm, :modrmA) { |o| o.args.reverse! }
        addop_vex 'vmovlps', [1,   128, nil,  0x0F], 0x12, :mrmxmm, :modrmA
        addop_vex('vmovlps', [nil, 128, nil,  0x0F], 0x13, :mrmxmm, :modrmA) { |o| o.args.reverse! }

        addop_vex 'vbroadcastss', [nil, 128, 0x66, 0x0F38, 0], 0x18, :mrmxmm, :modrmA
        addop_vex 'vbroadcastss', [nil, 256, 0x66, 0x0F38, 0], 0x18, :mrmymm, :modrmA
        addop_vex 'vbroadcastsd', [nil, 256, 0x66, 0x0F38, 0], 0x19, :mrmymm, :modrmA
        addop_vex 'vbroadcastf128', [nil, 256, 0x66, 0x0F38, 0], 0x1A, :mrmymm, :modrmA

        # general-purpose register operations
        addop_vex 'andn', [1, :vexvreg, 128, nil, 0x0F38], 0xF2, :mrm
        addop_vex 'bextr', [2, :vexvreg, 128, nil, 0x0F38], 0xF7, :mrm
        addop_vex 'blsi', [0, :vexvreg, 128, nil, 0x0F38], 0xF3, 3
        addop_vex 'blsmsk', [0, :vexvreg, 128, nil, 0x0F38], 0xF3, 2
        addop_vex 'blsr', [0, :vexvreg, 128, nil, 0x0F38], 0xF3, 1
        addop_vex 'bzhi', [2, :vexvreg, 128, nil, 0x0F38], 0xF5, :mrm
        addop('lzcnt', [0x0F, 0xBD], :mrm) { |o| o.props[:needpfx] = 0xF3 }
        addop_vex 'mulx', [1, :vexvreg, 128, 0xF2, 0x0F38], 0xF6, :mrm
        addop_vex 'pdep', [1, :vexvreg, 128, 0xF2, 0x0F38], 0xF5, :mrm
        addop_vex 'pext', [1, :vexvreg, 128, 0xF3, 0x0F38], 0xF5, :mrm
        addop_vex 'rorx', [nil, 128, 0xF2, 0x0F3A], 0xF0, :mrm, :u8
        addop_vex 'sarx', [2, :vexvreg, 128, 0xF3, 0x0F38], 0xF7, :mrm
        addop_vex 'shrx', [2, :vexvreg, 128, 0xF2, 0x0F38], 0xF7, :mrm
        addop_vex 'shlx', [2, :vexvreg, 128, 0x66, 0x0F38], 0xF7, :mrm
        addop('tzcnt', [0x0F, 0xBC], :mrm) { |o| o.props[:needpfx] = 0xF3 }
        addop('invpcid', [0x0F, 0x38, 0x82], :mrm) { |o| o.props[:needpfx] = 0x66 }
        addop 'rdrand', [0x0F, 0xC7], 6, :modrmR
        addop 'rdseed', [0x0F, 0xC7], 7, :modrmR
        addop('adcx', [0x0F, 0x38, 0xF6], :mrm) { |o| o.props[:needpfx] = 0x66 }
        addop('adox', [0x0F, 0x38, 0xF6], :mrm) { |o| o.props[:needpfx] = 0xF3 }

        # fp16
        addop_vex 'vcvtph2ps', [nil, 128, 0x66, 0x0F38, 0], 0x13, :mrmxmm
        addop_vex 'vcvtph2ps', [nil, 256, 0x66, 0x0F38, 0], 0x13, :mrmymm
        addop_vex('vcvtps2ph', [nil, 128, 0x66, 0x0F3A, 0], 0x1D, :mrmxmm, :u8) { |o| o.args.reverse! }
        addop_vex('vcvtps2ph', [nil, 256, 0x66, 0x0F3A, 0], 0x1D, :mrmymm, :u8) { |o| o.args.reverse! }

        # TSE
        addop 'xabort', [0xC6, 0xF8], nil, :i8        # may :stopexec
        addop 'xbegin', [0xC7, 0xF8], nil, :i # may :setip: xabortreturns to $_(xbegin) + off
        addop 'xend',   [0x0F, 0x01, 0xD5]
        addop 'xtest',  [0x0F, 0x01, 0xD6]

        # SMAP
        addop 'clac',  [0x0F, 0x01, 0xCA]
        addop 'stac',  [0x0F, 0x01, 0xCB]
end
init_backtrace_binding() click to toggle source

populate the @backtrace_binding hash with default values

# File metasm/cpu/ia32/decode.rb, line 339
def init_backtrace_binding
        @backtrace_binding ||= {}

        eax, ecx, edx, ebx, esp, ebp, esi, edi = register_symbols
        ebx = ebx

        mask = lambda { |di| (1 << opsz(di))-1 }      # 32bits => 0xffff_ffff
        sign = lambda { |v, di| Expression[[[v, :&, mask[di]], :>>, opsz(di)-1], :'!=', 0] }

        opcode_list.map { |ol| ol.basename }.uniq.sort.each { |op|
                binding = case op
                when 'mov', 'movzx', 'movd', 'movq'; lambda { |di, a0, a1| { a0 => Expression[a1] } }
                when 'movsx', 'movsxd'
                        lambda { |di, a0, a1|
                                sz1 = di.instruction.args[1].sz
                                sign1 = Expression[[a1, :>>, sz1-1], :&, 1]
                                { a0 => Expression[[a1, :|, [sign1, :*, (-1 << sz1)]], :&, mask[di]] }
                        }
                when 'lea'; lambda { |di, a0, a1| { a0 => a1.target } }
                when 'xchg'; lambda { |di, a0, a1|
                        # specialcase xchg al, ah (conflict on eax not handled in get_backtrace_binding)
                        if a0.kind_of?(Expression) and a1.kind_of?(Expression) and
                                        a0.op == :& and a0.rexpr == 255 and
                                        a1.op == :& and a1.rexpr == 255 and
                                        ((a0.lexpr.kind_of?(Expression) and a0.lexpr.lexpr == a1.lexpr and a0.lexpr.op == :>> and a0.lexpr.rexpr == 8) or
                                         (a1.lexpr.kind_of?(Expression) and a1.lexpr.lexpr == a0.lexpr and a1.lexpr.op == :>> and a1.lexpr.rexpr == 8))
                                tgreg = a0.lexpr.kind_of?(Expression) ? a1.lexpr : a0.lexpr
                                invmask = (@size == 64 ? 0xffff_ffff_ffff_0000 : 0xffff_0000)
                                { tgreg => Expression[[tgreg, :&, invmask], :|,
                                   [[[tgreg, :>>, 8], :&, 0x00ff], :|,
                                    [[tgreg, :<<, 8], :&, 0xff00]]] }
                        else
                                { a0 => Expression[a1], a1 => Expression[a0] }
                        end
                }
                when 'add', 'sub', 'or', 'xor', 'and', 'pxor', 'adc', 'sbb'
                        lambda { |di, a0, a1|
                                e_op = { 'add' => :+, 'sub' => :-, 'or' => :|, 'and' => :&, 'xor' => :^, 'pxor' => :^, 'adc' => :+, 'sbb' => :- }[op]
                                ret = Expression[a0, e_op, a1]
                                ret = Expression[ret, e_op, :eflag_c] if op == 'adc' or op == 'sbb'
                                # optimises eax ^ eax => 0
                                # avoid hiding memory accesses (to not hide possible fault)
                                ret = Expression[ret.reduce] if not a0.kind_of? Indirection
                                { a0 => ret }
                        }
                when 'xadd'; lambda { |di, a0, a1| { a0 => Expression[a0, :+, a1], a1 => Expression[a0] } }
                when 'inc'; lambda { |di, a0| { a0 => Expression[a0, :+, 1] } }
                when 'dec'; lambda { |di, a0| { a0 => Expression[a0, :-, 1] } }
                when 'not'; lambda { |di, a0| { a0 => Expression[a0, :^, mask[di]] } }
                when 'neg'; lambda { |di, a0| { a0 => Expression[:-, a0] } }
                when 'rol', 'ror'
                        lambda { |di, a0, a1|
                                e_op = (op[2] == ?r ? :>> : :<<)
                                inv_op = {:<< => :>>, :>> => :<< }[e_op]
                                operandsize = di.instruction.args[0].sz
                                operandmask = (1 << operandsize) - 1
                                sz = [a1, :%, operandsize]
                                isz = [[operandsize, :-, a1], :%, operandsize]
                                # ror a, b  =>  (a >> b) | (a << (32-b))
                                { a0 => Expression[[[[a0, :&, operandmask], e_op, sz], :|, [[a0, :&, operandmask], inv_op, isz]], :&, operandmask] }
                        }
                when 'shl', 'sal'; lambda { |di, a0, a1| { a0 => Expression[a0, (op[-1] == ?r ? :>> : :<<), [a1, :%, [opsz(di), 32].max]] } }
                when 'sar'; lambda { |di, a0, a1|
                        sz = [opsz(di), 32].max
                        a1 = [a1, :%, sz]
                        { a0 => Expression[[a0, :>>, a1], :|,
                                           [[[mask[di], :*, sign[a0, di]], :<<, [sz, :-, a1]], :&, mask[di]]] } }
                when 'shr'; lambda { |di, a0, a1| { a0 => Expression[[a0, :&, mask[di]], :>>, [a1, :%, opsz(di)]] } }
                when 'shrd'
                        lambda { |di, a0, a1, a2|
                                { a0 => Expression[[a0, :>>, [a2, :%, opsz(di)]], :|, [a1, :<<, [[opsz(di), :-, a2], :%, opsz(di)]]] }
                        }
                when 'shld'
                        lambda { |di, a0, a1, a2|
                                { a0 => Expression[[a0, :<<, [a2, :%, opsz(di)]], :|, [a1, :>>, [[opsz(di), :-, a2], :%, opsz(di)]]] }
                        }
                when 'cwd', 'cdq', 'cqo'; lambda { |di| { Expression[edx, :&, mask[di]] => Expression[mask[di], :*, sign[eax, di]] } }
                when 'cbw', 'cwde', 'cdqe'; lambda { |di|
                        o2 = opsz(di)/2 ; m2 = (1 << o2) - 1
                        { Expression[eax, :&, mask[di]] => Expression[[eax, :&, m2], :|, [m2 << o2, :*, [[eax, :>>, o2-1], :&, 1]]] } }
                when 'push'
                        lambda { |di, a0| { esp => Expression[esp, :-, opsz(di)/8],
                                Indirection[esp, opsz(di)/8, di.address] => Expression[a0] } }
                when 'pop'
                        lambda { |di, a0| { esp => Expression[esp, :+, opsz(di)/8],
                                a0 => Indirection[esp, opsz(di)/8, di.address] } }
                when 'pushfd', 'pushf', 'pushfq'
                        # TODO Unknown per bit
                        lambda { |di|
                                efl = Expression[0x202]
                                bts = lambda { |pos, v| efl = Expression[efl, :|, [[v, :&, 1], :<<, pos]] }
                                bts[0, :eflag_c]
                                bts[6, :eflag_z]
                                bts[7, :eflag_s]
                                bts[11, :eflag_o]
                                { esp => Expression[esp, :-, opsz(di)/8], Indirection[esp, opsz(di)/8, di.address] => efl }
                        }
                when 'popfd', 'popf', 'popfq'
                        lambda { |di| bt = lambda { |pos| Expression[[Indirection[esp, opsz(di)/8, di.address], :>>, pos], :&, 1] }
                                { esp => Expression[esp, :+, opsz(di)/8], :eflag_c => bt[0], :eflag_z => bt[6], :eflag_s => bt[7], :eflag_o => bt[11] } }
                when 'sahf'
                        lambda { |di| bt = lambda { |pos| Expression[[eax, :>>, 8+pos], :&, 1] }
                                { :eflag_c => bt[0], :eflag_z => bt[6], :eflag_s => bt[7] } }
                when 'lahf'
                        lambda { |di|
                                efl = Expression[2]
                                bts = lambda { |pos, v| efl = Expression[efl, :|, [[v, :&, 1], :<<, pos]] }
                                bts[0, :eflag_c] #bts[2, :eflag_p] #bts[4, :eflag_a]
                                bts[6, :eflag_z]
                                bts[7, :eflag_s]
                                { Expression[[eax, :>>, 8], :&, 0xff] => efl }
                        }
                when 'pushad'
                        lambda { |di|
                                ret = {}
                                st_off = 0
                                register_symbols.reverse_each { |r|
                                        ret[Indirection[Expression[esp, :+, st_off].reduce, opsz(di)/8, di.address]] = Expression[r]
                                        st_off += opsz(di)/8
                                }
                                ret[esp] = Expression[esp, :-, st_off]
                                ret
                        }
                when 'popad'
                        lambda { |di|
                                ret = {}
                                st_off = 0
                                register_symbols.reverse_each { |r|
                                        ret[r] = Indirection[Expression[esp, :+, st_off].reduce, opsz(di)/8, di.address]
                                        st_off += opsz(di)/8
                                }
                                ret[esp] = Expression[esp, :+, st_off]     # esp is not popped
                                ret
                        }
                when 'call'
                        lambda { |di, a0|
                                sz = opsz(di)/8
                                if a0.kind_of? Farptr
                                        { esp => Expression[esp, :-, 2*sz],
                                          Indirection[esp, sz, di.address] => Expression[di.next_addr],
                                          Indirection[[esp, :+, sz], sz, di.address] => Expression::Unknown }
                                else
                                        { esp => Expression[esp, :-, sz],
                                          Indirection[esp, sz, di.address] => Expression[di.next_addr] }
                                end
                        }
                when 'callf'
                        lambda { |di, a0|
                                sz = opsz(di)/8
                                { esp => Expression[esp, :-, 2*sz],
                                  Indirection[esp, sz, di.address] => Expression[di.next_addr],
                                  Indirection[[esp, :+, sz], sz, di.address] => Expression::Unknown } }
                when 'ret'; lambda { |di, *a| { esp => Expression[esp, :+, [opsz(di)/8, :+, a[0] || 0]] } }
                when 'retf';lambda { |di, *a| { esp => Expression[esp, :+, [opsz(di)/4, :+, a[0] || 0]] } }
                when 'loop', 'loopz', 'loopnz'; lambda { |di, a0| { ecx => Expression[ecx, :-, 1] } }
                when 'enter'
                        lambda { |di, a0, a1|
                                sz = opsz(di)/8
                                depth = a1.reduce % 32
                                b = {      Indirection[ebp, sz, di.address] => Expression[ebp],
                                        Indirection[[esp, :+, a0.reduce+sz*depth], sz, di.address] => Expression[ebp],
                                        ebp => Expression[esp, :-, sz],
                                        esp => Expression[esp, :-, a0.reduce+sz*depth+sz] }
                                (1..depth).each { |i|
                                        b[Indirection[[esp, :+, a0.reduce+i*sz], sz, di.address]] =
                                        b[Indirection[[ebp, :-, i*sz], sz, di.address]] =
                                                Expression::Unknown # TODO Indirection[[ebp, :-, i*sz], sz, di.address]
                                }
                                b
                        }
                when 'leave'; lambda { |di| { ebp => Indirection[[ebp], opsz(di)/8, di.address], esp => Expression[ebp, :+, opsz(di)/8] } }
                when 'aaa'; lambda { |di| { eax => Expression::Unknown, :incomplete_binding => Expression[1] } }
                when 'imul'
                        lambda { |di, *a|
                                if not a[1]
                                        # 1 operand from: store result in edx:eax
                                        bd = {}
                                        m = mask[di]
                                        s = opsz(di)
                                        e = Expression[Expression.make_signed(Expression[a[0], :&, m], s), :*, Expression.make_signed(Expression[eax, :&, m], s)]
                                        if s == 8
                                                bd[Expression[eax, :&, 0xffff]] = e
                                        else
                                                bd[Expression[eax, :&, m]] = Expression[e, :&, m]
                                                bd[Expression[edx, :&, m]] = Expression[[e, :>>, opsz(di)], :&, m]
                                        end
                                        # XXX eflags?
                                        next bd
                                end

                                if a[2]; e = Expression[a[1], :*, a[2]]
                                else e = Expression[[a[0], :*, a[1]], :&, (1 << (di.instruction.args.first.sz || opsz(di))) - 1]
                                end
                                { a[0] => e }
                        }
                when 'mul'
                        lambda { |di, *a|
                                m = mask[di]
                                e = Expression[a, :*, [eax, :&, m]]
                                if opsz(di) == 8
                                        { Expression[eax, :&, 0xffff] => e }
                                else
                                        { Expression[eax, :&, m] => Expression[e, :&, m],
                                          Expression[edx, :&, m] => Expression[[e, :>>, opsz(di)], :&, m] }
                                end
                        }
                when 'div', 'idiv'; lambda { |di, *a| { eax => Expression::Unknown, edx => Expression::Unknown, :incomplete_binding => Expression[1] } }
                when 'rdtsc'; lambda { |di| { eax => Expression::Unknown, edx => Expression::Unknown, :incomplete_binding => Expression[1] } }
                when /^(stos|movs|lods|scas|cmps)[bwdq]$/
                        lambda { |di, *a|
                                next {:incomplete_binding => 1} if di.opcode.args.include?(:regxmm)        # XXX movsd xmm0, xmm1...
                                op =~ /^(stos|movs|lods|scas|cmps)([bwdq])$/
                                e_op = $1
                                sz = { 'b' => 1, 'w' => 2, 'd' => 4, 'q' => 8 }[$2]
                                eax_ = self.class::Reg.new(0, 8*sz).symbolic
                                dir = :+
                                if di.block and (di.block.list.find { |ddi| ddi.opcode.name == 'std' } rescue nil)
                                        dir = :-
                                end
                                pesi = Indirection[esi, sz, di.address]
                                pedi = Indirection[edi, sz, di.address]
                                pfx = di.instruction.prefix || {}
                                bd =
                                case e_op
                                when 'movs'
                                        case pfx[:rep]
                                        when nil; { pedi => pesi, esi => Expression[esi, dir, sz], edi => Expression[edi, dir, sz] }
                                        else      { pedi => pesi, esi => Expression[esi, dir, [sz ,:*, ecx]], edi => Expression[edi, dir, [sz, :*, ecx]], ecx => 0 }
                                        end
                                when 'stos'
                                        case pfx[:rep]
                                        when nil; { pedi => Expression[eax_], edi => Expression[edi, dir, sz] }
                                        else      { pedi => Expression[eax_], edi => Expression[edi, dir, [sz, :*, ecx]], ecx => 0 }
                                        end
                                when 'lods'
                                        case pfx[:rep]
                                        when nil; { eax_ => pesi, esi => Expression[esi, dir, sz] }
                                        else      { eax_ => Indirection[[esi, dir, [sz, :*, [ecx, :-, 1]]], sz, di.address], esi => Expression[esi, dir, [sz, :*, ecx]], ecx => 0 }
                                        end
                                when 'scas'
                                        case pfx[:rep]
                                        when nil; { edi => Expression[edi, dir, sz], :eflag_z => Expression[pedi, :==, Expression[eax, :&, (1 << (sz*8))-1]] }
                                        else { edi => Expression::Unknown, ecx => Expression::Unknown }
                                        end
                                when 'cmps'
                                        case pfx[:rep]
                                        when nil; { edi => Expression[edi, dir, sz], esi => Expression[esi, dir, sz] }
                                        else { edi => Expression::Unknown, esi => Expression::Unknown, ecx => Expression::Unknown }
                                        end
                                end
                                bd[:incomplete_binding] = Expression[1] if pfx[:rep]
                                bd
                        }
                when 'clc'; lambda { |di| { :eflag_c => Expression[0] } }
                when 'stc'; lambda { |di| { :eflag_c => Expression[1] } }
                when 'cmc'; lambda { |di| { :eflag_c => Expression[:'!', :eflag_c] } }
                when 'cld'; lambda { |di| { :eflag_d => Expression[0] } }
                when 'std'; lambda { |di| { :eflag_d => Expression[1] } }
                when 'setalc'; lambda { |di| { Reg.new(0, 8).symbolic => Expression[:eflag_c, :*, 0xff] } }
                when /^set/; lambda { |di, *a| { a[0] => Expression[decode_cc_to_expr(op[/^set(.*)/, 1])] } }
                when /^cmov/; lambda { |di, *a| fl = decode_cc_to_expr(op[/^cmov(.*)/, 1]) ; { a[0] => Expression[[fl, :*, a[1]], :|, [[1, :-, fl], :*, a[0]]] } }
                when /^j/
                        lambda { |di, a0|
                                ret = { 'dummy_metasm_0' => Expression[a0] }       # mark modr/m as read
                                if fl = decode_cc_to_expr(op[/^j(.*)/, 1]) and fl != Expression::Unknown
                                        ret['dummy_metasm_1'] = fl        # mark eflags as read
                                end
                                ret
                        }
                when 'fstenv', 'fnstenv'
                        lambda { |di, a0|
                                # stores the address of the last non-control fpu instr run
                                lastfpuinstr = di.block.list[0...di.block.list.index(di)].reverse.find { |pdi|
                                        case pdi.opcode.name
                                        when /fn?init|fn?clex|fldcw|fn?st[cs]w|fn?stenv|fldenv|fn?save|frstor|f?wait/
                                        when /^f/; true
                                        end
                                } if di.block
                                lastfpuinstr = lastfpuinstr.address if lastfpuinstr
                                ret = {}
                                save_at = lambda { |off, val| ret[Indirection[a0.target + off, 4, di.address]] = val }
                                save_at[0, Expression::Unknown]
                                save_at[4, Expression::Unknown]
                                save_at[8, Expression::Unknown]
                                save_at[12, lastfpuinstr || Expression::Unknown]
                                save_at[16, Expression::Unknown]
                                save_at[20, Expression::Unknown]
                                save_at[24, Expression::Unknown]
                                ret
                        }
                when 'bt';  lambda { |di, a0, a1| { :eflag_c => Expression[[a0, :>>, [a1, :%, opsz(di)]], :&, 1] } }
                when 'bts'; lambda { |di, a0, a1| { :eflag_c => Expression[[a0, :>>, [a1, :%, opsz(di)]], :&, 1],
                        a0 => Expression[a0, :|, [1, :<<, [a1, :%, opsz(di)]]] } }
                when 'btr'; lambda { |di, a0, a1| { :eflag_c => Expression[[a0, :>>, [a1, :%, opsz(di)]], :&, 1],
                        a0 => Expression[a0, :&, [[1, :<<, [a1, :%, opsz(di)]], :^, mask[di]]] } }
                when 'btc'; lambda { |di, a0, a1| { :eflag_c => Expression[[a0, :>>, [a1, :%, opsz(di)]], :&, 1],
                        a0 => Expression[a0, :^, [1, :<<, [a1, :%, opsz(di)]]] } }
                when 'bswap'
                        lambda { |di, a0|
                                case opsz(di)
                                when 64
                                        { a0 => Expression[
                                                [[[[a0, :&, 0xff000000_00000000], :>>, 56],   :|,
                                                  [[a0, :&, 0x00ff0000_00000000], :>>, 40]],  :|,
                                                 [[[a0, :&, 0x0000ff00_00000000], :>>, 24],   :|,
                                                  [[a0, :&, 0x000000ff_00000000], :>>,  8]]], :|,
                                                [[[[a0, :&, 0x00000000_ff000000], :<<,  8],   :|,
                                                  [[a0, :&, 0x00000000_00ff0000], :<<, 24]],  :|,
                                                 [[[a0, :&, 0x00000000_0000ff00], :<<, 40],   :|,
                                                  [[a0, :&, 0x00000000_000000ff], :<<, 56]]]] }
                                when 32
                                        { a0 => Expression[
                                                [[[a0, :&, 0xff000000], :>>, 24],  :|,
                                                 [[a0, :&, 0x00ff0000], :>>,  8]], :|,
                                                [[[a0, :&, 0x0000ff00], :<<,  8],  :|,
                                                 [[a0, :&, 0x000000ff], :<<, 24]]] }
                                when 16
                                        # bswap ax => mov ax, 0
                                        { a0 => 0 }
                                end
                        }
                when 'movdqa', 'movdqu', 'movaps', 'movups'; lambda { |di, a0, a1| { a0 => Expression[a1] } }
                when 'cmpxchg'; lambda { |di, a0, a1|        # eax == a0 ? a0 <= a1, zf <= 1 : eax <= a0, zf <= 0
                        eax_ = self.class::Reg.new(0, opsz(di)).symbolic
                        cmp = Expression[eax_, :==, a0]
                        { :eflag_z => cmp,
                          eax_ => Expression[[cmp, :*, eax_], :|, [[1, :-, cmp], :*, a0]],
                          a0 => Expression[[cmp, :*, a1], :|, [[1, :-, cmp], :*, a0]] } }
                when 'cmpxchg8b', 'cmpxchg16b'; lambda { |di, a0|    # edx:eax == mem ? mem <= ecx:ebx, zf <= 1 : edx:eax <= mem, zf <= 0
                        sz = (di.opcode.name =~ /8b/ ? 32 : 64)
                        eax_ = self.class::Reg.new(0, sz).symbolic
                        ecx_ = self.class::Reg.new(1, sz).symbolic
                        edx_ = self.class::Reg.new(2, sz).symbolic
                        ebx_ = self.class::Reg.new(3, sz).symbolic
                        cmp = Expression[[[edx_, :<<, sz], :|, eax_], :==, a0]
                        { :eflag_z => cmp,
                          eax_ => Expression[[cmp, :*, eax_], :|, [[1, :-, cmp], :*, [a0, :&, (1 << sz) - 1]]],
                          edx_ => Expression[[cmp, :*, edx_], :|, [[1, :-, cmp], :*, [a0, :>>, sz]]],
                          a0 => Expression[[cmp, :*, [[ecx_, :<<, sz], :|, ebx_]], :|, [[1, :-, cmp], :*, a0]] } }
                when 'nop', 'pause', 'wait', 'cmp', 'test'; lambda { |di, *a| {} }
                end

                # add eflags side-effects

                full_binding = case op
                when 'adc', 'add', 'and', 'cmp', 'or', 'sbb', 'sub', 'xor', 'test', 'xadd'
                        lambda { |di, a0, a1|
                                e_op = { 'adc' => :+, 'add' => :+, 'xadd' => :+, 'and' => :&, 'cmp' => :-, 'or' => :|, 'sbb' => :-, 'sub' => :-, 'xor' => :^, 'test' => :& }[op]
                                res = Expression[[a0, :&, mask[di]], e_op, [a1, :&, mask[di]]]
                                res = Expression[res, e_op, :eflag_c] if op == 'adc' or op == 'sbb'

                                ret = (binding ? binding[di, a0, a1] : {})
                                ret[:eflag_z] = Expression[[res, :&, mask[di]], :==, 0]
                                ret[:eflag_s] = sign[res, di]
                                ret[:eflag_c] = case e_op
                                        when :+; Expression[res, :>, mask[di]]
                                        when :-; Expression[[a0, :&, mask[di]], :<, [a1, :&, mask[di]]]
                                        else Expression[0]
                                        end
                                ret[:eflag_o] = case e_op
                                        when :+; Expression[[sign[a0, di], :==, sign[a1, di]], :'&&', [sign[a0, di], :'!=', sign[res, di]]]
                                        when :-; Expression[[sign[a0, di], :==, [:'!', sign[a1, di]]], :'&&', [sign[a0, di], :'!=', sign[res, di]]]
                                        else Expression[0]
                                        end
                                ret
                        }
                when 'inc', 'dec', 'neg', 'shl', 'shr', 'sal', 'sar', 'ror', 'rol', 'rcr', 'rcl', 'shld', 'shrd'
                        lambda { |di, a0, *a|
                                ret = (binding ? binding[di, a0, *a] : {})
                                res = ret[a0] || Expression::Unknown
                                ret[:eflag_z] = Expression[[res, :&, mask[di]], :==, 0]
                                ret[:eflag_s] = sign[res, di]
                                case op
                                when 'neg'; ret[:eflag_c] = Expression[[res, :&, mask[di]], :'!=', 0]
                                when 'inc', 'dec'  # don't touch carry flag
                                when 'shr', 'sar', 'shrd'; ret[:eflag_c] = Expression[[a0, :>>, [a[0], :-, 1]], :&, 1]     # XXX shr 0 => no touch flag
                                when 'shl', 'sal', 'shld'; ret[:eflag_c] = Expression[[a0, :>>, [di.instruction.args[0].sz, :-, a[0]]], :&, 1]
                                else ret[:eflag_c] = Expression::Unknown   # :incomplete_binding ?
                                end
                                ret[:eflag_o] = case op
                                when 'inc'; Expression[[a0, :&, mask[di]], :==, mask[di] >> 1]
                                when 'dec'; Expression[[res , :&, mask[di]], :==, mask[di] >> 1]
                                when 'neg'; Expression[[a0, :&, mask[di]], :==, (mask[di]+1) >> 1]
                                else Expression::Unknown
                                end
                                ret
                        }
                when 'imul', 'mul', 'idiv', 'div', /^(scas|cmps)[bwdq]$/
                        lambda { |di, *a|
                                ret = (binding ? binding[di, *a] : {})
                                ret[:eflag_z] ||= Expression::Unknown
                                ret[:eflag_s] ||= Expression::Unknown
                                ret[:eflag_c] ||= Expression::Unknown
                                ret[:eflag_o] ||= Expression::Unknown
                                # :incomplete_binding ?
                                ret
                        }
                end

                @backtrace_binding[op] ||= full_binding || binding if full_binding || binding
        }
        @backtrace_binding
end
init_cpu_constants() click to toggle source
# File metasm/cpu/ia32/opcodes.rb, line 11
def init_cpu_constants
        @opcode_list ||= []
        @fields_mask.update :w => 1, :s => 1, :d => 1, :modrm => 0xC7,
                :reg => 7, :eeec => 7, :eeed => 7, :eeet => 7, :seg2 => 3, :seg3 => 7,
                :regfp => 7, :regmmx => 7, :regxmm => 7, :regymm => 7,
                :vex_r => 1, :vex_b => 1, :vex_x => 1, :vex_w => 1,
                :vex_vvvv => 0xF
        @fields_mask[:seg2A]    = @fields_mask[:seg2]
        @fields_mask[:seg3A]    = @fields_mask[:seg3]

        [:i, :i8, :u8, :u16, :reg, :seg2, :seg2A,
         :seg3, :seg3A, :eeec, :eeed, :eeet, :modrm, :mrm_imm,
         :farptr, :imm_val1, :imm_val3, :reg_cl, :reg_eax,
         :reg_dx, :regfp, :regfp0, :modrmmmx, :regmmx,
         :modrmxmm, :regxmm, :modrmymm, :regymm,
         :vexvxmm, :vexvymm, :vexvreg, :i4xmm, :i4ymm
        ].each { |a| @valid_args[a] = true }

        [:strop, :stropz, :opsz, :adsz, :argsz, :setip,
         :stopexec, :saveip, :unsigned_imm, :random, :needpfx,
         :xmmx, :modrmR, :modrmA, :mrmvex
        ].each { |a| @valid_props[a] = true }
end
init_fma_only() click to toggle source
# File metasm/cpu/ia32/opcodes.rb, line 1006
def init_fma_only
        init_cpu_constants

        [['vfmaddsub', 'p', 0x86],
         ['vfmsubadd', 'p', 0x87],
         ['vfmadd',    'p', 0x88],
         ['vfmadd',    's', 0x89],
         ['vfmsub',    'p', 0x8A],
         ['vfmsub',    's', 0x8B],
         ['vfnmadd',   'p', 0x8C],
         ['vfnmadd',   's', 0x8D],
         ['vfnmsub',   'p', 0x8E],
         ['vfnmsub',   's', 0x8F]].each { |n1, n2, bin|
                addop_vex n1 + '132' + n2 + 's', [1, 128, 0x66, 0x0F38, 0], bin | 0x10, :mrmxmm
                addop_vex n1 + '132' + n2 + 's', [1, 256, 0x66, 0x0F38, 0], bin | 0x10, :mrmymm
                addop_vex n1 + '132' + n2 + 'd', [1, 128, 0x66, 0x0F38, 1], bin | 0x10, :mrmxmm
                addop_vex n1 + '132' + n2 + 'd', [1, 256, 0x66, 0x0F38, 1], bin | 0x10, :mrmymm
                addop_vex n1 + '213' + n2 + 's', [1, 128, 0x66, 0x0F38, 0], bin | 0x20, :mrmxmm
                addop_vex n1 + '213' + n2 + 's', [1, 256, 0x66, 0x0F38, 0], bin | 0x20, :mrmymm
                addop_vex n1 + '213' + n2 + 'd', [1, 128, 0x66, 0x0F38, 1], bin | 0x20, :mrmxmm
                addop_vex n1 + '213' + n2 + 'd', [1, 256, 0x66, 0x0F38, 1], bin | 0x20, :mrmymm
                addop_vex n1 + '231' + n2 + 's', [1, 128, 0x66, 0x0F38, 0], bin | 0x30, :mrmxmm
                addop_vex n1 + '231' + n2 + 's', [1, 256, 0x66, 0x0F38, 0], bin | 0x30, :mrmymm
                addop_vex n1 + '231' + n2 + 'd', [1, 128, 0x66, 0x0F38, 1], bin | 0x30, :mrmxmm
                addop_vex n1 + '231' + n2 + 'd', [1, 256, 0x66, 0x0F38, 1], bin | 0x30, :mrmymm

                # pseudo-opcodes aliases (swap arg0/arg1)
                addop_vex(n1 + '312' + n2 + 's', [1, 128, 0x66, 0x0F38, 0], bin | 0x10, :mrmxmm) { |o| o.args[0, 2] = o.args[0, 2].reverse }
                addop_vex(n1 + '312' + n2 + 's', [1, 256, 0x66, 0x0F38, 0], bin | 0x10, :mrmymm) { |o| o.args[0, 2] = o.args[0, 2].reverse }
                addop_vex(n1 + '312' + n2 + 'd', [1, 128, 0x66, 0x0F38, 1], bin | 0x10, :mrmxmm) { |o| o.args[0, 2] = o.args[0, 2].reverse }
                addop_vex(n1 + '312' + n2 + 'd', [1, 256, 0x66, 0x0F38, 1], bin | 0x10, :mrmymm) { |o| o.args[0, 2] = o.args[0, 2].reverse }
                addop_vex(n1 + '123' + n2 + 's', [1, 128, 0x66, 0x0F38, 0], bin | 0x20, :mrmxmm) { |o| o.args[0, 2] = o.args[0, 2].reverse }
                addop_vex(n1 + '123' + n2 + 's', [1, 256, 0x66, 0x0F38, 0], bin | 0x20, :mrmymm) { |o| o.args[0, 2] = o.args[0, 2].reverse }
                addop_vex(n1 + '123' + n2 + 'd', [1, 128, 0x66, 0x0F38, 1], bin | 0x20, :mrmxmm) { |o| o.args[0, 2] = o.args[0, 2].reverse }
                addop_vex(n1 + '123' + n2 + 'd', [1, 256, 0x66, 0x0F38, 1], bin | 0x20, :mrmymm) { |o| o.args[0, 2] = o.args[0, 2].reverse }
                addop_vex(n1 + '321' + n2 + 's', [1, 128, 0x66, 0x0F38, 0], bin | 0x30, :mrmxmm) { |o| o.args[0, 2] = o.args[0, 2].reverse }
                addop_vex(n1 + '321' + n2 + 's', [1, 256, 0x66, 0x0F38, 0], bin | 0x30, :mrmymm) { |o| o.args[0, 2] = o.args[0, 2].reverse }
                addop_vex(n1 + '321' + n2 + 'd', [1, 128, 0x66, 0x0F38, 1], bin | 0x30, :mrmxmm) { |o| o.args[0, 2] = o.args[0, 2].reverse }
                addop_vex(n1 + '321' + n2 + 'd', [1, 256, 0x66, 0x0F38, 1], bin | 0x30, :mrmymm) { |o| o.args[0, 2] = o.args[0, 2].reverse }
        }
end
init_latest()
Alias for: init_all
init_opcode_list() click to toggle source

initializes the @opcode_list according to @family

# File metasm/cpu/ia32/main.rb, line 235
def init_opcode_list
        send("init_#@family")
        @opcode_list
end
init_p6() click to toggle source
# File metasm/cpu/ia32/opcodes.rb, line 1081
def init_p6
        init_pentium
        init_p6_only
end
init_p6_only() click to toggle source
# File metasm/cpu/ia32/opcodes.rb, line 396
def init_p6_only
        addop_macrotttn 'cmov', [0x0F, 0x40], :mrm

        %w{b e be u}.each_with_index { |tt, i|
                addop 'fcmov' + tt, [0xDA, 0xC0 | (i << 3)], :regfp
                addop 'fcmovn'+ tt, [0xDB, 0xC0 | (i << 3)], :regfp
        }
        addop 'fcomi', [0xDB, 0xF0], :regfp
        addop('fxrstor', [0x0F, 0xAE, 1<<3], :modrmA) { |o| o.props[:argsz] = 512*8 }
        addop('fxsave',  [0x0F, 0xAE, 0<<3], :modrmA) { |o| o.props[:argsz] = 512*8 }
        addop 'sysenter',[0x0F, 0x34]
        addop 'sysexit', [0x0F, 0x35]

        addop 'syscall', [0x0F, 0x05] # AMD
        addop_macroret 'sysret', [0x0F, 0x07] # AMD
end
init_pentium() click to toggle source
# File metasm/cpu/ia32/opcodes.rb, line 1071
def init_pentium
        init_486
        init_pentium_only
end
init_pentium_only() click to toggle source
# File metasm/cpu/ia32/opcodes.rb, line 358
def init_pentium_only
        init_cpu_constants

        addop('cmpxchg8b', [0x0F, 0xC7], 1) { |o| o.props[:opsz] = 32 ; o.props[:argsz] = 64 }
        # lock cmpxchg8b eax
        #addop 'f00fbug', [0xF0, 0x0F, 0xC7, 0xC8]

        # mmx
        addop 'emms',  [0x0F, 0x77]
        addop('movd',  [0x0F, 0x6E], :mrmmmx, {:d => [1, 4]}) { |o| o.args = [:modrm, :regmmx] ; o.props[:opsz] = o.props[:argsz] = 32 }
        addop('movq',  [0x0F, 0x6F], :mrmmmx, {:d => [1, 4]}) { |o| o.props[:argsz] = 64 }
        addop 'packssdw', [0x0F, 0x6B], :mrmmmx
        addop 'packsswb', [0x0F, 0x63], :mrmmmx
        addop 'packuswb', [0x0F, 0x67], :mrmmmx
        addop_macrogg 0..2, 'padd',  [0x0F, 0xFC], :mrmmmx
        addop_macrogg 0..1, 'padds', [0x0F, 0xEC], :mrmmmx
        addop_macrogg 0..1, 'paddus',[0x0F, 0xDC], :mrmmmx
        addop 'pand',  [0x0F, 0xDB], :mrmmmx
        addop 'pandn', [0x0F, 0xDF], :mrmmmx
        addop_macrogg 0..2, 'pcmpeq',[0x0F, 0x74], :mrmmmx
        addop_macrogg 0..2, 'pcmpgt',[0x0F, 0x64], :mrmmmx
        addop 'pmaddwd', [0x0F, 0xF5], :mrmmmx
        addop 'pmulhuw', [0x0F, 0xE4], :mrmmmx
        addop 'pmulhw',[0x0F, 0xE5], :mrmmmx
        addop 'pmullw',[0x0F, 0xD5], :mrmmmx
        addop 'por',   [0x0F, 0xEB], :mrmmmx
        [[1..3, 'psll', 3], [1..2, 'psra', 2], [1..3, 'psrl', 1]].each { |ggrng, name, val|
                addop_macrogg ggrng, name, [0x0F, 0xC0 | (val << 4)], :mrmmmx
                addop_macrogg ggrng, name, [0x0F, 0x70, 0xC0 | (val << 4)], nil, {:regmmx => [2, 0]}, :regmmx, :u8
        }
        addop_macrogg 0..2, 'psub',  [0x0F, 0xF8], :mrmmmx
        addop_macrogg 0..1, 'psubs', [0x0F, 0xE8], :mrmmmx
        addop_macrogg 0..1, 'psubus',[0x0F, 0xD8], :mrmmmx
        addop_macrogg 1..3, 'punpckh', [0x0F, 0x68], :mrmmmx
        addop_macrogg 1..3, 'punpckl', [0x0F, 0x60], :mrmmmx
        addop 'pxor',  [0x0F, 0xEF], :mrmmmx
end
init_sse() click to toggle source
# File metasm/cpu/ia32/opcodes.rb, line 1086
def init_sse
        init_p6
        init_sse_only
end
init_sse2() click to toggle source
# File metasm/cpu/ia32/opcodes.rb, line 1091
def init_sse2
        init_sse
        init_sse2_only
end
init_sse2_only() click to toggle source
# File metasm/cpu/ia32/opcodes.rb, line 503
def init_sse2_only
        init_cpu_constants

        @opcode_list.each { |o| o.props[:xmmx] = true if o.fields[:regmmx] and o.name !~ /^(?:mov(?:nt)?q|pshufw|cvt.*)$/ }

        # mirror of the init_sse part
        addop_macrosdpd 'addpd', [0x0F, 0x58], :mrmxmm
        addop('andnpd',  [0x0F, 0x55], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('andpd',   [0x0F, 0x54], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop_macrosdpd 'cmppd', [0x0F, 0xC2], :mrmxmm, :u8
        addop('comisd',  [0x0F, 0x2F], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }

        addop('cvtpi2pd', [0x0F, 0x2A], :mrmxmm) { |o| o.args[o.args.index(:modrmxmm)] = :modrmmmx ; o.props[:needpfx] = 0x66 }
        addop('cvtpd2pi', [0x0F, 0x2D], :mrmmmx) { |o| o.args[o.args.index(:modrmmmx)] = :modrmxmm ; o.props[:needpfx] = 0x66 }
        addop('cvtsi2sd', [0x0F, 0x2A], :mrmxmm) { |o| o.args[o.args.index(:modrmxmm)] = :modrm    ; o.props[:needpfx] = 0xF2 }
        addop('cvtsd2si', [0x0F, 0x2D], :mrm   ) { |o| o.args[o.args.index(:modrm   )] = :modrmxmm ; o.props[:needpfx] = 0xF2 }
        addop('cvttpd2pi',[0x0F, 0x2C], :mrmmmx) { |o| o.args[o.args.index(:modrmmmx)] = :modrmxmm ; o.props[:needpfx] = 0x66 }
        addop('cvttsd2si',[0x0F, 0x2C], :mrm   ) { |o| o.args[o.args.index(:modrm   )] = :modrmxmm ; o.props[:needpfx] = 0xF2 }

        addop('cvtpd2ps', [0x0F, 0x5A], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('cvtps2pd', [0x0F, 0x5A], :mrmxmm)
        addop('cvtsd2ss', [0x0F, 0x5A], :mrmxmm) { |o| o.props[:needpfx] = 0xF2 }
        addop('cvtss2sd', [0x0F, 0x5A], :mrmxmm) { |o| o.props[:needpfx] = 0xF3 }

        addop('cvtpd2dq', [0x0F, 0xE6], :mrmxmm) { |o| o.props[:needpfx] = 0xF2 }
        addop('cvttpd2dq',[0x0F, 0xE6], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('cvtdq2pd', [0x0F, 0xE6], :mrmxmm) { |o| o.props[:needpfx] = 0xF3 }
        addop('cvtps2dq', [0x0F, 0x5B], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('cvttps2dq',[0x0F, 0x5B], :mrmxmm) { |o| o.props[:needpfx] = 0xF3 }
        addop('cvtdq2ps', [0x0F, 0x5B], :mrmxmm)

        addop_macrosdpd 'divpd', [0x0F, 0x5E], :mrmxmm
        addop_macrosdpd 'maxpd', [0x0F, 0x5F], :mrmxmm
        addop_macrosdpd 'minpd', [0x0F, 0x5D], :mrmxmm
        addop('movapd',  [0x0F, 0x28], :mrmxmm, {:d => [1, 0]}) { |o| o.props[:needpfx] = 0x66 }

        addop('movlpd',  [0x0F, 0x12], :mrmxmm, {:d => [1, 0]}) { |o| o.props[:needpfx] = 0x66 }
        addop('movhpd',  [0x0F, 0x16], :mrmxmm, {:d => [1, 0]}) { |o| o.props[:needpfx] = 0x66 }

        addop('movmskpd',[0x0F, 0x50, 0xC0], nil, {:reg => [2, 3], :regxmm => [2, 0]}, :regxmm, :reg) { |o| o.props[:needpfx] = 0x66 }
        addop('movsd',   [0x0F, 0x10], :mrmxmm, {:d => [1, 0]}) { |o| o.props[:needpfx] = 0xF2 }
        addop('movupd',  [0x0F, 0x10], :mrmxmm, {:d => [1, 0]}) { |o| o.props[:needpfx] = 0x66 }
        addop_macrosdpd 'mulpd', [0x0F, 0x59], :mrmxmm
        addop('orpd',    [0x0F, 0x56], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('shufpd',  [0x0F, 0xC6], :mrmxmm, :u8) { |o| o.props[:needpfx] = 0x66 }
        addop_macrosdpd 'sqrtpd', [0x0F, 0x51], :mrmxmm
        addop_macrosdpd 'subpd', [0x0F, 0x5C], :mrmxmm
        addop('ucomisd', [0x0F, 0x2E], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('unpckhpd',[0x0F, 0x15], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('unpcklpd',[0x0F, 0x14], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('xorpd',   [0x0F, 0x57], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }

        addop('movdqa',  [0x0F, 0x6F], :mrmxmm, {:d => [1, 4]}) { |o| o.props[:needpfx] = 0x66 }
        addop('movdqu',  [0x0F, 0x6F], :mrmxmm, {:d => [1, 4]}) { |o| o.props[:needpfx] = 0xF3 }
        addop('movq2dq', [0x0F, 0xD6], :mrmxmm, :modrmR) { |o| o.args[o.args.index(:modrmxmm)] = :modrmmmx ; o.props[:needpfx] = 0xF3 }
        addop('movdq2q', [0x0F, 0xD6], :mrmmmx, :modrmR) { |o| o.args[o.args.index(:modrmmmx)] = :modrmxmm ; o.props[:needpfx] = 0xF2 }
        addop('movq',    [0x0F, 0x7E], :mrmxmm) { |o| o.props[:needpfx] = 0xF3 ; o.props[:argsz] = 128 }
        addop('movq',    [0x0F, 0xD6], :mrmxmm) { |o| o.args.reverse! ; o.props[:needpfx] = 0x66 ; o.props[:argsz] = 128 }

        addop 'paddq',   [0x0F, 0xD4], :mrmmmx, :xmmx
        addop 'pmuludq', [0x0F, 0xF4], :mrmmmx, :xmmx
        addop('pshuflw', [0x0F, 0x70], :mrmxmm, :u8) { |o| o.props[:needpfx] = 0xF2 }
        addop('pshufhw', [0x0F, 0x70], :mrmxmm, :u8) { |o| o.props[:needpfx] = 0xF3 }
        addop('pshufd',  [0x0F, 0x70], :mrmxmm, :u8) { |o| o.props[:needpfx] = 0x66 }
        addop('pslldq',  [0x0F, 0x73, 0xF8], nil, {:regxmm => [2, 0]}, :regxmm, :u8) { |o| o.props[:needpfx] = 0x66 }
        addop('psrldq',  [0x0F, 0x73, 0xD8], nil, {:regxmm => [2, 0]}, :regxmm, :u8) { |o| o.props[:needpfx] = 0x66 }
        addop 'psubq',   [0x0F, 0xFB], :mrmmmx, :xmmx
        addop('punpckhqdq', [0x0F, 0x6D], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('punpcklqdq', [0x0F, 0x6C], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }

        addop('clflush', [0x0F, 0xAE, 7<<3], :modrmA) { |o| o.props[:argsz] = 8 }
        addop('maskmovdqu', [0x0F, 0xF7], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('movntpd', [0x0F, 0x2B], :mrmxmm) { |o| o.args.reverse! ; o.props[:needpfx] = 0x66 }
        addop('movntdq', [0x0F, 0xE7], :mrmxmm) { |o| o.args.reverse! ; o.props[:needpfx] = 0x66 }
        addop('movnti',  [0x0F, 0xC3], :mrm) { |o| o.args.reverse! }
        addop('pause',   [0x90]) { |o| o.props[:needpfx] = 0xF3 }
        addop 'lfence',  [0x0F, 0xAE, 0xE8]
        addop 'mfence',  [0x0F, 0xAE, 0xF0]
end
init_sse3() click to toggle source
# File metasm/cpu/ia32/opcodes.rb, line 1096
def init_sse3
        init_sse2
        init_sse3_only
end
init_sse3_only() click to toggle source
# File metasm/cpu/ia32/opcodes.rb, line 583
def init_sse3_only
        init_cpu_constants

        addop('addsubpd', [0x0F, 0xD0], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('addsubps', [0x0F, 0xD0], :mrmxmm) { |o| o.props[:needpfx] = 0xF2 }
        addop('haddpd',   [0x0F, 0x7C], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('haddps',   [0x0F, 0x7C], :mrmxmm) { |o| o.props[:needpfx] = 0xF2 }
        addop('hsubpd',   [0x0F, 0x7D], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('hsubps',   [0x0F, 0x7D], :mrmxmm) { |o| o.props[:needpfx] = 0xF2 }

        addop 'monitor',  [0x0F, 0x01, 0xC8]
        addop 'mwait',    [0x0F, 0x01, 0xC9]

        addop('fisttp',   [0xDF, 1<<3], :modrmA) { |o| o.props[:argsz] = 16 }
        addop('fisttp',   [0xDB, 1<<3], :modrmA) { |o| o.props[:argsz] = 32 }
        addop('fisttp',   [0xDD, 1<<3], :modrmA) { |o| o.props[:argsz] = 64 }
        addop('lddqu',    [0x0F, 0xF0], :mrmxmm, :modrmA) { |o| o.args[o.args.index(:modrmxmm)] = :modrm ; o.props[:needpfx] = 0xF2 }
        addop('movddup',  [0x0F, 0x12], :mrmxmm) { |o| o.props[:needpfx] = 0xF2 }
        addop('movshdup', [0x0F, 0x16], :mrmxmm) { |o| o.props[:needpfx] = 0xF3 }
        addop('movsldup', [0x0F, 0x12], :mrmxmm) { |o| o.props[:needpfx] = 0xF3 }
end
init_sse41() click to toggle source
# File metasm/cpu/ia32/opcodes.rb, line 1106
def init_sse41
        init_ssse3
        init_sse41_only
end
init_sse41_only() click to toggle source
# File metasm/cpu/ia32/opcodes.rb, line 660
def init_sse41_only
        init_cpu_constants

        addop('blendpd',  [0x0F, 0x3A, 0x0D], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('blendps',  [0x0F, 0x3A, 0x0C], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('blendvpd', [0x0F, 0x38, 0x15], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('blendvps', [0x0F, 0x38, 0x14], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('dppd',     [0x0F, 0x3A, 0x41], :mrmxmm, :u8) { |o| o.props[:needpfx] = 0x66 }
        addop('dpps',     [0x0F, 0x3A, 0x40], :mrmxmm, :u8) { |o| o.props[:needpfx] = 0x66 }
        addop('extractps',[0x0F, 0x3A, 0x17], :mrmxmm, :u8) { |o| o.props[:needpfx] = 0x66 }
        addop('insertps', [0x0F, 0x3A, 0x21], :mrmxmm, :u8) { |o| o.props[:needpfx] = 0x66 }
        addop('movntdqa', [0x0F, 0x38, 0x2A], :mrmxmm, :modrmA) { |o| o.props[:needpfx] = 0x66 }
        addop('mpsadbw',  [0x0F, 0x3A, 0x42], :mrmxmm, :u8) { |o| o.props[:needpfx] = 0x66 }
        addop('packusdw', [0x0F, 0x38, 0x2B], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('pblendvb', [0x0F, 0x38, 0x10], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('pblendw',  [0x0F, 0x3A, 0x1E], :mrmxmm, :u8) { |o| o.props[:needpfx] = 0x66 }
        addop('pcmpeqq',  [0x0F, 0x38, 0x29], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('pextrb', [0x0F, 0x3A, 0x14], :mrmxmm, :u8) { |o| o.props[:needpfx] = 0x66; o.args.index(:modrmxmm); o.args.unshift(:modrm); o.props[:argsz] = 8 }
        addop('pextrw', [0x0F, 0x3A, 0x15], :mrmxmm, :u8) { |o| o.props[:needpfx] = 0x66; o.args.index(:modrmxmm); o.args.unshift(:modrm); o.props[:argsz] = 16 }
        addop('pextrd', [0x0F, 0x3A, 0x16], :mrmxmm, :u8) { |o| o.props[:needpfx] = 0x66; o.args.index(:modrmxmm); o.args.unshift(:modrm); o.props[:argsz] = 32 }
        addop('pinsrb', [0x0F, 0x3A, 0x20], :mrmxmm, :u8) { |o| o.props[:needpfx] = 0x66; o.args[o.args.index(:modrmxmm)] = :modrm; o.props[:argsz] = 8 }
        addop('pinsrw', [0x0F, 0x3A, 0x21], :mrmxmm, :u8) { |o| o.props[:needpfx] = 0x66; o.args[o.args.index(:modrmxmm)] = :modrm; o.props[:argsz] = 16 }
        addop('pinsrd', [0x0F, 0x3A, 0x22], :mrmxmm, :u8) { |o| o.props[:needpfx] = 0x66; o.args[o.args.index(:modrmxmm)] = :modrm; o.props[:argsz] = 32 }
        addop('phminposuw', [0x0F, 0x38, 0x41], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('pminsb', [0x0F, 0x38, 0x38], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('pminsd', [0x0F, 0x38, 0x39], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('pminuw', [0x0F, 0x38, 0x3A], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('pminud', [0x0F, 0x38, 0x3B], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('pmaxsb', [0x0F, 0x38, 0x3C], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('pmaxsd', [0x0F, 0x38, 0x3D], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('pmaxuw', [0x0F, 0x38, 0x3E], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('pmaxud', [0x0F, 0x38, 0x3F], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }

        addop('pmovsxbw', [0x0F, 0x38, 0x20], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('pmovsxbd', [0x0F, 0x38, 0x21], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('pmovsxbq', [0x0F, 0x38, 0x22], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('pmovsxwd', [0x0F, 0x38, 0x23], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('pmovsxwq', [0x0F, 0x38, 0x24], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('pmovsxdq', [0x0F, 0x38, 0x25], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('pmovzxbw', [0x0F, 0x38, 0x30], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('pmovzxbd', [0x0F, 0x38, 0x31], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('pmovzxbq', [0x0F, 0x38, 0x32], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('pmovzxwd', [0x0F, 0x38, 0x33], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('pmovzxwq', [0x0F, 0x38, 0x34], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('pmovzxdq', [0x0F, 0x38, 0x35], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }

        addop('pmuldq',  [0x0F, 0x38, 0x28], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('pmulld',  [0x0F, 0x38, 0x40], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('ptest',   [0x0F, 0x38, 0x17], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('roundps', [0x0F, 0x3A, 0x08], :mrmxmm, :u8) { |o| o.props[:needpfx] = 0x66 }
        addop('roundpd', [0x0F, 0x3A, 0x09], :mrmxmm, :u8) { |o| o.props[:needpfx] = 0x66 }
        addop('roundss', [0x0F, 0x3A, 0x0A], :mrmxmm, :u8) { |o| o.props[:needpfx] = 0x66 }
        addop('roundsd', [0x0F, 0x3A, 0x0B], :mrmxmm, :u8) { |o| o.props[:needpfx] = 0x66 }
end
init_sse42() click to toggle source
# File metasm/cpu/ia32/opcodes.rb, line 1111
def init_sse42
        init_sse41
        init_sse42_only
end
init_sse42_only() click to toggle source
# File metasm/cpu/ia32/opcodes.rb, line 715
def init_sse42_only
        init_cpu_constants

        addop('crc32', [0x0F, 0x38, 0xF0], :mrmw) { |o| o.props[:needpfx] = 0xF2 }
        addop('pcmpestrm', [0x0F, 0x3A, 0x60], :mrmxmm, :i8) { |o| o.props[:needpfx] = 0x66 }
        addop('pcmpestri', [0x0F, 0x3A, 0x61], :mrmxmm, :i8) { |o| o.props[:needpfx] = 0x66 }
        addop('pcmpistrm', [0x0F, 0x3A, 0x62], :mrmxmm, :i8) { |o| o.props[:needpfx] = 0x66 }
        addop('pcmpistri', [0x0F, 0x3A, 0x63], :mrmxmm, :i8) { |o| o.props[:needpfx] = 0x66 }
        addop('pcmpgtq', [0x0F, 0x38, 0x37], :mrmxmm) { |o| o.props[:needpfx] = 0x66 }
        addop('popcnt', [0x0F, 0xB8], :mrm) { |o| o.props[:needpfx] = 0xF3 }
end
init_sse_only() click to toggle source
# File metasm/cpu/ia32/opcodes.rb, line 433
def init_sse_only
        init_cpu_constants

        addop_macrossps 'addps', [0x0F, 0x58], :mrmxmm
        addop 'andnps',  [0x0F, 0x55], :mrmxmm
        addop 'andps',   [0x0F, 0x54], :mrmxmm
        addop_macrossps 'cmpps', [0x0F, 0xC2], :mrmxmm, :u8
        addop 'comiss',  [0x0F, 0x2F], :mrmxmm

        addop('cvtpi2ps', [0x0F, 0x2A], :mrmxmm) { |o| o.args[o.args.index(:modrmxmm)] = :modrmmmx }
        addop('cvtps2pi', [0x0F, 0x2D], :mrmmmx) { |o| o.args[o.args.index(:modrmmmx)] = :modrmxmm }
        addop('cvtsi2ss', [0x0F, 0x2A], :mrmxmm) { |o| o.args[o.args.index(:modrmxmm)] = :modrm ; o.props[:needpfx] = 0xF3 }
        addop('cvtss2si', [0x0F, 0x2D], :mrm)    { |o| o.args[o.args.index(:modrm)] = :modrmxmm ; o.props[:needpfx] = 0xF3 }
        addop('cvttps2pi',[0x0F, 0x2C], :mrmmmx) { |o| o.args[o.args.index(:modrmmmx)] = :modrmxmm }
        addop('cvttss2si',[0x0F, 0x2C], :mrm)    { |o| o.args[o.args.index(:modrm)] = :modrmxmm ; o.props[:needpfx] = 0xF3 }

        addop_macrossps 'divps', [0x0F, 0x5E], :mrmxmm
        addop 'ldmxcsr', [0x0F, 0xAE, 2<<3], :modrmA
        addop_macrossps 'maxps', [0x0F, 0x5F], :mrmxmm
        addop_macrossps 'minps', [0x0F, 0x5D], :mrmxmm
        addop 'movaps',  [0x0F, 0x28], :mrmxmm, {:d => [1, 0]}
        addop 'movhlps', [0x0F, 0x12], :mrmxmm, :modrmR
        addop 'movlps',  [0x0F, 0x12], :mrmxmm, {:d => [1, 0]}, :modrmA
        addop 'movlhps', [0x0F, 0x16], :mrmxmm, :modrmR
        addop 'movhps',  [0x0F, 0x16], :mrmxmm, {:d => [1, 0]}, :modrmA
        addop 'movmskps',[0x0F, 0x50, 0xC0], nil, {:reg => [2, 3], :regxmm => [2, 0]}, :regxmm, :reg
        addop('movss',   [0x0F, 0x10], :mrmxmm, {:d => [1, 0]}) { |o| o.props[:needpfx] = 0xF3 }
        addop 'movups',  [0x0F, 0x10], :mrmxmm, {:d => [1, 0]}
        addop_macrossps 'mulps', [0x0F, 0x59], :mrmxmm
        addop 'orps',    [0x0F, 0x56], :mrmxmm
        addop_macrossps 'rcpps',  [0x0F, 0x53], :mrmxmm
        addop_macrossps 'rsqrtps',[0x0F, 0x52], :mrmxmm
        addop 'shufps',  [0x0F, 0xC6], :mrmxmm, :u8
        addop_macrossps 'sqrtps', [0x0F, 0x51], :mrmxmm
        addop 'stmxcsr', [0x0F, 0xAE, 3<<3], :modrmA
        addop_macrossps 'subps', [0x0F, 0x5C], :mrmxmm
        addop 'ucomiss', [0x0F, 0x2E], :mrmxmm
        addop 'unpckhps',[0x0F, 0x15], :mrmxmm
        addop 'unpcklps',[0x0F, 0x14], :mrmxmm
        addop 'xorps',   [0x0F, 0x57], :mrmxmm

        # integer instrs, mmx only
        addop 'pavgb',   [0x0F, 0xE0], :mrmmmx
        addop 'pavgw',   [0x0F, 0xE3], :mrmmmx
        addop 'pextrw',  [0x0F, 0xC5, 0xC0], nil, {:reg => [2, 3], :regmmx => [2, 0]}, :reg, :regmmx, :u8
        addop 'pinsrw',  [0x0F, 0xC4, 0x00], nil, {:modrm => [2, 0], :regmmx => [2, 3]}, :regmmx, :modrm, :u8
        addop 'pmaxsw',  [0x0F, 0xEE], :mrmmmx
        addop 'pmaxub',  [0x0F, 0xDE], :mrmmmx
        addop 'pminsw',  [0x0F, 0xEA], :mrmmmx
        addop 'pminub',  [0x0F, 0xDA], :mrmmmx
        addop 'pmovmskb',[0x0F, 0xD7, 0xC0], nil, {:reg => [2, 3], :regmmx => [2, 0]}, :reg, :regmmx
        addop 'psadbw',  [0x0F, 0xF6], :mrmmmx
        addop 'pshufw',  [0x0F, 0x70], :mrmmmx, :u8

        addop 'maskmovq',[0x0F, 0xF7], :mrmmmx, :modrmR
        addop('movntq',  [0x0F, 0xE7], :mrmmmx) { |o| o.args.reverse! }
        addop('movntps', [0x0F, 0x2B], :mrmxmm) { |o| o.args.reverse! }
        addop 'prefetcht0', [0x0F, 0x18, 1<<3], :modrmA
        addop 'prefetcht1', [0x0F, 0x18, 2<<3], :modrmA
        addop 'prefetcht2', [0x0F, 0x18, 3<<3], :modrmA
        addop 'prefetchnta',[0x0F, 0x18, 0<<3], :modrmA
        addop 'sfence',  [0x0F, 0xAE, 0xF8]

        # the whole row of prefetch is actually nops
        addop 'nop', [0x0F, 0x1C], :mrmw, :d => [1, 1]        # incl. official version = 0f1f mrm
        addop 'nop_8', [0x0F, 0x18], :mrmw, :d => [1, 1]
        addop 'nop_d', [0x0F, 0x0D], :mrm
        addop 'nop', [0x0F, 0x1C], 0  # official asm syntax is 'nop [eax]'
end
init_ssse3() click to toggle source
# File metasm/cpu/ia32/opcodes.rb, line 1101
def init_ssse3
        init_sse3
        init_ssse3_only
end
init_ssse3_only() click to toggle source
# File metasm/cpu/ia32/opcodes.rb, line 605
def init_ssse3_only
        init_cpu_constants

        addop_macrogg 0..2, 'pabs', [0x0F, 0x38, 0x1C], :mrmmmx, :xmmx
        addop 'palignr',  [0x0F, 0x3A, 0x0F], :mrmmmx, :u8, :xmmx
        addop 'phaddd',   [0x0F, 0x38, 0x02], :mrmmmx, :xmmx
        addop 'phaddsw',  [0x0F, 0x38, 0x03], :mrmmmx, :xmmx
        addop 'phaddw',   [0x0F, 0x38, 0x01], :mrmmmx, :xmmx
        addop 'phsubd',   [0x0F, 0x38, 0x06], :mrmmmx, :xmmx
        addop 'phsubsw',  [0x0F, 0x38, 0x07], :mrmmmx, :xmmx
        addop 'phsubw',   [0x0F, 0x38, 0x05], :mrmmmx, :xmmx
        addop 'pmaddubsw',[0x0F, 0x38, 0x04], :mrmmmx, :xmmx
        addop 'pmulhrsw', [0x0F, 0x38, 0x0B], :mrmmmx, :xmmx
        addop 'pshufb',   [0x0F, 0x38, 0x00], :mrmmmx, :xmmx
        addop_macrogg 0..2, 'psignb', [0x0F, 0x38, 0x80], :mrmmmx, :xmmx
end
init_vmx_only() click to toggle source
# File metasm/cpu/ia32/opcodes.rb, line 635
def init_vmx_only
        init_cpu_constants

        addop 'vmcall',   [0x0F, 0x01, 0xC1]
        addop 'vmlaunch', [0x0F, 0x01, 0xC2]
        addop 'vmresume', [0x0F, 0x01, 0xC3]
        addop 'vmxoff',   [0x0F, 0x01, 0xC4]
        addop 'vmread',   [0x0F, 0x78], :mrm
        addop 'vmwrite',  [0x0F, 0x79], :mrm
        addop('vmclear',  [0x0F, 0xC7, 6<<3], :modrmA) { |o| o.props[:argsz] = 64 ; o.props[:needpfx] = 0x66 }
        addop('vmxon',    [0x0F, 0xC7, 6<<3], :modrmA) { |o| o.props[:argsz] = 64 ; o.props[:needpfx] = 0xF3 }
        addop('vmptrld',  [0x0F, 0xC7, 6<<3], :modrmA) { |o| o.props[:argsz] = 64 }
        addop('vmptrst',  [0x0F, 0xC7, 7<<3], :modrmA) { |o| o.props[:argsz] = 64 }
        addop('invept',   [0x0F, 0x38, 0x80], :mrmA) { |o| o.props[:needpfx] = 0x66 }
        addop('invvpid',  [0x0F, 0x38, 0x81], :mrmA) { |o| o.props[:needpfx] = 0x66 }

        addop 'getsec',   [0x0F, 0x37]

        addop 'xgetbv', [0x0F, 0x01, 0xD0]
        addop 'xsetbv', [0x0F, 0x01, 0xD1]
        addop 'rdtscp', [0x0F, 0x01, 0xF9]
        addop 'xrstor', [0x0F, 0xAE, 5<<3], :modrmA
        addop 'xsave',  [0x0F, 0xAE, 4<<3], :modrmA
end
instr_args_memoryptr(i) click to toggle source

returns the list of ModRMs in the instruction arguments may be converted into Indirection through ModRM#symbolic

# File metasm/cpu/ia32/main.rb, line 266
def instr_args_memoryptr(i)
        i = i.instruction if i.kind_of?(DecodedInstruction)
        i.args.grep(ModRM)
end
instr_args_memoryptr_getbase(mrm) click to toggle source

return the 'base' of the ModRM (Reg/nil)

# File metasm/cpu/ia32/main.rb, line 272
def instr_args_memoryptr_getbase(mrm)
        mrm.b || (mrm.i if mrm.s == 1)
end
instr_args_memoryptr_getoffset(mrm) click to toggle source

return the offset of the ModRM (Expression/nil)

# File metasm/cpu/ia32/main.rb, line 277
def instr_args_memoryptr_getoffset(mrm)
        mrm.imm
end
instr_args_memoryptr_setoffset(mrm, imm) click to toggle source

define ModRM offset (eg to changing imm into an ExpressionString)

# File metasm/cpu/ia32/main.rb, line 282
def instr_args_memoryptr_setoffset(mrm, imm)
        mrm.imm = (imm ? Expression[imm] : imm)
end
instr_args_regs(i) click to toggle source

returns the list of Regs in the instruction arguments may be converted into symbols through Reg#symbolic

# File metasm/cpu/ia32/main.rb, line 259
def instr_args_regs(i)
        i = i.instruction if i.kind_of?(DecodedInstruction)
        i.args.grep(Reg)
end
instr_jump_stop() click to toggle source
# File metasm/cpu/ia32/parse.rb, line 361
def instr_jump_stop
        parse_instruction("hlt")
end
instr_uncond_jump_to(target) click to toggle source
# File metasm/cpu/ia32/parse.rb, line 357
def instr_uncond_jump_to(target)
        parse_instruction("jmp #{target}")
end
instruction_context(i) click to toggle source
# File metasm/cpu/ia32/render.rb, line 79
def instruction_context(i)
        # XXX
        h = {}
        op = opcode_list_byname[i.opname].first
        if i.prefix and i.prefix[:rep]
                h['toogle repz'] = lambda { i.prefix[:rep] = {'repnz' => 'repz', 'repz' => 'repnz'}[i.prefix[:rep]] } if op.props[:stropz]
                h['rm rep']      = lambda { i.prefix.delete :rep }
        else
                h['set rep']     = lambda { (i.prefix ||= {})[:rep] = 'rep'  } if op.props[:strop]
                h['set rep']     = lambda { (i.prefix ||= {})[:rep] = 'repz' } if op.props[:stropz]
        end
        if i.args.find { |a| a.kind_of? ModRM and a.seg }
                h['rm seg'] = lambda { i.args.find { |a| a.kind_of? ModRM and a.seg }.seg = nil }
        end
        h['toggle lock'] = lambda { (i.prefix ||= {})[:lock] = !i.prefix[:lock] }
        h
end
name_local_vars(dasm, funcaddr) click to toggle source

trace the stack pointer register across a function, rename occurences of esp+XX to esp+var_XX

# File metasm/cpu/ia32/decode.rb, line 1341
def name_local_vars(dasm, funcaddr)
        esp = register_symbols[4]
        func = dasm.function[funcaddr]
        subs = []
        dasm.trace_function_register(funcaddr, esp => 0) { |di, r, off, trace|
                next if r.to_s =~ /flag/
                if di.opcode.name == 'call' and tf = di.block.to_normal.find { |t| dasm.function[t] and dasm.function[t].localvars }
                        subs << [trace[esp], dasm.function[tf].localvars]
                end
                di.instruction.args.grep(ModRM).each { |mrm|
                        b = mrm.b || (mrm.i if mrm.s == 1)
                        # its a modrm => b is read, so ignore r/off (not yet applied), use trace only
                        stackoff = trace[b.symbolic] if b
                        next if not stackoff
                        imm = mrm.imm || Expression[0]
                        frameoff = imm + stackoff
                        if frameoff.kind_of?(::Integer)
                                # XXX register args ? non-ABI standard register args ? (eg optimized x64)
                                str = 'var_%X' % (-frameoff)
                                str = 'arg_%X' % (frameoff-@size/8) if frameoff > 0
                                str = func.get_localvar_stackoff(frameoff, di, str) if func
                                imm = imm.expr if imm.kind_of?(ExpressionString)
                                mrm.imm = ExpressionString.new(imm, str, :stackvar)
                        end
                }
                off = off.reduce if off.kind_of?(Expression)
                next unless off.kind_of?(Integer)
                off
        }
        # if subfunctions are called at a fixed stack offset, rename var_3c -> subarg_0
        if func and func.localvars and not subs.empty? and subs.all? { |sb| sb[0] == subs.first[0] }
                func.localvars.each { |varoff, varname|
                        subargnames = subs.map { |o, sb| sb[varoff-o+@size/8] }.compact
                        if subargnames.uniq.length == 1
                                varname.replace 'sub'+subargnames[0]
                        end
                }
        end
end
new_ccompiler(parser, exe=ExeFormat.new) click to toggle source
# File metasm/cpu/ia32/compile_c.rb, line 1532
def new_ccompiler(parser, exe=ExeFormat.new)
        exe.cpu = self if not exe.instance_variable_get("@cpu")
        CCompiler.new(parser, exe)
end
opsz(di, op=nil) click to toggle source
# File metasm/cpu/ia32/decode.rb, line 326
def opsz(di, op=nil)
        if di and di.instruction.prefix and di.instruction.prefix[:opsz] and (op || di.opcode).props[:needpfx] != 0x66; 48-@size
        else @size
        end
end
parse_arg_valid?(o, spec, arg) click to toggle source

check if the argument matches the opcode's argument spec

# File metasm/cpu/ia32/parse.rb, line 241
def parse_arg_valid?(o, spec, arg)
        if o.name ==  'movsx' or o.name == 'movzx'
                if not arg.kind_of?(Reg) and not arg.kind_of?(ModRM)
                        return
                elsif not arg.sz
                        puts "ambiguous arg size for indirection in #{o.name}" if $VERBOSE
                        return
                elsif spec == :reg   # reg=dst, modrm=src (smaller)
                        return (arg.kind_of?(Reg) and arg.sz >= 16)
                elsif o.props[:argsz]
                        return arg.sz == o.props[:argsz]
                else
                        return arg.sz == 16
                end
        elsif o.name == 'crc32'
                if not arg.kind_of?(Reg) and not arg.kind_of?(ModRM)
                        return
                elsif not arg.sz
                        puts "ambiguous arg size for indirection in #{o.name}" if $VERBOSE
                        return
                elsif spec == :reg
                        return (arg.kind_of?(Reg) and arg.sz >= 32)
                elsif o.props[:argsz]
                        return arg.sz == o.props[:argsz]
                else
                        return arg.sz >= 16
                end
        end

        return false if arg.kind_of? ModRM and arg.adsz and o.props[:adsz] and arg.adsz != o.props[:adsz]

        cond = true
        if s = o.props[:argsz] and (arg.kind_of? Reg or arg.kind_of? ModRM)
                cond = (!arg.sz or arg.sz == s or spec == :reg_dx)
        end

        cond and
        case spec
        when :reg; arg.kind_of? Reg and (arg.sz >= 16 or o.props[:argsz])
        when :modrm; (arg.kind_of? ModRM or arg.kind_of? Reg) and (!arg.sz or arg.sz >= 16 or o.props[:argsz]) and (!o.props[:modrmA] or arg.kind_of? ModRM) and (!o.props[:modrmR] or arg.kind_of? Reg)
        when :i;        arg.kind_of? Expression
        when :imm_val1; arg.kind_of? Expression and arg.reduce == 1
        when :imm_val3; arg.kind_of? Expression and arg.reduce == 3
        when :reg_eax;  arg.kind_of? Reg     and arg.val == 0
        when :reg_cl;   arg.kind_of? Reg     and arg.val == 1 and arg.sz == 8
        when :reg_dx;   arg.kind_of? Reg     and arg.val == 2 and arg.sz == 16
        when :seg3;     arg.kind_of? SegReg
        when :seg3A;    arg.kind_of? SegReg  and arg.val > 3
        when :seg2;     arg.kind_of? SegReg  and arg.val < 4
        when :seg2A;    arg.kind_of? SegReg  and arg.val < 4 and arg.val != 1
        when :eeec;     arg.kind_of? CtrlReg
        when :eeed;     arg.kind_of? DbgReg
        when :eeet;     arg.kind_of? TstReg
        when :mrm_imm;  arg.kind_of? ModRM   and not arg.s and not arg.i and not arg.b
        when :farptr;   arg.kind_of? Farptr
        when :regfp;    arg.kind_of? FpReg
        when :regfp0;   arg.kind_of? FpReg   and (arg.val == nil or arg.val == 0)
        when :modrmmmx; arg.kind_of? ModRM   or (arg.kind_of? SimdReg and (arg.sz == 64 or (arg.sz == 128 and o.props[:xmmx]))) and (!o.props[:modrmA] or arg.kind_of? ModRM) and (!o.props[:modrmR] or arg.kind_of? SimdReg)
        when :regmmx;   arg.kind_of? SimdReg and (arg.sz == 64 or (arg.sz == 128 and o.props[:xmmx]))
        when :modrmxmm; arg.kind_of? ModRM   or (arg.kind_of? SimdReg and arg.sz == 128) and (!o.props[:modrmA] or arg.kind_of? ModRM) and (!o.props[:modrmR] or arg.kind_of? SimdReg)
        when :regxmm;   arg.kind_of? SimdReg and arg.sz == 128
        when :modrmymm; arg.kind_of? ModRM   or (arg.kind_of? SimdReg and arg.sz == 256) and (!o.props[:modrmA] or arg.kind_of? ModRM) and (!o.props[:modrmR] or arg.kind_of? SimdReg)
        when :regymm;   arg.kind_of? SimdReg and arg.sz == 256

        when :vexvreg;  arg.kind_of? Reg and arg.sz == @size
        when :vexvxmm, :i4xmm;  arg.kind_of? SimdReg and arg.sz == 128
        when :vexvymm, :i4ymm;  arg.kind_of? SimdReg and arg.sz == 256

        when :i8, :u8, :u16
                arg.kind_of? Expression and
                (o.props[:setip] or Expression.in_range?(arg, spec) != false)        # true or nil allowed
                # jz 0x28282828 may fit in :i8 depending on instr addr
        else raise EncodeError, "Internal error: unknown argument specification #{spec.inspect}"
        end
end
parse_argregclasslist() click to toggle source
# File metasm/cpu/ia32/parse.rb, line 179
def parse_argregclasslist
        [Reg, SimdReg, SegReg, DbgReg, TstReg, CtrlReg, FpReg]
end
parse_argument(lexer) click to toggle source

parses an arbitrary ia32 instruction argument

# File metasm/cpu/ia32/parse.rb, line 187
def parse_argument(lexer)
        lexer = AsmPreprocessor.new(lexer) if lexer.kind_of? String

        # reserved names (registers/segments etc)
        @args_token ||= parse_argregclasslist.map { |a| a.s_to_i.keys }.flatten.inject({}) { |h, e| h.update e => true }

        lexer.skip_space
        return if not tok = lexer.readtok

        if tok.type == :string and tok.raw == 'ST'
                lexer.skip_space
                if ntok = lexer.readtok and ntok.type == :punct and ntok.raw == '('
                        lexer.skip_space
                        if not nntok = lexer.readtok or nntok.type != :string or nntok.raw !~ /^[0-9]$/ or
                                        not ntok = (lexer.skip_space; lexer.readtok) or ntok.type != :punct or ntok.raw != ')'
                                raise tok, 'invalid FP register'
                        else
                                tok.raw << '(' << nntok.raw << ')'
                                fpr = parse_argregclasslist.last
                                if fpr.s_to_i.has_key? tok.raw
                                        return fpr.new(fpr.s_to_i[tok.raw])
                                else
                                        raise tok, 'invalid FP register'
                                end
                        end
                else
                        lexer.unreadtok ntok
                end
        end

        if ret = parse_modrm(lexer, tok, self)
                ret
        elsif @args_token[tok.raw]
                parse_argregclasslist.each { |a|
                        return a.from_str(tok.raw) if a.s_to_i.has_key? tok.raw
                }
                raise tok, 'internal error'
        else
                lexer.unreadtok tok
                expr = Expression.parse(lexer)
                lexer.skip_space

                # may be a farptr
                if expr and ntok = lexer.readtok and ntok.type == :punct and ntok.raw == ':'
                        raise tok, 'invalid farptr' if not addr = Expression.parse(lexer)
                        Farptr.new expr, addr
                else
                        lexer.unreadtok ntok
                        Expression[expr.reduce] if expr
                end
        end
end
parse_instruction_checkproto(i) click to toggle source
# File metasm/cpu/ia32/parse.rb, line 317
def parse_instruction_checkproto(i)
        case i.opname
        when 'imul'
                if i.args.length == 2 and i.args.first.kind_of? Reg and i.args.last.kind_of? Expression
                        i.args.unshift i.args.first.dup
                end
        end
        super(i)
end
parse_instruction_fixup(i) click to toggle source

fixup the sz of a modrm argument, defaults to other argument size or current cpu mode

# File metasm/cpu/ia32/parse.rb, line 328
def parse_instruction_fixup(i)
        if m = i.args.grep(ModRM).first and not m.sz
                if i.opname == 'movzx' or i.opname == 'movsx'
                        m.sz = 8
                else
                        if r = i.args.grep(Reg).first
                                m.sz = r.sz
                        elsif l = opcode_list_byname[i.opname].map { |o| o.props[:argsz] }.uniq and l.length == 1 and l.first
                                m.sz = l.first
                        else
                                # this is also the size of ctrlreg/dbgreg etc
                                # XXX fpu/simd ?
                                m.sz = i.prefix[:sz] || @size
                        end
                end
        end
        if m and not m.adsz
                if opcode_list_byname[i.opname].all? { |o| o.props[:adsz] }
                        m.adsz = opcode_list_byname[i.opname].first.props[:adsz]
                else
                        m.adsz = i.prefix[:sz] || @size
                end
        end
end
parse_modrm(lex, tok, cpu) click to toggle source
# File metasm/cpu/ia32/parse.rb, line 182
def parse_modrm(lex, tok, cpu)
        ModRM.parse(lex, tok, cpu)
end
parse_parser_instruction(lexer, instr) click to toggle source

handles cpu-specific parser instruction, falls back to Ancestor's version if unknown keyword XXX changing the cpu size in the middle of the code may have baaad effects…

Calls superclass method Metasm::CPU#parse_parser_instruction
# File metasm/cpu/ia32/parse.rb, line 148
def parse_parser_instruction(lexer, instr)
        case instr.raw.downcase
        when '.mode', '.bits'
                lexer.skip_space
                if tok = lexer.readtok and tok.type == :string and (tok.raw == '16' or tok.raw == '32')
                        @size = tok.raw.to_i
                        lexer.skip_space
                        raise instr, 'syntax error' if ntok = lexer.nexttok and ntok.type != :eol
                else
                        raise instr, 'invalid cpu mode'
                end
        else super(lexer, instr)
        end
end
parse_prefix(i, pfx) click to toggle source
# File metasm/cpu/ia32/parse.rb, line 163
def parse_prefix(i, pfx)
        # implicit 'true' return value when assignment occur
        i.prefix ||= {}
        case pfx
        when 'lock'; i.prefix[:lock] = true
        when 'rep';            i.prefix[:rep] = 'rep'
        when 'repe', 'repz';   i.prefix[:rep] = 'repz'
        when 'repne', 'repnz'; i.prefix[:rep] = 'repnz'
        when 'code16';         i.prefix[:sz] = 16
        when 'code32';         i.prefix[:sz] = 32
        when 'hintjmp', 'ht';  i.prefix[:jmphint] = 'hintjmp'
        when 'hintnojmp', 'hnt';i.prefix[:jmphint] = 'hintnojmp'
        when /^seg_([c-g]s)$/; i.prefix[:seg] = SegReg.new(SegReg.s_to_i[$1])
        end
end
register_symbols() click to toggle source
# File metasm/cpu/ia32/decode.rb, line 296
def register_symbols
        REG_SYMS
end
render() click to toggle source
# File metasm/cpu/ia32/render.rb, line 18
def render ; [self.class.i_to_s[@val]] end
render_instruction(i) click to toggle source
# File metasm/cpu/ia32/render.rb, line 61
def render_instruction(i)
        r = []
        if pfx = i.prefix
                r << 'lock ' if pfx[:lock]
                r << pfx[:rep] << ' ' if pfx[:rep]
                r << pfx[:jmphint] << ' ' if pfx[:jmphint]
                r << 'seg_' << pfx[:seg] << ' ' if pfx[:seg]
        end
        r << i.opname
        sep = ' '
        i.args.each { |a|
                a.instruction = i if a.kind_of? ModRM
                r << sep << a
                sep = ', '
        }
        r
end
replace_instr_arg_immediate(i, old, new) click to toggle source

updates an instruction's argument replacing an expression with another (eg label renamed)

# File metasm/cpu/ia32/decode.rb, line 1016
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]
                when ModRM
                        a.imm = (a.imm == old ? new : Expression[a.imm.bind(old => new).reduce]) if a.imm
                        a
                else a
                end
        }
end
shortname() click to toggle source
# File metasm/cpu/ia32/main.rb, line 286
def shortname
        "ia32#{'_16' if @size == 16}#{'_be' if @endianness == :big}"
end
str_to_reg(str) click to toggle source

returns a Reg/SimdReg object if the arg is a valid register (eg 'ax' => Reg.new(0, 16)) returns nil if str is invalid

# File metasm/cpu/ia32/main.rb, line 253
def str_to_reg(str)
        Reg.s_to_i.has_key?(str) ? Reg.from_str(str) : SimdReg.s_to_i.has_key?(str) ? SimdReg.from_str(str) : nil
end
tune_prepro(pp, nodefine = false) click to toggle source

defines some preprocessor macros to say who we are: _M_IX86 = 500, X86, i386 pass any value in nodefine to just call super w/o defining anything of our own

Calls superclass method Metasm::CPU#tune_prepro
# File metasm/cpu/ia32/main.rb, line 243
def tune_prepro(pp, nodefine = false)
        super(pp)
        return if nodefine
        pp.define_weak('_M_IX86', 500)
        pp.define_weak('_X86_')
        pp.define_weak('__i386__')
end